19. Editors

ucm lsp is a language server (LSP over stdio) that runs alongside TypeScript's own. TypeScript keeps doing completions, type errors, go-to-definition and refactors; ucm adds what only it knows. It works on scratch files: the .ts files at the top of the codebase directory, the ones the watcher checks.

19.1 What it shows

Commands, which editors expose as code-lens actions or a command palette:

CommandArguments
ucm.addfile URIucm add the file. The file must be saved (ucm saves what's on disk)
ucm.updatefile URIucm update from the file
ucm.mvfrom, toucm mv

Each reports its output as a message, and every open file's status is refreshed.

19.2 The codebase as documents

The code lives in the codebase, not in files, so the server also shows the codebase itself.

For clients that want to show more, the server answers:

ucm/tree
one level of the namespace tree: each definition's kind, role (app, type, table, schedule, server, browser, shared, test), signature, placement, whether it's an endpoint, test status
ucm/apps
each app with its endpoints, browser code, tables (with row counts), schedules and tests
ucm/data
tables, with row counts, rows of older row types, the declaring definition and the row type's fields
ucm/rows
a page of a table's rows for a viewer: searched, sorted by a field and paged in SQLite; the row type's fields (columns), and each row's cells, full value, and whether it's of an older row type
ucm/source
a ucm: document's text: a definition, ucm:/.data/<table>.txt, or ucm:/.data/<table>/<id>.txt
ucm/findByType
type search, as ucm find ':…'
ucm/page
a definition's page: rendered doc (links as ucm-page:<hash>), highlighted source with references as links, placement, uses and used-by
ucm/namespace
a namespace's page: its README rendered, and its definitions by role with doc summaries
ucm/find
the finder: names, doc words and types

It sends ucm/changed when the codebase changes and ucm/dataChanged when the data store does, here or from a terminal or a running app. ucm.dataMigrate <table> is a command, like ucm.add.

19.3 Pages

As in Unison's codebase UI, definitions and namespaces have pages. Each opens in its own editor tab, named after it (opening one that's already open focuses its tab), and links in a page open more. Open pages refresh when the codebase changes.

Clicking a definition in the Codebase view opens its page (Open source in its menu opens the text document); a namespace's Open page button (the book) opens the namespace's.

19.4 Finding things

ucm: Find (Cmd/Ctrl+Alt+U, or the search icon on the Codebase view) is one search box for everything: names (the letters in order, best matches first), words in docs, and types. Anything that looks like a type, (xs: T[]) => T, or starts with :, is a type search. Results show signatures and doc summaries, and open as pages.

19.5 VS Code

editors/vscode is the extension. Beyond starting ucm lsp in workspaces with a codebase, it adds:

The # icon in the activity bar opens three views:

All three refresh when the codebase changes (including from a terminal), and the Data view also when the data does, so rows appear as a running app writes them.

Try it on the polls example.

Build a .vsix (the first npx downloads vsce, which takes a while) and install it with Extensions → … → Install from VSIX…, or with VS Code's CLI (on macOS it's inside the app if code isn't on your PATH):

cd editors/vscode && npm install && npx @vscode/vsce package
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" --install-extension ucm-ts-0.1.0.vsix

Don't symlink the folder into ~/.vscode/extensions: VS Code only keeps extensions listed in its manifest, and marks unlisted folders as removed. A workspace you haven't trusted yet runs in Restricted Mode, where the extension stays off until you trust it.

Set ucm.path if ucm isn't on your PATH: either the path to ucm's packages/cli/bin/ucm.js (spaces in the path are fine; it's run with Node), or a command line with quotes around words containing spaces. Inlay hints must be enabled (editor.inlayHints.enabled, on by default) to see watch values.

19.6 Other editors

Any LSP client can run ucm lsp for TypeScript files, next to its TypeScript server. The workspace root must be the codebase directory (or inside it).

Neovim (0.10+):

vim.api.nvim_create_autocmd("FileType", {
  pattern = "typescript",
  callback = function(args)
    local root = vim.fs.root(args.buf, { ".ucm" })
    if root then vim.lsp.start({ name = "ucm", cmd = { "ucm", "lsp" }, root_dir = root }) end
  end,
})
vim.lsp.inlay_hint.enable(true)
vim.lsp.codelens.refresh() -- or map vim.lsp.codelens.run to run the Add/Update actions

Helix (languages.toml):

[language-server.ucm]
command = "ucm"
args = ["lsp"]

[[language]]
name = "typescript"
language-servers = ["typescript-language-server", "ucm"]

Zed needs an extension to register a language server; there isn't one yet.

19.7 Notes

built from 5890e54f