org.nvim

Links, IDs and attachments

Syntax: [[target][description]], [[target]], plain links (https://en.wikipedia.org/wiki/Foo_(bar): balanced parentheses are part of the link, trailing punctuation is not) and <angle> links. Bracket links may span lines. With ui.conceal_links (org-link-descriptive), only the description is shown.

Targets:
  https://… http://… ftp:… mailto:…   opened with vim.ui.open()
  doi:10.1000/x                       links.doi_server_url + path
  file:path  ./rel  ~/p  /abs          open a file; add a search option:
      ::42  ::*Heading  ::#custom-id  ::(coderef)  ::/regexp/  ::some text
  file:notes/*.org                    a wildcard (*?{) in the file name
                                      lists the matching files (Emacs
                                      opens Dired); <CR> opens the file on
                                      the line, q closes the list
  file+sys:path  file+emacs:path      system application / always Neovim
  id:UUID  id:UUID::search            headline with that ID (the search
                                      runs inside its subtree)
  #custom-id                          headline with that CUSTOM_ID
  *Heading                            headline in the current file
  (label)                             coderef in a src/example block
  <<target>>  text                    dedicated target, #+NAME, headline
  shell:cmd                           run in the file's directory (asks
                                      first); the output goes to a new
                                      *Org Shell Output* buffer, shown
                                      unless it is one line (then echoed);
                                      cmd & shows it at once and streams;
                                      links.shell_output = "terminal"
                                      runs it in a terminal instead
  elisp:(sexp)  elisp:command         evaluate in a separate Emacs, show
                                      the value (asks first); a command
                                      is called interactively there
  help:topic                          Vim :help
  man:printf(3)  man:ls::-l           :Man, with an optional search
  info:emacs#Top                      the info program in a terminal
                                      (exported to HTML as a link to the
                                      manual's URL in
                                      links.info_other_documents, else
                                      gnu.org or MANUAL.html)
  bibtex:refs.bib::key                a file link to a BibTeX entry
                                      (org-bibtex)
  attachment:name                     file in the entry's attachment dir
  abbrev:tag  abbrev::tag  abbrev     #+LINK: or links.abbreviations
                                      (%s = tag, %h = url-encoded,
                                      %(fn) = (fn tag) in Emacs, or a
                                      Lua function(tag) as the value)
  custom:path                         links.types (see below)

Searches (org-link-search) ignore case and runs of whitespace (a text search also matches across line breaks), and headline searches ignore the TODO keyword, priority, COMMENT, tags and statistics cookies. A fuzzy link tries <<target>>, #+NAME: and headlines. When nothing matches in an Org file, links.search_must_match_exact_headline decides: with "query-to-create" (default) you are asked "No match - create this as a new heading?" (the heading is added at the end of the file, or as the last child of the entry for id:ID::*Heading); with true the failure is reported; with false a plain text search follows, which never lands on the link itself. *Heading searches never fall back to text. ::/regexp/ takes an Emacs regexp: in Org files it shows a sparse tree (org-sparse-tree), elsewhere a location list (like occur); common Emacs syntax is translated to Vim regexps, see org-differences.

An abbreviation such as #+LINK: ex %(my-function) calls the Emacs function with the tag in a separate emacs --batch (babel.emacs_lisp; load the function with its args, e.g. { "-Q", "--batch", "-l", "~/my-links.el" }). Like Emacs, only functions whose org-link-abbrev-safe (or pure) property is t are called; another function, or one that does not return a string, gives a warning and the link is left as written. Results are kept for the session.

<CR>, gx, <prefix>o  Open the link, footnote or date at the cursor.
<C-c><C-o>      The same. On the tags of a headline, show a tags agenda for
                the tag under the cursor (a count: TODO entries only).
                Elsewhere on a headline, pick one of the entry's links, or
                "Open all links"; without links, open the attachment
                directory when there is one.
                With a count (C-u), files open in Neovim even when an
                external app applies, and internal links are shown in
                another window; with 16 (C-u C-u), files open with the
                system application.
                file: and id: links open in another window, splitting
                when there is none, like Emacs (links.frame_setup).
<prefix>ls      Store a link to the current location (works in any
                buffer, org-store-link). In org buffers this is, in order:
                the store function of a custom link type, an id: link
                (see links.use_id), the <<target>> under the cursor
                (file:…::target), the #+NAME: of the element at the
                cursor, the current line before the first heading, or the
                heading: file:…::#custom-id or file:…::*Heading. When
                the entry has a CUSTOM_ID, a file:…::#custom-id link is
                stored too. In Visual mode the selection is the search
                string (links.context_for_files may limit its lines).
                In other files, the current line is the search string (an
                empty line gives a plain file: link). Search strings
                collapse whitespace and drop statistics cookies. In the
                agenda, the link points to the item's entry; in a src
                block edit buffer (org-babel), it stores a coderef link
                file:…::(label) and adds a (ref:label) to the line when
                it has none. Help buffers give help:tag, man pages
                man:page, netrw/oil listings a link to the file under the
                cursor, BibTeX files file:refs.bib::key described by
                the authors, year and title (org-bibtex).
                A count of 4 (C-u) toggles links.context_for_files, 16
                (C-u C-u) skips custom store functions, 64 stores one link
                per selected line.
<prefix>li      Insert a link (org-insert-link). Stored links, their
                descriptions, link types and file paths (relative to the
                file's directory) are completed; an empty answer inserts
                the last stored link. Entering a type alone (file,
                id:, a custom type) completes it. On a link, edit it (a
                plain or angle link becomes a bracket link). In Visual
                mode, the selection becomes the description. Links to the
                current file become their search option, and file paths
                follow links.file_path_type. An inserted stored link is
                removed from the list (links.keep_stored_after_insertion).
                With a count of 4 (C-u), pick a file; 16 (C-u C-u) uses an
                absolute path; 64 inverts keep_stored_after_insertion.
<prefix>lL      Insert the last stored link and a newline, and forget it
                (C-c M-l). A count inserts that many. Links without a
                description get <no description>, like Emacs.
<prefix>lA      Insert every stored link as a -  line and forget them
                (C-c C-M-l); a count of 4 keeps them.
<prefix>lt      Toggle concealed link display (org-toggle-link-display):
                only links change, not emphasis markers.
<prefix>ln/lp   Next / previous link (takes a count). Links in src and
                example blocks, comments and verbatim are skipped;
                repeating a failed search wraps around the buffer.
<C-c>&          Jump back to where you followed a link from (<C-o> works
                too).
:Org open_at_point_global  Follow the Org link at the cursor in any buffer
                (org-open-at-point-global): a bracket, angle or plain link,
                a timestamp (the agenda of that day), else a URL or an
                e-mail address. It has no key, like Emacs; map it with
                mappings.global.open_at_point_global = "gX". Internal
                links (headings, targets) are not searched outside Org.
:Org occur_link_in_agenda_files  Search the agenda files for the link
                <prefix>ls would store here, [[file:…::*Heading][Heading]]
                (org-occur-link-in-agenda-files); the matches go to the
                quickfix list. The link is not stored.
<MiddleMouse>   Open the link clicked (org-open-at-mouse).
<RightMouse>    Open the link clicked in Neovim, even when file_apps
                names another program, internal links in another window
                (org-find-file-at-mouse). Away from a link both clicks do
                what they normally do.
<LeftMouse>     With links.mouse_1_follows_link (org-mouse-1-follows-link,
                default 450), a click on a link follows it: a click held
                longer than that many ms, or a drag, only moves the cursor;
                true follows on any click, "double" on a double click,
                false never.
<Tab>           With links.tab_follows_link (org-tab-follows-link), <Tab>
                on a link follows it instead of cycling.
Following a link, footnote or date with <CR>, <C-c><C-o>, <Tab> or the
mouse fires the User event OrgFollowLink (org-follow-link-hook), with
data.bufnr the buffer it was followed from:
vim.api.nvim_create_autocmd("User", {
  pattern = "OrgFollowLink",
  callback = function() vim.cmd("normal! zz") end,
})
A file: link to a directory opens the directory, or its index.org with
links.open_directory_means_index_dot_org
(org-open-directory-means-index-dot-org). An external program (file_apps
or the system application) is not started for a file that doesn't exist
("No such file: …") unless links.open_non_existing_files
(org-open-non-existing-files); Neovim opens missing files as new ones.
<C-c>'          On #+INCLUDE:, #+SETUPFILE: or #+BIBLIOGRAPHY:, visit
                the file.
:Org link_open_from_string [link]
                Open a link typed at a prompt (or given), as if it were in
                the buffer (org-link-open-from-string). The text must be
                one link: "No valid link in ..." / "Garbage after link in
                ...". Also the link_open_from_string action (unbound).

Radio targets <<<text>>> make every occurrence of "text" a link to them (also in export): case is ignored, the words must stand alone and may be split over lines. Radio links are highlighted; after adding a radio target, <C-c><C-c> on it refreshes the highlighting. Coderefs: a (ref:name) label at the end of a src or example block line (or the block's -l "fmt" format) can be reached with [[(name)]].

Custom link types (org-link-set-parameters) go in links.types, either a function (follow) or a table:

links = { types = { jira = {
  follow = function(path, link, count) vim.ui.open("https://jira/" .. path) end,
  complete = function() return "jira:" .. vim.fn.input("Issue: ") end,
  store = function(interactive) end,   -- { link = ..., desc = ... } or nil
  export = function(path, desc, backend) end,  -- "html", "md", "latex", "ascii"
  face = "DiagnosticWarn",
  insert_description = function(link, desc) return link:sub(6) end,
  preview = function(path, ctx) end,   -- image file: see |org-images|
} } }

links.make_description(link, desc) gives other links a default description. links.search_functions run first on search options and links.translation_function(type, path) rewrites links before they open.

IDs are stored in id.locations_file: JSON by default, or Emacs's
org-id-locations format (an alist (("~/org/a.org" "id1" "id2") ...)),
detected from the file's contents (a new file is JSON when its name ends
in .json) or forced with id.locations_format ("json" or "emacs"). Set
id.locations_file = "~/.emacs.d/.org-id-locations" to share the
database with Emacs; file names are written with ~ like Emacs, or
relative to the database with id.locations_file_relative
(org-id-locations-file-relative). :Org id_update_locations rebuilds
it from the agenda files, their archive files (id.search_archives,
default true), id.extra_files, the loaded org buffers and the files
already known; an ID that is not where the database says triggers the same
scan. The database also records what each file held when it was last
scanned (a " org.nvim-scan" member in JSON, a ; org.nvim-scan:
comment in Emacs's format, which Emacs ignores), so a rebuild reads again
only the files whose modification time or size changed. Refiled and archived entries are recorded at their new place.
New IDs follow id.method (org-id-method): "uuid" (default), "ts" (a time
stamp in id.ts_format) or "org" (a compact time based ID), with
id.prefix and a colon in front when set (org-id-prefix).
id.include_domain (org-id-include-domain) adds "@" and the host name
(a name without a dot gets ".mail-host-address-is-not-set", like Emacs)
to "ts" and "org" IDs.
Typing id: alone in org-insert-link completes a heading, not an ID
(org-id-completion-targets): the headings of id.completion_targets,
refile target specs (org-refile) that default to every heading of the
buffer and of the files holding known IDs (files = "id"), as outline
paths; the chosen heading gets an ID when it has none. In a buffer
without a file only the specs naming files count; with no heading to
offer you type the link.
Links to headings are file: links unless
links.use_id asks for id: links (IDs are then created in your files).
Before the first heading the ID goes into a file-level property drawer at
the top of the file (after leading # comments, before #+TITLE), like
Emacs 9.8; the link is described by the #+TITLE or the file name, and
from a later line it gets that line as search string (id:ID::text).
id: links get a search string for a named element or a selection below
the heading (id:ID::tbl1, id.link_use_context); with
id.link_consider_parent_id, a heading without an ID is linked through
its ancestor's ID (id:ID::*Child).
<prefix>lI      Get or create the entry's ID; with a count (C-u), replace
                it by a new one.
<prefix>lg      Go to an entry by ID (org-id-goto, IDs are completed).
<prefix>ly      Copy the entry's ID, creating it if needed (org-id-copy).
:Org id_store_link stores an id: link whatever links.use_id says.
<prefix>A opens the attachment menu (org-attach):
  a  attach a file (method from attach.method: cp/mv/ln/lns)
  c m l y  attach by copy / move / hard link / symbolic link
  u  download a URL (curl)   b  attach the contents of a buffer
  n  create a new file       z  sync the ATTACH tag with the directory
  o  open an attachment (system app or links.file_apps)
  O  open it in Neovim
  f  open the directory with the system app
  F  open the directory in Neovim
  d  delete one              D  delete all
  s  set the DIR property    S  remove the DIR property
Directories can be attached too (copied recursively with "cp").
attach.commands adds commands to the dispatcher or replaces them
(org-attach-commands): { x = { fn = function(target) ... end, desc =
"..." } }, where target is { bufnr, lnum } of the entry; false
removes a built-in key. With attach.expert (org-attach-expert) the
dispatcher asks for the key at a one-line prompt instead of showing the
menu.
:Org attach_from_file_manager
                In a netrw or oil.nvim buffer: attach the marked files
                (netrw), the Visual selection (oil) or the file under the
                cursor to the entry at the cursor of a window showing an
                Org buffer, with attach.method
                (org-attach-dired-to-subtree). Map it in those buffers,
                e.g. vim.keymap.set("n", "<C-c><C-x>a", ...) in a
                FileType netrw autocmd.

The directory is the DIR property (or the older ATTACH_DIR), else it comes from the ID: attach.dir + a folder from attach.id_to_path (org-attach-id-to-path-function-list: "uuid" gives ab/cdef..., "ts" gives 202609/25T..., "fallback" gives __/a/abcdef..., or your own functions); an existing folder wins, also under the default "data/". Parents' DIR / ID count only as attach.use_inheritance says ("selective", the default, follows use_property_inheritance). An entry without either gets an ID (attach.preferred_new_method: "id", "dir" asks for a DIR, "ask", or false). Attaching adds the attach.auto_tag tag (ATTACH) and stores a link (attach.store_link: "attached" an attachment: link, "file" a file: link to the attachment, true to the source, false none). Other options: dir_relative (store DIR relative to the file), sync_delete_empty_dir ("query", true, false) and archive_delete (delete the attachments of archived entries: false, true, "query").

Pasting images and files

:Org yank_media (no default key; Emacs's yank-media) pastes what the
system clipboard holds, read with wl-paste (Wayland), xclip (X11) or
osascript (macOS):
- an image: saved as clipboard-<time stamp>.png (or the name from
  yank.image_file_name_function) and linked at the cursor. With
  yank.image_save_method = "attach" (the default) it becomes an
  attachment of the entry ([[attachment:NAME][NAME]]); a directory
  (relative to the file's) or a function returning one saves it there
  ([[file:img/NAME.png]]) (org-yank-image-save-method).
- files copied in a file manager (GNOME, KDE, MATE, or a Finder file on
  macOS): handled like dropped files; cut files are moved.
When the clipboard holds neither, nothing happens.

Dropping files on the terminal pastes their paths; in an Org buffer a paste made only of existing files (file:// URIs or paths, quoted or escaped) is handled as a drop (yank.dnd_paste, true). yank.dnd_method (org-yank-dnd-method) says what to do: "attach" (with yank.dnd_default_attach_method, else attach.method; an image goes to the yank.image_save_method directory when that is one), "open", "file-link" (insert [[/path/to/file]]) or "ask" (the default; Esc pastes the text instead). Several files get a space after each link.

BibTeX entries (ol-bibtex)

A headline can hold a BibTeX entry: its type in the btype property
(bibtex.type_property_name), its key in CUSTOM_ID (bibtex.key_property)
and each field in a property of its name (TITLE, AUTHOR, ...), with
bibtex.prefix in front when set. When there is no TITLE, the headline is
the title (bibtex.treat_headline_as_title). These actions (unbound, like
Emacs; :Org <name> or a mapping) follow ol-bibtex:
  bibtex_export           write the entries of every headline to a .bib
                          file (asked for, default FILE.bib) (org-bibtex)
  bibtex_export_to_kill_ring  copy the entry of the headline to the
                          unnamed register
  bibtex_check            ask for the missing required fields and the key;
                          with a count (C-u) the optional fields too; a
                          choice (editor or author) asks "Field: " first
  bibtex_check_all        the same for every headline
  bibtex_create           a new headline: type, title, required fields
                          (count: optional ones too), key, bibtex.tags
  bibtex_create_in_current_entry  the same data for the current headline
  bibtex_read             in a .bib buffer: read the entry at the cursor
  bibtex_read_buffer / bibtex_read_file  read every entry of a buffer /
                          file ("Parsed N entries")
  bibtex_write            insert the first entry read as a headline after
                          the cursor line (org-bibtex-write)
  bibtex_yank             insert the BibTeX entry of the unnamed register;
                          with a count (C-u), into the current headline
  bibtex_import_from_file insert every entry of a file
  bibtex_search           search view of the agenda files for the entries
                          that match a string (STRING +{:btype:})
Written headlines use bibtex.headline_format_function(fields) for their
text (default the title). Keys are asked for ("id: ") unless
bibtex.autogen_keys makes them like Emacs' bibtex-generate-autokey
(doe20:_big_title). With bibtex.tags_are_keywords the keywords field
becomes tags (spaces become _) and tags become keywords (not those in
bibtex.tags or bibtex.no_export_tags; inherited ones too with
bibtex.inherit_tags). bibtex.export_arbitrary_fields (with a prefix)
exports every prefixed property instead of the fields of the type.
Storing a link in a .bib buffer gives file:refs.bib::key described like
"Doe & Roe 2020: Big Title"; following it puts the entry at the top of the
window. bibtex: links open like file: links.

Attachments in git (org-attach-git)

With attach.git = true (Emacs: (require 'org-attach-git)), a change made through org-attach (attach, download, attach a buffer, delete one or all, sync) is committed when the attachment root (attach.dir, relative to the org file) is inside a git work tree: new and modified files are git added, deleted ones git rmed, and a commit "Synchronized attachments" is made. attach.git_dir = "individual-repository" uses the entry's own attachment directory instead (org-attach-git-dir). When the repository has been set up with git annex init, files of at least attach.git_annex_cutoff bytes (32768; false: never) are added with git annex add, and opening an attachment (o/O) whose content is not present runs git annex get (attach.git_annex_auto_get: "ask", true or false). The Emacs hooks are User autocmds: OrgAttachAfterChange (data: { dir }) and OrgAttachOpen (data: { path }).

MobileOrg (org-mobile)

A port of Org Mobile, the protocol MobileOrg-style applications use to view and capture offline. Set mobile.directory to the staging directory the application syncs with (WebDAV, Dropbox, ...; when Neovim cannot write it directly, copy the files in mobile.post_push_hook and mobile.pre_pull_hook).

:Org mobile_push    Copy mobile.files (default: the agenda files) into
                    the staging directory, named relative to
                    org_directory, and write index.org (#+TODO lines
                    from todo_keywords and the files, #+TAGS,
                    #+ALLPRIORITIES from mobile.allpriorities, links to
                    the files), agendas.org (the agenda views chosen by
                    mobile.agendas: "default" is the week agenda and the
                    TODO list, "custom" the agenda.custom_commands, "all"
                    both, or a list of keys; search, stuck and sparse-tree
                    commands are left out) and checksums.dat. Agenda
                    entries first get an ID
                    (mobile.force_id_on_agenda_items; else agendas.org
                    refers to them by outline path, olp:).
:Org mobile_pull    Move the entries of mobileorg.org to the end of
                    mobile.inbox_for_pull (emptying it), then apply the
                    edit requests found there (below) and show the flagged
                    entries of the changed files in an agenda.
:Org mobile_apply   Apply the requests in the current buffer (Emacs
                    org-mobile-apply).
:Org mobile_goto_inbox   Open the inbox.
:Org mobile_flagged Agenda of the FLAGGED entries (dispatcher ?).
In the agenda: ? shows the flagging note of the entry in another window
and copies it to the unnamed register; ? again (without moving) offers
to remove the FLAGGED tag and the note. <C-c><C-x><CR>g pulls,
<C-c><C-x><CR>p pushes.

Requests look like * F(edit:todo) [[id:ID][title]] with ** Old value and ** New value children. Edits of todo (the value "DONEARCHIVE" marks the entry done and archives it), tags, priority, heading and body are applied only when the entry still has the old value (or the new one); mobile.force_mobile_change (true or a list of those kinds) applies them anyway. addheading adds a child, refile moves the subtree to the entry of the new value (an id:/olp: link), delete, archive and archive-sibling do what they say. * F() [[id:...]] adds the FLAGGED tag and keeps the text of the request in the THEFLAGGINGNOTE property. Applied requests are removed from the inbox; the others stay, with the error after their stars ("BAD REFERENCE", "BAD FLAG", or why the edit was refused). Changed files get a #+LAST_MOBILE_CHANGE: line. mobile.action_alist adds actions: { name = function(data, old, new, target) end } (raise an error to refuse).

Encryption: mobile.use_encryption = true stores every staged file encrypted with openssl enc -md md5 -aes-256-cbc and mobile.encryption_password (asked once per session when empty), like Emacs. mobile.checksum_binary (shasum, sha1sum, md5sum or md5, the first found) computes the file checksums. Hooks: mobile.pre_push_hook, post_push_hook, pre_pull_hook, before_process_capture_hook (receives { bufnr, line } of the new entries) and post_pull_hook, plus the User autocmds OrgMobilePrePush, OrgMobilePostPush, OrgMobilePrePull, OrgMobileBeforeProcessCapture and OrgMobilePostPull.