org.nvim

Differences from Emacs

  • Display: in indent mode (org-indent-mode) rows that wrap don't get the virtual indentation (Emacs sets a wrap-prefix; Neovim has no per-line one). Emphasis doesn't nest inside the same kind of emphasis or inside verbatim/code: Emacs fontifies *a *b* c* and =x *y* z= twice, org.nvim highlights the outer span only. In the agenda clock report, links are replaced by their description in the text (Emacs keeps the link and hides its brackets). A shrunk table column shows its full fields on the cursor line in modes missing from 'concealcursor' (Visual with the default "nc"), as Neovim reveals concealed text there. An image shown in place of its link whose line wraps after it, or on the first line of a closed fold, is drawn below the line or shown as its link text (Emacs makes that screen line as tall as the image). Vim draws syntax up to 'synmaxcol' (3000) of a line: emphasis is highlighted when it ends before that column, so a long line can't leave the lines below it bold. Emphasis over two lines is highlighted only when its part on the first line is at most 'synmaxcol' characters long: looking further for the end of every marker would make drawing a long line take time quadratic in its length.
  • Log notes (*Org Note*, note_buffer) open while the command runs: a state change or rescheduling is applied once the note is stored or cancelled (Emacs changes the entry first and opens the note buffer after the command). As in Emacs, a cancelled note (C-c C-k) logs nothing and the change itself stays. An empty add_note note stores nothing (Emacs stores the bare "Note taken on" heading).
  • Table formulas: GNU Calc is reimplemented in Lua for what tables use (org-table-calc): numbers, dates, complex numbers, HMS forms, error forms, intervals, modulo forms, vectors and matrices, usimplify with a table of common units, the symbolic algebra (Calc's normalization of formulas, simplify, expand, collect, subst, deriv, integ, solve) and the functions listed there. What still differs: simplify has Calc's rules for + - * / ^, sqrt, exp, trigonometric inverses and equations, not its whole rule set (nor esimplify, factor, rewrite rules); integ handles polynomials, powers, exp, ln, sin, cos, tan, sinh, cosh of a linear argument and parts by polynomial times exp/sin/cos, not Calc's rule-based integrator (its results can differ in form: integ(sin(2 x), x) is -90 cos(2 x) / pi where Calc writes (180 sin(x)^2 - 90) / pi; what it can't do stays integ(...)); solve finds one solution (Calc's default) up to quartic polynomials, not by numeric search for higher degrees, and no systems of equations; division by a matrix may differ from Calc in the last float digits; usimplify knows a subset of Calc's units; fsolve, roots, taylor, the polynomial functions (pdiv, pgcd, factor), spn/midi/freq, lud, kron, histogram and other rarer functions stay symbolic. Quoted strings ("big") are kept as text; Calc makes them vectors of character codes. Emacs Lisp formulas '(...) run on a small Lisp interpreter (the functions listed in lua/org/table/elisp.lua), not in Emacs: starting an Emacs for every field would be far too slow. Any other '(...) is a Lua expression.
  • Table formula errors: Emacs stops the whole recalculation on some errors (a reference outside the table, $2= defined twice, unknown remote tables); here the field shows #ERROR (the last definition wins) and the other fields are still computed. Left sides Emacs rejects are accepted: $name= for a named column, $2..$3= and @I$2..@>$2= ranges.
  • Footnote sorting and renumbering leave src, example, export and comment blocks alone: a line starting with [fn:N] inside one is code, not a definition. Emacs renumbers such lines too.
  • Tables in the table.el format (org-table.el) are recognized, converted, exported and edited, but the table.el editor itself (a separate large Emacs package) is not reproduced: in the <C-c>' buffer the grid is realigned when leaving Insert mode instead of while typing, cells never shrink, text is not refilled to the cell width, and the table.el cell commands (splitting, spanning and justifying cells, inserting rows and columns) are missing. Columns count characters, so double-width characters in cells break the grid. <C-c>~ outside a table inserts the new table below a non-blank line (Emacs draws it over the text at the cursor). Converting a table at the very start of the buffer puts a border after every row (Emacs misses the one after the first). Exported HTML cells are always align="left" valign="top" (Emacs too, as the table is not recognized first).
  • <M-CR> in an Org table splits the field only in Insert mode (in Normal mode it goes to the field below, as with meta_return_split_line = false), and wrapping a column acts on the Visual selection. Shrunk columns use conceal and virtual text (org-table-shrink), the header line is drawn in the 'winbar', <C-c>` edits a field in a prompt (follow-field mode uses a window) and plots use gnuplot directly (Emacs needs gnuplot.el). orgtbl-to-unicode converts Org markup in cells with a simplified exporter (verbatim, links and entities only).
  • Clocking (org-clock): the idle time comes from the system on macOS (ioreg) and X11 (xprintidle), elsewhere (Wayland, Windows) from Neovim's own input, so time spent in other applications counts as idle there. The mode line entry is the org-api statusline component; a click handler pops up the clock menu (org-clock-menu). Timers use counts, so a countdown's C-u (default timer) has no count form: a count is always minutes.
  • Citations (org-cite): keys are chosen with vim.ui.select, and cancelling it is Emacs' empty input; basic_complete_key_crm_separator is a Vim regexp read at an input() prompt (Emacs uses completing-read-multiple with an Emacs regexp); a bare key is accepted there as well as the completed "author year title" string. Neovim has no mouse-over faces: a <MouseMove> mapping highlights the key under the mouse with basic_mouse_over_key_face (org-cite-basic-mouse-over-key-face) and turns 'mousemoveevent' on. There are no tooltips, so the help-echo text over a key (the entry, or the close keys) has no counterpart; require("org.cite").key_help(bufnr, key) returns that text. Keys are highlighted everywhere, also in source blocks.
  • The csl citation processor runs a Lua port of citeproc-el instead of the Emacs library; it gives citeproc-el's output on 840 of the 845 tests of the CSL test suite (the others are errors in citeproc-el and items without an id). Sort keys compare by code point (Emacs uses the system collation, which is the same on macOS but not on glibc systems). BibTeX crossref inheritance copies every missing field except the biblatex exclusions (parsebib has per-type rules).
  • Bulk action f and agenda skip functions take Lua instead of Elisp. Capture %(sexp) takes Emacs Lisp (see above) or a Lua expression.
  • Capture edits the text in a separate buffer that is stored at the target (resolved when the capture starts) on finalize, instead of an indirect buffer narrowed to the text already inserted in the target (Neovim has no indirect buffers), so the target file does not show the new text until the capture is finished. unnarrowed captures do insert the text into the target right away (org-capture-unnarrowed). Mail and news %:keyword values (%:from, %:subject, ...) come from Emacs link sources (Gnus, mu4e, ...) that have no Neovim counterpart; only %:link, %:description, %:annotation, %:initial and %:type exist. The hook template function doesn't run for immediate_finish. With C-0 (:Org capture_here) Emacs splits the line at point; the Neovim cursor sits on a character, so the text goes before the cursor line at column 0 and after it otherwise.
  • The ID database can be Emacs's org-id-locations-file (org-links), but Emacs keeps it in memory and writes it on exit, so the last editor to save wins; files are sorted by name where Emacs keeps its hash order. The refile cache (refile.use_cache) keeps line numbers instead of markers: a target whose headline moved is found again by its text, and C-0 C-c C-w (a count cannot be 0) is :Org refile_cache_clear or C-u C-u C-u C-c C-w. Agenda archives-mode and view toggles (va, v[, ...) stay on until toggled off.
  • Babel sessions (org-babel Sessions) are live REPLs in terminal buffers for shells, python, js/node, ruby (irb) and R; there are no sessions for julia, SQL engines or other languages. Lua blocks run inside Neovim instead of an external lua unless the cmd of babel.languages.lua names one (lua sessions are a Neovim environment with a prompt buffer; in Neovim a table variable is a nested Lua table, where ob-lua makes key=value of two-column rows). Differences from comint: the code of a block reaches the REPL as one line that runs it from a file, not line by line, so the REPL shows that line instead of the code, and a python/node block's intermediate expression values are not echoed (as with python.el's file sending). Shells start without their rc files and a shell session named x is the buffer *x* (Emacs names it x). The R session is a plain R console, not ESS (no ess-ask-for-ess-directory, graphics devices or :results output graphics handling beyond what the code does itself). C-c C-v C-l runs the block in the session; Emacs only inserts it at the prompt. Timeouts (babel.timeout) give up on the result but do not interrupt the REPL. emacs-lisp blocks and elisp: links run in a separate emacs --batch process (or, without Emacs, on the table formula Lisp interpreter), not in the editor: they cannot see or change buffers, and nothing is kept between evaluations. :var and header Lisp forms run on the table formula Lisp interpreter, else in that separate Emacs (see org-babel).
  • Src edit buffers (org-babel-edit-special): the edited region of the Org buffer is neither highlighted nor read-only (edits there make :w refuse, see above) and a click on it does not return to the edit buffer (use :Org edit_src_continue); the hint is in the winbar, not a header line. edit_fixed_width_region_mode has no artist-mode: : areas get no filetype unless one is set. edit_src_turn_on_auto_save writes the auto-save file on 'updatetime' idle (Emacs: every 300 keystrokes or 30 idle seconds); the idle write-back (edit_src_auto_save_idle_delay) counts from the last change of the edit buffer. Association with a :session sends code with edit_src.send_to_session (Emacs sets the language mode's own "send to REPL" commands, like python.el's, to the session). src_tab_acts_natively indents with the filetype's 'indentexpr' (Vim's indentation rules, with the Org buffer's 'shiftwidth' unless the filetype sets one), not Emacs' major modes, and always with spaces. C-c C-c on a #+RESULTS[hash] hash works on the whole hash, which is not shortened to 4 characters as Emacs displays it.
  • The ports of org-babel-languages run a new process for each block where Emacs uses an Emacs package's REPL: gnuplot (gnuplot-mode sessions), haskell (inf-haskell: ghci is fed the block and markers), ocaml (tuareg: a new ocaml toplevel), julia (ESS), scheme (Geiser: impl's command evaluates the forms and writes the last value) and Common Lisp (org-babel-lisp-eval-fn SLIME/SLY: cmd, default sbcl --script, reads the form and prints its values like swank:eval-and-grab-output). The cider, inf-clojure and slime Clojure backends are not available. Julia table variables are written to a CSV file read by CSV.read (Emacs puts the CSV text itself there). The lilypond header arguments of arrange / basic mode apply as soon as the mode is toggled (Emacs sets them while evaluating, so they apply from the next block). LaTeX .png results use the Normal highlight's colors (Emacs the default face's). ob-screen's org-babel-screen-test waits at most babel.timeout (Emacs waits forever). ob-csharp runs the built program with DOTNET_ROOT set to the directory of dotnet's SDKs when it is unset, so a .NET installed outside the default location (by dotnet-install.sh, in ~/.dotnet) is found; Emacs leaves it unset and the program fails with "You must install .NET to run this application". fish :var values escape \ and ' inside their single quotes (Emacs quotes them like POSIX shells, which changes values with backslashes).
  • Emacs Lisp outside of blocks ((eval ...) macros, capture %(sexp), diary sexps, %(function) link abbreviations, header forms) runs on the table formula Lisp interpreter when it can and otherwise in a separate emacs --batch (babel.emacs_lisp) that knows nothing of your Emacs configuration unless its args load it; without an Emacs executable only the interpreter's subset works.
  • Export: see org-export-unsupported.
  • Links (org-links):

    • elisp:(sexp) links are evaluated in a separate emacs --batch (babel.emacs_lisp), not in the editor, after links.confirm_elisp (a y/n prompt; links.elisp_skip_confirm_regexp skips it); a link naming a command (elisp:emacs-version) calls it interactively there, so commands that act on the editor (elisp:org-agenda) or prompt fail. Abbreviations using %(my-function) call the function in that Emacs, which must define it (load it with babel.emacs_lisp.args); a Lua function as the abbreviation works too.
    • help: opens Vim's :help: Emacs help: links name Elisp functions and variables (describe-function), which do not exist here. info: runs the info program in a terminal, man: uses :Man.
    • Links to Emacs applications (gnus, rmail, mhe, bbdb, irc/erc, eww, w3m, calendar) can't be followed or stored: there is no such application in Neovim. docview: opens the file. BibTeX links (org-bibtex) work: bibtex: opens the file and a link stored in a .bib buffer finds the entry. bibtex_yank reads only the yanked text (Emacs writes an older read entry when the yanked text has none), bibtex_export_to_kill_ring on a headline without an entry type warns (Emacs signals a type error), and bibtex.autogen_keys works without a BibTeX buffer having been opened (in Emacs -Q it fails until bibtex-mode has set up its dialect).
    • shell: runs asynchronously (Emacs's shell-command waits for the command); its stdout and stderr are appended to *Org Shell Output* one after the other rather than interleaved. links.confirm_shell asks with a y/n prompt.
    • ::/regexp/ searches translate the common Emacs regexp syntax to Vim regexps; Emacs-only constructs (syntax classes other than whitespace and words, categories, backward references to point) are approximated or unsupported.
    • Internal links followed with a count open in another window on the same buffer; there are no indirect buffers (org-link-use-indirect-buffer-for-internals), and Vim folds are per window anyway.
    • Files Neovim cannot display (images, media, office documents, PDF, HTML) open with the system application, where Emacs opens images and office documents itself (org-file-apps); links.file_apps overrides it.
    • Wildcard file links (file:*.org) list the names of the matches in a scratch buffer instead of a Dired buffer (no ls -l details or Dired commands).
  • Diary sexps are emulated for the calendar functions of Emacs's diary library (all the diary-* sexp functions of diary-lib, cal-hebrew, cal-china, lunar and solar and the diary-*-date functions), org-calendar-holiday (org-agenda-holidays) and a side-effect-free subset of Emacs Lisp (org-agenda-diary-sexp); other sexps run in a separate Emacs when there is one. The Emacs diary file is read by a port of diary-lib (org-agenda-diary-file): custom diary-date-forms, and diary comments are not supported. Entries added with i in the agenda use calendar_date_style for the date forms where Emacs uses diary-date-insertion-form.
  • MobileOrg (org-mobile): mobile.directory must be a local directory (Emacs also accepts TRAMP paths; copy the files in the push/pull hooks instead). agendas.org lists the custom commands in key order (Emacs: the order of org-agenda-custom-commands) and its agenda lines look like this agenda's. force_mobile_change = { "priority" } forces priority edits (Emacs tests tags there). The new #+LAST_MOBILE_CHANGE: line does not make the first edit of a file fail, as it does in Emacs 9.8.
  • Image and LaTeX previews (org-images) need a terminal image backend. They replace the link or fragment like Emacs, but a terminal line can't grow: the image's first row is on the line and the rest in virtual lines under it, and the text shows again (with the image under the line) while the cursor is on the line so it can be edited. A fragment over several lines has its other lines hidden and its image drawn from its first line (Neovim 0.11+; before that, and with image.nvim, images stay under the line); the text after it on its last line follows the image without its highlighting. ui.images.max_width is in columns for "fill-column" and fractions (as Emacs) but its default fill column is 'textwidth' or 70; max_height is an addition. LaTeX previews pick an installed process ("auto") where Emacs always uses dvipng, and add "tectonic" and "pdflatex". Preview functions return an image file (or call back with one) instead of filling an overlay. ui.images.remote (org-display-remote-inline-images) applies to http(s) image links, since there are no TRAMP files.
  • Column view: the overlays cover each headline line and the column is the one drawn under the cursor. Emacs puts one column on each character, so every motion jumps a column; here h l w b <Left> <Right> <BS> <Space> $ are remapped on the rows, other motions (f, t, /, mouse) still move by character. The headline lines are read-only through their keys and Insert mode: a change started from another line (J on the line above, dk below, a Visual selection, an Ex command, the agenda) still reaches them, where Emacs refuses it. Like Emacs, digits 1-9 select allowed values on a column row, so counts are not available there (0 keeps its Vim meaning; counts work on other lines).
  • Feeds (org-feed): feeds are fetched with curl (or wget, or a Lua function) instead of url.el, and :parse-feed/:filter/handlers are Lua functions. Fixes over org-feed.el: a multi-line %field alone on its line is indented (Emacs means to but leaves the later lines at column 0), CDATA in RSS fields is unwrapped, Atom <content> without a type is text (Emacs writes "Unknown nil' content."), <summary> is used when there is no content and xhtml content is kept as markup rather than a Lisp form, the filter also applies to changed items (Emacs passes the filtered new items as changed ones) and OrgFeedBeforeAdding` fires (Emacs never runs org-feed-before-adding-hook). The inbox is not refolded after an update.
  • Structure editing:

    • Emacs acts at point, between two characters; Normal mode has a cursor on a character. Commands that insert at point (<M-CR>, C-c RET, drawers, templates, paste) act at the end of the line in Normal mode (at its start in column 0) and never split it; in Insert mode they act at the cursor like Emacs.
    • Emacs binds M-h to org-mark-element and C-x n b / C-x n e to narrowing; here <M-h> promotes and <C-x> decrements, so those are <prefix>v, <prefix>nb and <prefix>ne. Narrowing edits the region in a separate buffer instead of hiding the rest of the buffer.
    • A count repeats promote / demote (<M-h>, <<); Emacs ignores it.
    • Element commands work on whole lines.
    • Speed commands work in Insert mode at the start of a headline (where Emacs would insert the letter); Normal mode keeps Vim's letters.
    • Pretty sub/superscripts can't be raised: Unicode super/subscript characters are used when they exist, else a highlight.
    • org-list-checkbox-radio-mode is the buffer-local toggle :Org checkbox_radio_mode (no mode line lighter).
    • Indentation (org-indent): = indents line by line with org-indent-line, so a list or an example block is not moved as a whole like org-indent-region (C-M-\) does; :Org indent_region does that. Src block code keeps its own relative indentation instead of being indented by the language's major mode (Emacs's org-src-tab-acts-natively).
    • Filling (org-fill): gq fills to 'textwidth' rather than fill-column (70); src blocks are not filled (Emacs uses the language's major mode); gq over a comment of several paragraphs fills each of them (Emacs's region fill only fills the last one); only whitespace is used as a fill prefix (Emacs also takes > and other adaptive-fill-regexp characters).
  • Capture, clock, archive and other commands (parity round 5):

    • The global capture hooks are User autocmds (org-capture-hooks); the template functions are not called on abort as Emacs does, only the events fire (with aborted = true).
    • datetree_cleanup skips SCHEDULED/DEADLINE time stamps: Emacs's check for them never matches (\<DEADLINE:\>), so it files entries by their planning dates and leaves emptied day nodes behind.
    • protocol_create adds the project for the session and shows the Lua to put in setup(); Emacs saves it with Customize.
    • org-ctags reads Vim tags files through the 'tags' option instead of Emacs TAGS files; ctags.enabled replaces org-ctags-enable.
    • Files cut in a file manager and pasted with yank_media are moved, copied ones copied (Emacs swaps the two). A drop is a paste of file paths in the terminal (yank.dnd_paste); attaching a dropped file uses yank.dnd_default_attach_method (Emacs's private DND action).
    • attach_from_file_manager works in netrw and oil.nvim buffers (Emacs: org-attach-dired-to-subtree in Dired).
  • Visibility (see org-hidden-lines): hiding text between visible lines needs Neovim 0.11 (conceal_lines); hidden lines are shared by the windows of a buffer while folds are per window; Vim fold commands don't show hidden lines; linewise commands on a closed fold act on the whole fold; catch_invisible_edits watches edits in Insert mode (Normal mode commands such as x or J are not checked; Insert mode opens a closed fold, so only hidden lines count as invisible), and <BS>, <Del> and <CR> only when no mapping turns them into other keys (Neovim 0.11).
  • Sparse-tree searches leave the cursor on the first match; the default org-occur-hook of Emacs (org-first-headline-recenter) moves it to the first headline. The OrgOccur User autocmd runs after each search (org-occur-hook).
  • TODO keywords, tags, properties and dates (see org-todo, org-tags, org-properties, org-dates):

    • C-c C-t prefix arguments are counts: C-0 (no note) and C-- 1 (cancel repeaters) have no count form and are the actions todo_without_note and todo_cancel_repeaters; a count of 4 always means C-u, so the 4th keyword cannot be picked by count.
    • A repeated C-c C-t on a #+TYP_TODO: keyword (which walks the types instead of jumping to DONE) is one with no edit and no cursor motion since the previous one, rather than Emacs' last-command test.
    • The Emacs hooks are User autocmds (OrgTodoStateChange, OrgTodoRepeat, OrgPropertyChanged, OrgNoteStored, OrgLogBufferSetup) that cannot change the result; todo_blockers (org-blocker-hook) can veto a change and todo_get_default_hooks, after_todo_statistics_hooks and todo_statistics_hooks are Lua functions (org-todo). The statistics hooks run after state changes and M-S-RET, not after capture, archiving or C-c #.
    • The fast tag selection menu is a floating window read one key at a time; it does not show the tags on the headline while it is open.
    • Regexps in use_tag_inheritance and use_property_inheritance are Vim regexps; {...} in match strings and tag groups are Emacs regexps translated to Vim (Emacs-only constructs are approximated).
    • property_separators regexps are Vim regexps, and its name lists are compared ignoring case (Emacs compares them exactly). priority_get_priority_function always receives the headline line (the agenda passes Emacs its formatted agenda line).
    • The date prompt refuses text it cannot read at all instead of using the default date, and accepts today/tomorrow/yesterday/now; the calendar is the plugin's own (no Emacs calendar/diary commands such as holidays, sunrise or diary entries in it; the agenda has the holidays, moon, sunrise and date conversion keys, org-agenda-calendar-keys, and shows the diary file, org-agenda-diary-file).
    • The calendar shows one month (Emacs three): <C-v>/<M-v> move the selection as Emacs moves its cursor, ! shows the day's agenda instead of the diary entries, and the live interpretation of a typed date is drawn in the calendar instead of after the prompt. With read_date_popup_calendar = false no interpretation is shown, like Emacs. The calendar is not kept open after a prompt: <C-c>< inserts the date it was last left on, and today (not an error) before any calendar was shown. As the calendar is a prompt, it is never shown while a timestamp changes, so org-calendar-follow-timestamp-change has nothing to follow; its c and i keys close it.
    • links.mouse_1_follows_link times the click from press to release in the Org buffer; open_at_point_global reads links on the line (and bracket links over nearby lines), not the whole paragraph.
    • The custom timestamp display conceals the timestamp text: the cursor moves over the hidden characters, and the real text shows in Insert mode or with 'concealcursor' unset.
    • Habit graphs are only drawn in the agenda (as in Emacs); a habit's scheduled date must have a repeater of at least one day.
  • Menus and org-mouse (org-menus, org-mouse):

    • Menu entries that are toggles or radio buttons in Emacs show no check state in the menu bar (the org-mouse popups, built when shown, mark them "[X]" / "(*)"). "Complete Keyword" and "Complete Lisp Symbol" use omni completion; the CDLaTeX and RefTeX entries of the LaTeX submenu are always greyed out; "Show Org Manual" opens org, "Browse Org News" the org.nvim releases; "Which Column?" says the column (Emacs computes it silently); "Previous Keyword Set" goes to the previous set (Emacs 9.8.10 runs the next-set command there); "Reload Org" reloads the Lua modules with the options in effect, and refuses while a clock runs; "Enforce Dependencies", "Customize Feeds" and MobileOrg's "Setup" open org-customize on the option. Two agenda entries named "Archive default" are distinguished by a trailing space.
    • org-mouse's link menu opens links "in Neovim" instead of Emacs; its SCHEDULED:/DEADLINE: replacement works (Emacs 9.8.10 signals void-variable org-mouse-rest). A subtree dropped on a headline's stars goes before it at its level and one dropped right of them becomes its last child, as org-mouse's own message says (Emacs 9.8.10 counts the space after the stars as a level, so the subtree lands a level deeper, and loses the subtree when dropped under a later headline). Text dropped on the buffer arrives as a paste, so org-mouse's insertion of dropped text as a list item (org-mouse-punctuation) has no counterpart.
  • Customize (org-customize) lists lua/org/config/'s options and sets them for the session; there is nothing like Custom's "Save" (options live in your setup() call).
  • org-lint (org-lint): invalid-id-link reports an ID found neither in the file nor in the ID locations; Emacs 9.8.10 falls back on the current file for any ID (org-id-find-id-file), so it never reports one in a file buffer. misplaced-heading reports the misplaced stars themselves; Emacs 9.8.10 reads the match position after org-element-at-point has overwritten it, so its reports land elsewhere (later on the line, or on the line after the paragraph). suspicious-language-in-src-block knows the languages with a Babel backend or a Neovim syntax or ftplugin file (vim, json, ...), where Emacs knows those with a Babel backend or an installed major mode.