The Kalem Book GitHub

This Book describes Kalem's main branch, which may be ahead of the latest release.

Org: known differences from Emacs

Kalem's parser follows org-element.el (Org 9.7) and is checked against it with kalem diff-emacs on the test corpus and on randomly mutated files; its commands and exporters are checked the same way against Emacs's. This page lists the places where Kalem deliberately behaves differently. The parser's differences occur only with malformed input or input Emacs cannot parse; the others are listed with their reasons: Emacs Lisp that Kalem does not run, files and networks it does not reach, and choices an editor makes differently from a batch tool.

1. Elements never extend past their container

Emacs can let an element end after the element that contains it:

  • A #+CALL: line with an unbalanced [ or ( makes Emacs search the rest of the buffer for the closing bracket, and the call can swallow the following lines, even past the end of its section or block.
  • #+TBLFM: lines directly after the end of a container (for example a table at the very end of a drawer) are attached to the table.

A lossless syntax tree cannot have a child that is larger than its parent, so Kalem ends such elements at the end of their container. kalem diff-emacs counts these nodes as "known differences" instead of failures.

2. Nesting depth

Emacs's parser stops with a max-lisp-eval-depth error after a few hundred levels of nesting (about 300 nested lists or 600 nested emphasis markers in Emacs 30). Kalem parses up to MAX_DEPTH (4096) levels of elements and objects. Anything nested deeper stays in the tree as plain text, so the file still opens and round-trips.

3. Overlapping planning timestamps

When a planning line repeats a keyword and the timestamps overlap, as in SCHEDULED: <2026-01-01 Thu SCHEDULED: <2026-01-02 Fri>>, Emacs may keep two overlapping timestamp objects in different properties. Kalem keeps the timestamp Emacs uses for each keyword, and drops a timestamp that would overlap one it kept.

4. Link types depend on loaded libraries

In Emacs, the set of link types (and so which text is a plain link) depends on the libraries loaded in the session. Kalem uses a fixed list: the types registered by emacs -Q with Org, plus attachment from org-attach (see DEFAULT_LINK_TYPES). Applications can change it through ParseContext.

5. Line endings

Files with DOS line endings (\r\n) are parsed as Emacs parses them after decoding: the carriage returns are not part of any value. Property values such as source block code use \n line endings. The syntax tree still contains every byte of the file.

A file with mixed line endings (some lines ending in \r\n, others in \n) is decoded by Emacs as a Unix file: its carriage returns stay in the buffer as characters, so a line holding only \r is not blank and para\n\r\nnext is one paragraph. Kalem ends a line at every \r\n, so it reads two. The editor keeps a DOS file's \r\n in its text and its commands insert bare line feeds, so a DOS file is mixed while it is edited; reading it as Emacs reads a mixed file would change its meaning at the first new line. Saving writes \r\n everywhere again.

6. Sorting

  • Alphabetical sorting (org-sort-entries and org-table-sort-lines with a) compares code points, as Emacs does with org-sort-function set to org-sort-function-fallback. Emacs's default, string-collate-lessp, follows the collation of the operating system's locale, so its order can differ between machines; on macOS it also ignores the request to fold case. Without WITH-CASE, Kalem folds case as Emacs does on GNU/Linux. The differential tests compare code points the same way.
  • Dates are compared as local civil times. Emacs compares seconds since the epoch, which only differs for times in the repeated hour when daylight saving time ends.

7. Repeating tasks far in the past

When a task with a ++ repeater is marked done, Emacs shifts its date one interval at a time and asks whether to go on after ten intervals. Kalem shifts it as far as needed without asking.

8. Failing commands leave the text unchanged

When a command fails, Kalem's transaction is discarded as a whole. Emacs may already have changed the buffer: org-sort-entries adds a final newline to the file before it reports "Nothing to sort", and org-todo on a commented headline removes COMMENT before it rejects an unknown state.

9. Tabs in table fields

A field can keep a tab when text is made into a table with commas or spaces as separators. Emacs aligns such a field with tab stops, so its width depends on the column it starts at; Kalem counts a tab as one column. Converting tab-separated text, the usual case, turns the tabs into separators and is not affected.

10. Deleting grapheme clusters

Backspace and Delete remove a whole grapheme cluster: an é written as e and a combining accent, a flag, or an emoji with a skin tone. Emacs removes one character, leaving the rest of the cluster. The rules for tables and tags are the same as Emacs's, applied to the cluster.

11. Emacs bugs Kalem does not reproduce

The differential tests list these cases with the reason (tests/edit/cases.json, field known):

  • org-list-insert-item ignores the width of a new checkbox when it shifts the sub-items of the item it splits (M-S-RET inside an item with sub-items), and then indents the wrong lines. Kalem indents the sub-items under the new item.
  • org-cycle-list-bullet can leave a * bullet at column 0, which makes the item a headline, and then fails with a type error. Kalem writes the same text without the error.
  • org-timestamp next to a timestamp, on a line with a date range after point, replaces the next timestamp and gives it the repeater of another one: its range regular expression spans several timestamps, and the replacement uses stale match data. Kalem inserts a timestamp at point.
  • org-table-create in the middle of a line breaks the line and inserts the table after it, but then puts the rule under the first row at the original position, which is before the table, and aligns from the line above the table. Kalem creates the table at the start of the new line, indented like the broken line, with the rule after its first row.
  • org-toggle-heading on a region that starts at an item narrows the buffer to the region and the first character of the next line before it converts the list, so the bullet of the next item can become an empty heading and the levels depend on where the region ends. Kalem turns each item of the region into a heading one level below the entry, a nested item one level deeper, a checkbox into a TODO or DONE keyword.

