Use ubCode from any editor

Any LSP editor Stable

ubCode’s language server is the ubc binary itself: ubc serve lsp speaks the Language Server Protocol over stdio. The VS Code extension bundles ubc and starts it this way, and any other editor with a Language Server Protocol client can start it too, for reStructuredText and Markdown files. This page sets it up in Neovim, Helix and Emacs, and says what you get there compared with VS Code.

What you need

Every client below runs the same command, ubc serve lsp (options). What any client needs to know:

  • The server talks over stdio. With --port <PORT> it listens on 127.0.0.1 instead, for one client.

  • Attach it to reStructuredText and Markdown files.

  • The server finds the nearest ubproject.toml above each file itself, so the root folder your client picks decides which files share one server, not which configuration applies.

  • Markdown needs no editor-side MyST setup: the project’s [parse.parsers.md] table decides whether a .md file is MyST (Parsers and file routing).

  • The server takes no initialization options, and the VS Code extension’s ubcode.* settings do not apply. The server’s own settings live in the [server] table of ubproject.toml (Server); to override any project setting for one editor, add -c/--config to the command or set UBCODE_CONFIG_OVERRIDE in the environment the editor starts it in (Environment variables).

Neovim

With Neovim 0.11 or later, add this to your init.lua:

vim.lsp.config('ubcode', {
  cmd = { 'ubc', 'serve', 'lsp' },
  filetypes = { 'rst', 'markdown' },
  root_markers = { 'ubproject.toml' },
})
vim.lsp.enable('ubcode')

Neovim detects .rst and .md files as rst and markdown on its own. Its default keys then reach the server: K for hover, CTRL-] for go to definition, grr for references and CTRL-X CTRL-O for completion (lsp-defaults).

On Neovim 0.10, call vim.lsp.start with the same command from a FileType autocommand instead.

Helix

Add this to languages.toml in your Helix configuration directory (~/.config/helix/ on Linux and macOS):

[language-server.ubcode]
command = "ubc"
args = ["serve", "lsp"]

[[language]]
name = "rst"
roots = ["ubproject.toml"]
language-servers = ["ubcode"]

[[language]]
name = "markdown"
roots = ["ubproject.toml"]
language-servers = ["ubcode"]

Other Markdown servers can stay in the list beside ubCode, for example ["marksman", "ubcode"]. See Languages in the Helix documentation.

Emacs

Emacs 29 and later have the eglot client built in. Add this to your init.el:

(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '((rst-mode markdown-mode) . ("ubc" "serve" "lsp"))))
(add-hook 'rst-mode-hook #'eglot-ensure)
(add-hook 'markdown-mode-hook #'eglot-ensure)

rst-mode comes with Emacs; for Markdown files, install the markdown-mode package. eglot takes its root folder from project.el (for example, your Git repository), not from ubproject.toml, which is fine: the server finds ubproject.toml itself. See the eglot manual.

Other editors

Any other editor with a Language Server Protocol client works the same way: configure it to run ubc serve lsp for reStructuredText and Markdown files, with ubproject.toml as the root marker if the client asks for one.

What you get

In any of these editors, ubCode gives you:

  • each open file’s own diagnostics as you type: parse and markup problems, and the checks on its needs’ fields and link values, including the field-level schema constraints;

  • go to definition from need references and link options to the need (in reStructuredText);

  • find references of a need;

  • completion, for example of need IDs in a :need: role;

  • hover on directive names;

  • the document symbols (the section outline);

  • quick-fixes for diagnostics that have one;

  • formatting of reStructuredText files (not Markdown);

  • semantic highlighting of Markdown files: the MyST constructs a Markdown grammar leaves uncoloured (such as directive names and option keys, targets and comments), where the editor supports it;

  • MyST Markdown files parsed as the project configures them, with their own diagnostics and outline.

These stay in VS Code:

  • The diagnostics that need the whole project index: references to missing labels, dead links between needs, and the schemas.json rules (Schema validation). Run ubc check to see them (Linting on the command line).

  • The need card on hover over a need reference, and the image preview on hover. The server sends these as HTML, only to a client that allows HTML in hover Markdown; the Neovim, Helix and eglot clients do not.

  • CodeLenses, so the code_lenses setting has no effect in other editors.

  • The views, the live preview, and Pharaoh’s panels.

The feature matrix lists every feature by surface. The language server needs no license in any editor; the licensing page lists the few list-based features that are limited without one.

Troubleshooting

The server does not start

Check that ubc --version runs in a terminal. If it does and the editor still cannot start the server, the editor sees a different PATH: write the full path to ubc in the editor configuration instead.

You need the server’s log

The server sends its log messages to the client, at info and above; where they appear depends on the editor. For more detail, add --log-to-output debug to the command.

Results are stale after a change outside the editor

The server re-indexes when you open or save a document (index_on_save in the [server] settings); it does not watch files. After editing ubproject.toml, restart the server. After editing files outside the editor, save a document or restart the server.