org.nvim

Agenda

The agenda follows Emacs Org 9.8: the same entries, line layout, sorting and default options (the Emacs variable is named next to each option in lua/org/config/agenda.lua and org-config).

<prefix>a opens the dispatcher (org-agenda):

  a   agenda for the current week (or agenda.span)
  t   global TODO list        T   TODO list for one keyword
  m   tags/property match     M   match, TODO entries only
  s   search for words or a regexp
  S   like s, TODO entries only
  n   agenda and all TODOs    #   stuck projects
  /   multi-occur: a regexp in all agenda files and
      agenda.text_search_extra_files (org-agenda-multi-occur-extra-files),
      into the quickfix list
  <   restrict to the current file / subtree (press again to cycle)
  >   remove the restriction and the restriction lock
  e   export the agenda views of custom commands (org-agenda-export)
  *   toggle sticky agenda buffers (agenda.sticky)
  ?   the :FLAGGED: entries of MobileOrg (org-mobile)
  ... custom commands from agenda.custom_commands: the description (or
      a name for the type) and ": MATCH" (agenda.menu_show_matcher,
      org-agenda-menu-show-matcher); agenda.menu_two_columns lists
      them in two columns (org-agenda-menu-two-columns)
:Org agenda a|t [KW|KW]|T KW|m MATCH|M MATCH|s TEXT|S TEXT|n|#|/ REGEXP|
            *>?|e|export FILE|day|week|fortnight|month|year|N|<key>|DATE
Restriction lock (org-agenda-set-restriction-lock):
  <C-c><C-x><    lock every agenda command to the current subtree (to the
                 file when not under a headline, or with a count); also
                 in the agenda, from the entry at point
  <C-c><C-x>>    remove the lock
The locked subtree is highlighted with OrgAgendaRestrictionLock (only its
headline with agenda.restriction_lock_highlight_subtree = false,
org-agenda-restriction-lock-highlight-subtree).
Other keys in org buffers:
  <C-c>[ <C-c>]  add the current file to / remove it from the agenda files
                 (org-agenda-file-to-front, org-remove-file); a count
                 (C-u) adds or moves it to the end instead of the front
  <C-'> <C-,>    visit the next agenda file (org-cycle-agenda-files)
:Org edit_agenda_file_list  edit the list: the list file when
                 agenda_files names one, else a buffer with the list;
                 :w installs it (org-edit-agenda-file-list)
Like Emacs, these changes are saved: when agenda_files is the name of a
file listing the agenda files, that file is rewritten; otherwise the new
list (directories and globs expanded to their files) is saved in
stdpath("data")/org/agenda-files.json and replaces the configured list at
startup for as long as agenda_files in setup() keeps the value it was
made from (Emacs saves it with Customize).

