Skip to content

22. Editor Integration

Most of the time you author .arch files in an editor, not from a CLI. This chapter is about what the editor extensions actually do — what completions you get, what hover shows, what code actions appear, where the inline preview lives, and how to set things up if you’re using an editor that isn’t on the first-class list.

Two extensions are first-class:

  • VS Code — search for “ArchLang” in the marketplace.
  • JetBrains (IDEA, WebStorm, GoLand, PyCharm, RubyMine, Rider, CLion) — search for “ArchLang” in the JetBrains plugin marketplace.

Both bundle the same language server (@archlang/lsp). Feature parity is the goal; the two are kept in step.

Once the extension is installed and you open a .arch file, the language server activates. From that moment on:

Type micro and the completion list shows service (and any other registered types matching that prefix). After the type keyword, completion mints a fresh stable ID for you — a truly-random, meaningless token, exactly the kind you want and exactly the kind that’s hard to type by hand. Always let autocomplete generate the ID rather than hand-writing one (Chapter 13). Inside a body, completion knows what fields the type provides, flagging any required blanks still unfilled — for a service instance, you’ll see team, aspect domain, and widget.* props at the top of the list (none are required; team is a soft cascade).

Inside process steps, completion is path-aware: type Customer > Payments. and you get the list of interfaces (and surfaces, which you can dot-into further) declared on Payments. The LSP knows the entire workspace — completion across files in your package and across imported packages works the same way.

The completion palette is scoped to what the space actually imports. The editor also exposes a type viewer over the space-level use palette — the explicit list of types declared available in the package.archspace manifest — so you can browse the whole vocabulary the space offers before reaching for a type. Declaring imports at the space level keeps that palette discoverable in one place.

Hover any identifier. The LSP renders:

  • For a module reference: the module’s type, team, description (rendered with markdown + [[refs]] + @aspect), and a list of its interfaces.
  • For an interface reference: the interface’s type, description, and fields.
  • For a type keyword: the type’s defining type, its required blanks, and the chain up to the base type.
  • For a stable ID: the named declaration and its location.

The exact same renderer the viewer uses for descriptions also runs in the hover. What you see in the editor matches what you see in the diagram.

Every save runs the full validator. Errors, warnings, and TODOs underline in the source and appear in the Problems panel. The three are distinct, and the editor keeps them apart: errors are contradictions (fix them), warnings are discouraged-but-valid smells, and TODOs are draft debt — a referenced-but-undefined module, interface, or subprocess the compiler synthesized as a dashed stub. A TODO is missing detail, not wrong, so process-first drafting raises them by design and they never block the inner loop. The Problems panel surfaces the open-TODO count as your “what’s left to define” tally; treat draining it to zero — not silencing it — as the bar for a model that’s ready to propose. Diagnostic codes (USE_TYPE_NOT_EXPORTED, DEP_CYCLE, WIDGETS_FILE_NOT_FOUND, etc.) are stable across releases — you can filter or escalate specific ones in your project’s settings.

Right-click any identifier and jump to where it’s declared. Find-references shows everywhere it’s used — every process step invoking a given interface, every module declaring it, every description that cross-references it.

This works across files in the package and across imported packages. Imported types resolve to their declaration in the source package (the LSP follows the use chain).

Rename a module and every reference updates: process steps that named it, descriptions that linked to it, view show/hide selector patterns that matched it. Renames preserve the stable ID, so the diff engine still sees one renamed module rather than a delete-plus-add (Chapter 14).

Renames work for interfaces (within their enclosing module’s path), processes, views, and types.

The LSP exposes lightbulb code actions for common edits. The exact set evolves between releases; representative actions include fulfilling a required blank with a default value, dropping an inherited declaration, and converting between dotted and block aspect forms. Hover the lightbulb in any context to see what’s available.

Syntax highlighting in .arch files isn’t lexical — the LSP provides semantic tokens. Type keywords, stable IDs, qualified names, descriptions, and aspect paths each get their own token type, which means your color theme can distinguish them. The highlighting is identical in VS Code and JetBrains because both consume the same LSP.

