Org in Vim
The Vim plugin (editors/vim) runs Org through the server on top of an
LSP client: yegappan/lsp, coc.nvim, vim-lsp or ALE. It needs Vim
9.0.1485 or newer.
The client starts the server and keeps it up to date with the buffers; the plugin does the rest the same way for all four: it runs the server's commands with the cursor position and asks what they ask for, shows the agenda and capture buffers, highlights with the server's tokens (as text properties), folds with its folding ranges, and shows the clock.
Which client#
From the one that works best with org-lsp to the one that works least
well (client = 'auto' takes them in this order):
- yegappan/lsp
- Everything works, with the least glue: the plugin talks to the server on the client's own channel.
- coc.nvim
- Everything works, as with yegappan/lsp: the server tells
the plugin of the documents to show, rather than coc.nvim, so they open
as
win_split_modesays. One difference is left: a file an edit is for that is not loaded opens asworkspace.openResourceCommandsays (see What differs from Neovim). It needs Node.js. - vim-lsp
- Everything works too, but vim-lsp does not ask for the inlay
hints (the time clocked, the
…of shrunk columns) again when the server says they changed: the plugin asks for them after each command, and vim-lsp when the cursor moves. - ALE
- Works, but it has no inlay hints (the time clocked, the
…of shrunk columns), does not apply the server's edits itself (the plugin does), and one server serves the whole session, started for the directory of the first Org file.
Installing#
With vim-plug:
Plug 'yegappan/lsp' " or coc.nvim, vim-lsp, ALE: see Which client
Plug 'kristijanhusak/org-lsp', { 'rtp': 'editors/vim', 'do': ':OrgLspDownload' }With Vim's own packages, clone the repository and add editors/vim to
'runtimepath'.
Opening an Org file the first time downloads the server for your platform
from the nightly release (with curl and tar; on Windows, Windows' own tar, not one Git put first on the PATH), into
~/.local/share/vim/org-lsp, and starts it. :OrgLspDownload downloads it
again, to update it, and the do above does that on each update of the
plugin; a server that runs already is replaced when Vim starts again.
'release': 'v0.2.0' in g:org_lsp picks a release instead of the nightly
build, and 'download': 0 turns the download off. A release that needs a
GitHub login, as one of a private repository, is downloaded with the GitHub
CLI (gh) when it is installed and logged in.
To build the server instead (with cargo), set
'do': 'cargo build --release -p org-lsp'. The plugin takes the server
cmd names, else a build in its checkout (target/release), org-lsp on
PATH, or the downloaded one. :Org version says which server runs, with
the commit it was built from (org-lsp version 0.1.0-f7e7b68; -dev after
it for a server built by hand, not by the release workflow).
The plugin finds the client that is installed and registers the server with it. Leave the client's own semantic highlighting and folding off for Org files (they are off by default in all four): the plugin does both.
The plugin's syntax for Org (syntax/org.vim) colors what a pattern can
tell, as the server does and in the same colors: headline stars and titles
by level, timestamps, planning and clock lines, links, emphasis, code,
lists, tables, drawers, blocks, keywords and comments. TODO keywords and
the headlines they start, keywords and tags with faces of their own
(org_todo_keyword_faces, org_tag_faces) and the options of how
headlines look it colors as the server tells it for each file; before it
has, from the file's #+TODO: lines. It shows what you type before the
server has answered. What only the server can tell (priorities, entities,
LaTeX, what other options change) waits for it. It needs :syntax on.
To try it without touching your own configuration,
editors/vim/minimal.vim is a vimrc that puts vim-plug and yegappan/lsp in
a temporary folder and loads the plugin from a checkout (~/github/org-lsp;
change both places it is named for another):
cargo build --release -p org-lsp
vim -N -u editors/vim/minimal.vim ~/org/todo.orgSetting it up#
Set g:org_lsp before the plugin loads. The server's options go in
settings, the plugin's own beside it; every key may be left out:
let g:org_lsp = {
\ 'settings': {
\ 'org_agenda_files': ['~/org'],
\ 'org_todo_keywords': ['TODO(t)', 'NEXT(n)', '|', 'DONE(d!)'],
\ 'org_capture_templates': [
\ {'key': 't', 'description': 'Task', 'target': ['file', '~/org/inbox.org'], 'template': "* TODO %?\n %U"},
\ ],
\ 'org_agenda_custom_commands': [
\ {'key': 'n', 'description': 'Next actions', 'type': 'todo', 'match': 'NEXT'},
\ ],
\ },
\ 'client': 'lsp',
\ 'win_split_mode': 'vsplit',
\ 'mappings': {'org': {'org.todo.next': '<Leader>t'}},
\ }The server also reads the shared configuration file,
~/.config/org-lsp/config.toml, which every editor shares; settings goes
on top of it. configuration.org has everything else: the plugin's options
(the client, windows, the calendar, the statusline) and every option of the
server with its default.
Keys#
The keys are the Neovim plugin's (cit, <C-a>, <Leader>oa, <Tab> …),
and the agenda's are the ones it has in every editor. g? lists the keys
of the buffer at hand, K in the agenda shows the entry of the line in a
popup, and :Org agenda, :Org capture, :Org todo next … run any
command. keys/vim.org lists every key and how to change it.
What differs from Neovim#
- The agenda and the date prompt's calendar are files the server writes
(
Agenda.org-agenda,Date.org-datein a temporary directory), read again when it says they changed. - Column view covers each headline with a popup, and puts its header in the window's status line rather than above the text.
- What stands in for hidden text (a bullet, an entity's character, a custom time) is text of its own before the hidden text, and a line with a long link wraps as if the hidden target took room: Vim counts concealed text when it wraps.
- With coc.nvim, a file an edit is for that is not loaded (the target of
a refile or a capture) opens as
workspace.openResourceCommandsays, in a tab of its own unless that is set (""loads it without showing it, as the other clients do). coc.nvim then saysUse ':wa' to save changes …; with the command's message, that is more than a narrow command line holds, and Vim waits for Enter ("coc.preferences.messageLevel": "warning"keeps coc.nvim's to itself). - With ALE, one server serves the session, started for the directory of
the first Org file. ALE shows the hover where the cursor rests in the
command line (
g:ale_hover_cursor): in the agenda, the entry's headline, which takes the place of the command's message after'updatetime'.
What works#
| Feature | Parity | Vim | What does not work, and why |
|---|---|---|---|
| Highlighting | 100% | ❓ | A headline's line colored to the edge (org_fontify_whole_heading_line) is a sign without text, which shows the sign column. A line with a long link wraps as if the link's hidden target took room: Vim counts concealed text when it wraps. |
| Folding and visibility | 91% | ❓ | Cycling the whole file goes between overview and everything: it has no contents state, which needs folds of a headline's own text that Vim's fold levels do not make. A closed fold's line is not highlighted: Vim draws it in one color. <Tab> changes the level of a new entry in Normal mode; in Insert mode it is a tab. |
| Headings and structure | 96% | ✅ | |
| Lists and checkboxes | 93% | ✅ | |
| TODO keywords | 100% | ✅ | |
| Priorities | 100% | ✅ | |
| Tags | 88% | ✅ | |
| Properties and column view | 100% | ❓ | Column view's header is in the window's status line, shown when the window has one (laststatus). Custom properties are not hidden (org.custom.properties.toggle): Vim cannot hide a line, a closed fold still shows a line of its own, and concealing a line's text leaves the line, empty. |
| Dates and times | 100% | ✅ | |
| Agenda | 97% | ✅ | |
| Clocking | 100% | ✅ | |
| Capture | 100% | ✅ | |
| org-protocol | 86% | ✅ | |
| Notes (org-roam) | 94% | ✅ | |
| A journal (org-journal) | 93% | ✅ | |
| Refile and archive | 100% | ✅ | |
| Links | 96% | ✅ | |
| Footnotes | 100% | ✅ | |
| Tables | 100% | ✅ | |
| Table formulas | 100% | ✅ | |
| Code blocks (Babel) | 100% | ✅ | |
| Export and publishing | 100% | ✅ | |
| Citations | 82% | ✅ | |
| Diagnostics (org-lint) | 100% | ✅ | |
| Indentation and formatting | 100% | ❓ | The lines a long line wraps into line up with its own indentation, a headline's title or an item's text ('breakindent'), without the prefix of virtual indentation before them: Vim repeats no virtual text on them, and 'breakindentopt' shifts all the lines of a window alike. |
| Notes and logging | 100% | ✅ | |
| Completion and editing helpers | 100% | ✅ | |
| Timers | 100% | ✅ | |
| Attachments | 100% | ✅ | |
| Encryption | 100% | ✅ | |
| Inline images and LaTeX previews | 100% | ❌ | Vim cannot show images. |
| All of Org | 97% |
✅ 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#
editors/vim/test/run.sh runs the plugin against the real server once per
client, in Vim inside tmux. CLIENTS is a directory with the clients
checked out (lsp, vim-lsp, coc.nvim from its release branch, ale):
CLIENTS=~/vim-clients editors/vim/test/run.sh # all four
CLIENTS=~/vim-clients editors/vim/test/run.sh vim_lsp # oneAfter the plugin's own tests it runs the shared end-to-end scenarios, pressing the plugin's keys for each step (development.org).