Quick add (Todoist-style)
Stability: stable (org-extensions-stability)
quickadd adds an entry from one line, the way Todoist's quick add does:
require("org").setup({ extensions = { quickadd = true } })
:Org quickadd [text] (action quickadd, key <prefix>q, available anywhere) adds text, or asks for a line in a small float that previews the parsed entry as you type (with vim.ui.input() when there is no UI). <CR> adds it, <Esc> in Normal mode or <C-c> cancel. The entry goes under its @target, else into file (under headline), and the file is saved. For example
Call Bob fri 3pm #work #phone !A ~30m @Inbox due mon every week
becomes, under the best-matching "Inbox" heading,
* TODO [#A] Call Bob :work:phone:
DEADLINE: <2026-10-05 Mon> SCHEDULED: <2026-10-02 Fri 15:00 +1w>
:PROPERTIES:
:Effort: 0:30
:END:
What the line may contain
dates Anything the date prompt reads (org-dates):today,tomorrow(tmr),fri,fri 3pm,at 14:30,9am-10:30am,+2w,dec 24,24 dec,12/24,2026-12-24,w45, plusnext week,next fri,in 3 daysandnoon. Dates go to SCHEDULED (seedate_kind); afterdue(orby) to DEADLINE. A month name only counts next to a day number, so "May call Bob" stays a title. A weekday,now,noon,midnightortonighton its own is a date only at the end of the title (tags and the like may follow) or afterat,onordue: "Watch Friday Night Lights" stays a title, "Meeting on fri with Bob" and "Standup mon 9am with the team" get a date. When the line holds several dates, the last one counts (the lastdueone for the deadline), as in Todoist; the others stay in the title. repeatersevery day|week|month|year,every 2w,every 3 days,every other week,every mon(the next Monday, today on a Monday).every!repeats from the day it is done (.+). Without a date the repeater starts today. It goes on the SCHEDULED date, or on the deadline when there is only one.every weekday,every weekendand several days (every mon and thu,every mon,wed,fri) have no Org repeater and are written as a diary sexp,<%%(memq (calendar-day-of-week date) '(1 2 3 4 5))>.#taga tag;#42(digits only) stays in the title!A!1priority; alsop1,p2,p3(p1 is the highest)~30mEffort:~30m,~1h,~1h30,~1:30,~90,~1.5h@Headingtarget: the best match among the headings of thetargetsfiles (exact, then prefix, substring and fuzzy match);@Parent/Childmatches outline paths,@file/Headinglooks in one file (its name without.org),@file/adds at the top level of that file. A heading that doesn't exist in a named file is created. When nothing matches the entry goes tofileand the message says so.@"Two words"for spaces.*NEXTkeyword instead ofkeyword;*-for none
Quote words to keep them in the title ("Friday Night Lights", 'due fri'), or escape one character with a backslash (\#1, \fri). :Org quickadd reads the text as typed and completes @ headings (a heading with spaces as @"Big Project"), # tags and * keywords. The preview keeps an index of the headings of the targets files and reads a file again only when it (or its buffer) changed.
Capture templates
A capture template with quickadd = true reads its headline the same way when the capture is stored, so %^{Task} answers or text typed in the capture buffer can hold dates, tags, priorities and efforts. The template's own keyword, tags, planning and properties are kept and completed. An @target sends the entry to that heading instead of the template's target (whose date tree or heading, when the capture made one, is removed again); when it matches nothing, the template's target is used and a warning says so.
capture = { templates = {
q = { description = "Quick task", template = "* TODO %^{Task}",
target = "inbox.org", immediate_finish = true, quickadd = true },
} }
Options
filefile for entries without a target, relative toorg_directory(nil:default_notes_file)headlineheadline offileentries go under, created when missing (nil: the top level)keywordkeyword of new entries ("TODO"); false for nonedate_kindwhere dates withoutduego: "scheduled" or "deadline"targetsfiles@targetlooks in: "agenda" (the agenda files andfile) or a list of files and globspreviewpreview the parsed entry while typing (true)createdadd a:CREATED:inactive timestamp property (false)jumpgo to the new entry after adding it (false)
The parser is require("org.extensions.quickadd").parse(text, now), which returns the title, keyword, priority, tags, effort (minutes), dates, the planning timestamps and the target without touching any file.