org-lsp

Org in Zed

editors/zed is a Zed extension that runs Org through the server. Zed lets an extension do less than the other editors do, so the commands come as code actions, what they ask for is asked with buttons, and the agenda's keys go through Zed's tasks.

Installing#

The extension is not in Zed's registry: a script installs it, with the keys through tasks that a Zed extension cannot bring of its own:

curl -fsSL https://github.com/kristijanhusak/org-lsp/releases/download/nightly/install.sh | bash -s -- zed

or, in a checkout of this repository, scripts/install.sh zed (--release v0.2.0 for a release, --source for the server built from the checkout). It puts in place, from the nightly release:

  • the extension, built, in Zed's extensions/installed/org-lsp;
  • the server beside it, in extensions/work/org-lsp, where the extension looks first;
  • the tasks and keys of keys through tasks, added to the start of your tasks.json and keymap.json between two // BEGIN org-lsp and // END org-lsp comments, which it replaces when run again. Your own keys come after them, and win.

Restart Zed afterwards; run the script again to update.

Without the script, the extension starts org-lsp from your PATH, or else downloads the server of its own release for your platform once (v0.2.0 for version 0.2.0 of the extension, the nightly build for the nightly one), which needs the repository to be public; lsp.org-lsp.binary.path names a server binary of your own. To work on the extension, run zed: install dev extension and pick editors/zed of a checkout (Zed builds it with Rust).

Setting it up#

The settings go in Zed's settings (zed: open settings). Zed asks the server for its colors only with semantic_tokens, and for its folds (headlines, drawers, blocks) only with document_folding_ranges: without it, an Org file has no folds at all. It shows indentation, pretty entities and the clock's times as inlay hints. The server's options go in initialization_options:

{
  "languages": {
    "Org": {
      "semantic_tokens": "combined",
      "document_folding_ranges": "on",
      "inlay_hints": { "enabled": true }
    }
  },
  "lsp": {
    "org-lsp": {
      "initialization_options": {
        "org_agenda_files": ["~/org"],
        "org_todo_keywords": ["TODO(t)", "NEXT(n)", "|", "DONE(d!)"],
        "org_capture_templates": [
          { "key": "t", "description": "Task", "target": ["file+headline", "~/org/inbox.org", "Tasks"], "template": "* TODO %?\n  %U" }
        ],
        "org_agenda_custom_commands": [
          { "key": "n", "description": "Next actions", "type": "todo", "match": "NEXT" }
        ]
      }
    }
  }
}

The server also reads the shared configuration file, ~/.config/org-lsp/config.toml, which every editor shares; initialization_options goes on top of it. configuration.org has everything else: Zed's settings for Org and every option of the server with its default.

Using it#