Footnotes (org-footnote.el, 14 of the random cases in tests/edit):

  • org-footnote-sort, org-footnote-normalize and org-footnote-renumber-fn:N recurse without end when a definition refers to its own label (Lisp nesting exceeds max-lisp-eval-depth); Kalem stops the recursion and goes on.
  • org-footnote-normalize finds an anonymous footnote nested in a definition as the last anonymous footnote of the document, which is another one when there are several, and then extracts text past the end of the buffer (Args out of range); Kalem takes the nested footnote itself.

12. Table formulas

Kalem computes #+TBLFM formulas without Emacs: it reproduces Org's substitution of references and the part of Calc that table formulas use. On a corpus of 928 tables recalculated by Emacs every field agrees, except where noted here.

  • Emacs Lisp formulas ('(...)) are not evaluated. The fields they set keep their values, and Kalem names the formulas.
  • Calc's full algebra is not reproduced. Numbers, vectors, dates, durations, infinities and nan follow Calc; formulas over text fields stay symbolic with Calc's basic simplifications (x + 2, 2 a, n / (42 a)), but deeper algebra (the variance of a column with text, expanded) may be written differently.
  • Complex numbers in polar form ((2; 30)), the trigonometric and hyperbolic functions of complex numbers, factorials of fractions (the gamma function), sines of very large arguments, and radix, HMS and error forms (16#FF, 2@ 30' 0", 3 +/- 0.1) are left as formulas. Complex numbers in rectangular form (sqrt(-4) is (0, 2)) follow Calc.
  • Formulas over text fields with modulo forms ((x - 4) mod 3) follow Calc's coefficients, but where Calc leaves a sum or a product unsimplified Kalem may combine its terms or order them differently.
  • A formula Emacs cannot evaluate because Calc overflows its stack (a number divided by a vector) shows #ERROR instead of stopping the recalculation.
  • Errors that stop a recalculation (an unknown field, a reference outside the table) have Kalem's own wording.

13. Drawers around indented regions

org-insert-drawer around a region indents the region with indent-for-tab-command while the region is still active. When the region starts or ends inside a line's indentation, org-indent-region re-indents its lines by Org's indentation rules; Kalem wraps the lines as they are. Nine region cases in tests/edit show it.

14. Emacs quirks Kalem reproduces

Some Emacs behaviors look like bugs but change what documents mean, so Kalem follows them for compatibility:

  • In match strings, PROP<>"<2026-11-01>" compares times for equality: org-time<> is written (\ a b)=, which Emacs reads as ( a b)=.
  • In match strings, "<yesterday>" is compared as a string, not a time: the pattern that recognizes time operands omits it.
  • The PRIORITY special property is the first [#X] cookie anywhere in the headline line, even inside a link in the title.
  • org-entry-get and org-entry-properties disagree on duplicate drawer keys: the first returns the last value, the second the first.

15. Export without Emacs Lisp or the network

  • Macros defined as (eval ...) run Emacs Lisp in Emacs; Kalem expands them to nothing.
  • #+SETUPFILE: files are read to eight levels of nesting (Emacs follows them until a file repeats); a #+SETUPFILE: that is a URL is not fetched.
  • remote(NAME, REF) in table formulas finds a table by its #+NAME in the same document, not by an entry's ID or in another file.
  • HTML pages carry Kalem's style sheet instead of org-html-style-default, and name Kalem as their generator; the MathJax configuration is Emacs's.
  • With tex:svg (and the image types dvisvgm, dvipng and imagemagick), formulas are drawn by the editor's math engine as inline SVG; Emacs runs LaTeX and writes image files.
  • With no #+AUTHOR:, Emacs takes the author from user-full-name (the account's full name) for the title block, \author, pdfauthor and the HTML page's author meta element; Kalem writes none, so an export does not carry the name of the account it was made on. Give #+AUTHOR: in the document, or in a #+SETUPFILE: shared by a project.

16. Broken links on export

Emacs's default for org-export-with-broken-links is nil: a link that resolves to nothing stops the export with "Unable to resolve link". Kalem's default is mark, so an export from the editor always finishes, the link written as [BROKEN LINK: path] where it was. With #+OPTIONS: broken-links:nil Kalem stops as Emacs does, and with broken-links:t it leaves the link out.

17. The property macro at a missing headline

{{{property(NAME,SEARCH)}}} whose SEARCH names no headline stops Emacs's export with "Macro property failed: cannot find location"; Kalem expands it to nothing and exports the rest.

18. table.el tables with cells spanning columns

Emacs 30's table-generate-source, which ox-latex calls for a table.el table, drops the rows after a cell that spans columns when the table ends with such a cell (and fails on some tables with cells spanning rows with "Invalid search bound"). Kalem writes every row, a spanning cell as \multicolumn, and a rule under cells that span rows as \cline; on tables without spanning cells the output is Emacs's, byte for byte.

19. Entities' renderings

Kalem reads the same entity names as Org, but what each renders as comes from standards rather than from Org's org-entities.el (decision D18): the HTML Standard, LaTeX and Unicode. Where those differ from Org's choices, export and the editor follow them. A few characters differ (\phi is LaTeX's straight ϕ and \varphi the curly φ; \Amacr is Ā, not Ã; \diams is the suit's ♦); the LaTeX of some symbols is the math command where Org writes a text one or the reverse; and the ASCII and Latin-1 forms, which Org invents, are Unicode's decompositions and names (\Dagger is ++ in ASCII). In HTML a character is written with the name HTML 4.01 gives it, as Org mostly writes it (&infin;, &rarr;), and the names Org takes from later standards differ for a few (\ll is &ll;, not &Lt;). The export cases compared with Emacs agree, and so do Worg's files in HTML and Markdown but the two that list Org's entities in a table (org-syntax.org's appendix, the HTML publishing tutorial's symbols).