The whole picture¶
Three processes take part: your editor, this server, and mystmd’s myst start, which builds the project and serves the result from a content server.
Inside the server, src/server.ts speaks LSP to the editor and hands each request to src/service.ts, which has the features.
service.ts finds references with the regexes in src/syntax.ts and looks them up in the index in src/project.ts, which is filled as described in Two sources of truth.
On a keystroke¶
The editor sends the change, and
server.tspasses the document’s new text toservice.update.service.updatewaits until typing has stopped for 150 ms, then callsproject.setOpen.setOpenre-parses the document withparseMystand replaces that document’s targets and mystmd warnings, keyed by its URI.setOpenthen calls itsonChange, which isrefreshinserver.tsand also runs when the content server reports a rebuild.refreshpushes diagnostics for every open page of that project, or tells an editor that pulls diagnostics to pull again.service.diagnosticsreports unresolved references, duplicate labels in the index, and mystmd’s own warnings from the live parse.
On a hover or rename¶
Take the cursor on label in {ref}`label`.
server.tspasses the request toservice.hoverorservice.rename.refsInTextinsyntax.tsfinds every reference in the document, and the service picks the one whose span holds the cursor.resolveRefnormalizes the label as mystmd does and looks it up inproject.targets().Hover replies with the target’s title, file and text, such as
Figure 1 · chapter.md.Rename finds the label’s definition with
labelDefinitionand its references in the project’s.mdfiles and open documents. It replies with the edits, and the editor applies them.
Notebooks¶
Each Markdown cell of an open notebook is its own document, at the notebook’s path with the cell id as the fragment, like file:///nb.ipynb#<cell id> from JupyterLab.
The index keys open documents by URI, so each cell has its own live targets.
For find references and workspace diagnostics, markdownCells in service.ts splits notebooks on disk into their Markdown cells.
Rename and file moves only edit open cells, since the editor owns a notebook’s cells and may hold its file open.
To add a reference kind¶
Add its pattern to
refPatternsand its name to thekindofRefinsrc/syntax.ts, and tocontextsif it should complete.Resolve it in
resolveRefinsrc/service.ts, and add its completions under its trigger incompletion.If it points at a new kind of target, give that kind a completion icon in
src/kinds.tsand a modifier insemanticTokensLegendinsrc/service.ts.If mystmd’s parse shows it, add it to
kindsinscripts/conformance.ts.Add a test to
src/syntax.test.tsfor the pattern, or tosrc/service.test.tsfor the feature.Document it in Features.
Run
npm testandnpm run conformance.
The comment above known in scripts/conformance.ts lists the other things that change with it.
Two sources of truth¶
The first is the content server.
For each project with a myst.yml, the server starts myst start --headless, which builds every page and serves a content server.
Clients that already run mystmd pass --root and the content server’s address with --content-server instead.
The server indexes the result, so it knows every page, label and citation, and reloads the index when the content server reports a change.
Warnings for unknown targets and completion of labels from other pages need this.
The second is the open documents.
These are re-parsed live with mystmd’s parser, so unsaved labels are available immediately.
targets() in src/project.ts merges the two: an open document’s targets replace its file’s built ones.
Without a content server, only open documents are indexed, and unknown targets aren’t reported.
That happens when mystmd isn’t installed or fails to start, or when you pass --no-myst.
A document with no project, or one its project doesn’t build, gets no features at all (see Finding the project).
See What we learned for why it works this way, and what we measured.