Skip to content

Editor views and associations

Lapis resolves file-backed views with a VS Code-inspired association model. As a plugin author, you register editor metadata and view constructors so the workspace can route files to your editor.

APIPurpose
registerView(viewType, factory)Register the view class constructor
registerEditorView({ id, label, filenamePatterns, ... })Register selectable editor metadata
registerExtensions(extensions, viewType)Map file extensions to a view type (legacy fallback)

All three are typically needed for a new file-backed editor.

The bundled LangCode plugin registers JavaScript support like this:

this.registerView(
"javascript",
(leaf) => new TextView(leaf, "javascript", ["js"]),
);
this.registerEditorView({
id: "javascript",
label: "JavaScript",
filenamePatterns: ["*.js"],
priority: "default",
});
this.registerExtensions(["js"], "javascript");
this.registerEditorExtension(markupEditor(javascript()), "javascript");

Repeat the pattern for each language or file flavor your plugin owns.

When a file opens, Lapis checks in order:

  1. User or workspace editor associationsworkspace.editorAssociations glob → editor-view ID map
  2. Registered editor-view filename patterns — from registerEditorView() or manifest contributes.editorViews
  3. Registered extension fallbacks — from registerExtensions(); longest matching suffix wins

Examples users see today:

  • .md → Markdown view
  • .notebook.md → Notebook view (longer suffix beats .md)
  • .canvas → Canvas view
  • .base / .bases → Bases view

Users configure associations in Settings under Workspace → Editor associations. Keys are glob patterns; values are editor-view IDs from the registry.

Associations persist even when a plugin is disabled. Unknown saved values stay visible as missing rather than being reset, so reinstalling a plugin restores the previous mapping.

You can declare editor-view metadata in lapis.contributes.editorViews so Settings lists your editor before code runs:

{
"id": "example.editor",
"label": "Example editor",
"filenamePatterns": ["*.example"],
"priority": "default"
}

Manifest metadata alone does not open files in your editor. You must still:

  1. Register the view constructor with registerView()
  2. Map extensions with registerExtensions() when needed
  3. Register CodeMirror or custom UI extensions for text-backed views

File-backed editors extend the view hierarchy:

View
└── ItemView
└── FileView
└── TextFileView (CodeMirror)
└── MarkdownView (preview modes)

Graphical editors such as Canvas use FileView directly without CodeMirror. Choose the base class that matches your file format.

Editor-view metadata accepts priority:

  • default — normal selectable editor
  • option — appears as an alternate editor users can pick
  • exclusive — intended to own the file type when registered

Use option when multiple editors can open the same extension (for example Markdown source vs notebook).

The bundled notebook plugin registers .notebook.md as a first-class editor view with its own runtime, side panels, and generated output state. Plugin authors usually do not reimplement notebook execution; instead:

  • register editor associations or alternate views only when your plugin owns a distinct file type
  • use the documented lapis.* notebook surface from user-facing docs when explaining cell behavior to vault authors

For end-user notebook workflows, see the Notebook help section.

When adding a new file-backed editor:

  1. Pick a stable viewType string and editor-view id
  2. Implement and register the view constructor
  3. Register editor-view metadata with filenamePatterns
  4. Map extensions with registerExtensions()
  5. Register editor extensions if the view uses CodeMirror
  6. Optionally declare contributes.editorViews in the manifest
  7. Document recommended glob patterns for users who need associations