With the default agenda_files = {} (Emacs org-agenda-files nil) the agenda is empty, like in Emacs, until files are added with <C-c>[ or the option is set. A listed file that does not exist is asked about when an agenda is built: r removes it from the list (for the session), any other key aborts (org-check-agenda-file); agenda.skip_unavailable_files = true skips such files silently (org-agenda-skip-unavailable-files).

Agenda contents

For each day, per file, in this order (org-agenda-get-day-entries):
- Deadlines, on their day (Deadline:) and in today's agenda from
  deadline_warning_days (or the -Nd warning) before (In N d.:) and
  after (N d. ago:, for agenda.deadline_past_days). Done entries show
  on their day only.
- Scheduled entries on their day (Scheduled:) and, while not done, in
  today's agenda (Sched.Nx:, for agenda.scheduled_past_days). A delay
  (-2d) hides the entry until it is over; scheduled_delay_days
  (org-scheduled-delay-days, 0) is the delay of entries without one, and a
  negative value applies to all of them.
- Date ranges on each of their days ((2/3):), with the start time on
  the first day and the end time on the last.
- Active timestamps. Repeaters show on every repeat up to today and, after
  today, per agenda.show_future_repeats (true, false, "next");
  agenda.prefer_last_repeat shows the last repeat instead of the base
  date.
- Diary sexps, <%%(...)> timestamps and %%(...) lines
  (org-agenda-diary-sexp), including holidays (org-agenda-holidays).
- With agenda.include_diary, the entries of the Emacs diary file
  (org-agenda-diary-file).
- Habits (:STYLE: habit and a repeater) in today's agenda, without a
  leader, with a consistency graph from column
  agenda.habits.graph_column (40) that overwrites the end of the line
  (org-habit).
- A time grid (agenda.time_grid, org-agenda-time-grid) with the current
  time; the remove-match flag hides grid lines at the time of an entry.
- Entries under ARCHIVE-tagged or COMMENT headlines are excluded
  (agenda.skip_comment_trees = false keeps COMMENT trees).
  agenda.skip_function_global(headline) returning true leaves an entry
  out of every view, built-in ones included (org-agenda-skip-function-global).
agenda.entry_types selects the kinds ("deadline", "scheduled",
"timestamp", "sexp"; "deadline*" / "scheduled*" keep only timed ones).
Skipping: skip_scheduled_if_done, skip_deadline_if_done,
skip_timestamp_if_done, skip_scheduled_if_deadline_is_shown (true or
"not-today"), skip_timestamp_if_deadline_is_shown,
skip_scheduled_repeats_after_deadline,
skip_additional_timestamps_same_entry,
skip_deadline_prewarning_if_scheduled (true: no pre-warning when the
entry is scheduled; N: warn N days before; "pre-scheduled": not before the
scheduled date), skip_scheduled_delay_if_deadline. show_all_dates =
false hides days without entries.

Line format

Each line is agenda.prefix_format (org-agenda-prefix-format) followed
by the headline and its tags:
  agenda  " %i %-12:c%?-12t% s"      todo, tags, search  " %i %-12:c"
  %c  category          %i  icon (agenda.category_icons)
  %t  time of day       %s  leader (Scheduled:, In 3 d., ...)
  %e  effort            %l  level (spaces)
  %b  breadcrumbs (outline path + breadcrumbs_separator)
  %T  last tag          %(expr)  a Lua expression, with item and hl
A width like %-12 pads (no truncation), %-12.10 also truncates, %?
omits an empty field with its padding, and a character after the width
(: in %-12:c) is added after a non-empty value. The leaders are
scheduled_leaders, deadline_leaders, timerange_leaders and
inactive_leader. A time of day in the headline ("Call Bob 10:00",
"8pm") is used when the timestamp has none (search_headline_for_time)
and removed from the text (remove_times_when_in_prefix, true or "beg").
Times: time_leading_zero, timegrid_use_ampm,
default_appointment_duration (minutes). The grid separator
(time_grid.separator) follows the time of timed entries without an end.
Tags show inherited tags first, then "::" and the entry's own tags
(":ft::work:"); hide_tags_regexp (a Vim regexp) hides some,
remove_tags removes them (true, or "prefix" when %T is used),
tags_column places them ("auto" = right edge of the window, 0 = one space
after the entry text); ui.todo_keyword_faces and ui.tag_faces apply here
too. In the clock report, links show as their description. Day headers
use format_date (a strftime format or a function) and show the ISO week
on Mondays; weekend_days (0 = Sunday) get the weekend highlight, and
day_face_function(date) may return another highlight group
(org-agenda-day-face-function).
todo_keyword_format ("%-1s"; "%-12s" aligns the keywords, "" hides
them) formats the TODO keyword (org-agenda-todo-keyword-format).
fontify_priorities (org-agenda-fontify-priorities): "cookies" (default)
highlights the cookie, the highest priority bold
(OrgAgendaPriorityHighest) and the lowest italic
(OrgAgendaPriorityLowest), or with ui.priority_faces; true does so from
the cookie to the end of the line; a table { A = face, ... } (faces as
in ui.priority_faces) sets the faces; false turns it off.
deadline_faces (org-agenda-deadline-faces) picks the highlight of a
deadline line by the part of its warning period that has passed:
{ { 1.0, "OrgAgendaDeadline" }, { 0.5, "OrgAgendaDeadlineUpcoming" },
{ 0.0, "OrgAgendaDeadlineDistant" } }.
remove_timeranges_from_blocks removes a <...>--<...> range from the
headline text of its entries (org-agenda-remove-timeranges-from-blocks).

Sorting

agenda.sorting (org-agenda-sorting-strategy) per view:
  agenda  { "habit-down", "time-up", "urgency-down", "category-keep" }
  todo, tags  { "urgency-down", "category-keep" }
  search  { "category-keep" }
Strategies: time-, urgency-, priority-, category-, todo-state-, alpha-,
habit-, tag-, effort-, stats-, deadline-, scheduled-, timestamp-, ts-,
tsia-, user-defined- (each -up or -down), and category-keep. Urgency is
Emacs's: 99 + days late + priority for scheduled entries, priority + days
overdue (minus days left) for deadlines in today's agenda, the priority
for other entries (A = 2000, B = 1000, C = 0 with the default range) and
org-habit-get-urgency for habits. "user-defined-*" call
agenda.cmp_user_defined(a, b), which returns -1, 1 or nil. An unknown
strategy is an error. sort_notime_is_late and sort_noeffort_is_high
place entries without a time or an effort.

Agenda keys

All configurable under mappings.agenda:
  f / b / .       later / earlier / today
  vd vw vt vm vy  day / week / fortnight / month / year view (the span
                  starts at the day, week, month or year at point)
  v<Space>        reset the span to the default
  gd              go to a date (Emacs j)
  c               calendar: pick a date and go there
  gC M S H        the date at point in other calendars / phases of the
                  moon / sunrise and sunset / holidays
                  (org-agenda-calendar-keys; Emacs C M S H)
  r            rebuild the view; with a count N in a TODO list, the
                  Nth keyword of the hint line (a count past the last
                  one = ALL); with a count in a tags or search view, edit
                  the query (Emacs C-u r)
  gr              rebuild all agenda buffers (Emacs g)
  <CR> <Tab>      open the entry here / in another window
  <Space> <BS>    show the entry (its drawers open; with a count they stay
                  folded), keeping focus in the agenda; <Space> again
                  scrolls that window a page forward, <BS> a page back
                  (org-agenda-show-and-scroll-up / -show-scroll-down).
                  An agenda key that starts a global mapping or is your
                  leader (<Space>, \ or , as 'mapleader' or
                  'maplocalleader') waits 'timeoutlen' so the longer
                  keys still work
  cycle_show      (no default key) show the entry; again right away,
                  cycle it through children, subtree, folded; a count is
                  the level of show_1 (org-agenda-cycle-show)
  show_1          (no default key) show the entry with the detail of the
                  count: 1 the entry, 2 its children, 3 its subtree, 4
                  also the drawers (org-agenda-show-1; level 0, fold,
                  has no count form: require("org.agenda.view").show_1(0))
  <MiddleMouse>   go to the entry clicked (org-agenda-goto-mouse); with
                  agenda.mouse_1_follows_link also a short <LeftMouse>
                  click without a drag (a longer one only moves the
                  cursor; the limit is links.mouse_1_follows_link ms
                  when that is a number, else 450)
  <RightMouse>    show the entry clicked (org-agenda-show-mouse)
  <C-c><C-x>b     edit the entry's subtree in the other window (the
                  subtree edit buffer of org-narrow, since Neovim has no
                  indirect buffers; org-agenda-tree-to-indirect-buffer).
                  The previous one is closed unless modified. A count N
                  takes the subtree of the ancestor at level N.
                  agenda.follow_indirect makes follow mode show it
  L               show the entry and recenter its window
  o               close the other windows
  F               follow mode (also vf)
  n / p           next / previous item
  <C-c><C-n/p>    next / previous date line
  <C-Down/Up>     next / previous block
  <M-Down/Up>     move the line down / up (the agenda text only)
  t               change TODO state; <C-S-Right/Left> cycle it
  todo_yesterday  (no default key) like t, but the change is logged at
                  23:59 of yesterday (org-agenda-todo-yesterday)
  , + -           set / raise / lower priority (also <S-Up/Down>)
  : / T           set tags / show tags
  <C-c><C-x>p     set a property
  s / d           schedule / deadline
  <S-Right/Left>  move the date of the entry +1 / -1 day (a count moves
                  more): the timestamp the line comes from; one step on a
                  past date moves it to today
                  (agenda.move_date_from_past_immediately_to_today).
                  A count of 4 (C-u) moves the time one hour, 16 (C-u
                  C-u) one time_stamp_rounding_minutes step; the keys
                  keep that unit when pressed again without moving the
                  cursor. The actions date_later_hours,
                  date_earlier_hours, date_later_minutes and
                  date_earlier_minutes (no default keys) take a count
  >               prompt for a new date for that timestamp (the time is
                  kept)
  e               set effort
  I O X J         clock in / out / cancel / go to clocked entry
  ;               start a countdown timer; <C-c><C-x>_ stops the timer
  R               refile
  $               archive (also <C-c>$, <C-c><C-x><C-s>)
  a               archive with archive_default_command after
                  confirmation ("archive_subtree", "archive_to_sibling",
                  "set_tag" or a function; org-archive-default-
                  command); <C-c><C-x><C-a> without confirmation
  <C-c><C-x>A     move to the Archive sibling
  <C-c><C-x>a     toggle the ARCHIVE tag
  <C-c><C-a>      attach (org-attach on the entry)
  <C-k>           delete the entry in its file (asks when it is longer
                  than agenda.confirm_kill lines)
  <C-c><C-o>      open a link of the entry (choose when there are several)
  z               add a note
  K               capture, with the date at point as default date
                  (Emacs k)
  A               append another view to this one
  l               log mode: closed items and clocks (also vl); 1l also
                  shows state changes, 2l only log items; vL all log items
  C               clock report mode (also vR): a clock table of the
                  agenda files for the shown days
                  (agenda.clockreport_parameters)
  vc              clock check: only clocked entries, with their issues
                  (gaps, overlaps, too long / short, no end time;
                  agenda.clock_consistency_checks)
  E               entry text mode: show the first body lines of each
                  entry (agenda.entry_text_maxlines; a count N shows N
                  lines) (also vE), after entry_text_leaders, without
                  drawers, planning and CLOCK lines and the matches of
                  entry_text_exclude_regexps (Emacs regexps)
  vG              toggle the time grid (Emacs G, kept free for motion)
  !               show / hide deadlines (also v!)
  D               include / leave out the Emacs diary file
                  (org-agenda-diary-file)
  i               add a diary entry for the date at point
                  (org-agenda-diary-entry). With agenda.diary_entry_file
                  = "diary-file" (default, org-agenda-diary-file) a line
                  of the Emacs diary file (agenda.diary_file) is started,
                  like the calendar's i commands: [d]ay [w]eekly
                  [m]onthly [y]early [a]nniversary [b]lock [c]yclic, in
                  calendar_date_style; a count makes it non-marking
                  ("&"); type the text there. With an Org file: [d]ay
                  (a headline with the date, placed by
                  insert_diary_strategy: "date-tree", "date-tree-last"
                  or "top-level"; insert_diary_extract_time moves a
                  leading "10:30-11:00" into the timestamp), [a]nniversary
                  (an org-anniversary line under "* Anniversaries"),
                  [b]lock, [j]ump to the date tree. A block spans the days
                  at both ends of a Visual selection (Emacs: point and
                  mark)
  vh              turn habits off / on (agenda.habits.show_habits); with
                  a count, toggle whether today shows every habit, due or
                  not (habits.show_all_today)
                  (org-habit-toggle-display-in-agenda, Emacs K / C-u K);
                  the action toggle_habits (no default key) never takes
                  the count
  #               toggle dimming of blocked TODOs (agenda.dim_blocked_tasks:
                  true, false or "invisible"; needs
                  enforce_todo_dependencies or
                  enforce_todo_checkbox_dependencies)
  va / vA         include archived trees / also the archive files
                  (agenda.start_with_archives_mode: "trees" or true
                  turns them on in new agendas)
  v[              include inactive timestamps ([ leader)
  / \ < = _ ^ |   filters (org-agenda-filter)
  ~               limit the number of entries (org-agenda-limits)
  [ ] { }         search view: add a +word / -word / +{regexp} /
                  -{regexp} term, the new query also going to register
                  o (agenda.query_register, org-agenda-query-register)
                  (tags views: a +tag / -tag term);
                  date agenda: include inactive timestamps
  m u U           mark / unmark / unmark all
  <M-m> * <M-*>   toggle mark / mark all / toggle all marks
  %               mark entries matching a regexp
  B               bulk action (org-agenda-bulk)
  <C-c><C-x><C-c> column view (org-agenda-columns)
  <C-x><C-s>      save all modified org buffers
  <C-_> <C-/> <C-x>u  undo the last edit made from the agenda in its
                  file and rebuild the view; again for the edit before
                  (org-agenda-undo). r forgets the edits, like Emacs;
                  an edit is only undone while its file has not changed
                  since (Emacs undoes the file's last change regardless),
                  and an undone edit is dropped (a later undo that is not
                  a repetition does not redo it)
  <C-x><C-w>      write the agenda to a file (org-agenda-export)
  q               quit
  Q               quit and delete the agenda buffer
  x               quit, delete the agenda buffers and the unmodified
                  buffers the agenda loaded
A file the agenda (or refile, capture, the clock, ...) loads to make an
edit stays out of the buffer list (unlisted-buffer), so it doesn't show
up in :ls or a buffer tabline; it's listed once you open it in a window.
:wall still saves it and :qall still warns about unsaved changes.
  g?              help
  ?               show the MobileOrg flagging note of the entry; again to
                  remove the FLAGGED tag and the note (org-mobile)
  <C-c><C-x><CR>g / <C-c><C-x><CR>p   MobileOrg pull / push
Emacs-style aliases: <C-c><C-t> todo, <C-c><C-q> and <C-c><C-c> tags,
<C-c>, priority, <C-c><C-x>e effort, <C-c><C-z> note, <C-c><C-x><C-i>
<C-c><C-x><C-o> <C-c><C-x><C-x> <C-c><C-x><C-j> clock in / out / cancel /
goto, <C-c><C-x><Right/Left> date later / earlier.
A count stands for Emacs's prefix argument: 1 = C-u, 2 = C-u C-u, 3 =
C-u C-u C-u.

In Visual mode, schedule, deadline, >, the TODO state, the archive commands, <C-k>, set property and set effort act on every entry of the selection (org-agenda-loop-over-headlines-in-active-region: agenda.loop_over_headlines_in_active_region true, false, "start-level" or an Emacs regexp the lines must match). The one-letter keys that are Visual motions (t, $, e, a) keep their Vim meaning there; use their <C-c> forms.

Like Emacs, edits made from the agenda leave the source buffers modified (agenda.save_after_edit = false); save them with <C-x><C-s> or :wall. Buffers are listed, so :qa asks before losing changes. Set save_after_edit = true to write them after every edit.

Like Emacs (org-agenda-change-all-lines), changing the TODO state, the priority, tags, effort or a property, adding a note and clocking in or out re-format only the lines of that entry, in place: a DONE entry stays in the TODO list until the next r, and a line the filters now hide is removed. A repeating entry marked done on today's line shows DONE there. Date changes (S-Right, S-Left, >, schedule, deadline) show the new date at the right edge of the line, " => <date>" (" S => " / " D => " for schedule and deadline, highlight OrgAgendaNewTime), and the entry moves at the next r (org-agenda-show-new-time). Refile, archive, kill, bulk actions and the entry-text mode (E) rebuild the view.

Options: agenda.start_with_log_mode (true, "all" or "clockcheck"), start_with_follow_mode, start_with_clockreport_mode, start_with_entry_text_mode, show_outline_path (echo the outline path of the entry at point).

User autocmds (the Emacs hooks): OrgAgendaFinalize after the agenda buffer is built or rebuilt (org-agenda-finalize-hook), OrgAgendaFilter after a filter changed (org-agenda-filter-hook), with data.buf, data.filters and data.filter (the filter text of the window bar); OrgAgendaBeforeWrite before a written agenda (org-agenda-export). OrgAgendaFinalize also fires after the lines of an edited entry were re-formatted, with data.lines (the agenda lines that changed), like Emacs runs the hook on each of them.

vim.api.nvim_create_autocmd("User", {
  pattern = "OrgAgendaFinalize",
  callback = function(ev) vim.bo[ev.data.buf].textwidth = 0 end,
})

Windows and buffers

agenda.window (org-agenda-window-setup): "split" (default, Emacs reorganize-frame: the current window and the agenda below it), "only" (only-window), "other" (other-window), "current", "vsplit", "tab" (other-tab), "float". The "split" window fits its lines, between window_frame_fractions ({ 0.5, 0.75 } of the editor height, org-agenda-window-frame-fractions; { 1.0, 1.0 } = the only window). restore_windows_after_quit restores the previous window layout on q. With agenda.sticky (org-agenda-sticky, * in the dispatcher) every command has its own buffer (org://agenda(KEY)), shown as it is when the command is run again (press r to refresh); toggling it deletes the agenda buffers, like :Org agenda_kill_all_buffers (org-agenda-kill-all-agenda-buffers).

Filtering

Filters hide lines without rebuilding the view; they show in the window
bar (Emacs shows them in the mode line).
  /     org-agenda-filter: a combined filter such as +work-John<0:10-/plot/:
        +/- a tag or category (tags win), an effort comparison (< means
        at most, > at least), a /regexp/ (Emacs syntax). An empty answer
        removes the filters; a leading + (++work) or a count of 2
        adds to the current ones; a count of 1 negates the whole filter;
        a count of 3 runs agenda.auto_exclude_function
  \     filter by one tag: its selection key (#+TAGS / tags (k)
        keys), <Tab> to type a tag, <Space> any tag, ? untagged, . the
        tags of the entry at point, - / + exclude / include the next
        tag, <CR> auto_exclude_function, \ removes the tag filter.
        With group_tags on, a group tag (#+TAGS: [ Work : Lab ],
        org-tag-groups) keeps the entries with the group tag or any of
        its members, recursively (-Work hides them all); <Tab> and /
        offer the group tags too (org-agenda-filter-expand-tags)
  <     keep the category of the entry at point (again to clear); with a
        count, hide that category
  =     keep entries matching an Emacs regexp; with a count 1, hide them;
        again to clear
  _     effort: <, > or =, then a digit selecting a value of
        global_properties.Effort_ALL (default "0 0:10 0:30 1:00 2:00
        3:00 4:00 5:00 6:00 7:00"); _ again clears. Entries without
        an effort count as infinitely long (sort_noeffort_is_high)
  ^     keep the entries under the top headline of the entry at point
  |     remove all filters
agenda.auto_exclude_function(tag) returns "+tag", "-tag" or nil
(org-agenda-auto-exclude-function). Custom commands may preset filters
that stay on: tag_filter_preset, category_filter_preset,
regexp_filter_preset, effort_filter_preset (lists like { "+work" }).
agenda.persistent_filter keeps the filters when another agenda is
built.

Limits

agenda.max_entries, max_todos, max_tags and max_effort (minutes) limit each day or list, as a number or per view type ({ agenda = 5, todo = 10 }); a negative number keeps the entries without the property. ~ sets one interactively (e entries, t TODOs, T tags, E effort); with a count it removes them. Like Emacs, lines without the property (time grid lines too) are dropped while a limit is on.

Bulk actions

B acts on the marked entries, or on the entry at point without marks
(org-agenda-bulk-action):
  $ archive     A archive to the Archive sibling    t TODO state
  + / - add / remove a tag       r refile
  s / d   (re)schedule / set the deadline: a date, or ++2d to shift each
          entry's own date; an empty answer opens the calendar
  S       scatter: schedule each entry on a random day of the next N days
          (with a count, weekend days are skipped)
  f       apply a Lua function (function(target, item) ... end)
  (marked lines show agenda.bulk_mark_char, ">", org-agenda-bulk-mark-char)
  p       toggle persistent marks (agenda.persistent_marks: keep the
          marks after the action), then choose the action
  custom  keys of agenda.bulk_custom_functions:
          { x = { fn = function(target, item, ...) end, desc = "...",
          args = function() return { ... } end } }

Column view

<C-c><C-x><C-c> in the agenda shows each entry as columns, like column view in an org buffer. The format is agenda.overriding_columns_format, else the COLUMNS property or #+COLUMNS of the entry at point (or of the first entry), else columns_default_format. Date lines and block headers show the summaries of the entries below them (agenda.columns_show_summaries); with agenda.columns_add_appointments_to_effort_sum, an appointment without an effort counts its duration (org-agenda-columns-add-appointments-to- effort-sum). While it is on: q turns it off, e edits the value under the cursor, n/p (<S-Right>/<S-Left>) switch to the next/previous allowed value, v shows the full value. The column titles are in the window's winbar (Emacs header-line). Other agenda commands keep working on the entry at point. agenda.view_columns_initially turns it on for every new agenda.

Exporting agenda views

<C-x><C-w> in the agenda, or :Org agenda export FILE, writes the current
view (org-agenda-write). The extension picks the format:
  .txt        the agenda text
  .html .htm  the agenda with its colours, as a <pre> block
  .org        the subtrees of the entries in the view, as level-1 trees
  .ics        iCalendar: an event per timestamp (deadlines of non-TODO
              entries too), a VTODO per TODO entry
  .ps .pdf    the agenda printed like Emacs's ps-print: Courier text with
              the highlight colours and bold/italic, paged, with a header
              box ("Agenda View", page n/m and the date) on every page
The text of entry text mode (E) is not written, like Emacs's overlays;
agenda.add_entry_text_maxlines (org-agenda-add-entry-text-maxlines,
default 0) adds that many body lines under each entry ("    > ...").
agenda.before_write_hook(lines, path) can then change the lines (or
return new ones) and the User autocmd OrgAgendaBeforeWrite fires
(org-agenda-before-write-hook); when the lines change, HTML and
PostScript lose their colours. agenda.export_html_style replaces the
<style> section of HTML files (org-agenda-export-html-style).

PostScript and PDF are written by Neovim itself, no external program is needed. The layout is ps-print's (letter paper, 8.5 pt text, 72 lines of 97 columns per page; long lines wrap), so pages break where Emacs breaks them. Characters outside Latin-1 are replaced (box drawing by -/|/+, typographic dashes and quotes by ASCII ones, others by ?).

agenda.exporter_settings (org-agenda-exporter-settings) applies when views are written: ps-print variables (- may be written _) set up the printout, other keys are agenda options for the views that org-store-agenda-views builds. A custom command's settings may hold ps-print variables for its own files.

agenda = {
  exporter_settings = {
    ps_paper_type = "a4",           -- letter legal a3 a4small b4 b5 ...
    ps_landscape_mode = false,
    ps_number_of_columns = 1,       -- columns per sheet
    ps_font_size = { 7, 8.5 },      -- { landscape, portrait } or a number
    ps_print_header = true,         -- the header box
    ps_print_header_frame = true,
    ps_print_color_p = true,        -- false/"black-white": no colours
    ps2pdf = false,                 -- make .pdf with ps2pdf, like Emacs
  },
}

Also: ps_header_font_size, ps_header_title_font_size, ps_header_lines, ps_left_header and ps_right_header (lists of strings or functions (page, pages)), ps_show_n_of_n, and the margins ps_left_margin, ps_right_margin, ps_top_margin, ps_bottom_margin, ps_header_offset and ps_inter_column (points). With ps2pdf = true and ps2pdf installed, the .pdf is made from the PostScript by ps2pdf, as in Emacs.

Custom commands with export_files = { "~/today.html", "~/today.txt" }
are written all at once by e in the dispatcher or :Org agenda e
(org-store-agenda-views). From the shell (org-batch-agenda and
org-batch-agenda-csv):
  nvim --headless -c "lua require('org.agenda.export').batch('a',
    { span = 'day' })" -c q
  nvim --headless -c "lua require('org.agenda.export').batch_csv('t')" -c q
The first argument is a custom command key, a dispatcher key, or a match
string; the second one sets agenda options for the run. CSV records have
Emacs's fields: category,head,type,todo,tags,date,time,extra,priority-l,
priority-n,agenda-day.

Custom commands

agenda = {
  custom_commands = {
    w = {
      description = "Work overview",
      types = {
        { type = "agenda", span = "day", header = "Today" },
        { type = "tags_todo", match = "+work/!", header = "Work" },
        { type = "todo", match = "WAITING" },
      },
      settings = { prefix_format = " %-12:c %s" },
      export_files = { "~/work.html" },
    },
    u = { description = "Urgent", type = "tags", match = "+urgent" },
    p = { description = "Projects", type = "tags-tree",
          match = "project" },
  },
}
Block types: agenda, todo, tags, tags_todo, search, stuck.
tags-tree, todo-tree and occur-tree make a sparse tree of the current
org buffer instead (tags match, TODO keyword, Emacs regexp); they cannot
be blocks of a composite command. A composite command (types /
blocks) shows its blocks one after the other, separated by a blank line
and agenda.block_separator (none with compact_blocks, which also drops
the date agenda header). settings (Emacs's options list) applies to
every block unless the block sets the option itself.
Block options:
  match         match string (see org-match-syntax), TODO keywords for
                todo, or text for search
  header        heading shown above the block; "" for none; a function
                returning it (also org_agenda_overriding_header)
  span          span for agenda blocks
  start_day     first day for agenda blocks
  files         files to use instead of agenda_files
  skip          function(headline) that returns true to skip an entry
                (also org_agenda_skip_function). Helpers like Emacs'
                org-agenda-skip-entry-if / -subtree-if:
                  local agenda = require("org.agenda")
                  skip = agenda.skip_entry_if("scheduled", "deadline")
                  skip = agenda.skip_subtree_if("regexp", ":someday:")
                Conditions: scheduled, notscheduled, deadline,
                notdeadline, timestamp, nottimestamp, regexp RE,
                notregexp RE, todo KWS, nottodo KWS (KWS: a list of
                keywords, or "todo", "done", "any")
  sorting       sorting strategies of this block
  *_filter_preset  filters that stay on (org-agenda-filter)
  any other agenda option (prefix_format, entry_types,
  todo_ignore_*, show_future_repeats, max_entries, ...), also with
  Emacs names: org_agenda_entry_types, org_agenda_prefix_format, ...
Keys may be several characters long; a string entry (w = "Work") only
labels the group.

agenda.custom_commands_contexts (org-agenda-custom-commands-contexts) offers commands only in some buffers, with the rules of org-capture-contexts: { "p", { { in_mode = "org" } } } keeps p only in org buffers, { "p", "r", { rules } } runs the command of r under p where a rule holds (and hides r). It applies to the dispatcher, :Org agenda KEY and A.

TODO lists: todo_ignore_scheduled ("all", "future", "past" or days), todo_ignore_deadlines (true = "near": within the warning period, "far", "all", "future", "past" or days), todo_ignore_timestamp, todo_ignore_with_date hide dated entries (org-agenda-todo-ignore-*; they compare dates in days, or with the current time in seconds with todo_ignore_time_comparison_use_seconds); tags-todo (M) views honour them only with tags_todo_honor_ignore_options. todo_list_sublevels = false skips the children of a listed TODO; tags_match_list_sublevels = false those of a match.

Stuck projects

agenda.stuck_projects (org-stuck-projects): projects matching match ("+LEVEL=2/-DONE") are stuck unless a heading of their subtree, the project heading included, has one of todo_keywords ({ "TODO", "NEXT", "NEXTACTION" }; "*" = any not-done keyword) or tags ("*" = any tag), or the subtree text matches the Vim regexp text.

Search view

A plain query is a phrase whose spaces match any whitespace, newlines
included ("banana split" finds "banana\n  split"). A query starting with
+, - or { is boolean: +word must and -word must not occur, "a
phrase" in double quotes counts as one word, {regexp} is an Emacs
regexp, and word\ word joins two words. A leading * searches
headlines only, ! TODO entries only, : makes words match whole words.
Matching ignores case.
  agenda.search_view_always_boolean    treat every query as boolean
  agenda.search_view_force_full_words  words match whole words only
  agenda.search_view_max_outline_level entries below this level are
                                       searched as part of their ancestor
                                       (0 = no limit)
  agenda.text_search_extra_files       more files to search; include
                                       "agenda-archives" for the archive
                                       files

Diary sexp entries

Emacs lets you put diary s-expressions in timestamps, <%%(diary-float t 4 2)>, and on lines of their own at the start of a line, %%(org-anniversary 1990 5 17) Birthday of Joe (%d). agenda.diary_sexp_prefix (an Emacs regexp, org-agenda-diary-sexp-prefix) moves its match in the text of a %%(...) line to the leader (%s). Emacs evaluates them as Emacs Lisp. Neovim has no Lisp interpreter, so they are read with a small, safe s-expression reader and the common calendar functions are emulated, with the same results as Emacs:

  org-anniversary Y M D          diary-anniversary M D [Y]
  org-cyclic N Y M D             diary-cyclic N M D Y
  org-block Y1 M1 D1 Y2 M2 D2    diary-block M1 D1 Y1 M2 D2 Y2
  org-date Y M D                 diary-date M D Y
  diary-float MONTH DAYNAME N [DAY]
  org-class Y1 M1 D1 Y2 M2 D2 DAYNAME [ISO-WEEKS...]
  diary-remind SEXP DAYS [MARKING]   diary-offset SEXP DAYS
  diary-hebrew-date  diary-islamic-date  diary-bahai-date
  diary-chinese-date  diary-julian-date  diary-iso-date
  diary-astro-day-number  diary-french-date  diary-mayan-date
  diary-coptic-date  diary-ethiopic-date  diary-persian-date
  diary-day-of-year  diary-lunar-phases  diary-sunrise-sunset
  diary-hebrew-birthday M D Y [AFTER-SUNSET]
  diary-hebrew-yahrzeit M D Y [MARK AFTER-SUNSET]
  diary-hebrew-omer  diary-hebrew-parasha  diary-hebrew-rosh-hodesh
  diary-hebrew-sabbath-candles  diary-chinese-anniversary M D [Y]
  and  or  not  list  quote

Sexps can also combine these with a side-effect-free subset of Emacs Lisp, evaluated like Emacs with date bound to (MONTH DAY YEAR) and entry to the text after the sexp:

  if when unless cond let let* progn
  = /= < > <= >= eq eql equal memq memql member null not zerop
  + - * / % mod 1+ 1- abs max min   car cdr cadr nth length
  concat format number-to-string string= integerp numberp stringp
  calendar-day-of-week  calendar-extract-month/-day/-year
  calendar-absolute-from-gregorian  calendar-gregorian-from-absolute
  calendar-iso-from-absolute  calendar-nth-named-day
  calendar-last-day-of-month  calendar-leap-year-p  calendar-day-number
* Gym
%%(when (memq (calendar-day-of-week date) '(1 2 4 5)) "18:00 Gym")
* Payday
%%(= 0 (mod (- (calendar-absolute-from-gregorian date) (calendar-absolute-from-gregorian '(9 4 2026))) 14))

A string result replaces the entry text (the first example shows "18:00 Gym" at 18:00), a list of strings gives one line each, and any other non-nil value shows the text after the sexp. There are no loops, lambda or assignments, so every sexp finishes and changes nothing.

A sexp beyond this subset (other Lisp functions, cl-*, ...) is evaluated in a separate emacs --batch when there is one (babel.emacs_lisp), like org-diary-sexp-entry: (let ((entry ENTRY) (date '(M D Y))) SEXP) with Org and diary-lib loaded. One Emacs runs per sexp and agenda span (all the days at once, under half a second), and the results are kept for the session. Without Emacs the entry is skipped with a message.

The diary-* functions take their arguments in the order of agenda.calendar_date_style (calendar-date-style): "american" (month day year, the default), "european" (day month year) or "iso" (year month day). The org-* variants always use ISO order (year month day). In diary-date/org-date, t matches any value and a list '(1 2) matches any of its elements. In diary-float, MONTH may be t or a list, DAYNAME is 0 (Sunday) to 6, and a negative N counts from the end of the month. The entry text of anniversaries and cycles may contain %d (years or repetitions) and %s (its ordinal suffix, "st", "nd", ...); a result with "; " gives several agenda lines.

diary-remind shows the entry of a quoted SEXP on its day and "Reminder: Only N days until ENTRY" DAYS days before ("2 weeks" for whole weeks; DAYS may be a list, or -N for 1 to N days); MARKING only matters for the Emacs calendar and is ignored. diary-offset shows SEXP's entry DAYS days later (earlier when negative):

* Taxes
%%(diary-remind '(diary-date 4 15 t) -7) File the taxes
* Retro
%%(diary-offset '(diary-float t 4 -1) 1) Retro, the day after

The diary-*-date functions show the day in another calendar, e.g. "Hebrew date (until sunset): Tishri 16, 5787", like Emacs (in the date form of agenda.calendar_date_style: "16 Tishri 5787" in european style, "5787-07-16" in iso style). diary-lunar-phases shows the phases of the moon and diary-sunrise-sunset sunrise and sunset at agenda.calendar_latitude and agenda.calendar_longitude (org-agenda-calendar-keys; without them the sexp is skipped with a message), in the system time zone.

The entries of the Hebrew calendar are Emacs's (cal-hebrew.el): diary-hebrew-birthday ("Joe's 13th Hebrew birthday", and " (evening)" the day before; the entry text is the name) and diary-hebrew-yahrzeit ("Yahrzeit of Joe: 5th anniversary") take the civil date of birth or death, the day after with AFTER-SUNSET; diary-hebrew-omer counts the Omer, diary-hebrew-parasha names the weekly parasha on Saturdays (with the Israel and diaspora readings when they differ), diary-hebrew-rosh-hodesh shows Rosh Hodesh, Erev Rosh Hodesh and Shabbat Mevarchim, and diary-hebrew-sabbath-candles the candle lighting time on Fridays, agenda.hebrew_sabbath_candles_minutes (18) before sunset at the calendar location. diary-chinese-anniversary is diary-anniversary for a date of the Chinese calendar, with the year written CYCLE * 100 + YEAR (7843 for year 43 of cycle 78):

%%(diary-hebrew-yahrzeit 11 25 1990) Grandpa
%%(diary-chinese-anniversary 8 15 7840) Moon cake %d%s

Like Emacs, %%(...) lines are found in the whole file: lines before the first heading give entries with the file's category and FILETAGS. They have no heading, so <Tab>/<CR> jump to the line and commands that change an entry (t, s, :, ...) report "Before first headline". (Org 9.8.10 itself stops the agenda with that error when such a line matches.)

Holidays

A %%(org-calendar-holiday) line shows the day's holidays, like Emacs's calendar-check-holidays: several on one day give several agenda lines.

* Holidays
  :PROPERTIES:
  :CATEGORY: Holiday
  :END:
%%(org-calendar-holiday)

The holidays come from agenda.holidays (calendar-holidays), with the Emacs defaults: one list per holiday-*-holidays variable (general (the United States), local, other, christian, hebrew, islamic, bahai, oriental and solar), and the flags christian_all, hebrew_all, islamic_all, bahai_all and chinese_all (calendar-*-all-holidays-flag, false). Set a group to {} to drop it; list replaces the default. Items are Emacs's holiday forms written as Lua lists, or Lua functions:

agenda = {
  holidays = {
    general = {},  -- no US holidays
    christian = {  -- only Christmas and Easter Sunday
      { "holiday-fixed", 12, 25, "Christmas" },
      { "holiday-easter-etc", 0, "Easter Sunday" },
    },
    ["local"] = {
      { "holiday-fixed", 9, 16, "Independence Day" },
      { "holiday-float", 11, 1, 3, "Revolution Day" },  -- 3rd Monday
      function(year)  -- holiday-sexp: { MONTH, DAY, NAME } per year
        return { { 2, 5, "Constitution Day " .. (year - 1917) } }
      end,
    },
  },
}
    { "holiday-fixed", MONTH, DAY, NAME }
    { "holiday-float", MONTH, DAYNAME, N, NAME [, DAY] } (the Nth
      DAYNAME, 0 = Sunday, from DAY; negative N counts backwards)
    { "holiday-easter-etc" [, N, NAME] }, { "holiday-advent" [, N, NAME] },
    { "holiday-greek-orthodox-easter" [, N, NAME] } (N days from Easter,
      Advent Sunday or Pascha; without arguments the default holidays)
    { "holiday-julian" | "holiday-hebrew" | "holiday-islamic" |
      "holiday-bahai" | "holiday-chinese", MONTH, DAY, NAME } (a date
      of that calendar)
    { "holiday-hebrew-passover" }, -rosh-hashanah, -hanukkah,
    -tisha-b-av, -misc, { "holiday-islamic-new-year" },
    { "holiday-bahai-new-year" }, -ridvan, -twin-holy-birthdays,
    { "holiday-chinese-new-year" }, -qingming, -winter-solstice,
    { "solar-equinoxes-solstices" } and { "holiday-daylight-saving" }
      (the Emacs functions of the same names)
    { "if", FLAG, ITEM... } (the items when holidays[FLAG] is true)

Equinox, solstice and daylight saving times use the system time zone, like Emacs's calendar-time-zone. A bad item is reported once and skipped. Differences: Emacs evaluates each item as Lisp (holiday-sexp and other Lisp forms become Lua functions here).

Calendar commands

Emacs's calendar commands on the date at point of a date agenda (a date line or an item of that day):

  gC   the date in other calendars (org-agenda-convert-date, Emacs C):
       Gregorian, ISO week, day of the year, Julian, astronomical day
       number, Hebrew, Islamic, French Revolutionary, Bahá’í, Mayan,
       Coptic, Ethiopic, Persian and Chinese
  M    the phases of the moon in the three months around the date, with
       possible eclipses (org-agenda-phases-of-moon)
  S    sunrise, sunset and hours of daylight on the date
       (org-agenda-sunrise-sunset); with a count, ask for the location
  H    the holidays of the three months around the date, from
       agenda.holidays (org-agenda-holidays)

gC, M and H show a float (q closes it), S a message. Sunrise and sunset need a location:

agenda = {
  calendar_latitude = 40.7,    -- calendar-latitude, north positive
  calendar_longitude = -74.0,  -- calendar-longitude, east positive
  calendar_location_name = "New York",  -- default "40.7N, 74.0W"
}

When they are unset, S asks for them (Emacs's solar-setup) and keeps the answers for the session. Times use the system time zone and its daylight saving rules, like Emacs's calendar-time-zone. The strings are Emacs's, with the default calendar-date-display-form of agenda.calendar_date_style (american "Sunday, September 27, 2026", european "Sunday, 27 September 2026", iso "2026-09-27"); calendar-date-display-form itself, lunar-phase-names and calendar-time-display-form are not configurable.

The Emacs diary file

With agenda.include_diary (org-agenda-include-diary), or after D in the agenda, each day of a date agenda also lists the entries of the Emacs diary file for that day, with category "Diary", like Emacs:

agenda = {
  include_diary = true,
  diary_file = "~/.emacs.d/diary",  -- default: ~/diary if it exists,
                                    -- else the Emacs user directory's
  calendar_date_style = "american", -- "european", "iso"
  diary_show_holidays = true,       -- diary-show-holidays-flag
  diary_include_files = false,      -- read #include "FILE" lines
  diary_nongregorian = {},          -- "hebrew", "islamic", "bahai",
                                    -- "chinese" (H, I, B, C entries)
}

The file is read the way Emacs's diary-list-entries reads it: an entry starts at the beginning of a line with a date in one of the forms of calendar_date_style (diary-date-forms), followed by its text; indented lines below continue it:

9/27 Weekly review            MONTH/DAY
9/27/2026 Dentist 10:30       MONTH/DAY/YEAR (or a two-digit year)
Sep 27 Birthday               MONTHNAME DAY (full or three letters,
September 27, 2026 Party        with or without a period)
                              MONTHNAME DAY, YEAR
Tuesday 10am Meeting          DAYNAME (full or abbreviated): weekly
* 15 Pay day                  `*` matches any day, month or year
&10/3 Not marked              the `&` (non-marking) prefix
10/4                          a date alone: the indented lines are
  Trip to Boston                the entry
  2pm Meeting
%%(diary-float t 4 -1) Last Thursday   a sexp entry

European style reads DAY/MONTH, DAY/MONTH/YEAR, DAY MONTHNAME and DAY MONTHNAME YEAR; iso style MONTH/DAY, YEAR-MONTH-DAY (or /), MONTHNAME DAY and YEAR MONTHNAME DAY. The diary %%(...) sexps use the same functions as Org's, evaluated with date and entry bound.

The agenda lines are Emacs's: the day's holidays (agenda.holidays, unless diary_show_holidays is false) come first, then the sexp entries, then the dated entries (one date form after the other), the entries of other calendars and the included files; the lines of an entry are joined with "; " except the lines starting with a time, which become entries of their own. A time at the start of a line ("10:30", "2pm", "8:30-9:15") goes to the time column and the time grid; face attributes such as [foreground:red] are removed. D toggles the diary for the current agenda (org-agenda-toggle-diary); a block of agenda.custom_commands can set include_diary too. <Tab> and <CR> on a diary line jump to the line of the diary file (the last line of the entry, like Emacs); the commands that change an Org entry (t, s, :, ...) report "Command not allowed in this line", as do jumps from a holiday line.

With diary_include_files, #include "FILE" lines (relative to the diary file's directory) add the entries of other diary files, recursively (Emacs: diary-include-other-diary-files in diary-list-entries-hook). With diary_nongregorian, entries prefixed with H, I, B or C are dates of the Hebrew, Islamic, Bahá’í or Chinese calendar, in the same forms with that calendar's month names (not abbreviated): HTishri 16 Sukkot, IRamadan 1 Fast, C8/15 Mid-autumn (diary-nongregorian-listing-hook). Differences: customized diary-date-forms, diary-comment-start, diary-list-include-blanks and diary-face-attrs are not supported, and a missing or recursive include is skipped with a message (Emacs stops with an error).

Not supported in the agenda

  • Without an Emacs executable, Emacs Lisp beyond the subset above (loops, lambda, defun, assignments, other libraries) and the holidays argument of org-class: an unsupported sexp is skipped with a message (once per session). With one, they run in a separate Emacs that has none of your session's state (variables, functions you defined; calendar-date-style is set from agenda.calendar_date_style) unless babel.emacs_lisp.args loads it. Holidays (org-agenda-holidays) and the entries of other calendars (org-agenda-diary-sexp) are supported.
  • The Customize keys (C, ! in the dispatcher): there is no Customize.
  • Keys that differ on purpose, to keep Vim motions: n/p move by item (Emacs: by line; N/P by item), j and k are motions (go to date is gd, capture K), g is a Vim prefix (redo all is gr), s/d schedule and set deadlines (Emacs: save and day view; views are vd, vw, vt, vm, vy), R refiles (the clock report is C / vR, so the calendar conversion is gC), and habits are toggled with vh (Emacs K, which is capture here).