Both extensions ship an inline preview pane. Open the command palette and run ArchLang: Preview Diagram (or use the keybinding). A side panel renders the current package’s diagram and updates on every save — the same diff loop as Chapter 2’s worked example. The pane uses the same renderer as the hosted and embedded viewers.

The preview is a derived view: it reflects the text, it isn’t an editing surface. There’s nothing to drag, no layout to nudge — that’s the renderer’s job. Author for an accurate, complete model and the diagram follows; don’t reshape the model to make a prettier picture.

Preview pane controls:

  • Toggle between the package’s default view and any declared view in the file.
  • Zoom and pan; the LOD rules from Chapter 20 apply.
  • Hover a node to see its full body in a side popover.
  • Click a node to jump back to its declaration in the editor.

When a module instance is filling in a required blank, the editor shows the type that originally declared the blank as a faint hint next to the value:

frontend Portal {
aspect domain: "Web" ⋯ (required on type frontend)
}

Inlay hints can be toggled off if they’re noisy. They’re useful while you’re learning the metamodel; they fade into the background once you know it.

ArchLang authorship is bottom-up: the person who owns a piece is the best person to describe it. The editor is built for that. The in Parent form lets an owner attach their module to a parent declared in someone else’s file, and file-local imports let each contributor pull just the types they need — so people edit their own pieces in their own files without coordinating one giant document. Cross-file go-to-definition, find-references, and rename stitch those scattered files back into one model.

Single-file drafts are first-class here too. Open a new .arch, lean on local plus standard-library types, and the LSP gives you the full completion/hover/diagnostics loop with no package setup. The preview pane renders it the same way it renders a full workspace. Promote the draft into a managed package when it earns it.

The extension activates automatically on .arch and package.archspace files. The first activation downloads the bundled language server binary (it ships inside the extension, so no separate install).

Settings worth knowing:

SettingDefaultEffect
archlang.format.onSavetrueRun the formatter on every save
archlang.preview.enabledtrueAllow the inline preview pane to open
archlang.stdlib.path(toolchain default)Point at a custom stdlib if you’re working on the toolchain itself

Install from the plugin marketplace. The plugin registers a file type for .arch and starts the bundled language server lazily on first file open.

JetBrains-specific niceties:

  • The Structure tool window shows modules, interfaces, and processes as a tree.
  • The Project tool window’s “Show in ArchLang Viewer” right-click action opens the inline preview for any .arch file or package directory.
  • Find Usages and Refactor → Rename are wired to the corresponding LSP requests.

The language server is the standard LSP protocol over stdio:

Terminal window
@archlang/lsp # if installed via npm
archlang-lsp # if installed via @archlang/cli (the binary ships with both)

Hand your editor’s LSP client this command and an arch languageId. Specifically:

  • Neovim with nvim-lspconfig: register a server pointing at archlang-lsp for the arch filetype.
  • Emacs with lsp-mode: add (arch-mode . "archlang-lsp") to lsp-language-id-configuration.
  • Helix: add a [language] block in languages.toml pointing at archlang-lsp.
  • Sublime Text with LSP package: register a new language server with the same command.

Browser editors that want the LSP without a backend can use @archlang/lsp/browser’s startBrowserServer(vfs) entry point, which runs the server as a web worker with an in-memory virtual file system. The hosted demo at archlang.dev/demo uses exactly this.

  • Doesn’t render diagrams itself. The preview pane delegates to the viewer’s renderer; the LSP only provides the resolved model and updates.
  • Doesn’t execute processes. ArchLang is a description language; processes are documentation, not orchestration. The LSP describes flow; it doesn’t run it.
  • Doesn’t fetch packages over the network. Dependencies are resolved against local filesystem paths declared in package.archspace. If you want a registry, that’s the next layer up.
  • VS Code and JetBrains extensions are first-class; both bundle the same language server.
  • The LSP provides completion, hover, diagnostics, go-to-definition, find-references, rename, code actions, semantic tokens, inlay hints, and an inline preview pane.
  • Other editors connect via the standard LSP protocol over stdio (archlang-lsp) or as a browser worker (@archlang/lsp/browser).
  • Diagnostic codes are stable across releases; tooling keys off them for filtering and CI gating.

Chapter 23: Embedding Diagrams → — put architecture diagrams into your own pages, dashboards, and review tools.