Extras: help, completion, lint, speed keys, inline tasks, crypt ...
Table of Contents
- 1. How to use this file
- 2. Getting help inside Neovim
- 3. Checking your setup: :checkhealth org
- 4. Completion
- 5. Structure templates and org-tempo
- 6. Special edit buffers: <prefix>'
- 7. Elements: paragraphs, lists, blocks, tables …
- 8. Speed keys
- 9. Inline tasks
- 10. Encryption: org-crypt
- 11. Display options
- 12. Setup files: #+SETUPFILE
- 13. org-lint: check the syntax
- 14. Emacs keys
- 15. Integrations
- 16. Further reading
1. How to use this file
This file collects the features of org.nvim that don't belong to one of the other example files: finding help, completion, the syntax checker (org-lint), speed keys, inline tasks, encryption, special edit buffers, element commands, structure templates, display options, setup files, the Emacs keys, and the integrations (org-protocol, feeds, MobileOrg, the Lua API).
- The file starts folded (
#+STARTUP: overview). Put the cursor on a heading and press<Tab>to open it;<S-Tab>cycles the whole buffer. - Nothing breaks if you make a mess:
uundoes, andgit checkout examples/22-extras.orgrestores the file. g?lists every key of the buffer (see the next section).<prefix>means<leader>o, the defaultmappings.prefix. Withexamples/minimal_init.luathe leader is<Space>, so<prefix>'is<Space>o'.- Lines starting with Try: are exercises with the exact keys to press; Expect: says what you should see afterwards.
- Lines starting with
#followed by a space are Org comments that annotate the examples. They are dimmed and never exported.
Start Neovim from the repository root with the bundled init file, so your own config is not involved:
nvim -u examples/minimal_init.lua examples/22-extras.org
examples/minimal_init.lua sets the leader keys (<Space> and \),
points the agenda at examples/*.org, sends captures to a scratch
org_directory under stdpath("state"), and defines a few capture
templates and custom agenda commands. Everything this file talks about is
left at its default, so several sections show how to turn a feature on
for the current session with a :lua command. Those changes last until
you quit Neovim.
1.1. Keys in this file
| Key | Emacs key | What it does |
|---|---|---|
g? |
list the keys of the buffer | |
:Org |
pick any org command | |
<C-x><C-o> |
M-TAB |
complete (Insert mode) |
:Org lint |
M-x org-lint |
check the syntax of the buffer |
<prefix>' |
C-c ' |
edit in a separate buffer |
<prefix>ib |
C-c C-, |
insert a #+begin_ block |
<prefix>v |
M-h |
select the element |
<M-}> / <M-{> |
M-} / M-{ |
next / previous element |
<C-M-t> |
C-M-t |
swap with the previous element |
<C-c>: |
C-c : |
toggle fixed-width : lines |
<C-c><C-x>t |
C-c C-x t |
insert an inline task |
:Org num_mode |
M-x org-num-mode |
number the headlines |
<C-c><C-x>\ |
C-c C-x \ |
toggle pretty entities |
:checkhealth org |
check the setup |
2. Getting help inside Neovim
You never need to leave Neovim to find a key or a command.
2.1. g?: the keys of the current buffer
g? in an org buffer (or in the agenda) opens a floating window listing
every mapping of that buffer, grouped by topic ("Anywhere", "Structure",
"Links" …). Each row lists every key of one command (the Vim-style key,
the <prefix> key and the Emacs key) and what it does. i_ marks
Insert-mode keys.
In the window:
/searches the list like any buffer,{and}jump between sections,q,<Esc>org?close it.
Try: press g? here, then type /Footnote: and <CR>.
Expect: the cursor lands on the row
<leader>oif <C-c><C-x>f Footnote: jump / new / menu (count).
Press q to close the window.
Try: press g?, then } a few times.
Expect: the cursor jumps from one section heading to the next.
2.2. :Org: every command by name
Every action of org.nvim has a name (the names in :h org-keymaps), and
:Org {name} runs it, whether or not it has a key:
:Orgalone shows a picker with every command and its description.:Org <Tab>completes the names on the command line.- Some commands take arguments:
:Org agenda a,:Org capture t,:Org export html,:Org lint duplicate-name,:Org timer_countdown 5. - Some take a range, e.g.
:'<,'>Org link_previewpreviews the image links of the Visual selection.
Try: type :Org and <CR>, then type num to filter the list and pick
num_mode.
Expect: every headline of this file gets a number in front of it
(1, 2, 2.1 …). Run :Org num_mode again to remove them.
Try: type :Org toggle_ and press <Tab> repeatedly.
Expect: the command line cycles through toggle_archive_tag,
toggle_checkbox, toggle_comment and the other toggle_... commands.
2.3. :help
The manual is :h org. Every section has a tag, and most tags are named
after the Emacs feature: :h org-links, :h org-lint, :h org-crypt,
:h org-speed-commands, :h org-emacs-keys, :h org-differences.
:h org-keymaps lists every action with its default key, and
:h org-config every option.
Try: :h org-differences and read the part about the element commands.
3. Checking your setup: :checkhealth org
:checkhealth org checks everything org.nvim depends on and prints what
it found, in five parts:
- org.nvim
- Neovim version,
org_directory, agenda files, notes file, TODO keywords - external tools
- pandoc, makeinfo, LaTeX, and the interpreter of every Babel language
- image and LaTeX previews
- which image backend draws previews (see 21-images-latex)
- completion
- omnifunc, blink.cmp or nvim-cmp
- terminal keys
- whether tmux and the terminal pass the keys org maps
The last part matters more than it seems: keys such as <C-CR>, <S-CR>,
<C-,>, <M-S-CR> only reach Neovim when the terminal sends "extended
keys" (CSI u). Inside tmux you need set -s extended-keys on and a
terminal-features entry with extkeys. The check also reads the
keybinds of Ghostty, kitty and WezTerm and warns when one of them takes a
key org.nvim maps (Ghostty's ctrl+tab, kitty's ctrl+shift+enter, …).
On macOS, Option must send Alt for the <M-...> keys.
Try: run :checkhealth org.
Expect: a line "OK N agenda file(s) found" (the .org files of
examples/ plus those in the scratch directory), one line per Babel
language saying whether its interpreter was found, and a "terminal keys"
part listing the keys that need CSI u.
If a key does nothing, test it: in Insert mode press <C-v> and then the
key. Neovim inserts what it received: <C-CR> should give <C-CR>, not
a plain ^M.
4. Completion
org.nvim completes Org syntax as you type, like Emacs org-pcomplete
(M-TAB). The same candidates are available three ways:
- the built-in omnifunc,
<C-x><C-o>in Insert mode. It is set automatically in every org buffer, nothing to configure; - a blink.cmp source;
- an nvim-cmp source.
The blink.cmp source, in your blink.cmp options:
sources = {
per_filetype = { org = { inherit_defaults = true, "org" } },
providers = { org = { name = "Org", module = "org.completion.blink" } },
}
The nvim-cmp source:
require("cmp").register_source("org", require("org.completion.cmp").new())
-- and add { name = "org" } to the sources of cmp.setup.filetype("org", ...)
4.1. What completes where
You type … and it completes:
- stars and a space
- TODO keywords, and
COMMENT :after a headline title- tags of the file and
#+TAGS, not those already on the headline #+- keywords:
TITLE:,STARTUP:,BEGIN_SRC… #+STARTUP:and a space- startup words:
overview,indent,num… #+OPTIONS:and a space- export options:
toc:,num:,^:… #+FILETAGS: :- tags
#+begin_srcand a space- languages
#+begin_src lua :or#+HEADER: :- header arguments (
:results,:var…) #+BEGIN: clocktable :- clock table parameters
\- entities:
\alpha,\rarr… :at line start in a property drawer- property names the entry doesn't have yet
:at line start elsewhere- drawer names (
PROPERTIES:,LOGBOOK:…) [[- link types, stored links, abbreviations, headlines
[[*- headlines of the buffer
[[#CUSTOM_IDvalues of the buffer
With the omnifunc, what you already typed filters the list: #+ST then
<C-x><C-o> offers only STARTUP:. Keywords are offered in the case you
typed (#+st gives startup:).
4.2. Practice area
Try: on the empty line below, type #+STA then <C-x><C-o>.
Expect: #+STA becomes #+STARTUP: (the only match). Then type a space
and <C-x><C-o> again: a menu of startup words (fold, overview,
nofold …). Delete the line afterwards (dd).
Try: on the empty line below, type #+begin_src l then <C-x><C-o>.
Expect: a menu with latex, lisp and lua, the
languages starting with "l". Pick lua, type a space and :ex then
<C-x><C-o>: :exports is offered.
Try: on the empty line below, type [[*Comp then <C-x><C-o>.
Expect: [[*Completion (the heading of this section). Finish the link
with ]] and press <Esc>, then <CR> on it: the cursor jumps to
"* Completion".
Try: on the empty line below, type [[# then <C-x><C-o>.
Expect: the CUSTOM_ID values of this file: #extras-lint (the lint
section further down) and #same-id twice (from the intentionally broken
lint playground).
Try: on the empty line below, type \alp then <C-x><C-o>.
Expect: \Alpha and \alpha (the omnifunc ignores case when it
filters). With pretty entities on (see "Display options") they show as Α
and α.
4.2.1. Tags and TODO keywords
Try: put the cursor at the end of the headline "Tag practice" below
($), press a, type a space, : and <C-x><C-o>.
Expect: the menu offers crypt:, home: and urgent:, but not
work:, which the headline already has.
Try: on the headline "Keyword practice", put the cursor on the K of
"Keyword", press i and then <C-x><C-o>.
Expect: TODO, DONE and COMMENT. Pick TODO and type a space: the
headline now reads "TODO Keyword practice".
4.2.2. Properties
Try: put the cursor on the :Effort: line of the drawer below, press
o to open a new line in the drawer, type : and <C-x><C-o>.
Expect: property names such as ID:, CUSTOM_ID:, CATEGORY:,
ORDERED: … but not Effort:, which this entry already has. Delete
the line when done.
5. Structure templates and org-tempo
Blocks (#+begin_src, #+begin_quote …) are tedious to type. Two
helpers insert them.
5.1. <prefix>ib: insert a block
<prefix>ib (Emacs C-c C-,, org-insert-structure-template) asks for a
block type and inserts an empty block at the cursor, or wraps the Visual
selection in one. The cursor goes inside the block, or right after
#+begin_src and a space so you can type the language. The keys come from
structure_template_alist:
| Key | Block | Key | Block |
|---|---|---|---|
a |
#+begin_export ascii |
l |
#+begin_export latex |
c |
#+begin_center |
q |
#+begin_quote |
C |
#+begin_comment |
s |
#+begin_src |
e |
#+begin_example |
v |
#+begin_verse |
E |
#+begin_export |
h |
#+begin_export html |
<Tab> in the menu asks for any type by name (a custom block such as
#+begin_note too). Emacs writes that one in upper case
(#+BEGIN_NOTE), and so does org.nvim.
Try: select the two lines of the poem below with V j, press
<prefix>ib and then v.
Expect: the lines are wrapped:
#+begin_verse Roses are red, violets are blue. #+end_verse
Roses are red, violets are blue.
Try: on the empty line right after this paragraph (just before the
next heading), press <prefix>ib then s, type lua and <Esc>.
Expect: the empty line becomes an empty #+begin_src lua /
#+end_src block: after s the cursor waited right after
#+begin_src and a space, so what you typed became the language.
5.2. org-tempo: <s<Tab>
With tempo = true (the Emacs org-tempo module, off by default), typing
< plus a key of the table above and then <Tab> in Insert mode, alone
on a line, expands to the block. <L, <H, <A and <i expand to
the keywords #+latex:, #+html:, #+ascii: and #+index:, and
<I asks for a file to #+include:.
Try: turn it on for this session:
:lua require("org.config").opts.tempo = true
Then on the empty line below press i, type <q and <Tab>.
Expect: <q is replaced by
#+begin_quote #+end_quote
with the cursor on the empty line between them, still in Insert mode.
Try: on another empty line, i, <L, <Tab>, then type \newpage.
Expect: the line reads #+latex: \newpage.
6. Special edit buffers: <prefix>'
<prefix>' (Emacs C-c ', org-edit-special) opens the element at the
cursor in a separate buffer with the right filetype, so you get the
syntax, indentation and LSP of that language. Save it back:
<prefix>'(or<C-c>') in the edit buffer saves and closes it,:wsaves without closing,<C-c><C-k>throws the changes away (theedit_src.abortkey).
It works on:
| At the cursor | You edit |
|---|---|
a #+begin_src LANG block |
the code, filetype LANG |
an inline src_LANG{...} block |
the code, kept on one line |
a #+begin_example block |
the text |
a #+begin_export html block |
the text, filetype html |
a #+begin_comment block |
the text |
: fixed-width lines |
the lines without the : |
a LaTeX fragment $x$, \(x\), \[x\] |
the formula (filetype plaintex) |
a \begin{env} … \end{env} |
the environment |
a footnote reference [fn:label] |
the definition of that footnote |
#+INCLUDE:, #+SETUPFILE: |
visits the file |
a SCHEDULED: / DEADLINE: line |
runs <prefix>s / <prefix>d |
| a timestamp | the date prompt |
| a link | follows it |
Anywhere else it says "No special environment to edit here".
If the file changed under an open edit buffer, :w refuses to overwrite
the conflicting region; look at both versions and use :w! to force it.
6.1. Examples to edit
Try: put the cursor on print below and press <prefix>'.
Expect: a window with one line, print("from the edit buffer"), and
:set ft? says filetype=lua. Change the text, press <prefix>': the
block below shows your change.
print("from the edit buffer")
Try: <prefix>' on the first line of the fixed-width area below.
Expect: the edit buffer shows first line and second line without
the : markers. Add a third line, press <prefix>': it comes back as
: third line.
first line second line
Try: <prefix>' with the cursor inside the $...$ formula below.
Expect: a buffer with only a^2 + b^2 = c^2.
Pythagoras: \(a^2 + b^2 = c^2\) for a right triangle.
Try: <prefix>' on the environment below.
Expect: the whole \begin{align} … \end{align}, filetype
plaintex.
Try: <prefix>' on the inline block in this line: echo inline.
Expect: a buffer with echo inline, filetype sh.
Try: <prefix>' on [fn:extras1] in this sentence1.
Expect: a buffer with the text of the definition just below. Edit it
and press <prefix>' to put it back.
An example block: <prefix>' opens it too.
6.2. Narrowing is the same idea
<prefix>hn edits the current subtree in a separate buffer, <prefix>nb
the block at the cursor and <prefix>ne the element at the cursor
(Emacs C-x n s, C-x n b, C-x n e, which are taken by Vim keys
here). :w writes back, <C-c>' saves and closes. See
01-outline.
7. Elements: paragraphs, lists, blocks, tables …
Org calls the parts of an entry's text elements: paragraphs, plain
lists and their items, blocks, drawers, tables, fixed-width areas,
keywords and comments. The blank lines after an element belong to it.
Some commands work on whole elements, like Emacs' M-}, M-h, C-M-t:
| Key | Emacs | What it does |
|---|---|---|
<M-}> |
M-} |
next element at the same level |
<M-{> |
M-{ |
previous element |
<C-c><C-^> |
C-c C-^ |
up to the parent element |
<C-c><C-_> |
C-c C-_ |
into the first element inside |
<prefix>v |
M-h |
select it; again adds the next |
<C-M-t> |
C-M-t |
swap with the previous one |
<M-k> <M-j> |
M-up M-down |
on text: drag it up / down |
<C-c><M-f> |
C-c M-f |
next block (<C-c><M-b> prev) |
<C-c>: |
C-c : |
toggle : fixed-width |
Emacs' M-h is <M-h> (promote) in org.nvim, so select is <prefix>v.
The commands always act on whole lines.
7.1. Practice
Try: put the cursor on "First paragraph" and press <M-}> four times.
Expect: the cursor visits "- a list item", "- another item" (inside a
list it moves from item to item), the table, and "Last paragraph", in
that order. <M-{> goes back the same way.
First paragraph. It is one element even though it spans two lines.
- a list item
- another item
| a table | 1 |
Last paragraph.
Try: on "Last paragraph" press <C-M-t>.
Expect: "Last paragraph." and the table swap places, and the cursor is
after both. u to undo.
Try: on "First paragraph" press <prefix>v, then <prefix>v again.
Expect: Visual line mode with the two lines of the paragraph and the
blank line after it selected; the second <prefix>v adds the next
element, "- a list item".
Try: select the two lines of "Some output" below with Vj and press
<C-c>:.
Expect: both lines start with : and a space (fixed-width, shown verbatim and
exported as code). <C-c>: again removes the markers.
Some output of a command
8. Speed keys
Speed keys (Emacs org-use-speed-commands) are single letters that run a
command when you type them at the very start of a headline, before the
first star. Anywhere else the letter is inserted as usual. They are off by
default.
In org.nvim they work in Insert mode, with the cursor in column 0 of a
headline (in Normal mode those letters are Vim commands). So I (or 0i)
on a headline, then letters. After each command the cursor goes back to
column 0 of the headline it ends on, still in Insert mode, so you can
type several in a row. <Esc> leaves.
| Key | Command | Key | Command |
|---|---|---|---|
n / p |
next / previous heading | t |
change TODO state |
f / b |
next / previous sibling | , |
set priority |
u |
parent heading | 0 to 3 |
priority none, A, B, C |
F / B |
next / previous block | : |
set tags |
j |
go to a heading | e E |
set effort / next effort |
g |
go to a refile target | W |
set APPT_WARNTIME |
c / C |
cycle / global cycle | I O |
clock in / out |
SPC |
show the outline path | v |
agenda |
s |
narrow to the subtree | / |
sparse tree |
k |
cut the subtree | o |
open a link |
= |
column view | < > |
agenda restriction lock |
U / D |
move subtree up / down | i |
insert a heading |
r / l |
demote / promote | ^ |
sort children |
R / L |
the same with children | w |
refile |
a |
archive subtree | @ |
select the subtree |
# |
toggle COMMENT | ? |
list the speed keys |
Turn them on in your config with use_speed_commands = true, and add or
change keys with speed_commands:
require("org").setup({
use_speed_commands = true,
-- an action name, a function, or false to drop a key:
speed_commands = { x = "archive_subtree", n = false },
})
use_speed_commands may also be a function that returns true where speed
keys should apply.
8.1. Practice
Try: turn speed keys on for this session:
:lua require("org.config").opts.use_speed_commands = true
Then put the cursor on "Speed one" below and press 0i (column 0, Insert
mode). Type n, n, p.
Expect: the cursor moves to "Speed two", then "Speed three", then back
to "Speed two", staying in Insert mode in column 0. Nothing is inserted.
Try: still in Insert mode on "Speed two", type D.
Expect: "Speed two" moves below "Speed three": the order is one,
three, two.
Try: type r, then l.
Expect: "Speed two" gets one more star (a child of "Speed three"),
then l takes it away again.
Try: type SPC (the space bar).
Expect: the echo area shows the outline path, e.g.
Speed keys/Practice/Speed two.
Try: type ?.
Expect: a window listing every speed key by group ("Outline
Navigation", "Outline Visibility" …). q closes it.
Try: press <Esc>, then A at the end of a headline and type n.
Expect: a plain n is inserted: speed keys only work in column 0.
8.1.1. Speed one
8.1.2. Speed two
8.1.3. Speed three
9. Inline tasks
An inline task (the Emacs org-inlinetask module) is a TODO item in the
middle of an entry's text, which does not start a new entry: the text
after it still belongs to the entry above. It is a headline with at least
inlinetask_min_level stars (Emacs uses 15), optionally closed by a line
with the same stars and END:
* Meeting notes We discussed the budget. *************** TODO Send the slides to Ana Details of the task can go here. *************** END And the notes continue: this line is still part of "Meeting notes".
Inline tasks are off by default, as in Emacs without the module
(inlinetask_min_level = false). While they are off, a line of 15 stars
is simply a very deep headline. When they are on:
- they don't count as entries: folding, motions,
arand the agenda see the text around them as part of the entry above, <Tab>on one folds it up to itsENDline,<</>>promote and demote the task and itsENDline together, never below the minimum level,- only their last two stars are shown,
<C-c><C-x>t(org-inlinetask-insert-task) inserts one below the cursor, or around the Visual selection. It getsinlinetask_default_stateas keyword unless you give a count.- export writes them as a small box (
export.with_inlinetasks).
9.1. Practice
Try: turn inline tasks on:
:lua require("org.config").opts.inlinetask_min_level = 15
then reload the file so it is parsed again: :w and :e.
Open this section again and press <Tab> on the line "TODO Call the
plumber" (the one with 15 stars).
Expect: before, that line was a headline of its own. Now <Tab> folds
the task down to one line (its details and END line hidden), and the
line shows only two stars.
Try: put the cursor on "More text of the entry" and press <C-c><C-x>t.
Expect: two lines of 15 stars are added below it, the second one
followed by END, and you are in Insert mode after the stars of the
first one. Type TODO Buy milk and <Esc>.
Try: on "Kitchen" below, press ar in Visual mode (v a r).
Expect: the selection covers "Kitchen" down to "Last line of the
entry", inline task included: it is not a subtree of its own.
9.1.1. Kitchen
The sink leaks.
Ask for Tuesday morning.
More text of the entry. Last line of the entry.
10. Encryption: org-crypt
org-crypt encrypts the text of an entry with GnuPG, in the same format as
Emacs, so files encrypted in one editor decrypt in the other. It needs the
gpg program (crypt.gpg_program) installed and working; gpg --version
in a shell tells you.
- Entries tagged
:crypt:(crypt.tag_matcher, a match expression) are the ones the "entries" commands and encrypt-on-save act on. - The headline, the planning line, the property drawer, clock lines and
the LOGBOOK stay readable. The rest of the entry, children included,
becomes a
-----BEGIN PGP MESSAGE-----block. - The key: the
CRYPTKEYproperty, elsecrypt.key, is looked up in your public keyring. When nothing matches (crypt.keyis""by default, which never matches) the entry is encrypted symmetrically, with a passphrase you type.crypt.key = falsealways encrypts symmetrically. - Passphrases are read with
inputsecret()(twice when encrypting) and handed to gpg with--pinentry-mode loopback.
There are no default keys, as in Emacs. Use the commands (Emacs
M-x org-encrypt-entry and friends), or map the actions yourself:
| Command | What it does |
|---|---|
:Org crypt_encrypt_entry |
encrypt the entry at the cursor |
:Org crypt_decrypt_entry |
decrypt it |
:Org crypt_encrypt_entries |
encrypt every :crypt: entry |
:Org crypt_decrypt_entries |
decrypt them all |
<C-c><C-r> (:Org reveal) |
decrypts the entry first |
require("org").setup({
crypt = { key = "you@example.com", encrypt_on_save = true },
-- keep the tag on the entries that carry it, as Emacs recommends:
tags_exclude_from_inheritance = { "crypt" },
mappings = { org = {
crypt_encrypt_entry = "<prefix>xc",
crypt_decrypt_entry = "<prefix>xC",
} },
})
With crypt.encrypt_on_save = true, the :crypt: entries are encrypted
before every write, and stay encrypted in the buffer afterwards (decrypt
again to keep working), like Emacs' org-crypt-use-before-save-magic.
Leaks. Decrypted text can reach the disk through the swap file and a
persistent undo file. Before decrypting in a buffer with either,
crypt.disable_auto_save ("ask" by default) asks whether to turn them
off for that buffer. examples/minimal_init.lua already sets
noswapfile, so you won't be asked here. Yanked text goes to registers,
which shada may save: see :h org-crypt-leaks.
10.1. Practice
Try: put the cursor on "Wifi password" below and run
:Org crypt_encrypt_entry. Type a passphrase (test will do) and
<CR>, then the same again to confirm.
Expect: the message "No crypt key set, using symmetric encryption.",
and the two lines of text are replaced by a block like:
-----BEGIN PGP MESSAGE----- jA0ECQMI... -----END PGP MESSAGE-----
The headline and its tag stay as they are. The letters differ every time.
Try: on the same headline run :Org crypt_decrypt_entry and type the
passphrase.
Expect: the original two lines are back.
Try: encrypt it again, then press <C-c><C-r> (reveal) on the headline.
Expect: you are asked for the passphrase and the text is decrypted:
revealing an encrypted entry decrypts it, as in Emacs.
10.1.1. Wifi password  crypt
The network is "garden", the password is correct-horse-battery. This line is encrypted too.
11. Display options
These options change how the buffer looks, not the file. Most have a
#+STARTUP: word for one file and a ui option for all files. See
02-markup for emphasis and entities, and
21-images-latex for images and formulas.
Options under ui in your setup:
num- number headlines:
1,1.1,1.2… (#+STARTUP:num/nonum; toggle::Org num_mode) indent_mode- indent text under its headline (org-indent-mode)
(
#+STARTUP:indent/noindent) hide_leading_stars- show only the last star
(
#+STARTUP:hidestars/showstars) pretty_entities\alphashows as α,x^2as x² (#+STARTUP:entitiespretty/entitiesplain; toggle:<C-c><C-x>\)conceal_links- show only link descriptions
(toggle:
<prefix>lt) hide_emphasis_markers- hide the
*/_… markers bullets- symbols in place of the stars
checkboxes- icons for
[ ][-][X] todo_keyword_faces,priority_faces,tag_faces- colours per keyword / priority / tag
examples/minimal_init.lua sets bullets (◉ ○ ✸ ✿) and checkboxes,
which is why the stars of this file look the way they do.
Headline numbering follows the org-num options: num_max_level,
num_skip_commented, num_skip_tags, num_skip_unnumbered (a subtree
with an UNNUMBERED property) and num_format_function.
Try: :Org num_mode, then <S-Tab> until you see every headline.
Expect: "Org-Num mode enabled", and the headlines of this file are
numbered: "How to use this file" is 1, its child "Keys in this file" is
1.1, "Getting help inside Neovim" is 2 and so on. :Org num_mode
again removes the numbers.
Try: change the #+STARTUP: line at the top of this file to
#+STARTUP: overview indent, press <C-c><C-c> on it and reload with
:e.
Expect: the body text of each entry is indented under its headline
(only on screen: the file is unchanged) and only one star of each
headline shows. Put the line back afterwards.
Try: press <C-c><C-x>\ on this line: α → β, E = mc^2.
Expect: with pretty entities on you see α → β and mc². Press it again
to see the text as written.
Highlight groups (OrgTodo, OrgTags, OrgLink, OrgHeadlineLevel1
…) are listed in :h org-highlights; set them with nvim_set_hl().
12. Setup files: #+SETUPFILE
Several files can share their settings (#+TODO:, #+TAGS:, #+LINK:,
#+PROPERTY:, #+OPTIONS: …) through a setup file:
#+SETUPFILE: "~/org/setup/common.org"
Only the settings of that file are imported, never its headings. Paths
are relative to the file that names them; quotes allow spaces; setup
files may name other setup files (cycles are stopped). <prefix>' on
the line visits the file.
Try: add this line at the top of this file, below #+TAGS:, and press
<C-c><C-c> on it:
#+SETUPFILE: tutorial.org
Then put the cursor on any headline of this file and press <prefix>S.
Expect: the TODO keyword menu now offers TODO, NEXT, WAITING,
DONE and CANCELLED: the #+TODO: line of tutorial.org
was imported. Press <Esc>, delete the line again and press
<C-c><C-c> on the #+TAGS: line to refresh.
A missing setup file is ignored when the file is read; :Org lint
reports it (see the next section).
13. org-lint: check the syntax
:Org lint (Emacs M-x org-lint) looks for mistakes that make Org read
your text differently from what you meant: a link to a heading that
doesn't exist, a footnote without its definition, a #+begin_src without
a language, a misspelt header argument … It lists what it finds in the
location list (:lopen), titled "org-lint". Each entry is
checker: message; E marks the reliable checks ("high trust"), W the
heuristics ("low trust"). It has no default key, as in Emacs.
:Org lintruns every checker.:Org lint duplicate-name invalid-fuzzy-linkruns only those (<Tab>completes the checker names), like Emacs'C-u C-u M-x org-lint.:lnext/:lprev(or]l/[lin Neovim 0.11+) walk the list.- From Lua:
require("org.lint").lint(0)returns the reports as a list of{ lnum, col, checker, message, trust }.
Every checker of Org 9.8 is implemented; :h org-lint lists them.
The rest of this file is clean. The subtree below is full of mistakes on
purpose. It is marked COMMENT, so it is never exported and the agenda
ignores its TODO entries and dates, but org-lint still checks it.
Try: run :Org lint, then :lopen.
Expect: 28 entries, all in "COMMENT Lint playground", in the order of
the list below. <CR> on an entry jumps to its line.
Try: :Org lint duplicate-name.
Expect: exactly two entries, duplicate-name: Duplicate NAME "twice",
on the two #+NAME: twice lines.
Try: fix one mistake (for example add sh after the bare
#+begin_src), run :Org lint again.
Expect: that entry is gone and the others remain.
Each mistake of the playground and the entry it produces, in file order:
- two headings with
CUSTOM_ID: same-id duplicate-custom-id: Duplicate CUSTOM_ID property "same-id"(twice)[[*No such heading]]invalid-fuzzy-link: Unknown fuzzy location "No such heading"[[#no-such-id]]invalid-custom-id-link: Unknown custom ID "no-such-id"[[id:...]]to an unknown IDinvalid-id-link: Unknown ID "00000000-..."[[file:does-not-exist.org]]link-to-local-file: Link to non-existent local file "does-not-exist.org"- a link followed by a stray
] trailing-bracket-after-link: Trailing ']' after link end#+begin_srcwith no languagemissing-language-in-src-block: Missing language in source block:foo baron a src blockwrong-header-argument: Unknown header argument ":foo":results maybewrong-header-value: Unknown value "maybe" for header ":results"#+TBLNAME:obsolete-affiliated-keywords: Obsolete affiliated keyword: "TBLNAME". Use "NAME" instead#+BEGIN_HTMLblockdeprecated-export-blocks: Deprecated syntax for export block. Use "BEGIN_EXPORT HTML" instead#+NAME:with nothing after itorphaned-affiliated-keywords: Orphaned affiliated keyword: "NAME"[fn:nodef]without a definitionundefined-footnote-reference: Missing definition for footnote [nodef][fn:unused]definition, no referenceunreferenced-footnote-definition: No reference for footnote definition [unused]:EFFORT: lotsinvalid-effort-property: Invalid effort duration format: "lots":TODO:in a property drawerspecial-property-in-properties-drawer: Special property "TODO" found in a properties drawerSCHEDULED:after body textmisplaced-planning-info: Misplaced planning info lineSCHEDULED: [inactive date]planning-inactive: Inactive timestamp in SCHEDULED will not appear in agenda.<2026-12-03 Fri>(it's a Thursday)timestamp-syntax: Potentially malformed timestamp <2026-12-03 Fri>. Parsed as: <2026-12-03 Thu>[#Z]priority: Out-of-bounds priority 'Z'- a list numbered 1., 3.
item-number: Bullet counter "3. " is not the same with item position 2. Consider adding manual [@3] counter.- tags
::work:: spurious-colons: Tags contain a spurious colon- two
#+NAME: twice duplicate-name: Duplicate NAME "twice"(twice)#+ATTR_HTML :width 10(no colon)invalid-keyword-syntax: Possible missing colon in keyword "ATTR_HTML"#+SETUPFILE: no-such-setup.orgnon-existent-setupfile-parameter: Non-existent setup file "no-such-setup.org"#+begin_quotenever closedinvalid-block: Possible incomplete block "#+begin_quote"
14. Emacs keys
Coming from Emacs? Org's own keys (org-mode-map) work on top of the
Vim-style keys, so muscle memory keeps working. They live in three
sections of mappings, each of which can be turned off:
require("org").setup({
mappings = {
emacs = false, -- C-c ... keys in Normal mode
emacs_insert = false, -- <C-CR>, <C-S-CR> in Insert mode
emacs_global = false, -- <C-c>a agenda, <C-c>c capture, <C-c>l store link
},
})
C-ckeys are Normal-mode only: in Insert mode<C-c>still leaves Insert mode.- Emacs'
C-uprefix is a Vim count:4<C-c>.isC-u C-c .,16<C-c><C-t>isC-u C-u C-c C-t. - Keys marked "(ctx)" in
:h org-emacs-keysdepend on the cursor, like in Emacs:<C-c>-inserts a table rule in a table, cycles the bullet on a list item, and toggles an item elsewhere. - Emacs keys that Vim needs are moved:
M-h(mark element) is<prefix>v,C-x n s/C-x n b(narrow) are<prefix>hn/<prefix>nb.
The Emacs key is typed as written, in Vim's key notation: C-c C-t is
<C-c><C-t>, C-c . is <C-c>., M-RET is <M-CR>. The most used
ones and their Vim-style equivalents:
| Emacs | Vim-style key | Action |
|---|---|---|
C-c C-c |
<prefix><CR> |
act on the context |
C-c C-t |
cit / <prefix>S |
change TODO state |
C-c C-s / C-c C-d |
<prefix>s / <prefix>d |
schedule / deadline |
C-c . / C-c ! |
<prefix>i. / <prefix>i! |
timestamps |
C-c C-q |
<prefix>t |
set tags |
C-c C-x p |
<prefix>p |
set a property |
C-c C-l |
<prefix>li |
insert a link |
C-c C-o |
<CR> / gx |
open the link |
C-c l |
<prefix>ls |
store a link |
C-c a / C-c c |
<prefix>a / <prefix>c |
agenda / capture |
C-c C-w |
<prefix>r |
refile |
C-c C-x C-i / C-o |
<prefix>xi / <prefix>xo |
clock in / out |
C-c C-e |
<prefix>e |
export |
C-c ' |
<prefix>' |
edit special |
C-c C-x f |
<prefix>if |
footnote |
C-c C-, |
<prefix>ib |
structure template |
C-c / |
<prefix>/ |
sparse tree |
C-c ; |
<prefix>hC |
toggle COMMENT |
C-c * / C-c - |
<prefix>* / <prefix>- |
toggle heading / item |
C-c C-r |
reveal the context | |
C-c C-v e |
<prefix>be |
run a src block |
M-RET |
new heading / item / row | |
C-RET |
<prefix>ih |
heading after subtree |
Try: on the headline "Emacs practice" below, press <C-c><C-t>.
Expect: it becomes "TODO Emacs practice" (this file has only
TODO and DONE, so the key cycles). Press <C-c><C-t> twice more to
get DONE and then no keyword.
Try: on the same headline press <C-c>;.
Expect: "COMMENT Emacs practice". Again to remove it.
14.0.1. Emacs practice
15. Integrations
15.1. org-protocol: capture from the browser
org-protocol:// URLs let a browser or another program talk to a
running Neovim, like Emacs' org-protocol:
org-protocol://capture?template=t&url=URL&title=TITLE&body=TEXT- capture with template
t org-protocol://store-link?url=URL&title=TITLE- store the link for
<prefix>li org-protocol://open-source?url=URL- open the local file of a published page
Neovim must listen on a socket (nvim --listen ~/.cache/nvim/org.sock),
and the operating system must hand org-protocol: URLs to a small
handler that calls:
nvim --server ~/.cache/nvim/org.sock --remote-expr "v:lua.require'org.protocol'.handle('%u')"
:h org-protocol has the desktop file (Linux), the macOS app and a
bookmarklet. You can try it without any of that with :Org protocol:
Try: run
:Org protocol org-protocol://store-link?url=https%3A%2F%2Forgmode.org&title=Org%20Mode
then on the empty line below press <prefix>li, <CR> at the "Insert
link (default https://orgmode.org)" prompt and <CR> again to accept the
description "Org Mode".
Expect: the message "insert_link to insert new Org link, p to insert
"https://orgmode.org"", and the line becomes
[[https://orgmode.org][Org Mode]].
Try: run
:Org protocol org-protocol://capture?template=t&url=https%3A%2F%2Fneovim.io&title=Neovim
Expect: the capture window of the "Task" template of
minimal_init.lua, with a [[https://neovim.io][Neovim]] link in it.
<C-c><C-k> cancels.
15.2. RSS and Atom feeds
org-feed adds the items of RSS and Atom feeds as child headlines of an inbox heading. List the feeds in your config:
require("org").setup({
feed = {
feeds = {
{ name = "Neovim news", url = "https://neovim.io/news.xml",
file = "~/org/feeds.org", headline = "Neovim news" },
},
},
})
<C-c><C-x>g (:Org feed_update_all) fetches every feed with curl
and adds the new items; <C-c><C-x>G (:Org feed_goto_inbox) jumps to
an inbox. The items already seen are remembered in a :FEEDSTATUS:
drawer, in the format Emacs writes. See :h org-feed.
15.3. MobileOrg
:Org mobile_push copies your agenda files and agenda views into a
staging directory (mobile.directory) that a MobileOrg-style app syncs;
:Org mobile_pull brings back what you captured and edited on the phone.
See :h org-mobile.
15.4. The Lua API
require("org") has a small API for your own config and scripts
(:h org-api):
require("org").agenda("a")andrequire("org").capture("t")open a view or a template,require("org").action("cycle")runs any action by name,require("org").statusline()returns the running clock and timer as a string for your statusline, empty when nothing runs,- lower-level modules:
org.parser,org.files,org.date,org.agenda.search,org.export,org.dblock.
Lua source blocks run inside Neovim, so you can try the API right here.
Try: press <C-c><C-c> in the block below and answer y when asked
"Evaluate this lua code block on your system?".
Expect: the #+RESULTS: under it is rewritten with the same value,
<2026-10-05 Mon>: one week after the date in the code. Change 1 to
2 and run it again: <2026-10-12 Mon>.
local date = require("org.date")
return date.parse("<2026-09-28 Mon>"):add(1, "w"):to_string()
<2026-10-05 Mon>
16. Further reading
:h orgthe whole manual;:h org-keymapsevery key;:h org-commandsevery:Orgsubcommand;:h org-configevery option.:h org-completion,:h org-lint,:h org-speed-commands,:h org-inlinetask,:h org-crypt,:h org-crypt-leaks.:h org-babel-edit-special(special edit buffers),:h org-elements,:h org-tempo,:h org-appearance,:h org-highlights.:h org-setupfile,:h org-in-buffer-settings.:h org-emacs-keys,:h org-differences.:h org-protocol,:h org-feed,:h org-mobile,:h org-api.- The other example files: 00-index.
Footnotes:
The definition of this footnote, edited from the reference.