Org in Neovim
The Neovim plugin is the repository itself (plugin/, lua/), for Neovim
0.12 or newer. It sets its keys in Org buffers, so it is meant to be used
instead of nvim-orgmode for them, not next to it.
Installing#
Install kristijanhusak/org-lsp with Neovim's own plugin manager,
vim.pack (:help vim.pack), in your init.lua:
-- Download the server again whenever vim.pack updates the plugin.
vim.api.nvim_create_autocmd('PackChanged', {
callback = function(ev)
if ev.data.spec.name == 'org-lsp' and ev.data.kind == 'update' then
if not ev.data.active then
vim.cmd.packadd('org-lsp')
end
vim.cmd('OrgLspDownload')
end
end,
})
vim.g.org_lsp = { settings = { org_agenda_files = { '~/org' } } }
vim.pack.add({ 'https://github.com/kristijanhusak/org-lsp' })vim.pack.update() updates it. The autocommand comes before
vim.pack.add(), which loads the plugin, and vim.g.org_lsp is set before
it too. Another plugin manager works as well; with
lazy.nvim, the same is
{ 'kristijanhusak/org-lsp', build = ':OrgLspDownload', init = function()
vim.g.org_lsp = { … } end }.
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) and starts it;
:OrgLspDownload downloads it again, to update it, and the autocommand
above does that on each update of the plugin. release = 'v0.2.0' picks a
release instead of the nightly build, and download = false 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), run
:OrgLspBuild, in the autocommand in place of OrgLspDownload to build it
on each update. A
build in the plugin's directory and org-lsp on PATH come before the
download. :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).
:checkhealth org-lsp reports the server the plugin found, where it came
from, and the agenda files.
Setting it up#
Set vim.g.org_lsp before the plugin loads (before vim.pack.add(), as above),
or call setup() at any time. The server's options go in settings, the
plugin's own beside it. The server reads its options when it starts: once it
runs, settings changed by setup() take effect after :lsp restart.
require('org-lsp').setup({
-- The server's options: Org's variables, named with `_`.
settings = {
org_agenda_files = { '~/org', '~/notes/**/*.org' },
org_todo_keywords = { 'TODO(t)', 'NEXT(n)', 'WAIT(w@/!)', '|', 'DONE(d!)', 'CANCELLED(c@)' },
org_log_done = 'time',
org_capture_templates = {
{ key = 't', description = 'Task', target = { 'file+headline', '~/org/inbox.org', 'Tasks' }, template = '* TODO %?\n %U' },
},
org_agenda_custom_commands = {
{
key = 'd',
description = 'Today, then what is waiting',
blocks = {
{ type = 'agenda', settings = { org_agenda_span = 'day' } },
{ type = 'todo', match = 'WAIT', header = 'Waiting for' },
},
},
},
},
-- The plugin's own.
win_split_mode = 'vsplit',
calendar_win_mode = 'cursor',
})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
(windows, the calendar, the statusline, highlighting, images) and every
option of the server with its default.
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 a file before the server's
highlighting arrives, and what you type before the server has answered.
What only the server can tell (priorities, entities, LaTeX, what other
options change) waits for it. Keep :syntax on
(Neovim's default); replace_syntax = true turns the syntax off once the
server's highlighting has come.
Keys#
The plugin binds nvim-orgmode's default keys: cit changes the TODO
state, ciR and cir the priority, <C-a> and <C-x> the part of the
timestamp under the cursor, <Leader>ois schedules, <Leader>or refiles,
<Leader>oc captures, <Tab> and <S-Tab> cycle folds, and <Leader>oa
opens the agenda from anywhere. g? shows the keys of the buffer at hand.
The agenda has the keys it has in every editor: Emacs's (f and b, t,
+ and -, <CR>, <C-c><C-s>), with J for a date and the views under
v (vd, vw), so that no motion is taken; q closes it. Counts work,
and the editing keys repeat with .. Dates are asked for with Emacs's
calendar, or typed as at Org's prompt (fri 10:00, +2d, 2/15, ++1w).
Keys that a mapping or a macro gives after a key that asks wait for the
question and answer it, as in Emacs; keys pasted a few milliseconds after
<Leader>ois, before the calendar is there, may reach the Org file instead.
keys/neovim.org lists every key and how to change it.
What works#
| Feature | Parity | Neovim | What does not work, and why |
|---|---|---|---|
| Highlighting | 100% | ✅ | |
| 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 Neovim's fold levels do not make. <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% | ✅ | |
| 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: Neovim repeats no inline virtual text on them, and 'breakindentopt' shifts all the lines of a window alike. A line with a long link wraps as if the link's hidden target took room: Neovim counts concealed text when it wraps; the org-roam buffer, whose links are long, does not wrap. |
| Notes and logging | 100% | ✅ | |
| Completion and editing helpers | 100% | ✅ | Completion needs an LSP completion plugin (or vim.lsp.completion). |
| Timers | 100% | ✅ | |
| Attachments | 100% | ✅ | |
| Encryption | 100% | ✅ | |
| Inline images and LaTeX previews | 100% | ❓ | From Neovim 0.13 (vim.ui.img), in a terminal with the Kitty graphics protocol: an image shows below its line, not over the link; images other than PNG need ImageMagick. |
| 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#
scripts/test-nvim.sh runs the plugin's tests headless against a fresh
debug build of the server (NVIM names the Neovim to use).