Hacking
This appendix describes some ways a user can extend the functionality of Org.
Hooks
Org has a large number of hook variables for adding functionality. A complete list of hooks with documentation is maintained by the Worg project at https://orgmode.org/worg/doc.html#hooks.
Add-on Packages
Various authors wrote a large number of add-on packages for Org. Some
of these packages used to be part of the org-mode repository but are
now hosted in a separate org-contrib repository
here. A Worg page with more
information is at: https://orgmode.org/worg/org-contrib/.
Adding Hyperlink Types
Org has many built-in hyperlink types (see Hyperlinks), and an interface for adding new link types. The following example shows the process of adding Org links to Unix man pages, which look like this
[[man:printf][The printf manual]]
The following ol-man.el file implements it
;;; ol-man.el - Support for links to man pages in Org mode
(require 'ol)
(org-link-set-parameters "man"
:follow #'org-man-open
:export #'org-man-export
:store #'org-man-store-link)
(defcustom org-man-command 'man
"The Emacs command to be used to display a man page."
:group 'org-link
:type '(choice (const man) (const woman)))
(defun org-man-open (path _)
"Visit the manpage on PATH.
PATH should be a topic that can be thrown at the man command."
(funcall org-man-command path))
(defun org-man-store-link (&optional _interactive?)
"Store a link to a man page."
(when (memq major-mode '(Man-mode woman-mode))
;; This is a man page, we do make this link.
(let* ((page (org-man-get-page-name))
(link (concat "man:" page))
(description (format "Man page for %s" page)))
(org-link-store-props
:type "man"
:link link
:description description))))
(defun org-man-get-page-name ()
"Extract the page name from the buffer name."
;; This works for both `Man-mode' and `woman-mode'.
(if (string-match " \\(\\S-+\\)\\*" (buffer-name))
(match-string 1 (buffer-name))
(error "Cannot create link to this man page")))
(defun org-man-export (link description format _)
"Export a man page link from Org files."
(let ((path (format "http://man.he.net/?topic=%s§ion=all" link))
(desc (or description link)))
(pcase format
(`html (format "<a target=\"_blank\" href=\"%s\">%s</a>" path desc))
(`latex (format "\\href{%s}{%s}" path desc))
(`texinfo (format "@uref{%s,%s}" path desc))
(`ascii (format "%s (%s)" desc path))
(t path))))
(provide ol-man)
;;; ol-man.el ends hereTo activate links to man pages in Org, enter this in the Emacs init file:
(require 'ol-man)
A review of ol-man.el:
- First,
(require 'ol)ensures thatol.elis loaded.
Then org-link-set-parameters defines a new link type with man
prefix and associates functions for following, exporting and
storing such links. See the variable org-link-parameters for
a complete list of possible associations.
- The rest of the file implements necessary variables and functions.
For example,
org-man-store-linkis responsible for storing a link whenorg-store-link(see Handling Links) is called from a buffer displaying a man page. It is passed an argumentinteractive?which this function does not use, but other store functions use to behave differently when a link is stored interactively by the user. It first checks if the major mode is appropriate. If check fails, the function returnsnil, which means it isn't responsible for creating a link to the current buffer. Otherwise the function makes a link string by combining theman:prefix with the man topic. It also provides a default description. The functionorg-insert-linkcan insert it back into an Org buffer later on.
Adding Export Backends
Org's export engine makes it easy for writing new backends. The framework on which the engine was built makes it easy to derive new backends from existing ones.
The two main entry points to the export engine are:
org-export-define-backend and org-export-define-derived-backend.
To grok these functions, see ox-latex.el for an example of defining
a new backend from scratch, and ox-beamer.el for an example of
deriving from an existing engine.
For creating a new backend from scratch, first set its name as
a symbol in an alist consisting of elements and export functions. To
make the backend visible to the export dispatcher, set :menu-entry
keyword. For export options specific to this backend, set the
:options-alist.
For creating a new backend from an existing one, set
:translate-alist to an alist of export functions. This alist
replaces the parent backend functions.
For complete documentation, see the Org Export Reference on Worg.
Tables in Arbitrary Syntax
Due to Org's success in handling tables with Orgtbl, a frequently requested feature is the use of Org's table functions in other modes, e.g., LaTeX. This would be hard to do in a general way without complicated customization nightmares. Moreover, that would take Org away from its simplicity roots that Orgtbl has proven. There is, however, an alternate approach to accomplishing the same.
This approach involves implementing a custom translate function that operates on a native Org source table to produce a table in another format. This strategy would keep the excellently working Orgtbl simple and isolate complications, if any, confined to the translate function. To add more alien table formats, we just add more translate functions. Also the burden of developing custom translate functions for new table formats is in the hands of those who know those formats best.
Radio tables
Radio tables are target locations for translated tables that are not near their source. Org finds the target location and inserts the translated table.
The key to finding the target location is the magic words BEGIN/END
RECEIVE ORGTBL. They have to appear as comments in the current mode.
If the mode is C, then:
/* BEGIN RECEIVE ORGTBL table_name */ /* END RECEIVE ORGTBL table_name */
At the location of source, Org needs a special line to direct Orgtbl to translate and to find the target for inserting the translated table. For example:
#+ORGTBL: SEND table_name translation_function arguments ...
table_name is the table's reference name, which is also used in the
receiver lines, and the translation_function is the Lisp function
that translates. This line, in addition, may also contain alternating
key and value arguments at the end. The translation function gets
these values as a property list. A few standard parameters are
already recognized and acted upon before the translation function is
called:
-
:skip N - Skip the first N lines of the table. Hlines do count; include them if they are to be skipped.
-
:skipcols (n1 n2 ...) -
List of columns to be skipped. First Org automatically discards
columns with calculation marks and then sends the table to the
translator function, which then skips columns as specified in
skipcols.
To keep the source table intact in the buffer without being disturbed when the source file is compiled or otherwise being worked on, use one of these strategies:
- Place the table in a block comment. For example, in C mode you
could wrap the table between
/*and*/lines. - Put the table after an "end" statement. For example
\byein TeX and\end{document}in LaTeX. - Comment and un-comment each line of the table during edits. The
M-x orgtbl-toggle-commentcommand makes toggling easy.
A LaTeX example of radio tables
To wrap a source table in LaTeX, use the comment environment
provided by =comment.sty=(note: https://www.ctan.org/pkg/comment). To
activate it, put \usepackage{comment} in the document header.
Orgtbl mode inserts a radio table skeleton55 with the command
M-x orgtbl-insert-radio-table, which prompts for a table
name. For example, if salesfigures is the name, the template
inserts:
% BEGIN RECEIVE ORGTBL salesfigures
% END RECEIVE ORGTBL salesfigures
\begin{comment}
#+ORGTBL: SEND salesfigures orgtbl-to-latex
| | |
\end{comment}
The line #+ORGTBL: SEND tells Orgtbl mode to use the function
orgtbl-to-latex to convert the table to LaTeX format, then insert
the table at the target (receive) location named salesfigures. Now
the table is ready for data entry. It can even use spreadsheet
features56:
% BEGIN RECEIVE ORGTBL salesfigures
% END RECEIVE ORGTBL salesfigures
\begin{comment}
#+ORGTBL: SEND salesfigures orgtbl-to-latex
| Month | Days | Nr sold | per day |
|-------+------+---------+---------|
| Jan | 23 | 55 | 2.4 |
| Feb | 21 | 16 | 0.8 |
| March | 22 | 278 | 12.6 |
#+TBLFM: $4=$3/$2;%.1f
% $ (optional extra dollar to keep Font Lock happy, see footnote)
\end{comment}
After editing, C-c C-c inserts the translated table at the
target location, between the two marker lines.
For hand-made custom tables, note that the translator needs to skip the first two lines of the source table. Also the command has to splice out the target table without the header and footer.
\begin{tabular}{lrrr}
Month & \multicolumn{1}{c}{Days} & Nr.\ sold & per day\\
% BEGIN RECEIVE ORGTBL salesfigures
% END RECEIVE ORGTBL salesfigures
\end{tabular}
%
\begin{comment}
#+ORGTBL: SEND salesfigures orgtbl-to-latex :splice t :skip 2
| Month | Days | Nr sold | per day |
|-------+------+---------+---------|
| Jan | 23 | 55 | 2.4 |
| Feb | 21 | 16 | 0.8 |
| March | 22 | 278 | 12.6 |
#+TBLFM: $4=$3/$2;%.1f
\end{comment}
The LaTeX translator function orgtbl-to-latex is already part of
Orgtbl mode and uses a tabular environment to typeset the table and
marks horizontal lines with \hline. For additional parameters to
control output, see Translator functions:
-
:splice BOOLEAN -
When BOOLEAN is non-
nil, return only table body lines; i.e., not wrapped intabularenvironment. Default isnil. -
:fmt FMT -
Format string to warp each field. It should contain
%sfor the original field value. For example, to wrap each field value in dollar symbol, you could use:fmt "$%s$". Format can also wrap a property list with column numbers and formats, for example:fmt (2 "$%s$" 4 "%s\\%%"). In place of a string, a function of one argument can be used; the function must return a formatted string. -
:efmt EFMT -
Format numbers as exponentials. The spec should have
%stwice for inserting mantissa and exponent, for example"%s\\times10^{%s}". This may also be a property list with column numbers and formats, for example:efmt (2 "$%s\\times10^{%s}$" 4 "$%s\\cdot10^{%s}$"). After EFMT has been applied to a value, FMT—see above—is also applied. Functions with two arguments can be supplied instead of strings. By default, no special formatting is applied.
Translator functions
Orgtbl mode has built-in translator functions: orgtbl-to-csv
(comma-separated values), orgtbl-to-tsv (TAB-separated values),
orgtbl-to-latex, orgtbl-to-html, orgtbl-to-texinfo,
orgtbl-to-unicode and orgtbl-to-orgtbl. They use the generic
translator, orgtbl-to-generic, which delegates translations to
various export backends.
Properties passed to the function through the ORGTBL SEND line take
precedence over properties defined inside the function. For example,
this overrides the default LaTeX line endings, \\, with \\[2mm]:
#+ORGTBL: SEND test orgtbl-to-latex :lend " \\\\[2mm]"
For a new language translator, define a converter function. It can be
a generic function, such as shown in this example. It marks
a beginning and ending of a table with !BTBL! and !ETBL!;
a beginning and ending of lines with !BL! and !EL!; and uses a TAB
for a field separator:
(defun orgtbl-to-language (table params)
"Convert the orgtbl-mode TABLE to language."
(orgtbl-to-generic
table
(org-combine-plists
'(:tstart "!BTBL!" :tend "!ETBL!" :lstart "!BL!" :lend "!EL!" :sep "\t")
params)))
The documentation for the orgtbl-to-generic function shows
a complete list of parameters, each of which can be passed through to
orgtbl-to-latex, orgtbl-to-texinfo, and any other function using
that generic function.
For complicated translations the generic translator function could be
replaced by a custom translator function. Such a custom function must
take two arguments and return a single string containing the formatted
table. The first argument is the table whose lines are a list of
fields or the symbol hline. The second argument is the property
list consisting of parameters specified in the #+ORGTBL: SEND line.
Please share your translator functions by posting them to the Org
users mailing list, at mailto:emacs-orgmode@gnu.org.
Dynamic Blocks
Org supports dynamic blocks in Org documents. They are inserted with begin and end markers like any other code block, but the contents are updated automatically by a user function.
You can insert a dynamic block with org-dynamic-block-insert-dblock,
which is bound to C-c C-x x by default. For example,
C-c C-x x c l o c k t a b l e RET inserts a table that
updates the work time (see Clocking Work Time).
Dynamic blocks can have names and function parameters. The syntax is similar to source code block specifications:
#+BEGIN: myblock :parameter1 value1 :parameter2 value2 ... ... #+END:
These commands update dynamic blocks:
-
C-c C-x C-u(org-dblock-update)
Update dynamic block at point.
-
C-u C-c C-x C-u
Update all dynamic blocks in the current file.
Before updating a dynamic block, Org removes content between the
BEGIN and END markers. Org then reads the parameters on the
BEGIN line for passing to the writer function as a plist. The
previous content of the dynamic block becomes erased from the buffer
and appended to the plist under :content.
The syntax for naming a writer function with a dynamic block labeled
myblock is: org-dblock-write:myblock.
The following is an example of a dynamic block and a block writer function that updates the time when the function was last run:
#+BEGIN: block-update-time :format "on %m/%d/%Y at %H:%M" ... #+END:
The dynamic block's writer function:
(defun org-dblock-write:block-update-time (params)
(let ((fmt (or (plist-get params :format) "%d. %m. %Y")))
(insert "Last block update at: "
(format-time-string fmt))))
To keep dynamic blocks up-to-date in an Org file, use the function,
org-update-all-dblocks in hook, such as before-save-hook. The
org-update-all-dblocks function does not run if the file is not in
Org mode.
Dynamic blocks, like any other block, can be narrowed with
org-narrow-to-block.
Special Agenda Views
Org provides a special hook to further limit items in agenda views:
agenda, agenda*~(note: The ~agenda* view is the same as agenda
except that it only considers appointments, i.e., scheduled and
deadline items that have a time specification [h]h:mm in their
timestamps.), todo, alltodo, tags, tags-todo, tags-tree.
Specify a custom function that tests inclusion of every matched item
in the view. This function can also skip as much as is needed.
For a global condition applicable to agenda views, use the
org-agenda-skip-function-global variable. Org uses a global
condition with org-agenda-skip-function for custom searching.
This example defines a function for a custom view showing TODO items
with waiting status. Manually this is a multi-step search process,
but with a custom view, this can be automated as follows:
The custom function searches the subtree for the waiting tag and
returns nil on match. Otherwise it gives the location from where
the search continues.
(defun my-skip-unless-waiting ()
"Skip trees that are not waiting"
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(if (re-search-forward ":waiting:" subtree-end t)
nil ; tag found, do not skip
subtree-end))) ; tag not found, continue after end of subtreeTo use this custom function in a custom agenda command:
(org-add-agenda-custom-command
'("b" todo "PROJECT"
((org-agenda-skip-function 'my-skip-unless-waiting)
(org-agenda-overriding-header "Projects waiting for something: "))))Note that this also binds org-agenda-overriding-header to a more
meaningful string suitable for the agenda view.
Search for entries with a limit set on levels for the custom search.
This is a general approach to creating custom searches in Org. To
include all levels, use =LEVEL>0=(note: Note that, for
org-odd-levels-only, a level number corresponds to order in the
hierarchy, not to the number of stars.). Then to selectively pick the
matched entries, use org-agenda-skip-function, which also accepts
Lisp forms, such as org-agenda-skip-entry-if and
org-agenda-skip-subtree-if. For example:
-
(org-agenda-skip-entry-if 'scheduled) - Skip current entry if it has been scheduled.
-
(org-agenda-skip-entry-if 'notscheduled) - Skip current entry if it has not been scheduled.
-
(org-agenda-skip-entry-if 'deadline) - Skip current entry if it has a deadline.
-
(org-agenda-skip-entry-if 'scheduled 'deadline) - Skip current entry if it has a deadline, or if it is scheduled.
-
(org-agenda-skip-entry-if 'todo '("TODO" "WAITING")) - Skip current entry if the TODO keyword is TODO or WAITING.
-
(org-agenda-skip-entry-if 'todo 'done) - Skip current entry if the TODO keyword marks a DONE state.
-
(org-agenda-skip-entry-if 'timestamp) - Skip current entry if it has any timestamp, may also be deadline or scheduled.
-
(org-agenda-skip-entry-if 'regexp "regular expression") - Skip current entry if the regular expression matches in the entry.
-
(org-agenda-skip-entry-if 'notregexp "regular expression") - Skip current entry unless the regular expression matches.
-
(org-agenda-skip-subtree-if 'regexp "regular expression") - Same as above, but check and skip the entire subtree.
The following is an example of a search for waiting without the
special function:
(org-add-agenda-custom-command
'("b" todo "PROJECT"
((org-agenda-skip-function '(org-agenda-skip-subtree-if
'regexp ":waiting:"))
(org-agenda-overriding-header "Projects waiting for something: "))))Speeding Up Your Agendas
Some agenda commands slow down when the Org files grow in size or number. Here are tips to speed up:
- Reduce the number of Org agenda files to avoid slowdowns due to hard drive accesses.
- Reduce the number of DONE and archived headlines so agenda operations that skip over these can finish faster.
- Do not dim blocked tasks:
(setq org-agenda-dim-blocked-tasks nil)- Stop preparing agenda buffers on startup:
(setq org-agenda-inhibit-startup t)- Disable tag inheritance for agendas:
(setq org-agenda-use-tag-inheritance nil)-
Disable parsing of some properties:
(setq org-agenda-ignore-properties '(stats))This will disable parsing and updating statistic cookies.
These options can be applied to selected agenda views. For more details about generation of agenda views, see the docstrings for the relevant variables, and this dedicated Worg page for agenda optimization.
Extracting Agenda Information
Org provides commands to access agendas through Emacs batch mode. Through this command-line interface, agendas are automated for further processing or printing.
org-batch-agenda creates an agenda view in ASCII and outputs to
standard output. This command takes one string parameter. When
string consists of a single character, Org uses it as a key to
org-agenda-custom-commands. These are the same ones available
through the agenda dispatcher (see The Agenda Dispatcher).
This example command line directly prints the TODO list to the printer:
emacs -batch -l ~/.emacs -eval '(org-batch-agenda "t")' | lpr
When the string parameter length is two or more characters, Org
matches it with tags/TODO strings. For example, this example command
line prints items tagged with shop, but excludes items tagged with
NewYork:
emacs -batch -l ~/.emacs \
-eval '(org-batch-agenda "+shop-NewYork")' | lpr
An example showing on-the-fly parameter modifications:
emacs -batch -l ~/.emacs \
-eval '(org-batch-agenda "a" \
org-agenda-span (quote month) \
org-agenda-include-diary nil \
org-agenda-files (quote ("~/org/project.org")))' \
| lpr
which produces an agenda for the next 30 days from just the
~/org/projects.org file.
For structured processing of agenda output, use org-batch-agenda-csv
with the following fields:
- category
- The category of the item
- head
- The headline, without TODO keyword, TAGS and PRIORITY
- type
-
The type of the agenda entry, can be
todoselected in TODO match tagsmatchselected in tags match diaryimported from diary deadlinea deadline scheduledscheduled timestampappointment, selected by timestamp closedentry was closed on date upcoming-deadlinewarning about nearing deadline past-scheduledforwarded scheduled item blockentry has date block including date - todo
- The TODO keyword, if any
- tags
- All tags including inherited ones, separated by colons
- date
- The relevant date, like
2007-2-14 - time
- The time, like
15:00-16:50 - extra
- String with extra planning info
- priority-l
- The priority letter if any was given
- priority-n
- The computed numerical priority
If the selection of the agenda item was based on a timestamp,
including those items with DEADLINE and SCHEDULED keywords, then
Org includes date and time in the output.
If the selection of the agenda item was based on a timestamp (or deadline/scheduled), then Org includes date and time in the output.
Here is an example of a post-processing script in Perl. It takes the CSV output from Emacs and prints with a checkbox:
#!/usr/bin/perl
# define the Emacs command to run
$cmd = "emacs -batch -l ~/.emacs -eval '(org-batch-agenda-csv \"t\")'";
# run it and capture the output
$agenda = qx{$cmd 2>/dev/null};
# loop over all lines
foreach $line (split(/\n/,$agenda)) {
# get the individual values
($category,$head,$type,$todo,$tags,$date,$time,$extra,
$priority_l,$priority_n) = split(/,/,$line);
# process and print
print "[ ] $head\n";
}Using the Property API
Here is a description of the functions that can be used to work with properties.
Get all properties of the entry at point-or-marker POM.
This includes the TODO keyword, the tags, time strings for deadline,
scheduled, and clocking, and any additional properties defined in the
entry. The return value is an alist. Keys may occur multiple times
if the property key was used several times. POM may also
be nil, in which case the current entry is used. If
WHICH is nil or all, get all properties. If
WHICH is special or standard, only get that subclass.
Get value of PROPERTY for entry at point-or-marker
POM. By default, this only looks at properties defined
locally in the entry. If INHERIT is non-nil and the
entry does not have the property, then also check higher levels of the
hierarchy. If INHERIT is the symbol selective, use
inheritance if and only if the setting of
org-use-property-inheritance selects PROPERTY for
inheritance.
Delete the property PROPERTY from entry at point-or-marker POM.
Set PROPERTY to VALUES for entry at point-or-marker POM.
Get all property keys in the current buffer.
Insert a property drawer for the current entry.
Set PROPERTY at point-or-marker POM to VALUES. VALUES should be a list of strings. They are concatenated, with spaces as separators.
Treat the value of the property PROPERTY as a whitespace-separated list of values and return the values as a list of strings.
Treat the value of the property PROPERTY as a whitespace-separated list of values and make sure that VALUE is in this list.
Treat the value of the property PROPERTY as a whitespace-separated list of values and make sure that VALUE is not in this list.
Treat the value of the property PROPERTY as a whitespace-separated list of values and check if VALUE is in this list.
Hook for functions supplying allowed values for a specific property.
The functions must take a single argument, the name of the property,
and return a flat list of allowed values. If :ETC is one of the
values, use the values as completion help, but allow also other values
to be entered. The functions must return nil if they are not
responsible for this property.
Using the Mapping API
Org has sophisticated mapping capabilities to find all entries satisfying certain criteria. Internally, this functionality is used to produce agenda views, but there is also an API that can be used to execute arbitrary functions for each or selected entries. The main entry point for this API is:
Call FUNC at each headline selected by MATCH in SCOPE.
FUNC is a function or a Lisp form. With point positioned at the beginning of the headline, call the function without arguments. Org returns a list of return values of calls to the function.
To avoid preserving point, Org wraps the call to FUNC in
save-excursion form. After evaluation, Org moves point to the end
of the line that was just processed. Search continues from that point
forward. This may not always work as expected under some conditions,
such as if the current subtree was removed by a previous archiving
operation. In such rare circumstances, Org skips the next entry
entirely when it should not. To stop Org from such skips, make
FUNC set the variable org-map-continue-from to a specific
buffer position.
MATCH is a tags/property/TODO match. Org iterates only
matched headlines. Org iterates over all headlines when
MATCH is nil or t.
SCOPE determines the scope of this command. It can be any of:
-
nil - The current buffer, respecting the restriction, if any.
-
tree - The subtree started with the entry at point.
-
region - The entries within the active region, if any.
-
file - The current buffer, without restriction.
-
file-with-archives - The current buffer, and any archives associated with it.
-
agenda - All agenda files.
-
agenda-with-archives - All agenda files with any archive files associated with them.
- list of filenames
- If this is a list, all files in the list are scanned.
The remaining arguments are treated as settings for the scanner's skipping facilities. Valid arguments are:
-
archive -
Skip trees with the
ARCHIVEtag. -
comment - Skip trees with the COMMENT keyword.
- function or Lisp form
Used as value for org-agenda-skip-function, so whenever the
function returns t, FUNC is called for that entry and
search continues from the point where the function leaves it.
The mapping routine can call any arbitrary function, even functions that change meta data or query the property API (see Using the Property API). Here are some handy functions:
Change the TODO state of the entry. See the docstring of the functions for the many possible values for the argument ARG.
Change the priority of the entry. See the docstring of this function for the possible values for ACTION.
Toggle the tag TAG in the current entry. Setting
ONOFF to either on or off does not toggle tag, but
ensure that it is either on or off.
Promote the current entry.
Demote the current entry.
This example turns all entries tagged with TOMORROW into TODO
entries with keyword UPCOMING. Org ignores entries in comment trees
and archive trees.
(org-map-entries '(org-todo "UPCOMING")
"+TOMORROW" 'file 'archive 'comment)
The following example counts the number of entries with TODO keyword
WAITING, in all agenda files.
(length (org-map-entries t "/+WAITING" 'agenda))Footnotes
-
55
By default this works only for LaTeX, HTML, and Texinfo. Configure the variable
orgtbl-radio-table-templatesto install templates for other modes.Backrefs: 1
-
56
If the
TBLFMkeyword contains an odd number of dollar characters, this may cause problems with Font Lock in LaTeX mode. As shown in the example you can fix this by adding an extra line inside thecommentenvironment that is used to balance the dollar expressions. If you are using AUCTeX with the font-latex library, a much better solution is to add thecommentenvironment to the variableLaTeX-verbatim-environments.Backrefs: 1