In Org files the commands are in the code actions (editor: toggle code actions, ctrl-. or cmd-.), which list what applies where the cursor is. What a command asks for is asked with buttons: a TODO state, a priority, a refile target, a capture template, yes or no. Zed has no box of its own for a server to ask for typed text, so a command that wants a date or text says PROMPT: answer with a rename at the cursor: rename at the cursor (F2) and type the answer for the new name, or - alone for an empty answer (the prompt's default), as Zed sends no empty new name.

Org's keys, and the agenda's, run the commands once two files of the extension are pasted into your tasks and keymap. The agenda, the TODO list, the agenda menu and capture then need no Org file open: run them from the task picker (task: spawn; type Org:). keys/zed.org says how, and lists the keys.

What works#

FeatureParityZedWhat does not work, and why
Highlighting100%❓Links, emphasis markers, pretty entities and sub- and superscripts are not concealed: Zed lets no server hide text. The colors need "semantic_tokens": "combined"; code blocks take their language's colors, inline code does not. A headline's look does not go on to the edge of the window (org_fontify_whole_heading_line): Zed lets no server color a line past its text.
Folding and visibility91%❓Zed folds with its own folding: Org's cycling and sparse trees need folds an extension can set, which Zed does not offer.
Headings and structure96%❓A command on a selection acts on the cursor's line: Zed tells a server no selection. The commands go through the keys of keymap.json (Zed's tasks); for cloning, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Turning a line into a heading (org.toggle.heading) is left out: it has no key in keymap.json and is not among the code actions. C-a, C-e, C-k and C-y keep Zed's own meaning, and nothing pasted is folded: Zed lets no server fold.
Lists and checkboxes93%❓RET stays Zed's own, and so do Shift and Alt with the arrows, which cycle bullets and move items in Org.
TODO keywords100%❓States are picked with buttons; for a note for a state change (@), the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Faces of particular keywords and priorities are not shown: Zed colors tokens by its theme only.
Priorities100%✅
Tags88%❓Tags are typed, not set with keys pressed in turn: the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Completion and renaming work. Faces of particular tags are not shown: Zed colors tokens by its theme only.
Properties and column view100%❓A property, its value and the effort are typed, without completion: the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Column view is left out: it draws over the headlines. Custom properties are not hidden (org.custom.properties.toggle): Zed lets no server hide text. Deleting a property has no key in keymap.json and is not among the code actions; cycling a property's allowed values needs Shift with the arrows, which stay Zed's.
Dates and times100%❓Dates are typed (+2d, fri 10:00), with no calendar: the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Changing a timestamp works. Removing the scheduled date or the deadline is left out: those commands have no key in keymap.json and are not among the code actions.
Agenda97%❓The agenda is a file, its commands keys through Zed's tasks (keymap.json), enter and tab going to an entry; adding today's appointments (org.agenda.appt) has no key and is not among the code actions; for a search and a date, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Follow mode and the outline path of the cursor's entry are left out: Zed reports no cursor moves to an extension. Column view in the agenda is left out: it draws over the lines. The days of a habit's graph have Org's backgrounds for a dark background, a little transparent, with any theme: an extension's colors for tokens are the same with every theme.
Clocking100%❓No clock in the status line: Zed has no place there for an extension's text. In the agenda, the clocked entry's line has the background of org-agenda-clocking under its text, not to the window's edge: Zed lets no extension highlight a line, only give its tokens a background, and the tokens end with the text.
Capture100%❓For a template's prompts, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. The capture buffer's tab stays open once the entry is filed: Zed lets no server close a tab.
org-protocol86%❓The server starts a capture itself: the template is picked from Zed's question, and for its prompts the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text.
Notes (org-roam)94%❓A node's title is typed, without completion: the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. The org-roam buffer does not follow the cursor: Zed reports no cursor moves to an extension. The commands without a key of Org's (a random node, aliases, tags, the buffer of a node and refreshing it) are left out: Zed runs a server's commands only from the keys of keymap.json and the code actions, and those are not among them.
A journal (org-journal)93%❓For the dates and searches the commands ask for, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. The other entries stay as they show: Zed lets no server fold. Files without an extension are Org files only with file_types in Zed's settings. The calendar and the keys org-journal adds to it are left out: Zed shows no date prompt. The entries to come (TODO, and with a date) are left out: they have no key in keymap.json and are not among the code actions.
Refile and archive100%❓Archiving every done subtree (org.archive.done) is left out: it has no key in keymap.json and is not among the code actions.
Links96%❓A link's target is typed, without completion: the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Following works through go to definition, for links inside Org files and to files. Creating an ID (org.id.create) is left out: it has no key in keymap.json and is not among the code actions.
Footnotes100%✅
Tables100%❓Moving between fields with TAB and copying down (S-RET) are left out: those keys stay Zed's own. For a new table's size, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. A shrunk column shows its whole text and …: Zed lets no server hide text.
Table formulas100%❓Iterating until the table is stable (org.table.iterate) is left out: it has no key in keymap.json and is not among the code actions.
Code blocks (Babel)100%❓Detangling (org.babel.detangle) is left out: it has no key in keymap.json and is not among the code actions.
Export and publishing100%✅
Citations82%❓The BibTeX commands' fields are asked for in turn, and the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Those without a key of Org's (a new entry, copying, exporting, pasting and importing BibTeX) are left out: they are not among the code actions.
Diagnostics (org-lint)100%✅
Indentation and formatting100%❓Virtual indentation shows as inlay hints, the leading stars still showing; new lines are not indented as they are typed. With soft wrap on, the lines a long line wraps into line up with the prefix and its own indentation, not with a headline's title or an item's text: Zed wraps under a line's leading blanks, inlay hints among them. Numbering the headlines by command (org-num-mode) is left out: it has no key in keymap.json and is not among the code actions; #+STARTUP: num numbers them.
Notes and logging100%❓A note is one line, with no buffer to write it in: the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. Changing a TODO state as if it were yesterday is left out: it has no key in keymap.json and is not among the code actions.
Completion and editing helpers100%✅
Timers100%❓For a countdown's length, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text. No timer in the status line: Zed has no place there for an extension's text.
Attachments100%❓attachment: links open; the dispatcher's keys come as buttons, and for the file to attach, the answer is typed as a rename at the cursor (F2), as Zed has no box of its own for typed text.
Encryption100%❓Encrypting and decrypting by command are left out: they have no key in keymap.json and are not among the code actions. Encrypting before saving needs an editor that sends LSP's willSaveWaitUntil.
Inline images and LaTeX previews100%❌Zed lets no extension show images in a file.
All of Org97%

✅ all that org-lsp does works there, ❓ some does not (the last column says what and why), ❌ none does, — nothing to support yet. Parity is the share of what Emacs Org does in that area that org-lsp does; parity.org lists both.

TESTING.org lists what to try by hand.

Developing#

node editors/zed/generate.mjs writes the semantic token rules (languages/org/semantic_token_rules.json), tasks.json and keymap.json from the server's manifest; --check fails if they are out of date. cargo build --target wasm32-wasip2 in editors/zed builds the extension. editors/zed/test/run.py runs the shared end-to-end scenarios in Zed, pressing its keys (end-to-end scenarios).