forked from mirrors/org-mode
Compare commits
240 Commits
c67b29cadf
...
12941a9df6
Author | SHA1 | Date |
---|---|---|
TEC | 12941a9df6 | |
TEC | 2eae918538 | |
TEC | 145aec1036 | |
TEC | ec78868507 | |
TEC | 6665f5274c | |
TEC | 16240adb94 | |
TEC | d42ccf4390 | |
TEC | ec088a2def | |
TEC | 72b6555d87 | |
TEC | f303074e67 | |
TEC | 1a0f6ba424 | |
TEC | 8a04f21e90 | |
TEC | 30295b8ef9 | |
TEC | 583f171439 | |
Karthik Chikmagalur | 094f4b1487 | |
TEC | ccebe00fc8 | |
TEC | 3b461cfa19 | |
TEC | d530fb5e6d | |
TEC | 1f5237f230 | |
Karthik Chikmagalur | f2af9023c9 | |
TEC | b1b7e9b468 | |
TEC | bb95960920 | |
TEC | e54cc82172 | |
TEC | b415f5ef2c | |
Karthik Chikmagalur | 74e62b6abf | |
TEC | d62e49f45f | |
TEC | 5b7254440f | |
Karthik Chikmagalur | 2aacef36b7 | |
Karthik Chikmagalur | a5830e2494 | |
Karthik Chikmagalur | 3bc1194184 | |
TEC | ab395fdcdb | |
TEC | 8cdd2c8273 | |
Karthik Chikmagalur | 1d9baf095a | |
TEC | 8ecfa10539 | |
TEC | 662eddd4d5 | |
TEC | e6e0ed98a1 | |
TEC | 2a3c5d662a | |
TEC | 277928c988 | |
TEC | fac65c8d64 | |
TEC | 95ef1d70f0 | |
TEC | 3c1f1b0601 | |
TEC | 282ac1e6ec | |
TEC | d1cace50f5 | |
TEC | 281f5efb2d | |
Karthik Chikmagalur | c0d324940a | |
Karthik Chikmagalur | d0e409998c | |
Karthik Chikmagalur | 3e265d0e40 | |
Karthik Chikmagalur | 546f8fb2dd | |
Karthik Chikmagalur | 4b15ba9f0d | |
Karthik Chikmagalur | f44b741e7a | |
Karthik Chikmagalur | a3c6ab8a4f | |
Karthik Chikmagalur | e0867ab72b | |
Karthik Chikmagalur | d989a6ee47 | |
Karthik Chikmagalur | 3bdc048b73 | |
Karthik Chikmagalur | 44b53a36d0 | |
Karthik Chikmagalur | b188f5ba7f | |
Karthik Chikmagalur | 4ee09d64ed | |
TEC | afb431cfe0 | |
Karthik Chikmagalur | e1348b280b | |
Karthik Chikmagalur | d4739c64d6 | |
Karthik Chikmagalur | 970b2fd96d | |
Karthik Chikmagalur | 4dc877cdc3 | |
Karthik Chikmagalur | 144ae10da7 | |
Karthik Chikmagalur | cbd5d1ca59 | |
Karthik Chikmagalur | db91a3b9a1 | |
TEC | 4424da9b8b | |
TEC | 8566cd774c | |
TEC | 734253d299 | |
TEC | 7efa19fc0c | |
TEC | e3addbe5f0 | |
TEC | 645d71f949 | |
TEC | 36349dd5a3 | |
TEC | 9593c81d1c | |
TEC | 13f37014ee | |
TEC | bf1ec9ab85 | |
TEC | b60f8d810b | |
TEC | c3cce64d9e | |
Karthik Chikmagalur | d4761a5575 | |
TEC | 856b15ef50 | |
TEC | 3ed98c1eb8 | |
TEC | 52cc553bc3 | |
TEC | 9d81812629 | |
TEC | 83489cb0d0 | |
TEC | ec5bb4a797 | |
TEC | 5b0f954b32 | |
TEC | c7e0616ce2 | |
TEC | 1fa471a2eb | |
TEC | 2f382fa52d | |
Karthik Chikmagalur | b7a2b22b4e | |
TEC | db430a240d | |
TEC | 749e775600 | |
TEC | 39c070bcbb | |
TEC | 8e8b73a97e | |
TEC | 83090ae8bf | |
Karthik Chikmagalur | 58654796e6 | |
TEC | 0b624d785e | |
TEC | 932373e15f | |
Karthik Chikmagalur | c9c27e108b | |
TEC | 9dd6cda864 | |
Karthik Chikmagalur | bc00ffab1e | |
TEC | d8d2f289c6 | |
TEC | 6fff40a0f2 | |
Karthik Chikmagalur | f141c04eeb | |
Karthik Chikmagalur | 75ba3e2d44 | |
Karthik Chikmagalur | 0d2d122de3 | |
TEC | ada057a2e9 | |
TEC | 98d57fcc16 | |
TEC | 61b0995070 | |
TEC | 126522aa38 | |
Karthik Chikmagalur | fa2b4f979e | |
Karthik Chikmagalur | 400fd0ffc9 | |
Karthik Chikmagalur | 8e839dff62 | |
Karthik Chikmagalur | 58d358fcba | |
TEC | cd9abc224b | |
Karthik Chikmagalur | 872d322dea | |
Karthik Chikmagalur | 0aec8a1103 | |
TEC | 375ee16ab8 | |
TEC | 734947dc1f | |
Karthik Chikmagalur | e5d2f78984 | |
TEC | f19a69a909 | |
TEC | 78654afc57 | |
TEC | 5f1d5a79dd | |
TEC | 27cb324afc | |
TEC | 64b227ec6a | |
Karthik Chikmagalur | 3fecc1f68c | |
TEC | c7783d10c7 | |
TEC | 08490489fd | |
Karthik Chikmagalur | 6fcbdb29a8 | |
Karthik Chikmagalur | 91030fd289 | |
TEC | 5d98460919 | |
TEC | b392768f24 | |
Karthik Chikmagalur | 2baabb2ab3 | |
Karthik Chikmagalur | 41e13c581e | |
Karthik Chikmagalur | ae81640d20 | |
Karthik Chikmagalur | 3f92449b8e | |
TEC | 41ddd5fa5b | |
Karthik Chikmagalur | 15268d324d | |
TEC | 1f61558011 | |
TEC | 81dd8dca93 | |
TEC | 01b221c3ac | |
TEC | efcfb0af71 | |
TEC | f1594e6c86 | |
TEC | 946ca89939 | |
TEC | 1413ca45b9 | |
TEC | 4723e2cb64 | |
TEC | bd3534de77 | |
TEC | 52416fbf93 | |
Karthik Chikmagalur | c2bb4808af | |
Karthik Chikmagalur | d4090d2d6a | |
Karthik Chikmagalur | 38c86de1c6 | |
TEC | f6932aa60d | |
Karthik Chikmagalur | 5e1ad9fa24 | |
TEC | c81cd36090 | |
TEC | b3072ead33 | |
TEC | 603b05a040 | |
TEC | b2ed12b1ca | |
TEC | 58cc279623 | |
Karthik Chikmagalur | 581b85c898 | |
Karthik Chikmagalur | 0fd3e46e32 | |
TEC | 9d3391f8d1 | |
TEC | 139c270496 | |
TEC | 1e8e73c841 | |
TEC | 62eda0455f | |
TEC | f352cfa174 | |
Karthik Chikmagalur | 3a490c4ef3 | |
Karthik Chikmagalur | fd6a9baf2f | |
TEC | 333f19c6f8 | |
Karthik Chikmagalur | 0a9236e251 | |
TEC | c88f221f08 | |
TEC | f1620233c8 | |
TEC | 9b808659b3 | |
TEC | c500886d40 | |
TEC | cff3668878 | |
TEC | 88d308aa63 | |
TEC | e962fb52a9 | |
TEC | c07c20961d | |
TEC | a044b79eed | |
TEC | 27c373d53d | |
TEC | f297ef8f4b | |
TEC | 180972266c | |
TEC | 6be558e9cd | |
TEC | 62f45e2bab | |
Bastien Guerry | 57ec015ab3 | |
Bastien Guerry | 4ee6e88b2f | |
Bastien Guerry | 044dc80a77 | |
Ihor Radchenko | cab81f2428 | |
Ihor Radchenko | 0a6c881174 | |
Ihor Radchenko | 0608ae1062 | |
Ihor Radchenko | 01a0f15b6b | |
Ihor Radchenko | 8eb78048f0 | |
Ihor Radchenko | 1151c614b3 | |
Gerard Vermeulen | c2ea553be8 | |
Ihor Radchenko | 5f5db3d353 | |
Anand Deopurkar | 8ac99c33f0 | |
Ihor Radchenko | 2da622cad7 | |
Anand Deopurkar | 902dacb9ca | |
Ihor Radchenko | ca061cfacb | |
Leo Butler | 60ddec4827 | |
Ihor Radchenko | b127d7257e | |
Ihor Radchenko | c88da815ed | |
Ihor Radchenko | a3ebd8c669 | |
Ihor Radchenko | ac49228040 | |
Ihor Radchenko | 8648576573 | |
Ihor Radchenko | 6ef0154576 | |
Ihor Radchenko | 731d16f9e9 | |
Ihor Radchenko | c841be9656 | |
Ihor Radchenko | 341a01a07d | |
Olivier Lischer | dd4d05a159 | |
Ihor Radchenko | 9029470eb9 | |
Ihor Radchenko | ac1ed2bf85 | |
Ihor Radchenko | d2df9624ce | |
Ihor Radchenko | 286a8fb798 | |
Ihor Radchenko | 755fef38f5 | |
Ihor Radchenko | fe9bc9efd1 | |
Ihor Radchenko | 80dff08ed1 | |
Tim Ruffing | 356072c1d6 | |
Ihor Radchenko | 8651c83991 | |
Ihor Radchenko | 25a51bad2c | |
Ihor Radchenko | 87c9f9b5db | |
Ihor Radchenko | fef873b1cf | |
Ihor Radchenko | 9f6894c106 | |
Ihor Radchenko | 8bac4d386a | |
Ihor Radchenko | 655e97208c | |
Thierry Banel | 407a55c1c0 | |
Ihor Radchenko | af9100382c | |
Ihor Radchenko | 9daad41ccf | |
Martin Kampas | 37e468cf16 | |
Ihor Radchenko | 5d186b499d | |
Rick Lupton | 95554543b9 | |
Rick Lupton | 6e7e0b2cd3 | |
Ihor Radchenko | f90322377c | |
Ihor Radchenko | 505778950a | |
Ihor Radchenko | 19db45f2cc | |
Ihor Radchenko | 4b1399fb21 | |
Ihor Radchenko | e52a480f9f | |
Ihor Radchenko | bfe253f7eb | |
Ihor Radchenko | 256caa0823 | |
Ihor Radchenko | 24ce9abce7 | |
Ihor Radchenko | 67d9372487 | |
Leo Butler | b3b35b03ff |
|
@ -3300,10 +3300,6 @@ Here is the full set of built-in link types:
|
||||||
|
|
||||||
File links. File name may be remote, absolute, or relative.
|
File links. File name may be remote, absolute, or relative.
|
||||||
|
|
||||||
Additionally, you can specify a line number, or a text search.
|
|
||||||
In Org files, you may link to a headline name, a custom ID, or a
|
|
||||||
code reference instead.
|
|
||||||
|
|
||||||
As a special case, "file" prefix may be omitted if the file name
|
As a special case, "file" prefix may be omitted if the file name
|
||||||
is complete, e.g., it starts with =./=, or =/=.
|
is complete, e.g., it starts with =./=, or =/=.
|
||||||
|
|
||||||
|
@ -3367,44 +3363,50 @@ Here is the full set of built-in link types:
|
||||||
|
|
||||||
Execute a shell command upon activation.
|
Execute a shell command upon activation.
|
||||||
|
|
||||||
|
|
||||||
|
For =file:= and =id:= links, you can additionally specify a line
|
||||||
|
number, or a text search string, separated by =::=. In Org files, you
|
||||||
|
may link to a headline name, a custom ID, or a code reference instead.
|
||||||
|
|
||||||
The following table illustrates the link types above, along with their
|
The following table illustrates the link types above, along with their
|
||||||
options:
|
options:
|
||||||
|
|
||||||
| Link Type | Example |
|
| Link Type | Example |
|
||||||
|------------+----------------------------------------------------------|
|
|------------+--------------------------------------------------------------------|
|
||||||
| http | =http://staff.science.uva.nl/c.dominik/= |
|
| http | =http://staff.science.uva.nl/c.dominik/= |
|
||||||
| https | =https://orgmode.org/= |
|
| https | =https://orgmode.org/= |
|
||||||
| doi | =doi:10.1000/182= |
|
| doi | =doi:10.1000/182= |
|
||||||
| file | =file:/home/dominik/images/jupiter.jpg= |
|
| file | =file:/home/dominik/images/jupiter.jpg= |
|
||||||
| | =/home/dominik/images/jupiter.jpg= (same as above) |
|
| | =/home/dominik/images/jupiter.jpg= (same as above) |
|
||||||
| | =file:papers/last.pdf= |
|
| | =file:papers/last.pdf= |
|
||||||
| | =./papers/last.pdf= (same as above) |
|
| | =./papers/last.pdf= (same as above) |
|
||||||
| | =file:/ssh:me@some.where:papers/last.pdf= (remote) |
|
| | =file:/ssh:me@some.where:papers/last.pdf= (remote) |
|
||||||
| | =/ssh:me@some.where:papers/last.pdf= (same as above) |
|
| | =/ssh:me@some.where:papers/last.pdf= (same as above) |
|
||||||
| | =file:sometextfile::NNN= (jump to line number) |
|
| | =file:sometextfile::NNN= (jump to line number) |
|
||||||
| | =file:projects.org= |
|
| | =file:projects.org= |
|
||||||
| | =file:projects.org::some words= (text search)[fn:12] |
|
| | =file:projects.org::some words= (text search)[fn:12] |
|
||||||
| | =file:projects.org::*task title= (headline search) |
|
| | =file:projects.org::*task title= (headline search) |
|
||||||
| | =file:projects.org::#custom-id= (headline search) |
|
| | =file:projects.org::#custom-id= (headline search) |
|
||||||
| attachment | =attachment:projects.org= |
|
| attachment | =attachment:projects.org= |
|
||||||
| | =attachment:projects.org::some words= (text search) |
|
| | =attachment:projects.org::some words= (text search) |
|
||||||
| docview | =docview:papers/last.pdf::NNN= |
|
| docview | =docview:papers/last.pdf::NNN= |
|
||||||
| id | =id:B7423F4D-2E8A-471B-8810-C40F074717E9= |
|
| id | =id:B7423F4D-2E8A-471B-8810-C40F074717E9= |
|
||||||
| news | =news:comp.emacs= |
|
| | =id:B7423F4D-2E8A-471B-8810-C40F074717E9::*task= (headline search) |
|
||||||
| mailto | =mailto:adent@galaxy.net= |
|
| news | =news:comp.emacs= |
|
||||||
| mhe | =mhe:folder= (folder link) |
|
| mailto | =mailto:adent@galaxy.net= |
|
||||||
| | =mhe:folder#id= (message link) |
|
| mhe | =mhe:folder= (folder link) |
|
||||||
| rmail | =rmail:folder= (folder link) |
|
| | =mhe:folder#id= (message link) |
|
||||||
| | =rmail:folder#id= (message link) |
|
| rmail | =rmail:folder= (folder link) |
|
||||||
| gnus | =gnus:group= (group link) |
|
| | =rmail:folder#id= (message link) |
|
||||||
| | =gnus:group#id= (article link) |
|
| gnus | =gnus:group= (group link) |
|
||||||
| bbdb | =bbdb:R.*Stallman= (record with regexp) |
|
| | =gnus:group#id= (article link) |
|
||||||
| irc | =irc:/irc.com/#emacs/bob= |
|
| bbdb | =bbdb:R.*Stallman= (record with regexp) |
|
||||||
| help | =help:org-store-link= |
|
| irc | =irc:/irc.com/#emacs/bob= |
|
||||||
| info | =info:org#External links= |
|
| help | =help:org-store-link= |
|
||||||
| shell | =shell:ls *.org= |
|
| info | =info:org#External links= |
|
||||||
| elisp | =elisp:(find-file "Elisp.org")= (Elisp form to evaluate) |
|
| shell | =shell:ls *.org= |
|
||||||
| | =elisp:org-agenda= (interactive Elisp command) |
|
| elisp | =elisp:(find-file "Elisp.org")= (Elisp form to evaluate) |
|
||||||
|
| | =elisp:org-agenda= (interactive Elisp command) |
|
||||||
|
|
||||||
#+cindex: VM links
|
#+cindex: VM links
|
||||||
#+cindex: Wanderlust links
|
#+cindex: Wanderlust links
|
||||||
|
@ -3465,8 +3467,9 @@ current buffer:
|
||||||
- /Org mode buffers/ ::
|
- /Org mode buffers/ ::
|
||||||
|
|
||||||
For Org files, if there is a =<<target>>= at point, the link points
|
For Org files, if there is a =<<target>>= at point, the link points
|
||||||
to the target. Otherwise it points to the current headline, which
|
to the target. If there is a named block (using =#+name:=) at
|
||||||
is also the description.
|
point, the link points to that name. Otherwise it points to the
|
||||||
|
current headline, which is also the description.
|
||||||
|
|
||||||
#+vindex: org-id-link-to-org-use-id
|
#+vindex: org-id-link-to-org-use-id
|
||||||
#+cindex: @samp{CUSTOM_ID}, property
|
#+cindex: @samp{CUSTOM_ID}, property
|
||||||
|
@ -3484,6 +3487,32 @@ current buffer:
|
||||||
timestamp, depending on ~org-id-method~. Later, when inserting the
|
timestamp, depending on ~org-id-method~. Later, when inserting the
|
||||||
link, you need to decide which one to use.
|
link, you need to decide which one to use.
|
||||||
|
|
||||||
|
#+vindex: org-id-link-consider-parent-id
|
||||||
|
#+vindex: org-id-link-use-context
|
||||||
|
#+vindex: org-link-context-for-files
|
||||||
|
When ~org-id-link-consider-parent-id~ is ~t~[fn:: Also,
|
||||||
|
~org-link-context-for-files~ and ~org-id-link-use-context~ should be
|
||||||
|
both enabled (which they are, by default).], parent =ID= properties
|
||||||
|
are considered. This allows linking to specific targets, named
|
||||||
|
blocks, or headlines (which may not have a globally unique =ID=
|
||||||
|
themselves) within the context of a parent headline or file which
|
||||||
|
does.
|
||||||
|
|
||||||
|
For example, given this org file:
|
||||||
|
|
||||||
|
#+begin_src org
|
||||||
|
,* Parent
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: abc
|
||||||
|
:END:
|
||||||
|
,** Child 1
|
||||||
|
,** Child 2
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Storing a link with point at "Child 1" will produce a link
|
||||||
|
=<id:abc::*Child 1>=, which precisely links to the "Child 1"
|
||||||
|
headline even though it does not have its own ID.
|
||||||
|
|
||||||
- /Email/News clients: VM, Rmail, Wanderlust, MH-E, Gnus/ ::
|
- /Email/News clients: VM, Rmail, Wanderlust, MH-E, Gnus/ ::
|
||||||
|
|
||||||
#+vindex: org-link-email-description-format
|
#+vindex: org-link-email-description-format
|
||||||
|
@ -3763,7 +3792,9 @@ the link completion function like this:
|
||||||
:ALT_TITLE: Search Options
|
:ALT_TITLE: Search Options
|
||||||
:END:
|
:END:
|
||||||
#+cindex: search option in file links
|
#+cindex: search option in file links
|
||||||
|
#+cindex: search option in id links
|
||||||
#+cindex: file links, searching
|
#+cindex: file links, searching
|
||||||
|
#+cindex: id links, searching
|
||||||
#+cindex: attachment links, searching
|
#+cindex: attachment links, searching
|
||||||
|
|
||||||
File links can contain additional information to make Emacs jump to a
|
File links can contain additional information to make Emacs jump to a
|
||||||
|
@ -3775,8 +3806,8 @@ example, when the command ~org-store-link~ creates a link (see
|
||||||
line as a search string that can be used to find this line back later
|
line as a search string that can be used to find this line back later
|
||||||
when following the link with {{{kbd(C-c C-o)}}}.
|
when following the link with {{{kbd(C-c C-o)}}}.
|
||||||
|
|
||||||
Note that all search options apply for Attachment links in the same
|
Note that all search options apply for Attachment and ID links in the
|
||||||
way that they apply for File links.
|
same way that they apply for File links.
|
||||||
|
|
||||||
Here is the syntax of the different ways to attach a search to a file
|
Here is the syntax of the different ways to attach a search to a file
|
||||||
link, together with explanations for each:
|
link, together with explanations for each:
|
||||||
|
@ -11333,7 +11364,7 @@ pretty output for a number of export backends.
|
||||||
:END:
|
:END:
|
||||||
#+cindex: @LaTeX{} fragments
|
#+cindex: @LaTeX{} fragments
|
||||||
|
|
||||||
#+vindex: org-format-latex-header
|
#+vindex: org-latex-preview-header
|
||||||
Org mode can contain LaTeX math fragments, and it supports ways to
|
Org mode can contain LaTeX math fragments, and it supports ways to
|
||||||
process these for several export backends. When exporting to LaTeX,
|
process these for several export backends. When exporting to LaTeX,
|
||||||
the code is left as it is. When exporting to HTML, Org can use either
|
the code is left as it is. When exporting to HTML, Org can use either
|
||||||
|
@ -11390,20 +11421,19 @@ lines:
|
||||||
:END:
|
:END:
|
||||||
#+cindex: @LaTeX{} fragments, preview
|
#+cindex: @LaTeX{} fragments, preview
|
||||||
|
|
||||||
#+vindex: org-preview-latex-default-process
|
#+vindex: org-latex-preview-process-default
|
||||||
If you have a working LaTeX installation and =dvipng=, =dvisvgm= or
|
If you have a working LaTeX installation and =dvipng=, =dvisvgm= or
|
||||||
=convert= installed[fn:38], LaTeX fragments can be processed to
|
=convert= installed[fn:38], LaTeX fragments can be processed to
|
||||||
produce images of the typeset expressions to be used for inclusion
|
produce images of the typeset expressions to be used for inclusion
|
||||||
while exporting to HTML (see [[*LaTeX fragments]]), or for inline
|
while exporting to HTML (see [[*LaTeX fragments]]), or for inline
|
||||||
previewing within Org mode.
|
previewing within Org mode.
|
||||||
|
|
||||||
#+vindex: org-format-latex-options
|
#+vindex: org-latex-preview-appearance-options
|
||||||
#+vindex: org-format-latex-header
|
#+vindex: org-latex-preview-header
|
||||||
You can customize the variables ~org-format-latex-options~ and
|
You can customize the variables ~org-latex-preview-appearance-options~ and
|
||||||
~org-format-latex-header~ to influence some aspects of the preview.
|
~org-latex-preview-header~ to influence some aspects of the preview.
|
||||||
In particular, the ~:scale~ (and for HTML export, ~:html-scale~)
|
In particular, the ~:scale~ and ~:zoom~ properties of the former can
|
||||||
property of the former can be used to adjust the size of the preview
|
be used to adjust the size of the preview images.
|
||||||
images.
|
|
||||||
|
|
||||||
- {{{kbd(C-c C-x C-l)}}} (~org-latex-preview~) ::
|
- {{{kbd(C-c C-x C-l)}}} (~org-latex-preview~) ::
|
||||||
#+kindex: C-c C-x C-l
|
#+kindex: C-c C-x C-l
|
||||||
|
@ -11427,6 +11457,24 @@ To disable it, simply use
|
||||||
|
|
||||||
: #+STARTUP: nolatexpreview
|
: #+STARTUP: nolatexpreview
|
||||||
|
|
||||||
|
#+vindex: org-latex-preview-numbered
|
||||||
|
When generating previews, Org mode can track equation numbers and keep
|
||||||
|
them consistent by regenerating previews when necessary. This
|
||||||
|
behavior is controlled by the variable ~org-latex-preview-numbered~.
|
||||||
|
|
||||||
|
Org mode can automatically preview LaTeX fragments as you type them,
|
||||||
|
and hide and reveal preview images as the cursor moves into or out of
|
||||||
|
them. To enable this, turn on the minor mode
|
||||||
|
~org-latex-preview-auto-mode~.
|
||||||
|
|
||||||
|
|
||||||
|
- (~org-latex-preview-auto-mode~) ::
|
||||||
|
#+findex: org-latex-preview-auto-mode
|
||||||
|
|
||||||
|
Generate previews of LaTeX fragments as they are entered in the
|
||||||
|
buffer. Previews will be generated when there is any change to the
|
||||||
|
buffer that includes the insertion of a LaTeX environment or
|
||||||
|
fragment.
|
||||||
*** Using CDLaTeX to enter math
|
*** Using CDLaTeX to enter math
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:DESCRIPTION: Speed up entering of formulas.
|
:DESCRIPTION: Speed up entering of formulas.
|
||||||
|
@ -12527,7 +12575,7 @@ Inclusions may specify a file-link to extract an object matched by
|
||||||
ranges for =:lines= keyword are relative to the requested element.
|
ranges for =:lines= keyword are relative to the requested element.
|
||||||
Therefore,
|
Therefore,
|
||||||
|
|
||||||
: #+INCLUDE: "./paper.org::*conclusion" :lines 1-20
|
: #+INCLUDE: "./paper.org::*conclusion" :lines "1-20"
|
||||||
|
|
||||||
#+texinfo: @noindent
|
#+texinfo: @noindent
|
||||||
includes the first 20 lines of the headline named =conclusion=.
|
includes the first 20 lines of the headline named =conclusion=.
|
||||||
|
@ -16740,6 +16788,378 @@ user-friendly improvements. See
|
||||||
<https://orgmode.org/worg/dev/org-export-reference.html> for more
|
<https://orgmode.org/worg/dev/org-export-reference.html> for more
|
||||||
details.
|
details.
|
||||||
|
|
||||||
|
*** Export features
|
||||||
|
**** The underlying idea
|
||||||
|
|
||||||
|
Across export backends it is common to want to include certain chunks
|
||||||
|
of content that are only relevant in particular situations.
|
||||||
|
|
||||||
|
With static export templates, one is forced to choose between
|
||||||
|
including everything that /might/ be wanted, or including very little
|
||||||
|
by default and requiring common content to be manually added every
|
||||||
|
time it is wanted.
|
||||||
|
|
||||||
|
"Export features" allow for a third option, a much more sophisticated
|
||||||
|
method of resolving this dilemma. At the start of the export process,
|
||||||
|
the buffer being exported and the export communication plist (~info~)
|
||||||
|
are scanned to determine which capabilities are relevant to the
|
||||||
|
current export. During the construction of the final output this list
|
||||||
|
of capabilities is used to produce snippets of content to be included.
|
||||||
|
|
||||||
|
This can be thought of as the construction of a graph between conditions,
|
||||||
|
features, and feature implementations. For example, say we have three conditions
|
||||||
|
we want to support:
|
||||||
|
+ Say that images need some extra setup to be supported well, we can
|
||||||
|
just include it when image links are found in the buffer.
|
||||||
|
+ Say we can better support emojis by treating them as images in a
|
||||||
|
particular export backend. We could look for a signal in the buffer
|
||||||
|
that emojis should be handled as images, and then make use of some
|
||||||
|
"image support" and "emoji support" snippets.
|
||||||
|
+ Say that LaTeX maths requires some extra setup, we can just
|
||||||
|
do this when inline LaTeX fragments are found.
|
||||||
|
This situation can be crudely drawn with the following graph:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
condition feature implementation
|
||||||
|
========= ======= ===============
|
||||||
|
|
||||||
|
[emoji] -----------> emoji ------> [emoji plist]
|
||||||
|
\
|
||||||
|
'---->----.
|
||||||
|
\
|
||||||
|
[image link] ------> image ------> [image plist]
|
||||||
|
|
||||||
|
[inline LaTeX] ----> maths ------> [maths plist]
|
||||||
|
|
||||||
|
\____________________/ \__________________/
|
||||||
|
phase 1 phase 2
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
In phase 1 "feature detection" the relevant features are determined, and in
|
||||||
|
phase 2 "feature implementation" how those features can be provided is worked
|
||||||
|
out.
|
||||||
|
|
||||||
|
**** Feature detection
|
||||||
|
|
||||||
|
After the expansion of =#+include= statements and the removal of
|
||||||
|
comments, the export communication plist (~info~) is annotated. At the
|
||||||
|
very end of the annotation process, ~org-export-detect-features~ is
|
||||||
|
run to determine the list of capabilities relevant to the current export.
|
||||||
|
|
||||||
|
This operates by merging the global feature condition alist
|
||||||
|
(~org-export-conditional-features~) with the ~feature-conditions~ slot
|
||||||
|
of the current backend and each of its parents. This produces the
|
||||||
|
total feature conditions alist, which has the form:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
((condition . implied-features)
|
||||||
|
...)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Where =condition= is a test that implies that =implied-features= are
|
||||||
|
relevant. While =implied-features= is always a list of feature
|
||||||
|
symbols, for convenience =condition= can take a number of forms,
|
||||||
|
namely:
|
||||||
|
+ A regexp which is searched for in the export buffer.
|
||||||
|
+ A variable, if a string it is used as a regexp search, otherwise any
|
||||||
|
non-nil value is taken to imply =implied-features=.
|
||||||
|
+ A (unary) function, which is called with on the export communication
|
||||||
|
plist (~info~). A returned string is used as a regexp search,
|
||||||
|
otherwise any non-nil value is taken to imply =implied-features=.
|
||||||
|
|
||||||
|
As an example, a feature conditions alist which checks whether any
|
||||||
|
headings exist, and if the word "hello" appears could take the
|
||||||
|
following form:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(((lambda (info)
|
||||||
|
(org-element-map (plist-get info :parse-tree) 'heading
|
||||||
|
#'identity info t))
|
||||||
|
headlines)
|
||||||
|
("hello" has-greeting))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Conditions are inherited from parent export backends and (for conditions general
|
||||||
|
enough to apply across backends) the variable ~org-export-conditional-features~.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
,--> beamer
|
||||||
|
/
|
||||||
|
html <----. ,----> latex --'
|
||||||
|
\ /
|
||||||
|
org-export-conditional-features
|
||||||
|
/ \
|
||||||
|
ascii <----' '----> odt
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
**** Feature implementations
|
||||||
|
|
||||||
|
The other half of the export feature system is of course producing
|
||||||
|
snippets of content from the list of features. Export backends can do
|
||||||
|
this at any point via ~org-export-expand-feature-snippets~. This
|
||||||
|
operates on a /feature implementation alist/. The implementation alist
|
||||||
|
is essentially a mirror of the condition alist, instead of =(condition
|
||||||
|
. feature-list)= elements it takes =(feature . implementation-plist)=
|
||||||
|
elements. This reversal of order may seem a bit odd, but it should
|
||||||
|
help make the condition--feature--implementation graph more apparent.
|
||||||
|
|
||||||
|
For example, considering this example condition alist and implementation alist
|
||||||
|
would be represented as the earlier graph.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
;; The condition alist
|
||||||
|
(([emoji predicate] emoji image)
|
||||||
|
([image predicate] image)
|
||||||
|
([inline LaTeX predicate] maths))
|
||||||
|
;; The implementation alist
|
||||||
|
((emoji [plist])
|
||||||
|
(image [plist])
|
||||||
|
(maths [plist])
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
The implementation plist recognises a number of keywords, but the
|
||||||
|
primary keyword is ~:snippet~. The snippet value provides the snippet
|
||||||
|
content used to provide the feature's capability. Much like
|
||||||
|
~condition~, it accepts a number of forms for convenience, namely:
|
||||||
|
+ A string, which is passed on.
|
||||||
|
+ A variable symbol, the value of which must be a string.
|
||||||
|
+ A (unary) function, which is called on the export communication
|
||||||
|
plist (~info~), and must return a string.
|
||||||
|
|
||||||
|
Note that no keys are mandatory in the implementation plist (not even
|
||||||
|
~:snippet~).
|
||||||
|
|
||||||
|
Like conditions, implementations are also inherited from parent backends, but
|
||||||
|
there is no "root" global list of implementations, as they are always
|
||||||
|
backend-specific.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
,--> beamer
|
||||||
|
/
|
||||||
|
html <---o o---> latex --'
|
||||||
|
|
||||||
|
ascii <---o o---> odt
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
**** Feature dependency and incompatibility
|
||||||
|
|
||||||
|
While just connecting features with snippets satisfies most use cases,
|
||||||
|
this system is designed to also allow for complex configurations of
|
||||||
|
inter-dependent snippets.
|
||||||
|
|
||||||
|
In slightly more complex examples, we may run across implementations
|
||||||
|
which either (a) only make sense when another feature is active, or
|
||||||
|
(b) require another implementation to be used in order to work. These
|
||||||
|
two situations are covered by the ~:when~ and ~:requires~ keywords
|
||||||
|
respectively. The accept either a single feature symbol or a list of
|
||||||
|
feature symbols.
|
||||||
|
|
||||||
|
For example, if an implementation contains ~:when featA~, it will only
|
||||||
|
be used when =featA= is active. If the implementation contains ~:when
|
||||||
|
(featA featB)~ it will require /both/ =featA= and =featB= to be
|
||||||
|
active. The ~:requires~ keyword works in the same way, but
|
||||||
|
unconditionally requires implementations instead of testing for them.
|
||||||
|
|
||||||
|
Occasionally one implementation may be incompatible with another. For
|
||||||
|
example, in LaTeX loading the same package with different options will
|
||||||
|
often produce an "options clash" error. To ensure that incompatible
|
||||||
|
implementations are not used, the ~:prevents~ keyword makes it as if
|
||||||
|
the feature were never used in the first place.
|
||||||
|
|
||||||
|
Circular ~:requires~ and ~:prevents~, or features that are
|
||||||
|
simultaneously required and prevented result in undefined behaviour.
|
||||||
|
Similarly, the behaviour of mutual ~:when~s (e.g. ~(a :when b) (b
|
||||||
|
:when a)~ is also undefined.
|
||||||
|
|
||||||
|
**** Feature ordering
|
||||||
|
|
||||||
|
In many scenarios it is not only /which/ snippets are included that
|
||||||
|
matters, but the /order/ in which they are placed. A requirement for a
|
||||||
|
certain snippet to appear before/after others can be specified through
|
||||||
|
the ~:before~ and ~:after~ keywords. Like ~:when~, ~:requires~, and
|
||||||
|
~:prevents~ they accept either a single feature symbol or a list of
|
||||||
|
feature symbols.
|
||||||
|
|
||||||
|
As an example, should an implementation plist contain ~:before featA
|
||||||
|
:after (featB featC)~ it will be placed after =featA= but before
|
||||||
|
=featB= and =featC=. It is possible to accidentally create circular
|
||||||
|
dependencies, in which case an error will be raised.
|
||||||
|
|
||||||
|
While ~:before~ and ~:after~ work well for specifying relative
|
||||||
|
ordering, it can also be useful to specify the /absolute/ ordering,
|
||||||
|
for instance to put something first or last. This can be controlled
|
||||||
|
via the ~:order~ keyword. Each implementation has an ~:order~ of zero
|
||||||
|
by default. Implementations with a higher ~:order~ come later.
|
||||||
|
|
||||||
|
# REVIEW maybe give a convention on :order ranges?
|
||||||
|
# Perhaps take inspiration from ~add-hook~.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
The overall ordering behaviour can be characterized as a ascending
|
||||||
|
sort of ~:order~ followed by a stable [[https://en.wikipedia.org/wiki/Topological_sorting][topological sort]] based on
|
||||||
|
~:before~ and ~:after~.
|
||||||
|
|
||||||
|
**** Adding or editing export features
|
||||||
|
|
||||||
|
The export features of a backend can be modified via the convenience
|
||||||
|
macro ~org-export-update-features~. This is invoked with the following
|
||||||
|
form:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'BACKEND
|
||||||
|
(FEATURE-NAME
|
||||||
|
:PROPERTY VALUE
|
||||||
|
...)
|
||||||
|
...)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
For each feature mentioned, it sets the each =:PROPERTY= to =VALUE= in
|
||||||
|
the implementation plist. The one exception to this is ~:condition~ in
|
||||||
|
which case the backend's feature condition alist is modified so that
|
||||||
|
the condition is taken to imply the feature.
|
||||||
|
|
||||||
|
Setting ~:condition t~ will thus make the feature enabled by default. This is not
|
||||||
|
a special case, but rather an instance of the general behaviour of obtaining the
|
||||||
|
value of any non-function symbol provided, and as ~(symbol-value 't)~ is always
|
||||||
|
non-nil, the associated features will always be considered active. Conversely
|
||||||
|
setting ~:condition nil~ will make it so no conditions imply the feature. This is
|
||||||
|
possible thanks to special behavior that removes the feature from all other
|
||||||
|
conditions' associations when ~nil~ is given.
|
||||||
|
|
||||||
|
Since having a anonymous function (lambda) is expected to be
|
||||||
|
reasonably common with ~:condition~ and ~:snippet~, for those keywords
|
||||||
|
and sexp given is implicitly wrapped with ~(lambda (info) SEXP)~.
|
||||||
|
|
||||||
|
If the backend is not available at the time the feature update is run,
|
||||||
|
an error will be raised.
|
||||||
|
|
||||||
|
**** Custom export feature examples
|
||||||
|
|
||||||
|
To make the usage of ~org-export-update-features~ and the capabilities
|
||||||
|
of the export feature system clearer, here are a few examples
|
||||||
|
~org-export-update-features~ invocations.
|
||||||
|
|
||||||
|
Say you want to apply the [[https://ctan.org/pkg/chickenize][chickenize]] package to the word "wacky" when
|
||||||
|
the title starts with "wacky". We can implement that by testing for
|
||||||
|
the regexp =^#\\+title: Wacky= and including
|
||||||
|
src_latex{\usepackage[chickenstring[1]='wacky']{chickenize}} when it is
|
||||||
|
found.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'latex
|
||||||
|
(wacky-chicken
|
||||||
|
:condition "^#\\+title: Wacky"
|
||||||
|
:snippet "\\usepackage[chickenstring[1]='wacky']{chickenize}"))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
However, if =#+title: Wacky= is placed inside an example block, this
|
||||||
|
regexp will match even though the match isn't actually parsed as a
|
||||||
|
keyword. To be more robust, we can inspect ~info~ instead.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'latex
|
||||||
|
(wacky-chicken
|
||||||
|
:condition (and (car (plist-get info :title))
|
||||||
|
(string-match-p "^Wacky" (car (plist-get info :title))))
|
||||||
|
:snippet "\\usepackage[chickenstring[1]='wacky']{chickenize}"))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
=chickenize= is a LuaLaTeX only package, and so trying to use this
|
||||||
|
when compiling with pdfLaTeX or XeLaTeX will cause issues. This may
|
||||||
|
well apply to other snippets too, so it could make sense to make a
|
||||||
|
=lualatex= feature to indicate when LuaLaTeX is being used.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'latex
|
||||||
|
(lualatex
|
||||||
|
:condition (equal (plist-get info :latex-compiler) "lualatex")))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
To only use the chickenize snippet with LuaLaTeX, we can now add
|
||||||
|
=lualatex= as a ~:when~ clause.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'latex
|
||||||
|
(wacky-chicken
|
||||||
|
:when lualatex))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Hopefully this has given you an impression of how ~:condition~, ~:when~, and
|
||||||
|
~:snippet~ can look in practice. To demonstrate ~:requires~, ~:prevents~, and
|
||||||
|
~:after~ we will consider another LaTeX example.
|
||||||
|
|
||||||
|
Say that you wanted a few named special blocks to automatically export nicely,
|
||||||
|
such as =#+begin_warning=, =#+begin_info=, and =#+begin_note=. All three of
|
||||||
|
these can be individually detected and handled appropriately. For the sake of
|
||||||
|
simplicity, a crude regexp will be used here, however examining the parse tree
|
||||||
|
would be a more robust solution.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'latex
|
||||||
|
(box-warning
|
||||||
|
:condition "^[ \t]*#\\+begin_warning"
|
||||||
|
:snippet "\\mysetupbox{warning}"
|
||||||
|
:requires mysetupbox
|
||||||
|
:after mysetupbox)
|
||||||
|
(box-info
|
||||||
|
:condition "^[ \t]*#\\+begin_info"
|
||||||
|
:snippet "\\mysetupbox{info}"
|
||||||
|
:requires mysetupbox
|
||||||
|
:after mysetupbox)
|
||||||
|
(box-note
|
||||||
|
:condition "^[ \t]*#\\+begin_note"
|
||||||
|
:snippet "\\mysetupbox{note}"
|
||||||
|
:requires mysetupbox
|
||||||
|
:after mysetupbox)
|
||||||
|
(mysetupbox
|
||||||
|
:snippet "\newcommand{\mysetupbox}...")
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Here, all three box types make use of LaTeX command ~\mysetupbox~ which is
|
||||||
|
provided by the =mysetupbox= feature implementation. By using a ~:requires~ for
|
||||||
|
this, we can make sure it is availible that it is loaded once, and thanks to the
|
||||||
|
~:after mysetupbox~ lines the specific box setup invocations will occur after
|
||||||
|
the ~\newcommand{\mysetupbox}...~ definition.
|
||||||
|
|
||||||
|
Say that when using beamer you use a package that defines its own
|
||||||
|
warning/info/note environments and you'd like to use those. In that case one can
|
||||||
|
make an on-by-default beamer feature that prevents the box features from being
|
||||||
|
used.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(org-export-update-features 'beamer
|
||||||
|
(no-custom-boxes
|
||||||
|
:condition t
|
||||||
|
:prevents (box-warning box-info box-note)))
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
**** Detailed explanation of the implementation resolution process
|
||||||
|
|
||||||
|
The previous descriptions of the "export feature" behaviour should give a clear
|
||||||
|
overview of how this feature works. In case more detail is wanted, or should
|
||||||
|
there be any ambiguity, here is a more technical description of the overall
|
||||||
|
feature implementation resolution process.
|
||||||
|
|
||||||
|
1. The list of detected features is used to obtain all applicable corresponding
|
||||||
|
feature implementations. Detected feature symbols may have /no/ corresponding
|
||||||
|
implementation.
|
||||||
|
2. The list of feature implementations is sorted according to ~:order~.
|
||||||
|
3. Using a queue, all required features (~:requires~) are added to the list of feature
|
||||||
|
implementations, as are their requirements recursively. Should a feature that
|
||||||
|
has no implementation be required, an ~org-missing-feature-dependency~ error is raised.
|
||||||
|
4. The implementations with a ~:when~ condition are scanned and marked as
|
||||||
|
confirmed when all of the ~:when~ conditions are known to be satisfied. This is
|
||||||
|
repeated until there is no change in the list of confirmed implementations,
|
||||||
|
at which point all non-confirmed implementations are removed.
|
||||||
|
5. For each of the feature implementations in turn, prevented features
|
||||||
|
(~:prevents~) are removed from the list. Feature implementations that are only
|
||||||
|
present because of a feature that has now been removed are themselves
|
||||||
|
removed, recursively.
|
||||||
|
6. The list of feature implementations is sorted according to ~:order~, again.
|
||||||
|
7. A stable topological sort is performed using ~:before~ and ~:after~. Should any
|
||||||
|
circular dependencies be found, a ~org-circular-feature-dependency~ error is raised.
|
||||||
|
|
||||||
** Export Region
|
** Export Region
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:DESCRIPTION: Author tables and lists in Org syntax.
|
:DESCRIPTION: Author tables and lists in Org syntax.
|
||||||
|
@ -18090,9 +18510,9 @@ Here are examples of passing values by reference:
|
||||||
names---because the second row is a horizontal rule---then Org
|
names---because the second row is a horizontal rule---then Org
|
||||||
removes the column names, processes the table, puts back the column
|
removes the column names, processes the table, puts back the column
|
||||||
names, and then writes the table to the results block. Using =yes=,
|
names, and then writes the table to the results block. Using =yes=,
|
||||||
Org does the same to the first row, even if the initial table does
|
Org does the same to the first non-hline row, even if the initial
|
||||||
not contain any horizontal rule. When set to =no=, Org does not
|
table does not contain any horizontal rule. When set to =no=, Org
|
||||||
pre-process column names at all.
|
does not pre-process column names at all.
|
||||||
|
|
||||||
# We keep python blocks unindented on purpose - to keep the example
|
# We keep python blocks unindented on purpose - to keep the example
|
||||||
# working even for users who changed the default value of ~org-src-preserve-indentation~
|
# working even for users who changed the default value of ~org-src-preserve-indentation~
|
||||||
|
@ -21522,7 +21942,7 @@ The following =ol-man.el= file implements it
|
||||||
PATH should be a topic that can be thrown at the man command."
|
PATH should be a topic that can be thrown at the man command."
|
||||||
(funcall org-man-command path))
|
(funcall org-man-command path))
|
||||||
|
|
||||||
(defun org-man-store-link ()
|
(defun org-man-store-link (&optional _interactive?)
|
||||||
"Store a link to a man page."
|
"Store a link to a man page."
|
||||||
(when (memq major-mode '(Man-mode woman-mode))
|
(when (memq major-mode '(Man-mode woman-mode))
|
||||||
;; This is a man page, we do make this link.
|
;; This is a man page, we do make this link.
|
||||||
|
@ -21582,13 +22002,15 @@ A review of =ol-man.el=:
|
||||||
|
|
||||||
For example, ~org-man-store-link~ is responsible for storing a link
|
For example, ~org-man-store-link~ is responsible for storing a link
|
||||||
when ~org-store-link~ (see [[*Handling Links]]) is called from a buffer
|
when ~org-store-link~ (see [[*Handling Links]]) is called from a buffer
|
||||||
displaying a man page. It first checks if the major mode is
|
displaying a man page. It is passed an argument ~interactive?~
|
||||||
appropriate. If check fails, the function returns ~nil~, which
|
which this function does not use, but other store functions use to
|
||||||
means it isn't responsible for creating a link to the current
|
behave differently when a link is stored interactively by the user.
|
||||||
buffer. Otherwise the function makes a link string by combining
|
It first checks if the major mode is appropriate. If check fails,
|
||||||
the =man:= prefix with the man topic. It also provides a default
|
the function returns ~nil~, which means it isn't responsible for
|
||||||
description. The function ~org-insert-link~ can insert it back
|
creating a link to the current buffer. Otherwise the function
|
||||||
into an Org buffer later on.
|
makes a link string by combining the =man:= prefix with the man
|
||||||
|
topic. It also provides a default description. The function
|
||||||
|
~org-insert-link~ can insert it back into an Org buffer later on.
|
||||||
|
|
||||||
** Adding Export Backends
|
** Adding Export Backends
|
||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
|
@ -23102,7 +23524,7 @@ used to create images, any LaTeX environment is handled.
|
||||||
[fn:38] These are respectively available at
|
[fn:38] These are respectively available at
|
||||||
[[https://sourceforge.net/projects/dvipng/]], [[http://dvisvgm.bplaced.net/]]
|
[[https://sourceforge.net/projects/dvipng/]], [[http://dvisvgm.bplaced.net/]]
|
||||||
and from the ImageMagick suite. Choose the converter by setting the
|
and from the ImageMagick suite. Choose the converter by setting the
|
||||||
variable ~org-preview-latex-default-process~ accordingly.
|
variable ~org-latex-preview-process-default~ accordingly.
|
||||||
|
|
||||||
[fn:39] This works automatically for the HTML backend (it requires
|
[fn:39] This works automatically for the HTML backend (it requires
|
||||||
version 1.34 of the =htmlize.el= package, which you need to install).
|
version 1.34 of the =htmlize.el= package, which you need to install).
|
||||||
|
|
271
etc/ORG-NEWS
271
etc/ORG-NEWS
|
@ -460,6 +460,14 @@ timestamp object. Possible values: ~timerange~, ~daterange~, ~nil~.
|
||||||
~org-element-timestamp-interpreter~ takes into account this property
|
~org-element-timestamp-interpreter~ takes into account this property
|
||||||
and returns an appropriate timestamp string.
|
and returns an appropriate timestamp string.
|
||||||
|
|
||||||
|
**** =org-link= store functions are passed an ~interactive?~ argument
|
||||||
|
|
||||||
|
The ~:store:~ functions set for link types using
|
||||||
|
~org-link-set-parameters~ are now passed an ~interactive?~ argument,
|
||||||
|
indicating whether ~org-store-link~ was called interactively.
|
||||||
|
|
||||||
|
Existing store functions will continue to work.
|
||||||
|
|
||||||
*** ~org-priority=show~ command no longer adjusts for scheduled/deadline
|
*** ~org-priority=show~ command no longer adjusts for scheduled/deadline
|
||||||
|
|
||||||
In agenda views, ~org-priority=show~ command previously displayed the
|
In agenda views, ~org-priority=show~ command previously displayed the
|
||||||
|
@ -493,6 +501,157 @@ constant.
|
||||||
If you still want to use python-mode with ob-python, you might
|
If you still want to use python-mode with ob-python, you might
|
||||||
consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where the code to support python-mode
|
consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where the code to support python-mode
|
||||||
has been ported to.
|
has been ported to.
|
||||||
|
*** The LaTeX preview system has been overhauled
|
||||||
|
|
||||||
|
Org's LaTeX preview system has been overhauled
|
||||||
|
|
||||||
|
|
||||||
|
Org mode's LaTeX preview system has been rewritten with several improvements and new features.
|
||||||
|
|
||||||
|
- LaTeX Previews are generated in bulk, and hundreds of LaTeX fragments can be processed per second.
|
||||||
|
- Images are placed in the buffer continuously as they are generated.
|
||||||
|
- Preview generation is asynchronous and will not block Emacs.
|
||||||
|
- Inline previews are aligned and scaled to match the font baseline and size.
|
||||||
|
- Previews scale along with text when the text scale is changed.
|
||||||
|
- Previews are coloured to match surrounding text and the active theme.
|
||||||
|
- SVG previews automatically change colors when the active theme changes.
|
||||||
|
- Error encountered when compiling LaTeX fragments can be accessed by mousing over preview images.
|
||||||
|
- Preview overlays can hide and show themselves dynamically based on cursor position.
|
||||||
|
- Org mode can auto-generate LaTeX previews as you type or edit the text of existing ones.
|
||||||
|
- Org mode can keep equation numbering consistent by regenerating previews as needed.
|
||||||
|
|
||||||
|
**** New features
|
||||||
|
|
||||||
|
***** New minor mode ~org-latex-preview-auto-mode~
|
||||||
|
|
||||||
|
When this mode is turned on, LaTeX preview overlays in the buffer can be temporarily disabled by moving the cursor into them. Moving the cursor out will display the LaTeX preview image overlay again. Additionally, editing LaTeX fragments that have previews will cause the preview images to be updated.
|
||||||
|
|
||||||
|
If the option ~org-latex-preview-auto-track-inserts~ is non-nil (which see), previews for LaTeX fragments will be auto-generated as you type.
|
||||||
|
|
||||||
|
|
||||||
|
**** New options
|
||||||
|
|
||||||
|
***** A new option ~org-latex-preview-compiler-command-map~ for setting precompile commands
|
||||||
|
|
||||||
|
This alist maps compilers in ~org-latex-compilers~ to command strings
|
||||||
|
used for LaTeX precompilation when creating previews or LaTeX exports.
|
||||||
|
|
||||||
|
***** New option ~org-latex-preview-cache~ to enable preview image caching
|
||||||
|
|
||||||
|
When set to =persist=, images produced using ~org-latex-preview~ will
|
||||||
|
be cached and persisted across Emacs sessions using
|
||||||
|
~org-persist~. Temporary or custom directories can also be used.
|
||||||
|
|
||||||
|
***** New option ~org-latex-preview-persist-expiry~ to set persistence period
|
||||||
|
|
||||||
|
This is the number of days for which LaTeX preview images will be
|
||||||
|
cached, assuming persistence is turned on with
|
||||||
|
~org-latex-preview-cache~.
|
||||||
|
|
||||||
|
***** New option ~org-latex-preview-process-active-indicator~ to indicate preview processing for LaTeX fragments
|
||||||
|
|
||||||
|
This option controls the style of visual indicator for LaTeX fragments
|
||||||
|
currently being processed.
|
||||||
|
|
||||||
|
***** New option ~org-latex-preview-numbered~ to automatically renumber previewed LaTeX environments
|
||||||
|
|
||||||
|
When non-nil, previewed LaTeX environments in the buffer are
|
||||||
|
renumbered automatically as required.
|
||||||
|
|
||||||
|
***** New option =:page-width= in ~org-latex-preview-appearance-options~ to control the text width for LaTeX fragment previews
|
||||||
|
|
||||||
|
This option controls the width of text in preview images. This is
|
||||||
|
relevant for displaymath-type LaTeX environments, and can be specified
|
||||||
|
in multiple ways.
|
||||||
|
|
||||||
|
***** New option ~org-latex-preview-process-precompiled~ to control LaTeX precompilation
|
||||||
|
|
||||||
|
This option controls whether LaTeX headers for buffers are precompiled
|
||||||
|
when generating the first preview for the buffer. Precompilation can
|
||||||
|
speed up preview generation by a moderate to large amount depending on
|
||||||
|
the complexity of the header.
|
||||||
|
|
||||||
|
***** New option ~org-latex-preview-auto-track-inserts~ to auto-generate LaTeX previews when typing
|
||||||
|
|
||||||
|
When using ~org-latex-preview-auto-mode~ (which see), setting this option to a non-nil value will cause LaTeX previews to be rendered as you type LaTeX fragments. This works however new text is added to the buffer, for example by yanking. When this option is nil and ~org-latex-preview-auto-mode~ is on, existing previews for LaTeX fragments will still be auto-regenerated when edited but no new ones will be auto-generated.
|
||||||
|
|
||||||
|
**** Removed or renamed functions and variables
|
||||||
|
|
||||||
|
***** Mark ~org-format-latex-options~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview-appearance-options~ instead. The replacement acts in the
|
||||||
|
same way, except that
|
||||||
|
- the preview image render size and display size can be independently
|
||||||
|
controlled with the =:scale= and =:zoom= plist keys, and
|
||||||
|
- the HTML export-specific parameters =:html-scale=,
|
||||||
|
=:html-foreground= and =:html-background= have been moved to
|
||||||
|
~org-html-latex-image-options~, which see.
|
||||||
|
- the new option =:page-width= (mentioned earlier)
|
||||||
|
|
||||||
|
***** Mark ~org-format-latex-header~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview-header~ instead. The replacement variable
|
||||||
|
behaves the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-preview-latex-default-process~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview-process-default~ instead. The replacement
|
||||||
|
variable behaves the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-preview-latex-process-alist~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview-process-alist~ instead. The replacement
|
||||||
|
variable acts in the same way, except that =:latex-compiler= must now
|
||||||
|
be a single command or a list containing a single command. The
|
||||||
|
placeholder ="%S"= in the format string for =:latex-compiler= is
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
***** Mark ~org-clear-latex-preview~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview-clear-overlays~ instead. The replacement
|
||||||
|
function acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-place-formula-image~ obsolete
|
||||||
|
|
||||||
|
This functionality is replaced with ~org-latex-preview-place-image~
|
||||||
|
(for images) and ~org-latex-preview-place-image-link~ (for links).
|
||||||
|
|
||||||
|
***** Mark ~org-latex-color-format~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview--format-color~ instead. The replacement
|
||||||
|
function acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-latex-color~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview--attr-color~ instead. The replacement function
|
||||||
|
acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-normalize-color~ obsolete
|
||||||
|
|
||||||
|
Use ~org-latex-preview--normalize-color~ instead. The replacement
|
||||||
|
function acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-latex-to-mathml-jar-file~ obsolete
|
||||||
|
|
||||||
|
Use ~org-mathml-converter-jar-file~ instead. The replacement variable
|
||||||
|
acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-latex-to-mathml-convert-command~ obsolete
|
||||||
|
|
||||||
|
Use ~org-mathml-convert-command~ instead. The replacement variable
|
||||||
|
acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-format-latex-mathml-available-p~ obsolete
|
||||||
|
|
||||||
|
Use ~org-mathml-converter-available-p~ instead. The replacement
|
||||||
|
function acts in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
***** Mark ~org-create-math-formula~ obsolete
|
||||||
|
|
||||||
|
Use ~org-mathml-convert-latex~ instead. The replacement function acts
|
||||||
|
in the exact same way and is renamed for consistency.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*** =ox-icalendar.el= line ending fix may affect downstream packages
|
*** =ox-icalendar.el= line ending fix may affect downstream packages
|
||||||
|
|
||||||
|
@ -538,7 +697,45 @@ The change is breaking when ~org-use-property-inheritance~ is set to ~t~.
|
||||||
*** ~org-babel-lilypond-compile-lilyfile~ ignores optional second argument
|
*** ~org-babel-lilypond-compile-lilyfile~ ignores optional second argument
|
||||||
|
|
||||||
The =TEST= parameter is better served by Emacs debugging tools.
|
The =TEST= parameter is better served by Emacs debugging tools.
|
||||||
|
|
||||||
|
*** =id:= links support search options; ~org-id-store-link~ adds search option by default
|
||||||
|
|
||||||
|
Adding search option by ~org-id-store-link~ can be disabled by setting
|
||||||
|
~org-id-link-use-context~ to ~nil~, or toggled for a single call by
|
||||||
|
passing universal argument.
|
||||||
|
|
||||||
|
When using this feature, IDs should not include =::=, which is used in
|
||||||
|
links to indicate the start of the search string. For backwards
|
||||||
|
compability, existing IDs including =::= will still be matched (but
|
||||||
|
cannot be used together with search option). A new org-lint checker
|
||||||
|
has been added to warn about this.
|
||||||
|
|
||||||
|
*** ~org-store-link~ behaviour storing additional =CUSTOM_ID= links has changed
|
||||||
|
|
||||||
|
Previously, when storing =id:= link, ~org-store-link~ stored an
|
||||||
|
additional "human readable" link using a node's =CUSTOM_ID= property.
|
||||||
|
|
||||||
|
This behaviour has been expanded to store an additional =CUSTOM_ID=
|
||||||
|
link when storing any type of external link type in an Org file, not
|
||||||
|
just =id:= links.
|
||||||
|
|
||||||
** New and changed options
|
** New and changed options
|
||||||
|
*** New option ~org-beamer-frame-environment~
|
||||||
|
|
||||||
|
The new option defines name of an alternative environment to be used
|
||||||
|
for fragile beamer frames. This option is needed to work around
|
||||||
|
beamer bug with frame contents containing literal =\end{frame}= string
|
||||||
|
(for example, inside example blocks). See
|
||||||
|
https://github.com/josephwright/beamer/issues/360
|
||||||
|
|
||||||
|
The default value is =orgframe=.
|
||||||
|
|
||||||
|
The option should normally not be changed, except when you need to put
|
||||||
|
=\end{orgframe}= string inside beamer frames.
|
||||||
|
|
||||||
|
A checker has been added to =M-x org-lint= to detect instances of
|
||||||
|
~org-beamer-frame-environment~ in Org documents.
|
||||||
|
|
||||||
*** New option ~org-export-process-citations~
|
*** New option ~org-export-process-citations~
|
||||||
|
|
||||||
The new option controls whether to use citation processors to process
|
The new option controls whether to use citation processors to process
|
||||||
|
@ -852,7 +1049,51 @@ This option starts the agenda to automatically include archives,
|
||||||
propagating the value for this variable to ~org-agenda-archives-mode~.
|
propagating the value for this variable to ~org-agenda-archives-mode~.
|
||||||
For acceptable values and their meaning, see the value of that variable.
|
For acceptable values and their meaning, see the value of that variable.
|
||||||
|
|
||||||
|
*** New option ~org-id-link-consider-parent-id~ to allow =id:= links to parent headlines
|
||||||
|
|
||||||
|
For =id:= links, when this option is enabled, ~org-store-link~ will
|
||||||
|
look for ids from parent/ancestor headlines, if the current headline
|
||||||
|
does not have an id.
|
||||||
|
|
||||||
|
Combined with the new ability for =id:= links to use search options
|
||||||
|
[fn:: when =org-id-link-use-context= is =t=, which is the default],
|
||||||
|
this allows linking to specific headlines without requiring every
|
||||||
|
headline to have an id property, as long as the headline is unique
|
||||||
|
within a subtree that does have an id property.
|
||||||
|
|
||||||
|
For example, given this org file:
|
||||||
|
|
||||||
|
#+begin_src org
|
||||||
|
,* Parent
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: abc
|
||||||
|
:END:
|
||||||
|
,** Child 1
|
||||||
|
,** Child 2
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Storing a link with point at "Child 1" will produce a link
|
||||||
|
=<id:abc::*Child 1>=, which precisely links to the "Child 1" headline
|
||||||
|
even though it does not have its own ID. By giving files top-level id
|
||||||
|
properties, links to headlines in the file can also be made more
|
||||||
|
robust by using the file id instead of the file path.
|
||||||
|
|
||||||
** New features
|
** New features
|
||||||
|
*** =ob-tangle.el=: New flag to remove tangle targets before writing
|
||||||
|
|
||||||
|
When ~org-babel-tangle-remove-file-before-write~ is set to ~t~ the
|
||||||
|
tangle target is removed before writing. This will allow overwriting
|
||||||
|
read-only tangle targets. However, when tangle target is a symlink,
|
||||||
|
this will convert the tangle target into an ordinary file.
|
||||||
|
|
||||||
|
The default value is ~auto~ -- overwrite tangle targets when they are
|
||||||
|
read-only.
|
||||||
|
|
||||||
|
*** ~org-bibtex-yank~ accepts a prefix argument
|
||||||
|
|
||||||
|
When called with a prefix argument, ~org-bibtex-yank~ adds data to the
|
||||||
|
headline of the entry at point instead of creating a new one.
|
||||||
|
|
||||||
*** =ob-plantuml.el=: Support tikz file format output
|
*** =ob-plantuml.el=: Support tikz file format output
|
||||||
|
|
||||||
=ob-plantuml.el= now output =tikz= :file format via
|
=ob-plantuml.el= now output =tikz= :file format via
|
||||||
|
@ -1052,6 +1293,11 @@ The same can be done via startup options:
|
||||||
: #+STARTUP: fnanon
|
: #+STARTUP: fnanon
|
||||||
|
|
||||||
** New functions and changes in function arguments
|
** New functions and changes in function arguments
|
||||||
|
*** New optional argument =UPDATE-HEADING= for ~org-bibtex-yank~
|
||||||
|
|
||||||
|
When the new argument is non-nil, add data to the headline of the
|
||||||
|
entry at point.
|
||||||
|
|
||||||
*** New API functions to store data within ~org-element-cache~
|
*** New API functions to store data within ~org-element-cache~
|
||||||
|
|
||||||
Elisp programs can now store data inside Org element cache.
|
Elisp programs can now store data inside Org element cache.
|
||||||
|
@ -1142,6 +1388,25 @@ as the function can also act on objects.
|
||||||
|
|
||||||
*** ~org-export-get-parent-element~ is renamed to ~org-element-parent-element~ and moved to =lisp/org-element.el=
|
*** ~org-export-get-parent-element~ is renamed to ~org-element-parent-element~ and moved to =lisp/org-element.el=
|
||||||
|
|
||||||
|
*** ~org-insert-heading~ optional argument =TOP= is now =LEVEL=
|
||||||
|
|
||||||
|
A numeric value forces a heading at that level to be inserted. For
|
||||||
|
backwards compatibility, non-numeric non-nil values insert level 1
|
||||||
|
headings as before.
|
||||||
|
|
||||||
|
*** New optional argument for ~org-id-get~
|
||||||
|
|
||||||
|
New optional argument =INHERIT= means inherited ID properties from
|
||||||
|
parent entries are considered when getting an entry's ID (see
|
||||||
|
~org-id-link-consider-parent-id~ option).
|
||||||
|
|
||||||
|
*** New optional argument for ~org-link-search~
|
||||||
|
|
||||||
|
If a missing heading is created to match the search string, the new
|
||||||
|
optional argument =NEW-HEADING-CONTAINER= specifies where in the
|
||||||
|
buffer it will be added. If not specified, new headings are created
|
||||||
|
at level 1 at the end of the accessible part of the buffer, as before.
|
||||||
|
|
||||||
** Miscellaneous
|
** Miscellaneous
|
||||||
*** =org-crypt.el= now applies initial visibility settings to decrypted entries
|
*** =org-crypt.el= now applies initial visibility settings to decrypted entries
|
||||||
|
|
||||||
|
@ -1181,6 +1446,12 @@ For symmetry with =\S= and =\sect= for the section symbol, =\P= has
|
||||||
been added as an another form for the pilcrow symbol currently
|
been added as an another form for the pilcrow symbol currently
|
||||||
available as =\para=.
|
available as =\para=.
|
||||||
|
|
||||||
|
*** ~org-table-to-lisp~ no longer clobbers the regexp global state
|
||||||
|
|
||||||
|
It does no longer use regexps.
|
||||||
|
|
||||||
|
It is also faster. Large tables can be read quickly.
|
||||||
|
|
||||||
* Version 9.6
|
* Version 9.6
|
||||||
|
|
||||||
** Important announcements and breaking changes
|
** Important announcements and breaking changes
|
||||||
|
|
118
lisp/ob-core.el
118
lisp/ob-core.el
|
@ -73,10 +73,12 @@
|
||||||
(declare-function org-element-parent "org-element-ast" (node))
|
(declare-function org-element-parent "org-element-ast" (node))
|
||||||
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
|
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
|
||||||
(declare-function org-element-type-p "org-element-ast" (node &optional types))
|
(declare-function org-element-type-p "org-element-ast" (node &optional types))
|
||||||
|
(declare-function org-element-interpret-data "org-element" (data))
|
||||||
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
|
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
|
||||||
(declare-function org-escape-code-in-region "org-src" (beg end))
|
(declare-function org-escape-code-in-region "org-src" (beg end))
|
||||||
(declare-function org-forward-heading-same-level "org" (arg &optional invisible-ok))
|
(declare-function org-forward-heading-same-level "org" (arg &optional invisible-ok))
|
||||||
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance))
|
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance))
|
||||||
|
(declare-function org-indent-block "org" ())
|
||||||
(declare-function org-indent-line "org" ())
|
(declare-function org-indent-line "org" ())
|
||||||
(declare-function org-list-get-list-end "org-list" (item struct prevs))
|
(declare-function org-list-get-list-end "org-list" (item struct prevs))
|
||||||
(declare-function org-list-prevs-alist "org-list" (struct))
|
(declare-function org-list-prevs-alist "org-list" (struct))
|
||||||
|
@ -700,8 +702,9 @@ By default, consider the block at point. However, when optional
|
||||||
argument DATUM is provided, extract information from that parsed
|
argument DATUM is provided, extract information from that parsed
|
||||||
object instead.
|
object instead.
|
||||||
|
|
||||||
Return nil if point is not on a source block. Otherwise, return
|
Return nil if point is not on a source block (blank lines after a
|
||||||
a list with the following pattern:
|
source block are considered a part of that source block).
|
||||||
|
Otherwise, return a list with the following pattern:
|
||||||
|
|
||||||
(language body arguments switches name start coderef)"
|
(language body arguments switches name start coderef)"
|
||||||
(let* ((datum (or datum (org-element-context)))
|
(let* ((datum (or datum (org-element-context)))
|
||||||
|
@ -1192,11 +1195,16 @@ Return t if a code block was found at point, nil otherwise."
|
||||||
;; we want to restore this location after executing BODY.
|
;; we want to restore this location after executing BODY.
|
||||||
(outside-position
|
(outside-position
|
||||||
(and (<= (line-beginning-position)
|
(and (<= (line-beginning-position)
|
||||||
(org-element-post-affiliated element))
|
(org-element-post-affiliated element))
|
||||||
(point-marker)))
|
(point-marker)))
|
||||||
(org-src-window-setup 'switch-invisibly))
|
(org-src-window-setup 'switch-invisibly))
|
||||||
(when (and (org-babel-where-is-src-block-head element)
|
(when (and (org-babel-where-is-src-block-head element)
|
||||||
(org-edit-src-code))
|
(condition-case nil
|
||||||
|
(org-edit-src-code)
|
||||||
|
(t
|
||||||
|
(org-edit-src-exit)
|
||||||
|
(when outside-position (goto-char outside-position))
|
||||||
|
nil)))
|
||||||
(unwind-protect (progn ,@body)
|
(unwind-protect (progn ,@body)
|
||||||
(org-edit-src-exit)
|
(org-edit-src-exit)
|
||||||
(when outside-position (goto-char outside-position)))
|
(when outside-position (goto-char outside-position)))
|
||||||
|
@ -1826,6 +1834,8 @@ HEADER-ARGUMENTS is alist of all the arguments."
|
||||||
Return a cons cell, the `car' of which contains the TABLE less
|
Return a cons cell, the `car' of which contains the TABLE less
|
||||||
colnames, and the `cdr' of which contains a list of the column
|
colnames, and the `cdr' of which contains a list of the column
|
||||||
names."
|
names."
|
||||||
|
;; Skip over leading hlines.
|
||||||
|
(while (eq 'hline (car table)) (pop table))
|
||||||
(if (eq 'hline (nth 1 table))
|
(if (eq 'hline (nth 1 table))
|
||||||
(cons (cddr table) (car table))
|
(cons (cddr table) (car table))
|
||||||
(cons (cdr table) (car table))))
|
(cons (cdr table) (car table))))
|
||||||
|
@ -1887,9 +1897,16 @@ of the vars, cnames and rnames."
|
||||||
(when (and (not (equal colnames "no"))
|
(when (and (not (equal colnames "no"))
|
||||||
;; Compatibility note: avoid `length>', which
|
;; Compatibility note: avoid `length>', which
|
||||||
;; isn't available until Emacs 28.
|
;; isn't available until Emacs 28.
|
||||||
(or colnames (and (> (length (cdr var)) 1)
|
(or colnames
|
||||||
(eq (nth 1 (cdr var)) 'hline)
|
;; :colnames nil (default)
|
||||||
(not (member 'hline (cddr (cdr var)))))))
|
;; Auto-assign column names when the table
|
||||||
|
;; has hline as the second line after
|
||||||
|
;; non-hline row.
|
||||||
|
(and (> (length (cdr var)) 1)
|
||||||
|
(not (eq (car (cdr var)) 'hline)) ; first row
|
||||||
|
(eq (nth 1 (cdr var)) 'hline) ; second row
|
||||||
|
(not (member 'hline (cddr (cdr var)))) ; other rows
|
||||||
|
)))
|
||||||
(let ((both (org-babel-get-colnames (cdr var))))
|
(let ((both (org-babel-get-colnames (cdr var))))
|
||||||
(setq cnames (cons (cons (car var) (cdr both))
|
(setq cnames (cons (cons (car var) (cdr both))
|
||||||
cnames))
|
cnames))
|
||||||
|
@ -2075,7 +2092,7 @@ With optional prefix argument ARG, jump backward ARG many source blocks."
|
||||||
(goto-char (match-beginning 5)))))
|
(goto-char (match-beginning 5)))))
|
||||||
|
|
||||||
(defun org-babel-demarcate-block (&optional arg)
|
(defun org-babel-demarcate-block (&optional arg)
|
||||||
"Wrap or split the code in the region or on the point.
|
"Wrap or split the code in an active region or at point.
|
||||||
|
|
||||||
With prefix argument ARG, also create a new heading at point.
|
With prefix argument ARG, also create a new heading at point.
|
||||||
|
|
||||||
|
@ -2085,41 +2102,76 @@ is created. In both cases if the region is demarcated and if the
|
||||||
region is not active then the point is demarcated.
|
region is not active then the point is demarcated.
|
||||||
|
|
||||||
When called within blank lines after a code block, create a new code
|
When called within blank lines after a code block, create a new code
|
||||||
block of the same language with the previous."
|
block of the same language as the previous."
|
||||||
(interactive "P")
|
(interactive "P")
|
||||||
(let* ((info (org-babel-get-src-block-info 'no-eval))
|
(let* ((info (org-babel-get-src-block-info 'no-eval))
|
||||||
(start (org-babel-where-is-src-block-head))
|
(start (org-babel-where-is-src-block-head))
|
||||||
;; `start' will be nil when within space lines after src block.
|
;; `start' will be nil when within space lines after src block.
|
||||||
(block (and start (match-string 0)))
|
(block (and start (match-string 0)))
|
||||||
(headers (and start (match-string 4)))
|
(body-beg (and start (match-beginning 5)))
|
||||||
|
(body-end (and start (match-end 5)))
|
||||||
(stars (concat (make-string (or (org-current-level) 1) ?*) " "))
|
(stars (concat (make-string (or (org-current-level) 1) ?*) " "))
|
||||||
(upper-case-p (and block
|
(upper-case-p (and block
|
||||||
(let (case-fold-search)
|
(let (case-fold-search)
|
||||||
(string-match-p "#\\+BEGIN_SRC" block)))))
|
(string-match-p "#\\+BEGIN_SRC" block)))))
|
||||||
(if (and info start) ;; At src block, but not within blank lines after it.
|
(if (and info start) ;; At src block, but not within blank lines after it.
|
||||||
(mapc
|
(let* ((copy (org-element-copy (org-element-at-point)))
|
||||||
(lambda (place)
|
(before (org-element-begin copy))
|
||||||
(save-excursion
|
(beyond (org-element-end copy))
|
||||||
(goto-char place)
|
(parts
|
||||||
(let ((lang (nth 0 info))
|
(if (org-region-active-p)
|
||||||
(indent (make-string (org-current-text-indentation) ?\s)))
|
(list body-beg (region-beginning) (region-end) body-end)
|
||||||
(when (string-match "^[[:space:]]*$"
|
(list body-beg (point) body-end)))
|
||||||
(buffer-substring (line-beginning-position)
|
(pads ;; To calculate left-side white-space padding.
|
||||||
(line-end-position)))
|
(if (org-region-active-p)
|
||||||
(delete-region (line-beginning-position) (line-end-position)))
|
(list (region-beginning) (region-end))
|
||||||
(insert (concat
|
(list (point))))
|
||||||
(if (looking-at "^") "" "\n")
|
(n (- (length parts) 2)) ;; 1 or 2 parts in `dolist' below.
|
||||||
indent (if upper-case-p "#+END_SRC\n" "#+end_src\n")
|
;; `post-blank' caches the property before setting it to 0.
|
||||||
(if arg stars indent) "\n"
|
(post-blank (org-element-property :post-blank copy)))
|
||||||
indent (if upper-case-p "#+BEGIN_SRC " "#+begin_src ")
|
;; Point or region are within body when parts is in increasing order.
|
||||||
lang
|
(unless (apply #'<= parts)
|
||||||
(if (> (length headers) 1)
|
(user-error "Select within the source block body to split it"))
|
||||||
(concat " " headers) headers)
|
(setq parts (mapcar (lambda (p) (buffer-substring (car p) (cdr p)))
|
||||||
(if (looking-at "[\n\r]")
|
(seq-mapn #'cons parts (cdr parts))))
|
||||||
""
|
;; Map positions to columns for white-space padding.
|
||||||
(concat "\n" (make-string (current-column) ? )))))))
|
(setq pads (mapcar (lambda (p) (save-excursion
|
||||||
(move-end-of-line 2))
|
(goto-char p)
|
||||||
(sort (if (org-region-active-p) (list (mark) (point)) (list (point))) #'>))
|
(current-column)))
|
||||||
|
pads))
|
||||||
|
(push 0 pads) ;; The 1st part never requires white-space padding.
|
||||||
|
(setq parts (mapcar (lambda (p) (string-join
|
||||||
|
(list (make-string (car p) ?\s)
|
||||||
|
(cdr p))))
|
||||||
|
(seq-mapn #'cons pads parts)))
|
||||||
|
(delete-region before beyond)
|
||||||
|
;; Set `:post-blank' to 0. We take care of spacing between blocks.
|
||||||
|
(org-element-put-property copy :post-blank 0)
|
||||||
|
(org-element-put-property copy :value (car parts))
|
||||||
|
(insert (org-element-interpret-data copy))
|
||||||
|
;; `org-indent-block' may see another `org-element' (e.g. paragraph)
|
||||||
|
;; immediately after the block. Ensure to indent the inserted block
|
||||||
|
;; and move point to its end.
|
||||||
|
(org-babel-previous-src-block 1)
|
||||||
|
(org-indent-block)
|
||||||
|
(goto-char (org-element-end (org-element-at-point)))
|
||||||
|
(org-element-put-property copy :caption nil)
|
||||||
|
(org-element-put-property copy :name nil)
|
||||||
|
;; Insert the 2nd block, and the 3rd block when region is active.
|
||||||
|
(dolist (part (cdr parts))
|
||||||
|
(org-element-put-property copy :value part)
|
||||||
|
(insert (if arg (concat stars "\n") "\n"))
|
||||||
|
(cl-decf n)
|
||||||
|
(when (= n 0)
|
||||||
|
;; Use `post-blank' to reset the property of the last block.
|
||||||
|
(org-element-put-property copy :post-blank post-blank))
|
||||||
|
(insert (org-element-interpret-data copy))
|
||||||
|
;; Ensure to indent the inserted block and move point to its end.
|
||||||
|
(org-babel-previous-src-block 1)
|
||||||
|
(org-indent-block)
|
||||||
|
(goto-char (org-element-end (org-element-at-point))))
|
||||||
|
;; Leave point at the last inserted block.
|
||||||
|
(goto-char (org-babel-previous-src-block 1)))
|
||||||
(let ((start (point))
|
(let ((start (point))
|
||||||
(lang (or (car info) ; Reuse language from previous block.
|
(lang (or (car info) ; Reuse language from previous block.
|
||||||
(completing-read
|
(completing-read
|
||||||
|
|
|
@ -187,7 +187,7 @@ code."
|
||||||
;; value of the variable
|
;; value of the variable
|
||||||
(mapc (lambda (pair)
|
(mapc (lambda (pair)
|
||||||
(setq body (replace-regexp-in-string
|
(setq body (replace-regexp-in-string
|
||||||
(format "\\$%s" (car pair)) (cdr pair) body)))
|
(format "\\$%s" (car pair)) (cdr pair) body t t)))
|
||||||
vars)
|
vars)
|
||||||
(when prologue (funcall add-to-body prologue))
|
(when prologue (funcall add-to-body prologue))
|
||||||
(when epilogue (setq body (concat body "\n" epilogue)))
|
(when epilogue (setq body (concat body "\n" epilogue)))
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
(require 'ob)
|
(require 'ob)
|
||||||
(require 'org-macs)
|
(require 'org-macs)
|
||||||
|
|
||||||
(declare-function org-create-formula-image "org" (string tofile options buffer &optional type))
|
(declare-function org-latex-preview-create-image "org-latex-preview" (string tofile options buffer &optional type))
|
||||||
(declare-function org-latex-compile "ox-latex" (texfile &optional snippet))
|
(declare-function org-latex-compile "ox-latex" (texfile &optional snippet))
|
||||||
(declare-function org-latex-guess-inputenc "ox-latex" (header))
|
(declare-function org-latex-guess-inputenc "ox-latex" (header))
|
||||||
(declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra))
|
(declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra))
|
||||||
|
@ -48,10 +48,10 @@
|
||||||
(defvar org-babel-tangle-lang-exts)
|
(defvar org-babel-tangle-lang-exts)
|
||||||
(add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex"))
|
(add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex"))
|
||||||
|
|
||||||
(defvar org-format-latex-header) ; From org.el
|
(defvar org-latex-preview-header) ; From org-latex-preview.el
|
||||||
(defvar org-format-latex-options) ; From org.el
|
(defvar org-latex-preview-appearance-options) ; From org-latex-preview.el
|
||||||
(defvar org-latex-default-packages-alist) ; From org.el
|
(defvar org-latex-default-packages-alist) ; From org-latex-preview.el
|
||||||
(defvar org-latex-packages-alist) ; From org.el
|
(defvar org-latex-packages-alist) ; From org-latex-preview.el
|
||||||
|
|
||||||
(defvar org-babel-default-header-args:latex
|
(defvar org-babel-default-header-args:latex
|
||||||
'((:results . "latex") (:exports . "results"))
|
'((:results . "latex") (:exports . "results"))
|
||||||
|
@ -136,7 +136,7 @@ exporting the literal LaTeX source."
|
||||||
(regexp-quote (format "%S" (car pair)))
|
(regexp-quote (format "%S" (car pair)))
|
||||||
(if (stringp (cdr pair))
|
(if (stringp (cdr pair))
|
||||||
(cdr pair) (format "%S" (cdr pair)))
|
(cdr pair) (format "%S" (cdr pair)))
|
||||||
body)))
|
body t t)))
|
||||||
(org-babel--get-vars params))
|
(org-babel--get-vars params))
|
||||||
(let ((prologue (cdr (assq :prologue params)))
|
(let ((prologue (cdr (assq :prologue params)))
|
||||||
(epilogue (cdr (assq :epilogue params))))
|
(epilogue (cdr (assq :epilogue params))))
|
||||||
|
@ -167,11 +167,11 @@ This function is called by `org-babel-execute-src-block'."
|
||||||
(append (cdr (assq :packages params)) org-latex-packages-alist)))
|
(append (cdr (assq :packages params)) org-latex-packages-alist)))
|
||||||
(cond
|
(cond
|
||||||
((and (string-suffix-p ".png" out-file) (not imagemagick))
|
((and (string-suffix-p ".png" out-file) (not imagemagick))
|
||||||
(let ((org-format-latex-header
|
(let ((org-latex-preview-header
|
||||||
(concat org-format-latex-header "\n"
|
(concat org-latex-preview-header "\n"
|
||||||
(mapconcat #'identity headers "\n"))))
|
(mapconcat #'identity headers "\n"))))
|
||||||
(org-create-formula-image
|
(org-latex-preview-create-image
|
||||||
body out-file org-format-latex-options in-buffer)))
|
body out-file org-latex-preview-appearance-options in-buffer)))
|
||||||
((string= "svg" extension)
|
((string= "svg" extension)
|
||||||
(with-temp-file tex-file
|
(with-temp-file tex-file
|
||||||
(insert (concat (funcall org-babel-latex-preamble params)
|
(insert (concat (funcall org-babel-latex-preamble params)
|
||||||
|
@ -238,7 +238,7 @@ This function is called by `org-babel-execute-src-block'."
|
||||||
(insert
|
(insert
|
||||||
(org-latex-guess-inputenc
|
(org-latex-guess-inputenc
|
||||||
(org-splice-latex-header
|
(org-splice-latex-header
|
||||||
org-format-latex-header
|
org-latex-preview-header
|
||||||
(delq
|
(delq
|
||||||
nil
|
nil
|
||||||
(mapcar
|
(mapcar
|
||||||
|
|
|
@ -142,7 +142,7 @@ blocks.")
|
||||||
(replace-regexp-in-string
|
(replace-regexp-in-string
|
||||||
(concat "$" (regexp-quote name))
|
(concat "$" (regexp-quote name))
|
||||||
(if (stringp value) value (format "%S" value))
|
(if (stringp value) value (format "%S" value))
|
||||||
body))))
|
body t t))))
|
||||||
vars)
|
vars)
|
||||||
(concat
|
(concat
|
||||||
(and prologue (concat prologue "\n"))
|
(and prologue (concat prologue "\n"))
|
||||||
|
|
|
@ -51,7 +51,7 @@ $VAR instances are replaced by VAR values defined in PARAMS."
|
||||||
(setq body (replace-regexp-in-string
|
(setq body (replace-regexp-in-string
|
||||||
(regexp-quote (format "$%s" (car var)))
|
(regexp-quote (format "$%s" (car var)))
|
||||||
(format "%s" (cdr var))
|
(format "%s" (cdr var))
|
||||||
body nil 'literal)))
|
body 'fixedcase 'literal)))
|
||||||
|
|
||||||
(let ((prologue (cdr (assq :prologue params)))
|
(let ((prologue (cdr (assq :prologue params)))
|
||||||
(epilogue (cdr (assq :epilogue params))))
|
(epilogue (cdr (assq :epilogue params))))
|
||||||
|
|
|
@ -113,9 +113,7 @@
|
||||||
Set `sql-product' in Org edit buffer according to the
|
Set `sql-product' in Org edit buffer according to the
|
||||||
corresponding :engine source block header argument."
|
corresponding :engine source block header argument."
|
||||||
(let ((product (cdr (assq :engine (nth 2 info)))))
|
(let ((product (cdr (assq :engine (nth 2 info)))))
|
||||||
(condition-case nil
|
(sql-set-product product)))
|
||||||
(sql-set-product product)
|
|
||||||
(user-error "Cannot set `sql-product' in Org Src edit buffer"))))
|
|
||||||
|
|
||||||
(defun org-babel-sql-dbstring-mysql (host port user password database)
|
(defun org-babel-sql-dbstring-mysql (host port user password database)
|
||||||
"Make MySQL cmd line args for database connection. Pass nil to omit that arg."
|
"Make MySQL cmd line args for database connection. Pass nil to omit that arg."
|
||||||
|
@ -409,11 +407,11 @@ argument mechanism."
|
||||||
val (if sqlite
|
val (if sqlite
|
||||||
nil
|
nil
|
||||||
'(:fmt (lambda (el) (if (stringp el)
|
'(:fmt (lambda (el) (if (stringp el)
|
||||||
el
|
el
|
||||||
(format "%S" el))))))))
|
(format "%S" el))))))))
|
||||||
data-file)
|
data-file)
|
||||||
(if (stringp val) val (format "%S" val))))
|
(if (stringp val) val (format "%S" val))))
|
||||||
body)))
|
body t t)))
|
||||||
vars)
|
vars)
|
||||||
body)
|
body)
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,23 @@ read-write permissions for the user, read-only for everyone else."
|
||||||
:package-version '(Org . "9.6")
|
:package-version '(Org . "9.6")
|
||||||
:type 'integer)
|
:type 'integer)
|
||||||
|
|
||||||
|
(defcustom org-babel-tangle-remove-file-before-write 'auto
|
||||||
|
"How to overwrite the existing tangle target.
|
||||||
|
When set to nil, `org-babel-tangle' will replace contents of an existing
|
||||||
|
tangle target (and fail when tangle target is read-only).
|
||||||
|
When set to t, the tangle target (including read-only) will be deleted
|
||||||
|
first and a new file, possibly with different ownership and
|
||||||
|
permissions, will be created.
|
||||||
|
When set to symbol `auto', overwrite read-only tangle targets and
|
||||||
|
replace contents otherwise."
|
||||||
|
:group 'org-babel-tangle
|
||||||
|
:package-version '(Org . "9.7")
|
||||||
|
:type '(choice
|
||||||
|
(const :tag "Replace contents, but keep the same file" nil)
|
||||||
|
(const :tag "Re-create file" t)
|
||||||
|
(const :tag "Re-create when read-only" auto))
|
||||||
|
:safe t)
|
||||||
|
|
||||||
(defun org-babel-find-file-noselect-refresh (file)
|
(defun org-babel-find-file-noselect-refresh (file)
|
||||||
"Find file ensuring that the latest changes on disk are
|
"Find file ensuring that the latest changes on disk are
|
||||||
represented in the file."
|
represented in the file."
|
||||||
|
@ -323,7 +340,19 @@ matching a regular expression."
|
||||||
(error "Not allowed to tangle into the same file as self"))
|
(error "Not allowed to tangle into the same file as self"))
|
||||||
;; We do not erase, but overwrite previous file
|
;; We do not erase, but overwrite previous file
|
||||||
;; to preserve any existing symlinks.
|
;; to preserve any existing symlinks.
|
||||||
(write-region nil nil file-name)
|
;; This behavior is modified using
|
||||||
|
;; `org-babel-tangle-remove-file-before-write' to
|
||||||
|
;; tangle to read-only files.
|
||||||
|
(when (and
|
||||||
|
(file-exists-p file-name)
|
||||||
|
(pcase org-babel-tangle-remove-file-before-write
|
||||||
|
(`auto (not (file-writable-p file-name)))
|
||||||
|
(`t t)
|
||||||
|
(`nil nil)
|
||||||
|
(_ (error "Invalid value of `org-babel-tangle-remove-file-before-write': %S"
|
||||||
|
org-babel-tangle-remove-file-before-write))))
|
||||||
|
(delete-file file-name))
|
||||||
|
(write-region nil nil file-name)
|
||||||
(mapc (lambda (mode) (set-file-modes file-name mode)) modes))
|
(mapc (lambda (mode) (set-file-modes file-name mode)) modes))
|
||||||
(push file-name path-collector))))))
|
(push file-name path-collector))))))
|
||||||
(if (equal arg '(4))
|
(if (equal arg '(4))
|
||||||
|
|
|
@ -79,6 +79,12 @@
|
||||||
(declare-function org-open-file "org" (path &optional in-emacs line search))
|
(declare-function org-open-file "org" (path &optional in-emacs line search))
|
||||||
|
|
||||||
(declare-function org-element-interpret-data "org-element" (data))
|
(declare-function org-element-interpret-data "org-element" (data))
|
||||||
|
(declare-function org-element-parse-secondary-string "org-element" (string restriction &optional parent))
|
||||||
|
(declare-function org-element-map "org-element"
|
||||||
|
( data types fun
|
||||||
|
&optional
|
||||||
|
info first-match no-recursion
|
||||||
|
with-affiliated no-undefer))
|
||||||
(declare-function org-element-property "org-element-ast" (property node))
|
(declare-function org-element-property "org-element-ast" (property node))
|
||||||
(declare-function org-element-type-p "org-element-ast" (node types))
|
(declare-function org-element-type-p "org-element-ast" (node types))
|
||||||
(declare-function org-element-contents "org-element-ast" (node))
|
(declare-function org-element-contents "org-element-ast" (node))
|
||||||
|
@ -367,7 +373,7 @@ personal names of the form \"family, given\"."
|
||||||
(cond
|
(cond
|
||||||
((stringp names) (setq names-string names))
|
((stringp names) (setq names-string names))
|
||||||
((org-element-type-p names 'raw)
|
((org-element-type-p names 'raw)
|
||||||
(setq names-string (mapconcat #'identity (org-element-contents names))
|
(setq names-string (mapconcat #'identity (org-element-contents names) "")
|
||||||
raw-p t)))
|
raw-p t)))
|
||||||
(when names-string
|
(when names-string
|
||||||
(setq names-string
|
(setq names-string
|
||||||
|
@ -463,6 +469,38 @@ necessary, unless optional argument NO-SUFFIX is non-nil."
|
||||||
new))))
|
new))))
|
||||||
(if no-suffix year (concat year suffix)))))))
|
(if no-suffix year (concat year suffix)))))))
|
||||||
|
|
||||||
|
(defun org-cite-basic--print-bibtex-string (element &optional info)
|
||||||
|
"Print Bibtex formatted string ELEMENT, according to Bibtex syntax.
|
||||||
|
Remove all the {...} that are not a part of LaTeX macros and parse the
|
||||||
|
LaTeX fragments. Do nothing when current backend is derived from
|
||||||
|
LaTeX, according to INFO.
|
||||||
|
|
||||||
|
Return updated ELEMENT."
|
||||||
|
(if (org-export-derived-backend-p (plist-get info :back-end) 'latex)
|
||||||
|
;; Derived from LaTeX, no need to use manual ad-hoc LaTeX
|
||||||
|
;; parser.
|
||||||
|
element
|
||||||
|
;; Convert ELEMENT to anonymous when ELEMENT is string.
|
||||||
|
;; Otherwise, we cannot modify ELEMENT by side effect.
|
||||||
|
(when (org-element-type-p element 'plain-text)
|
||||||
|
(setq element (org-element-create 'anonymous nil element)))
|
||||||
|
;; Approximately parse LaTeX fragments, assuming Org mode syntax
|
||||||
|
;; (it is close to original LaTeX, and we do not want to
|
||||||
|
;; re-implement complete LaTeX parser here))
|
||||||
|
(org-element-map element t
|
||||||
|
(lambda (str)
|
||||||
|
(when (stringp str)
|
||||||
|
(org-element-set
|
||||||
|
str
|
||||||
|
(org-element-parse-secondary-string
|
||||||
|
str '(latex-fragment entity))))))
|
||||||
|
;; Strip the remaining { and }.
|
||||||
|
(org-element-map element t
|
||||||
|
(lambda (str)
|
||||||
|
(when (stringp str)
|
||||||
|
(org-element-set str (replace-regexp-in-string "[{}]" "" str)))))
|
||||||
|
element))
|
||||||
|
|
||||||
(defun org-cite-basic--print-entry (entry style &optional info)
|
(defun org-cite-basic--print-entry (entry style &optional info)
|
||||||
"Format ENTRY according to STYLE string.
|
"Format ENTRY according to STYLE string.
|
||||||
ENTRY is an alist, as returned by `org-cite-basic--get-entry'.
|
ENTRY is an alist, as returned by `org-cite-basic--get-entry'.
|
||||||
|
@ -474,27 +512,29 @@ Optional argument INFO is the export state, as a property list."
|
||||||
(org-cite-basic--get-field 'journal entry info)
|
(org-cite-basic--get-field 'journal entry info)
|
||||||
(org-cite-basic--get-field 'institution entry info)
|
(org-cite-basic--get-field 'institution entry info)
|
||||||
(org-cite-basic--get-field 'school entry info))))
|
(org-cite-basic--get-field 'school entry info))))
|
||||||
(pcase style
|
(org-cite-basic--print-bibtex-string
|
||||||
("plain"
|
(pcase style
|
||||||
(let ((year (org-cite-basic--get-year entry info 'no-suffix)))
|
("plain"
|
||||||
(org-cite-concat
|
(let ((year (org-cite-basic--get-year entry info 'no-suffix)))
|
||||||
(org-cite-basic--shorten-names author) ". "
|
(org-cite-concat
|
||||||
title (and from (list ", " from)) ", " year ".")))
|
(org-cite-basic--shorten-names author) ". "
|
||||||
("numeric"
|
title (and from (list ", " from)) ", " year ".")))
|
||||||
(let ((n (org-cite-basic--key-number (cdr (assq 'id entry)) info))
|
("numeric"
|
||||||
(year (org-cite-basic--get-year entry info 'no-suffix)))
|
(let ((n (org-cite-basic--key-number (cdr (assq 'id entry)) info))
|
||||||
(org-cite-concat
|
(year (org-cite-basic--get-year entry info 'no-suffix)))
|
||||||
(format "[%d] " n) author ", "
|
(org-cite-concat
|
||||||
(org-cite-emphasize 'italic title)
|
(format "[%d] " n) author ", "
|
||||||
(and from (list ", " from)) ", "
|
(org-cite-emphasize 'italic title)
|
||||||
year ".")))
|
(and from (list ", " from)) ", "
|
||||||
;; Default to author-year. Use year disambiguation there.
|
year ".")))
|
||||||
(_
|
;; Default to author-year. Use year disambiguation there.
|
||||||
(let ((year (org-cite-basic--get-year entry info)))
|
(_
|
||||||
(org-cite-concat
|
(let ((year (org-cite-basic--get-year entry info)))
|
||||||
author " (" year "). "
|
(org-cite-concat
|
||||||
(org-cite-emphasize 'italic title)
|
author " (" year "). "
|
||||||
(and from (list ", " from)) "."))))))
|
(org-cite-emphasize 'italic title)
|
||||||
|
(and from (list ", " from)) "."))))
|
||||||
|
info)))
|
||||||
|
|
||||||
|
|
||||||
;;; "Activate" capability
|
;;; "Activate" capability
|
||||||
|
|
|
@ -376,61 +376,47 @@ INFO is the export state, as a property list."
|
||||||
(other
|
(other
|
||||||
(user-error "Invalid entry %S in `org-cite-biblatex-styles'" other))))))
|
(user-error "Invalid entry %S in `org-cite-biblatex-styles'" other))))))
|
||||||
|
|
||||||
(defun org-cite-biblatex-prepare-preamble (output _keys files style &rest _)
|
(defun org-cite-biblatex--generate-latex-usepackage (info)
|
||||||
"Prepare document preamble for \"biblatex\" usage.
|
"Ensure that the biblatex package is loaded.
|
||||||
|
This is performed by extracting relevant information from the
|
||||||
|
INFO export plist, and modifying any existing
|
||||||
|
\\usepackage{biblatex} statement in the LaTeX header."
|
||||||
|
(let ((style (org-cite-bibliography-style info))
|
||||||
|
(usepackage-rx (rx "\\usepackage"
|
||||||
|
(opt (group "[" (*? anything) "]"))
|
||||||
|
"{biblatex}")))
|
||||||
|
(concat
|
||||||
|
(if (string-match usepackage-rx (plist-get info :latex-full-header))
|
||||||
|
;; "biblatex" package loaded, but with none (or different) options.
|
||||||
|
;; Replace with style-including command.
|
||||||
|
(plist-put info :latex-full-header
|
||||||
|
(replace-match
|
||||||
|
(format "\\usepackage%s{biblatex}"
|
||||||
|
(save-match-data
|
||||||
|
(org-cite-biblatex--package-options nil style)))
|
||||||
|
t t
|
||||||
|
(plist-get info :latex-full-header)))
|
||||||
|
;; No "biblatex" package loaded. Insert "usepackage" command
|
||||||
|
;; with appropriate options, including style.
|
||||||
|
(format "\\usepackage%s{biblatex}\n"
|
||||||
|
(org-cite-biblatex--package-options
|
||||||
|
org-cite-biblatex-options style))))))
|
||||||
|
|
||||||
OUTPUT is the final output of the export process. FILES is the list of file
|
(defun org-cite-biblatex--generate-latex-bibresources (info)
|
||||||
names used as the bibliography.
|
"From INFO generate LaTeX that loads the relevant bibliography resource files."
|
||||||
|
(let ((files (plist-get info :bibliography)))
|
||||||
This function ensures \"biblatex\" package is required. It also adds resources
|
(mapconcat (lambda (f)
|
||||||
to the document, and set styles."
|
(format "\\addbibresource%s{%s}"
|
||||||
(with-temp-buffer
|
(if (org-url-p f) "[location=remote]" "")
|
||||||
(save-excursion (insert output))
|
f))
|
||||||
(when (search-forward "\\begin{document}" nil t)
|
files
|
||||||
;; Ensure there is a \usepackage{biblatex} somewhere or add one.
|
"\n")))
|
||||||
;; Then set options.
|
|
||||||
(goto-char (match-beginning 0))
|
|
||||||
(let ((re (rx "\\usepackage"
|
|
||||||
(opt (group "[" (*? anything) "]"))
|
|
||||||
"{biblatex}")))
|
|
||||||
(cond
|
|
||||||
;; No "biblatex" package loaded. Insert "usepackage" command
|
|
||||||
;; with appropriate options, including style.
|
|
||||||
((not (re-search-backward re nil t))
|
|
||||||
(save-excursion
|
|
||||||
(insert
|
|
||||||
(format "\\usepackage%s{biblatex}\n"
|
|
||||||
(org-cite-biblatex--package-options
|
|
||||||
org-cite-biblatex-options style)))))
|
|
||||||
;; "biblatex" package loaded, but without any option.
|
|
||||||
;; Include style only.
|
|
||||||
((not (match-beginning 1))
|
|
||||||
(search-forward "{" nil t)
|
|
||||||
(insert (org-cite-biblatex--package-options nil style)))
|
|
||||||
;; "biblatex" package loaded with some options set. Override
|
|
||||||
;; style-related options with ours.
|
|
||||||
(t
|
|
||||||
(replace-match
|
|
||||||
(save-match-data
|
|
||||||
(org-cite-biblatex--package-options (match-string 1) style))
|
|
||||||
nil nil nil 1))))
|
|
||||||
;; Insert resources below.
|
|
||||||
(forward-line)
|
|
||||||
(insert (mapconcat (lambda (f)
|
|
||||||
(format "\\addbibresource%s{%s}"
|
|
||||||
(if (org-url-p f) "[location=remote]" "")
|
|
||||||
f))
|
|
||||||
files
|
|
||||||
"\n")
|
|
||||||
"\n"))
|
|
||||||
(buffer-string)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Register `biblatex' processor
|
;;; Register `biblatex' processor
|
||||||
(org-cite-register-processor 'biblatex
|
(org-cite-register-processor 'biblatex
|
||||||
:export-bibliography #'org-cite-biblatex-export-bibliography
|
:export-bibliography #'org-cite-biblatex-export-bibliography
|
||||||
:export-citation #'org-cite-biblatex-export-citation
|
:export-citation #'org-cite-biblatex-export-citation
|
||||||
:export-finalizer #'org-cite-biblatex-prepare-preamble
|
|
||||||
:cite-styles #'org-cite-biblatex-list-styles)
|
:cite-styles #'org-cite-biblatex-list-styles)
|
||||||
|
|
||||||
(provide 'oc-biblatex)
|
(provide 'oc-biblatex)
|
||||||
|
|
|
@ -847,27 +847,11 @@ INFO is the export state, as a property list."
|
||||||
;; process.
|
;; process.
|
||||||
(org-cite-parse-elements output)))))
|
(org-cite-parse-elements output)))))
|
||||||
|
|
||||||
(defun org-cite-csl-finalizer (output _keys _files _style _backend info)
|
|
||||||
"Add \"hanging\" package if missing from LaTeX output.
|
|
||||||
OUTPUT is the export document, as a string. INFO is the export state, as a
|
|
||||||
property list."
|
|
||||||
(org-cite-csl--barf-without-citeproc)
|
|
||||||
(if (not (eq 'org-latex (org-cite-csl--output-format info)))
|
|
||||||
output
|
|
||||||
(with-temp-buffer
|
|
||||||
(save-excursion (insert output))
|
|
||||||
(when (search-forward "\\begin{document}" nil t)
|
|
||||||
(goto-char (match-beginning 0))
|
|
||||||
;; Insert the CSL-specific parts of the LaTeX preamble.
|
|
||||||
(insert (org-cite-csl--generate-latex-preamble info)))
|
|
||||||
(buffer-string))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Register `csl' processor
|
;;; Register `csl' processor
|
||||||
(org-cite-register-processor 'csl
|
(org-cite-register-processor 'csl
|
||||||
:export-citation #'org-cite-csl-render-citation
|
:export-citation #'org-cite-csl-render-citation
|
||||||
:export-bibliography #'org-cite-csl-render-bibliography
|
:export-bibliography #'org-cite-csl-render-bibliography
|
||||||
:export-finalizer #'org-cite-csl-finalizer
|
|
||||||
:cite-styles
|
:cite-styles
|
||||||
'((("author" "a") ("bare" "b") ("caps" "c") ("full" "f") ("bare-caps" "bc") ("caps-full" "cf") ("bare-caps-full" "bcf"))
|
'((("author" "a") ("bare" "b") ("caps" "c") ("full" "f") ("bare-caps" "bc") ("caps-full" "cf") ("bare-caps-full" "bcf"))
|
||||||
(("noauthor" "na") ("bare" "b") ("caps" "c") ("bare-caps" "bc"))
|
(("noauthor" "na") ("bare" "b") ("caps" "c") ("bare-caps" "bc"))
|
||||||
|
|
|
@ -168,32 +168,25 @@ state, as a property list."
|
||||||
(org-cite-natbib--build-optional-arguments citation info)
|
(org-cite-natbib--build-optional-arguments citation info)
|
||||||
(org-cite-natbib--build-arguments citation)))
|
(org-cite-natbib--build-arguments citation)))
|
||||||
|
|
||||||
(defun org-cite-natbib-use-package (output &rest _)
|
(defun org-cite-natbib--generate-latex-preamble (info)
|
||||||
"Ensure output requires \"natbib\" package.
|
"Ensure that the \"natbib\" package is loaded.
|
||||||
OUTPUT is the final output of the export process."
|
INFO is a plist used as a communication channel."
|
||||||
(with-temp-buffer
|
(and (not (string-match
|
||||||
(save-excursion (insert output))
|
(rx "\\usepackage" (opt "[" (*? nonl) "]") "{natbib}")
|
||||||
(when (search-forward "\\begin{document}" nil t)
|
(plist-get info :latex-full-header)))
|
||||||
;; Ensure there is a \usepackage{natbib} somewhere or add one.
|
(format "\\usepackage%s{natbib}\n"
|
||||||
(goto-char (match-beginning 0))
|
(if (null org-cite-natbib-options)
|
||||||
(let ((re (rx "\\usepackage" (opt "[" (*? nonl) "]") "{natbib}")))
|
""
|
||||||
(unless (re-search-backward re nil t)
|
(format "[%s]"
|
||||||
(insert
|
(mapconcat #'symbol-name
|
||||||
(format "\\usepackage%s{natbib}\n"
|
org-cite-natbib-options
|
||||||
(if (null org-cite-natbib-options)
|
","))))))
|
||||||
""
|
|
||||||
(format "[%s]"
|
|
||||||
(mapconcat #'symbol-name
|
|
||||||
org-cite-natbib-options
|
|
||||||
","))))))))
|
|
||||||
(buffer-string)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Register `natbib' processor
|
;;; Register `natbib' processor
|
||||||
(org-cite-register-processor 'natbib
|
(org-cite-register-processor 'natbib
|
||||||
:export-bibliography #'org-cite-natbib-export-bibliography
|
:export-bibliography #'org-cite-natbib-export-bibliography
|
||||||
:export-citation #'org-cite-natbib-export-citation
|
:export-citation #'org-cite-natbib-export-citation
|
||||||
:export-finalizer #'org-cite-natbib-use-package
|
|
||||||
:cite-styles
|
:cite-styles
|
||||||
'((("author" "a") ("caps" "a") ("full" "f"))
|
'((("author" "a") ("caps" "a") ("full" "f"))
|
||||||
(("noauthor" "na") ("bare" "b"))
|
(("noauthor" "na") ("bare" "b"))
|
||||||
|
|
|
@ -226,7 +226,7 @@ date year)."
|
||||||
|
|
||||||
;;; Implementation
|
;;; Implementation
|
||||||
|
|
||||||
(defun org-bbdb-store-link ()
|
(defun org-bbdb-store-link (&optional _interactive?)
|
||||||
"Store a link to a BBDB database entry."
|
"Store a link to a BBDB database entry."
|
||||||
(when (eq major-mode 'bbdb-mode)
|
(when (eq major-mode 'bbdb-mode)
|
||||||
;; This is BBDB, we make this link!
|
;; This is BBDB, we make this link!
|
||||||
|
|
|
@ -507,7 +507,7 @@ ARG, when non-nil, is a universal prefix argument. See
|
||||||
`org-open-file' for details."
|
`org-open-file' for details."
|
||||||
(org-link-open-as-file path arg))
|
(org-link-open-as-file path arg))
|
||||||
|
|
||||||
(defun org-bibtex-store-link ()
|
(defun org-bibtex-store-link (&optional _interactive?)
|
||||||
"Store a link to a BibTeX entry."
|
"Store a link to a BibTeX entry."
|
||||||
(when (eq major-mode 'bibtex-mode)
|
(when (eq major-mode 'bibtex-mode)
|
||||||
(let* ((search (org-create-file-search-in-bibtex))
|
(let* ((search (org-create-file-search-in-bibtex))
|
||||||
|
@ -640,22 +640,23 @@ With prefix argument OPTIONAL also prompt for optional fields."
|
||||||
"Return headline text according to ENTRY title."
|
"Return headline text according to ENTRY title."
|
||||||
(cdr (assq :title entry)))
|
(cdr (assq :title entry)))
|
||||||
|
|
||||||
(defun org-bibtex-create (&optional arg nonew)
|
(defun org-bibtex-create (&optional arg update-heading)
|
||||||
"Create a new entry at the given level.
|
"Create a new entry at the given level.
|
||||||
With a prefix arg, query for optional fields as well.
|
With a prefix ARG, query for optional fields as well.
|
||||||
If nonew is t, add data to the headline of the entry at point."
|
If UPDATE-HEADING is non-nil, add data to the headline of the entry at
|
||||||
|
point."
|
||||||
(interactive "P")
|
(interactive "P")
|
||||||
(let* ((type (completing-read
|
(let* ((type (completing-read
|
||||||
"Type: " (mapcar (lambda (type)
|
"Type: " (mapcar (lambda (type)
|
||||||
(substring (symbol-name (car type)) 1))
|
(substring (symbol-name (car type)) 1))
|
||||||
org-bibtex-types)
|
org-bibtex-types)
|
||||||
nil nil (when nonew
|
nil nil (when update-heading
|
||||||
(org-bibtex-get org-bibtex-type-property-name))))
|
(org-bibtex-get org-bibtex-type-property-name))))
|
||||||
(type (if (keywordp type) type (intern (concat ":" type))))
|
(type (if (keywordp type) type (intern (concat ":" type))))
|
||||||
(org-bibtex-treat-headline-as-title (if nonew nil t)))
|
(org-bibtex-treat-headline-as-title (if update-heading nil t)))
|
||||||
(unless (assoc type org-bibtex-types)
|
(unless (assoc type org-bibtex-types)
|
||||||
(error "Type:%s is not known" type))
|
(error "Type:%s is not known" type))
|
||||||
(if nonew
|
(if update-heading
|
||||||
(org-back-to-heading)
|
(org-back-to-heading)
|
||||||
(org-insert-heading)
|
(org-insert-heading)
|
||||||
(let ((title (org-bibtex-ask :title)))
|
(let ((title (org-bibtex-ask :title)))
|
||||||
|
@ -722,29 +723,32 @@ Return the number of saved entries."
|
||||||
(interactive "fFile: ")
|
(interactive "fFile: ")
|
||||||
(org-bibtex-read-buffer (find-file-noselect file 'nowarn 'rawfile)))
|
(org-bibtex-read-buffer (find-file-noselect file 'nowarn 'rawfile)))
|
||||||
|
|
||||||
(defun org-bibtex-write (&optional noindent)
|
(defun org-bibtex-write (&optional noindent update-heading)
|
||||||
"Insert a heading built from the first element of `org-bibtex-entries'.
|
"Insert a heading built from the first element of `org-bibtex-entries'.
|
||||||
When optional argument NOINDENT is non-nil, do not indent the properties
|
When optional argument NOINDENT is non-nil, do not indent the properties
|
||||||
drawer."
|
drawer. If UPDATE-HEADING is non-nil, add data to the headline of the
|
||||||
|
entry at point."
|
||||||
(interactive)
|
(interactive)
|
||||||
(unless org-bibtex-entries
|
(unless org-bibtex-entries
|
||||||
(error "No entries in `org-bibtex-entries'"))
|
(error "No entries in `org-bibtex-entries'"))
|
||||||
(let* ((entry (pop org-bibtex-entries))
|
(let* ((entry (pop org-bibtex-entries))
|
||||||
(org-special-properties nil) ; avoids errors with `org-entry-put'
|
(org-special-properties nil) ; avoids errors with `org-entry-put'
|
||||||
(val (lambda (field) (cdr (assoc field entry))))
|
(val (lambda (field) (cdr (assoc field entry))))
|
||||||
(togtag (lambda (tag) (org-toggle-tag tag 'on))))
|
(togtag (lambda (tag) (org-toggle-tag tag 'on)))
|
||||||
(org-insert-heading)
|
(insert-raw (not update-heading)))
|
||||||
(insert (funcall org-bibtex-headline-format-function entry))
|
(unless update-heading
|
||||||
(insert "\n:PROPERTIES:\n")
|
(org-insert-heading)
|
||||||
(org-bibtex-put "TITLE" (funcall val :title) 'insert)
|
(insert (funcall org-bibtex-headline-format-function entry))
|
||||||
|
(insert "\n:PROPERTIES:\n"))
|
||||||
|
(org-bibtex-put "TITLE" (funcall val :title) insert-raw)
|
||||||
(org-bibtex-put org-bibtex-type-property-name
|
(org-bibtex-put org-bibtex-type-property-name
|
||||||
(downcase (funcall val :type))
|
(downcase (funcall val :type))
|
||||||
'insert)
|
insert-raw)
|
||||||
(dolist (pair entry)
|
(dolist (pair entry)
|
||||||
(pcase (car pair)
|
(pcase (car pair)
|
||||||
(:title nil)
|
(:title nil)
|
||||||
(:type nil)
|
(:type nil)
|
||||||
(:key (org-bibtex-put org-bibtex-key-property (cdr pair) 'insert))
|
(:key (org-bibtex-put org-bibtex-key-property (cdr pair) insert-raw))
|
||||||
(:keywords (if org-bibtex-tags-are-keywords
|
(:keywords (if org-bibtex-tags-are-keywords
|
||||||
(dolist (kw (split-string (cdr pair) ", *"))
|
(dolist (kw (split-string (cdr pair) ", *"))
|
||||||
(funcall
|
(funcall
|
||||||
|
@ -752,25 +756,28 @@ drawer."
|
||||||
(replace-regexp-in-string
|
(replace-regexp-in-string
|
||||||
"[^[:alnum:]_@#%]" ""
|
"[^[:alnum:]_@#%]" ""
|
||||||
(replace-regexp-in-string "[ \t]+" "_" kw))))
|
(replace-regexp-in-string "[ \t]+" "_" kw))))
|
||||||
(org-bibtex-put (car pair) (cdr pair) 'insert)))
|
(org-bibtex-put (car pair) (cdr pair) insert-raw)))
|
||||||
(_ (org-bibtex-put (car pair) (cdr pair) 'insert))))
|
(_ (org-bibtex-put (car pair) (cdr pair) insert-raw))))
|
||||||
(insert ":END:\n")
|
(unless update-heading
|
||||||
|
(insert ":END:\n"))
|
||||||
(mapc togtag org-bibtex-tags)
|
(mapc togtag org-bibtex-tags)
|
||||||
(unless noindent
|
(unless noindent
|
||||||
(org-indent-region
|
(org-indent-region
|
||||||
(save-excursion (org-back-to-heading t) (point))
|
(save-excursion (org-back-to-heading t) (point))
|
||||||
(point)))))
|
(point)))))
|
||||||
|
|
||||||
(defun org-bibtex-yank ()
|
(defun org-bibtex-yank (&optional update-heading)
|
||||||
"If kill ring holds a bibtex entry yank it as an Org headline."
|
"If kill ring holds a bibtex entry yank it as an Org headline.
|
||||||
(interactive)
|
When called with non-nil prefix argument UPDATE-HEADING, add data to the
|
||||||
|
headline of the entry at point."
|
||||||
|
(interactive "P")
|
||||||
(let (entry)
|
(let (entry)
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(yank 1)
|
(yank 1)
|
||||||
(bibtex-mode)
|
(bibtex-mode)
|
||||||
(setf entry (org-bibtex-read)))
|
(setf entry (org-bibtex-read)))
|
||||||
(if entry
|
(if entry
|
||||||
(org-bibtex-write)
|
(org-bibtex-write nil update-heading)
|
||||||
(error "Yanked text does not appear to contain a BibTeX entry"))))
|
(error "Yanked text does not appear to contain a BibTeX entry"))))
|
||||||
|
|
||||||
(defun org-bibtex-import-from-file (file)
|
(defun org-bibtex-import-from-file (file)
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
(error "No such file: %s" path))
|
(error "No such file: %s" path))
|
||||||
(when page (doc-view-goto-page page))))
|
(when page (doc-view-goto-page page))))
|
||||||
|
|
||||||
(defun org-docview-store-link ()
|
(defun org-docview-store-link (&optional _interactive?)
|
||||||
"Store a link to a docview buffer."
|
"Store a link to a docview buffer."
|
||||||
(when (eq major-mode 'doc-view-mode)
|
(when (eq major-mode 'doc-view-mode)
|
||||||
;; This buffer is in doc-view-mode
|
;; This buffer is in doc-view-mode
|
||||||
|
|
|
@ -60,7 +60,7 @@ followed by a colon."
|
||||||
(insert command)
|
(insert command)
|
||||||
(eshell-send-input)))
|
(eshell-send-input)))
|
||||||
|
|
||||||
(defun org-eshell-store-link ()
|
(defun org-eshell-store-link (&optional _interactive?)
|
||||||
"Store eshell link.
|
"Store eshell link.
|
||||||
When opened, the link switches back to the current eshell buffer and
|
When opened, the link switches back to the current eshell buffer and
|
||||||
the current working directory."
|
the current working directory."
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
"Open URL with Eww in the current buffer."
|
"Open URL with Eww in the current buffer."
|
||||||
(eww url))
|
(eww url))
|
||||||
|
|
||||||
(defun org-eww-store-link ()
|
(defun org-eww-store-link (&optional _interactive?)
|
||||||
"Store a link to the url of an EWW buffer."
|
"Store a link to the url of an EWW buffer."
|
||||||
(when (eq major-mode 'eww-mode)
|
(when (eq major-mode 'eww-mode)
|
||||||
(org-link-store-props
|
(org-link-store-props
|
||||||
|
|
|
@ -123,7 +123,7 @@ If `org-store-link' was called with a prefix arg the meaning of
|
||||||
(url-encode-url message-id))
|
(url-encode-url message-id))
|
||||||
(concat "gnus:" group "#" message-id)))
|
(concat "gnus:" group "#" message-id)))
|
||||||
|
|
||||||
(defun org-gnus-store-link ()
|
(defun org-gnus-store-link (&optional _interactive?)
|
||||||
"Store a link to a Gnus folder or message."
|
"Store a link to a Gnus folder or message."
|
||||||
(pcase major-mode
|
(pcase major-mode
|
||||||
(`gnus-group-mode
|
(`gnus-group-mode
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
:insert-description #'org-info-description-as-command)
|
:insert-description #'org-info-description-as-command)
|
||||||
|
|
||||||
;; Implementation
|
;; Implementation
|
||||||
(defun org-info-store-link ()
|
(defun org-info-store-link (&optional _interactive?)
|
||||||
"Store a link to an Info file and node."
|
"Store a link to an Info file and node."
|
||||||
(when (eq major-mode 'Info-mode)
|
(when (eq major-mode 'Info-mode)
|
||||||
(let ((link (concat "info:"
|
(let ((link (concat "info:"
|
||||||
|
|
|
@ -103,7 +103,7 @@ attributes that are found."
|
||||||
parts))
|
parts))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-irc-store-link ()
|
(defun org-irc-store-link (&optional _interactive?)
|
||||||
"Dispatch to the appropriate function to store a link to an IRC session."
|
"Dispatch to the appropriate function to store a link to an IRC session."
|
||||||
(cond
|
(cond
|
||||||
((eq major-mode 'erc-mode)
|
((eq major-mode 'erc-mode)
|
||||||
|
|
|
@ -82,7 +82,7 @@ matched strings in man buffer."
|
||||||
(set-window-point window point)
|
(set-window-point window point)
|
||||||
(set-window-start window point)))))))
|
(set-window-start window point)))))))
|
||||||
|
|
||||||
(defun org-man-store-link ()
|
(defun org-man-store-link (&optional _interactive?)
|
||||||
"Store a link to a README file."
|
"Store a link to a README file."
|
||||||
(when (memq major-mode '(Man-mode woman-mode))
|
(when (memq major-mode '(Man-mode woman-mode))
|
||||||
;; This is a man page, we do make this link
|
;; This is a man page, we do make this link
|
||||||
|
|
|
@ -80,7 +80,7 @@ supported by MH-E."
|
||||||
(org-link-set-parameters "mhe" :follow #'org-mhe-open :store #'org-mhe-store-link)
|
(org-link-set-parameters "mhe" :follow #'org-mhe-open :store #'org-mhe-store-link)
|
||||||
|
|
||||||
;; Implementation
|
;; Implementation
|
||||||
(defun org-mhe-store-link ()
|
(defun org-mhe-store-link (&optional _interactive?)
|
||||||
"Store a link to an MH-E folder or message."
|
"Store a link to an MH-E folder or message."
|
||||||
(when (or (eq major-mode 'mh-folder-mode)
|
(when (or (eq major-mode 'mh-folder-mode)
|
||||||
(eq major-mode 'mh-show-mode))
|
(eq major-mode 'mh-show-mode))
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
:store #'org-rmail-store-link)
|
:store #'org-rmail-store-link)
|
||||||
|
|
||||||
;; Implementation
|
;; Implementation
|
||||||
(defun org-rmail-store-link ()
|
(defun org-rmail-store-link (&optional _interactive?)
|
||||||
"Store a link to an Rmail folder or message."
|
"Store a link to an Rmail folder or message."
|
||||||
(when (or (eq major-mode 'rmail-mode)
|
(when (or (eq major-mode 'rmail-mode)
|
||||||
(eq major-mode 'rmail-summary-mode))
|
(eq major-mode 'rmail-summary-mode))
|
||||||
|
|
432
lisp/ol.el
432
lisp/ol.el
|
@ -52,18 +52,19 @@
|
||||||
(declare-function org-do-occur "org" (regexp &optional cleanup))
|
(declare-function org-do-occur "org" (regexp &optional cleanup))
|
||||||
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
|
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
|
||||||
(declare-function org-element-cache-refresh "org-element" (pos))
|
(declare-function org-element-cache-refresh "org-element" (pos))
|
||||||
|
(declare-function org-element-cache-reset "org-element" (&optional all no-persistence))
|
||||||
(declare-function org-element-context "org-element" (&optional element))
|
(declare-function org-element-context "org-element" (&optional element))
|
||||||
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
|
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
|
||||||
(declare-function org-element-link-parser "org-element" ())
|
(declare-function org-element-link-parser "org-element" ())
|
||||||
(declare-function org-element-property "org-element-ast" (property node))
|
(declare-function org-element-property "org-element-ast" (property node))
|
||||||
(declare-function org-element-begin "org-element" (node))
|
(declare-function org-element-begin "org-element" (node))
|
||||||
|
(declare-function org-element-end "org-element" (node))
|
||||||
(declare-function org-element-type-p "org-element-ast" (node types))
|
(declare-function org-element-type-p "org-element-ast" (node types))
|
||||||
(declare-function org-element-update-syntax "org-element" ())
|
(declare-function org-element-update-syntax "org-element" ())
|
||||||
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
|
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
|
||||||
(declare-function org-find-property "org" (property &optional value))
|
(declare-function org-find-property "org" (property &optional value))
|
||||||
(declare-function org-get-heading "org" (&optional no-tags no-todo no-priority no-comment))
|
(declare-function org-get-heading "org" (&optional no-tags no-todo no-priority no-comment))
|
||||||
(declare-function org-id-find-id-file "org-id" (id))
|
(declare-function org-id-find-id-file "org-id" (id))
|
||||||
(declare-function org-id-store-link "org-id" ())
|
|
||||||
(declare-function org-insert-heading "org" (&optional arg invisible-ok top))
|
(declare-function org-insert-heading "org" (&optional arg invisible-ok top))
|
||||||
(declare-function org-load-modules-maybe "org" (&optional force))
|
(declare-function org-load-modules-maybe "org" (&optional force))
|
||||||
(declare-function org-mark-ring-push "org" (&optional pos buffer))
|
(declare-function org-mark-ring-push "org" (&optional pos buffer))
|
||||||
|
@ -532,6 +533,16 @@ links more efficient."
|
||||||
|
|
||||||
(defvar-local org-target-link-regexp nil
|
(defvar-local org-target-link-regexp nil
|
||||||
"Regular expression matching radio targets in plain text.")
|
"Regular expression matching radio targets in plain text.")
|
||||||
|
(defconst org-target-link-regexp-limit (ash 2 12)
|
||||||
|
"Maximum allowed length of regexp.
|
||||||
|
The number should generally be ~order of magnitude smaller than
|
||||||
|
MAX_BUF_SIZE in src/regex-emacs.c. The number of regexp-emacs.c is
|
||||||
|
for processed regexp, which appears to be larger compared to the
|
||||||
|
original string length.")
|
||||||
|
(defvar-local org-target-link-regexps nil
|
||||||
|
"List of regular expressions matching radio targets in plain text.
|
||||||
|
This list is non-nil, when a single regexp would be too long to match
|
||||||
|
all the possible targets, exceeding Emacs' regexp length limit.")
|
||||||
|
|
||||||
(defvar org-link-types-re nil
|
(defvar org-link-types-re nil
|
||||||
"Matches a link that has a url-like prefix like \"http:\".")
|
"Matches a link that has a url-like prefix like \"http:\".")
|
||||||
|
@ -818,6 +829,74 @@ spec."
|
||||||
(org-with-point-at (car region)
|
(org-with-point-at (car region)
|
||||||
(not (org-in-regexp org-link-any-re))))
|
(not (org-in-regexp org-link-any-re))))
|
||||||
|
|
||||||
|
(defun org-link--try-link-store-functions (interactive?)
|
||||||
|
"Try storing external links, prompting if more than one is possible.
|
||||||
|
|
||||||
|
Each function returned by `org-store-link-functions' is called in
|
||||||
|
turn. If multiple functions return non-nil, prompt for which
|
||||||
|
link should be stored.
|
||||||
|
|
||||||
|
Argument INTERACTIVE? indicates whether `org-store-link' was
|
||||||
|
called interactively and is passed to the link store functions.
|
||||||
|
|
||||||
|
Return t when a link has been stored in `org-link-store-props'."
|
||||||
|
(let ((results-alist nil))
|
||||||
|
(dolist (f (org-store-link-functions))
|
||||||
|
(when (condition-case nil
|
||||||
|
(funcall f interactive?)
|
||||||
|
;; FIXME: The store function used (< Org 9.7) to accept
|
||||||
|
;; no arguments; provide backward compatibility support
|
||||||
|
;; for them.
|
||||||
|
(wrong-number-of-arguments
|
||||||
|
(funcall f)))
|
||||||
|
;; FIXME: return value is not link's plist, so we store the
|
||||||
|
;; new value before it is modified. It would be cleaner to
|
||||||
|
;; ask store link functions to return the plist instead.
|
||||||
|
(push (cons f (copy-sequence org-store-link-plist))
|
||||||
|
results-alist)))
|
||||||
|
(pcase results-alist
|
||||||
|
(`nil nil)
|
||||||
|
(`((,_ . ,_)) t) ;single choice: nothing to do
|
||||||
|
(`((,name . ,_) . ,_)
|
||||||
|
;; Reinstate link plist associated to the chosen
|
||||||
|
;; function.
|
||||||
|
(apply #'org-link-store-props
|
||||||
|
(cdr (assoc-string
|
||||||
|
(completing-read
|
||||||
|
(format "Store link with (default %s): " name)
|
||||||
|
(mapcar #'car results-alist)
|
||||||
|
nil t nil nil (symbol-name name))
|
||||||
|
results-alist)))
|
||||||
|
t))))
|
||||||
|
|
||||||
|
(defun org-link--add-to-stored-links (link desc)
|
||||||
|
"Add LINK to `org-stored-links' with description DESC."
|
||||||
|
(cond
|
||||||
|
((not (member (list link desc) org-stored-links))
|
||||||
|
(push (list link desc) org-stored-links)
|
||||||
|
(message "Stored: %s" (or desc link)))
|
||||||
|
((equal (list link desc) (car org-stored-links))
|
||||||
|
(message "This link has already been stored"))
|
||||||
|
(t
|
||||||
|
(setq org-stored-links
|
||||||
|
(delete (list link desc) org-stored-links))
|
||||||
|
(push (list link desc) org-stored-links)
|
||||||
|
(message "Link moved to front: %s" (or desc link)))))
|
||||||
|
|
||||||
|
(defun org-link--file-link-to-here ()
|
||||||
|
"Return as (LINK . DESC) a file link with search string to here."
|
||||||
|
(let ((link (concat "file:"
|
||||||
|
(abbreviate-file-name
|
||||||
|
(buffer-file-name (buffer-base-buffer)))))
|
||||||
|
desc)
|
||||||
|
(when org-link-context-for-files
|
||||||
|
(pcase (org-link-precise-link-target)
|
||||||
|
(`nil nil)
|
||||||
|
(`(,search-string ,search-desc ,_position)
|
||||||
|
(setq link (format "%s::%s" link search-string))
|
||||||
|
(setq desc search-desc))))
|
||||||
|
(cons link desc)))
|
||||||
|
|
||||||
|
|
||||||
;;; Public API
|
;;; Public API
|
||||||
|
|
||||||
|
@ -1044,7 +1123,9 @@ LINK is escaped with backslashes for inclusion in buffer."
|
||||||
"List of functions that are called to create and store a link.
|
"List of functions that are called to create and store a link.
|
||||||
|
|
||||||
The functions are defined in the `:store' property of
|
The functions are defined in the `:store' property of
|
||||||
`org-link-parameters'.
|
`org-link-parameters'. Each function should accept an argument
|
||||||
|
INTERACTIVE? which indicates whether the user has initiated
|
||||||
|
`org-store-link' interactively.
|
||||||
|
|
||||||
Each function will be called in turn until one returns a non-nil
|
Each function will be called in turn until one returns a non-nil
|
||||||
value. Each function should check if it is responsible for
|
value. Each function should check if it is responsible for
|
||||||
|
@ -1068,6 +1149,9 @@ and then used in capture templates."
|
||||||
if store-func
|
if store-func
|
||||||
collect store-func))
|
collect store-func))
|
||||||
|
|
||||||
|
(defvar org-link--abbrev-functions nil
|
||||||
|
"Alist of abbrev link expressions and functions.")
|
||||||
|
|
||||||
(defun org-link-expand-abbrev (link)
|
(defun org-link-expand-abbrev (link)
|
||||||
"Replace link abbreviations in LINK string.
|
"Replace link abbreviations in LINK string.
|
||||||
Abbreviations are defined in `org-link-abbrev-alist'."
|
Abbreviations are defined in `org-link-abbrev-alist'."
|
||||||
|
@ -1082,14 +1166,27 @@ Abbreviations are defined in `org-link-abbrev-alist'."
|
||||||
(setq rpl (cdr as))
|
(setq rpl (cdr as))
|
||||||
(cond
|
(cond
|
||||||
((symbolp rpl) (funcall rpl tag))
|
((symbolp rpl) (funcall rpl tag))
|
||||||
((string-match "%(\\([^)]+\\))" rpl)
|
((string-match "%(\\([^) ]+\\))" rpl) ; %(function)
|
||||||
(replace-match
|
(replace-match
|
||||||
(save-match-data
|
(save-match-data
|
||||||
(funcall (intern-soft (match-string 1 rpl)) tag))
|
(funcall (intern-soft (match-string 1 rpl)) tag))
|
||||||
t t rpl))
|
t t rpl))
|
||||||
((string-match "%s" rpl) (replace-match (or tag "") t t rpl))
|
((string-match "%(\\(.+\\))" rpl) ; %(sexpr using tag)
|
||||||
((string-match "%h" rpl)
|
(replace-match
|
||||||
(replace-match (url-hexify-string (or tag "")) t t rpl))
|
(save-match-data
|
||||||
|
(funcall (or (cdr (assoc (match-string 1 rpl)
|
||||||
|
org-link--abbrev-functions))
|
||||||
|
(cdar (push (cons (match-string 1 rpl)
|
||||||
|
(eval (read (format
|
||||||
|
"(lambda (tag) (%s))"
|
||||||
|
(match-string 1 rpl)))))
|
||||||
|
org-link--abbrev-functions)))
|
||||||
|
tag))
|
||||||
|
t t rpl))
|
||||||
|
((string-match-p "%[0-<>^_]?[0-9]*\\(?:\\.[0-9]+\\)?s" rpl)
|
||||||
|
(format-spec rpl `((?s . ,(or tag "")))))
|
||||||
|
((string-match-p "%[0-<>^_]?[0-9]*\\(?:\\.[0-9]+\\)?h" rpl)
|
||||||
|
(format-spec rpl `((?h . ,(url-hexify-string (or tag ""))))))
|
||||||
(t (concat rpl tag)))))))
|
(t (concat rpl tag)))))))
|
||||||
|
|
||||||
(defun org-link-open (link &optional arg)
|
(defun org-link-open (link &optional arg)
|
||||||
|
@ -1163,7 +1260,7 @@ Optional argument ARG is passed to `org-open-file' when S is a
|
||||||
(`nil (user-error "No valid link in %S" s))
|
(`nil (user-error "No valid link in %S" s))
|
||||||
(link (org-link-open link arg))))
|
(link (org-link-open link arg))))
|
||||||
|
|
||||||
(defun org-link-search (s &optional avoid-pos stealth)
|
(defun org-link-search (s &optional avoid-pos stealth new-heading-container)
|
||||||
"Search for a search string S in the accessible part of the buffer.
|
"Search for a search string S in the accessible part of the buffer.
|
||||||
|
|
||||||
If S starts with \"#\", it triggers a custom ID search.
|
If S starts with \"#\", it triggers a custom ID search.
|
||||||
|
@ -1183,6 +1280,13 @@ When optional argument STEALTH is non-nil, do not modify
|
||||||
visibility around point, thus ignoring `org-show-context-detail'
|
visibility around point, thus ignoring `org-show-context-detail'
|
||||||
variable.
|
variable.
|
||||||
|
|
||||||
|
When optional argument NEW-HEADING-CONTAINER is an element, any
|
||||||
|
new heading that is created (see
|
||||||
|
`org-link-search-must-match-exact-headline') will be added as a
|
||||||
|
subheading of NEW-HEADING-CONTAINER. Otherwise, new headings are
|
||||||
|
created at level 1 at the end of the accessible part of the
|
||||||
|
buffer.
|
||||||
|
|
||||||
Search is case-insensitive and ignores white spaces. Return type
|
Search is case-insensitive and ignores white spaces. Return type
|
||||||
of matched result, which is either `dedicated' or `fuzzy'. Search
|
of matched result, which is either `dedicated' or `fuzzy'. Search
|
||||||
respects buffer narrowing."
|
respects buffer narrowing."
|
||||||
|
@ -1281,11 +1385,24 @@ respects buffer narrowing."
|
||||||
((and (derived-mode-p 'org-mode)
|
((and (derived-mode-p 'org-mode)
|
||||||
(eq org-link-search-must-match-exact-headline 'query-to-create)
|
(eq org-link-search-must-match-exact-headline 'query-to-create)
|
||||||
(yes-or-no-p "No match - create this as a new heading? "))
|
(yes-or-no-p "No match - create this as a new heading? "))
|
||||||
(goto-char (point-max))
|
(let* ((container-ok (and new-heading-container
|
||||||
(unless (bolp) (newline))
|
(org-element-type-p new-heading-container '(headline))))
|
||||||
(org-insert-heading nil t t)
|
(new-heading-position (if container-ok
|
||||||
(insert s "\n")
|
(- (org-element-end new-heading-container) 1)
|
||||||
(forward-line -1))
|
(point-max)))
|
||||||
|
(new-heading-level (if container-ok
|
||||||
|
(+ 1 (org-element-property :level new-heading-container))
|
||||||
|
1)))
|
||||||
|
;; Need to widen when target is outside accessible portion of
|
||||||
|
;; buffer, since the we want the user to end up there.
|
||||||
|
(unless (and (<= (point-min) new-heading-position)
|
||||||
|
(>= (point-max) new-heading-position))
|
||||||
|
(widen))
|
||||||
|
(goto-char new-heading-position)
|
||||||
|
(unless (bolp) (newline))
|
||||||
|
(org-insert-heading nil t new-heading-level)
|
||||||
|
(insert (if starred (substring s 1) s) "\n")
|
||||||
|
(forward-line -1)))
|
||||||
;; Only headlines are looked after. No need to process
|
;; Only headlines are looked after. No need to process
|
||||||
;; further: throw an error.
|
;; further: throw an error.
|
||||||
((and (derived-mode-p 'org-mode)
|
((and (derived-mode-p 'org-mode)
|
||||||
|
@ -1335,6 +1452,70 @@ priority cookie or tag."
|
||||||
(org-link--normalize-string
|
(org-link--normalize-string
|
||||||
(or string (org-get-heading t t t t)))))
|
(or string (org-get-heading t t t t)))))
|
||||||
|
|
||||||
|
(defun org-link-precise-link-target ()
|
||||||
|
"Determine search string and description for storing a link.
|
||||||
|
|
||||||
|
If a search string (see `org-link-search') is found, return
|
||||||
|
list (SEARCH-STRING DESC POSITION). Otherwise, return nil.
|
||||||
|
|
||||||
|
If there is an active region, the contents (or a part of it, see
|
||||||
|
`org-link-context-for-files') is used as the search string.
|
||||||
|
|
||||||
|
In Org buffers, if point is at a named element (such as a source
|
||||||
|
block), the name is used for the search string. If at a heading,
|
||||||
|
its CUSTOM_ID is used to form a search string of the form
|
||||||
|
\"#id\", if present, otherwise the current heading text is used
|
||||||
|
in the form \"*Heading\".
|
||||||
|
|
||||||
|
If none of those finds a suitable search string, the current line
|
||||||
|
is used as the search string.
|
||||||
|
|
||||||
|
The description DESC is nil (meaning the user will be prompted
|
||||||
|
for a description when inserting the link) for search strings
|
||||||
|
based on a region or the current line. For other cases, DESC is
|
||||||
|
a cleaned-up version of the name or heading at point.
|
||||||
|
|
||||||
|
POSITION is the buffer position at which the search string
|
||||||
|
matches."
|
||||||
|
(let* ((region (org-link--context-from-region))
|
||||||
|
(result
|
||||||
|
(cond
|
||||||
|
(region
|
||||||
|
(list (org-link--normalize-string region t)
|
||||||
|
nil
|
||||||
|
(region-beginning)))
|
||||||
|
|
||||||
|
((derived-mode-p 'org-mode)
|
||||||
|
(let* ((element (org-element-at-point))
|
||||||
|
(name (org-element-property :name element))
|
||||||
|
(heading (org-element-lineage element '(headline inlinetask) t))
|
||||||
|
(custom-id (org-entry-get heading "CUSTOM_ID")))
|
||||||
|
(cond
|
||||||
|
(name
|
||||||
|
(list name
|
||||||
|
name
|
||||||
|
(org-element-begin element)))
|
||||||
|
((org-before-first-heading-p)
|
||||||
|
(list (org-link--normalize-string (org-current-line-string) t)
|
||||||
|
nil
|
||||||
|
(line-beginning-position)))
|
||||||
|
(heading
|
||||||
|
(list (if custom-id (concat "#" custom-id)
|
||||||
|
(org-link-heading-search-string))
|
||||||
|
(org-link--normalize-string
|
||||||
|
(org-get-heading t t t t))
|
||||||
|
(org-element-begin heading))))))
|
||||||
|
|
||||||
|
;; Not in an org-mode buffer, no region
|
||||||
|
(t
|
||||||
|
(list (org-link--normalize-string (org-current-line-string) t)
|
||||||
|
nil
|
||||||
|
(line-beginning-position))))))
|
||||||
|
|
||||||
|
;; Only use search option if there is some text.
|
||||||
|
(when (org-string-nw-p (car result))
|
||||||
|
result)))
|
||||||
|
|
||||||
(defun org-link-open-as-file (path in-emacs)
|
(defun org-link-open-as-file (path in-emacs)
|
||||||
"Pretend PATH is a file name and open it.
|
"Pretend PATH is a file name and open it.
|
||||||
|
|
||||||
|
@ -1407,7 +1588,7 @@ PATH is a symbol name, as a string."
|
||||||
((and (pred boundp) variable) (describe-variable variable))
|
((and (pred boundp) variable) (describe-variable variable))
|
||||||
(name (user-error "Unknown function or variable: %s" name))))
|
(name (user-error "Unknown function or variable: %s" name))))
|
||||||
|
|
||||||
(defun org-link--store-help ()
|
(defun org-link--store-help (&optional _interactive?)
|
||||||
"Store \"help\" type link."
|
"Store \"help\" type link."
|
||||||
(when (eq major-mode 'help-mode)
|
(when (eq major-mode 'help-mode)
|
||||||
(let ((symbol
|
(let ((symbol
|
||||||
|
@ -1542,7 +1723,12 @@ prefix ARG forces storing a link for each line in the
|
||||||
active region.
|
active region.
|
||||||
|
|
||||||
Assume the function is called interactively if INTERACTIVE? is
|
Assume the function is called interactively if INTERACTIVE? is
|
||||||
non-nil."
|
non-nil.
|
||||||
|
|
||||||
|
In Org buffers, an additional \"human-readable\" simple file link
|
||||||
|
is stored as an alternative to persistent org-id or other links,
|
||||||
|
if at a heading with a CUSTOM_ID property or an element with a
|
||||||
|
NAME."
|
||||||
(interactive "P\np")
|
(interactive "P\np")
|
||||||
(org-load-modules-maybe)
|
(org-load-modules-maybe)
|
||||||
(if (and (equal arg '(64)) (org-region-active-p))
|
(if (and (equal arg '(64)) (org-region-active-p))
|
||||||
|
@ -1557,36 +1743,19 @@ non-nil."
|
||||||
(move-beginning-of-line 2)
|
(move-beginning-of-line 2)
|
||||||
(set-mark (point)))))
|
(set-mark (point)))))
|
||||||
(setq org-store-link-plist nil)
|
(setq org-store-link-plist nil)
|
||||||
(let (link cpltxt desc search custom-id agenda-link) ;; description
|
;; Negate `org-context-in-file-links' when given a single universal arg.
|
||||||
|
(let ((org-link-context-for-files (org-xor org-link-context-for-files
|
||||||
|
(equal arg '(4))))
|
||||||
|
link cpltxt desc search agenda-link) ;; description
|
||||||
(cond
|
(cond
|
||||||
;; Store a link using an external link type, if any function is
|
;; Store a link using an external link type, if any function is
|
||||||
;; available. If more than one can generate a link from current
|
;; available, unless external link types are skipped for this
|
||||||
;; location, ask which one to use.
|
;; call using two universal args. If more than one function
|
||||||
|
;; can generate a link from current location, ask the user
|
||||||
|
;; which one to use.
|
||||||
((and (not (equal arg '(16)))
|
((and (not (equal arg '(16)))
|
||||||
(let ((results-alist nil))
|
(org-link--try-link-store-functions interactive?))
|
||||||
(dolist (f (org-store-link-functions))
|
(setq link (plist-get org-store-link-plist :link))
|
||||||
(when (funcall f)
|
|
||||||
;; XXX: return value is not link's plist, so we
|
|
||||||
;; store the new value before it is modified. It
|
|
||||||
;; would be cleaner to ask store link functions to
|
|
||||||
;; return the plist instead.
|
|
||||||
(push (cons f (copy-sequence org-store-link-plist))
|
|
||||||
results-alist)))
|
|
||||||
(pcase results-alist
|
|
||||||
(`nil nil)
|
|
||||||
(`((,_ . ,_)) t) ;single choice: nothing to do
|
|
||||||
(`((,name . ,_) . ,_)
|
|
||||||
;; Reinstate link plist associated to the chosen
|
|
||||||
;; function.
|
|
||||||
(apply #'org-link-store-props
|
|
||||||
(cdr (assoc-string
|
|
||||||
(completing-read
|
|
||||||
(format "Store link with (default %s): " name)
|
|
||||||
(mapcar #'car results-alist)
|
|
||||||
nil t nil nil (symbol-name name))
|
|
||||||
results-alist)))
|
|
||||||
t))))
|
|
||||||
(setq link (plist-get org-store-link-plist :link))
|
|
||||||
;; If store function actually set `:description' property, use
|
;; If store function actually set `:description' property, use
|
||||||
;; it, even if it is nil. Otherwise, fallback to nil (ask user).
|
;; it, even if it is nil. Otherwise, fallback to nil (ask user).
|
||||||
(setq desc (plist-get org-store-link-plist :description)))
|
(setq desc (plist-get org-store-link-plist :description)))
|
||||||
|
@ -1637,6 +1806,7 @@ non-nil."
|
||||||
(org-with-point-at m
|
(org-with-point-at m
|
||||||
(setq agenda-link (org-store-link nil interactive?))))))
|
(setq agenda-link (org-store-link nil interactive?))))))
|
||||||
|
|
||||||
|
;; Calendar mode
|
||||||
((eq major-mode 'calendar-mode)
|
((eq major-mode 'calendar-mode)
|
||||||
(let ((cd (calendar-cursor-to-date)))
|
(let ((cd (calendar-cursor-to-date)))
|
||||||
(setq link
|
(setq link
|
||||||
|
@ -1645,6 +1815,7 @@ non-nil."
|
||||||
(org-encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
|
(org-encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
|
||||||
(org-link-store-props :type "calendar" :date cd)))
|
(org-link-store-props :type "calendar" :date cd)))
|
||||||
|
|
||||||
|
;; Image mode
|
||||||
((eq major-mode 'image-mode)
|
((eq major-mode 'image-mode)
|
||||||
(setq cpltxt (concat "file:"
|
(setq cpltxt (concat "file:"
|
||||||
(abbreviate-file-name buffer-file-name))
|
(abbreviate-file-name buffer-file-name))
|
||||||
|
@ -1662,15 +1833,22 @@ non-nil."
|
||||||
(setq cpltxt (concat "file:" file)
|
(setq cpltxt (concat "file:" file)
|
||||||
link cpltxt)))
|
link cpltxt)))
|
||||||
|
|
||||||
|
;; Try `org-create-file-search-functions`. If any are
|
||||||
|
;; successful, create a file link to the current buffer with
|
||||||
|
;; the provided search string. (sets `link` and `cpltxt` to
|
||||||
|
;; the same thing; it looks like the intention originally was
|
||||||
|
;; that cpltxt was a description, which might have been set by
|
||||||
|
;; the search-function (removed in switch to lexical binding)).
|
||||||
((setq search (run-hook-with-args-until-success
|
((setq search (run-hook-with-args-until-success
|
||||||
'org-create-file-search-functions))
|
'org-create-file-search-functions))
|
||||||
(setq link (concat "file:" (abbreviate-file-name buffer-file-name)
|
(setq link (concat "file:" (abbreviate-file-name buffer-file-name)
|
||||||
"::" search))
|
"::" search))
|
||||||
(setq cpltxt (or link))) ;; description
|
(setq cpltxt (or link))) ;; description
|
||||||
|
|
||||||
|
;; Main logic for storing built-in link types in org-mode
|
||||||
|
;; buffers
|
||||||
((and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode))
|
((and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode))
|
||||||
(org-with-limited-levels
|
(org-with-limited-levels
|
||||||
(setq custom-id (org-entry-get nil "CUSTOM_ID"))
|
|
||||||
(cond
|
(cond
|
||||||
;; Store a link using the target at point
|
;; Store a link using the target at point
|
||||||
((org-in-regexp "[^<]<<\\([^<>]+\\)>>[^>]" 1)
|
((org-in-regexp "[^<]<<\\([^<>]+\\)>>[^>]" 1)
|
||||||
|
@ -1684,74 +1862,21 @@ non-nil."
|
||||||
;; links. Maybe the case of identical target and
|
;; links. Maybe the case of identical target and
|
||||||
;; description should be handled by `org-insert-link'.
|
;; description should be handled by `org-insert-link'.
|
||||||
cpltxt nil
|
cpltxt nil
|
||||||
desc nil
|
desc nil))
|
||||||
;; Do not append #CUSTOM_ID link below.
|
(t
|
||||||
custom-id nil))
|
|
||||||
((and (featurep 'org-id)
|
|
||||||
(or (eq org-id-link-to-org-use-id t)
|
|
||||||
(and interactive?
|
|
||||||
(or (eq org-id-link-to-org-use-id 'create-if-interactive)
|
|
||||||
(and (eq org-id-link-to-org-use-id
|
|
||||||
'create-if-interactive-and-no-custom-id)
|
|
||||||
(not custom-id))))
|
|
||||||
(and org-id-link-to-org-use-id (org-entry-get nil "ID"))))
|
|
||||||
;; Store a link using the ID at point
|
|
||||||
(setq link (condition-case nil
|
|
||||||
(prog1 (org-id-store-link)
|
|
||||||
(setq desc (plist-get org-store-link-plist :description)))
|
|
||||||
(error
|
|
||||||
;; Probably before first headline, link only to file
|
|
||||||
(concat "file:"
|
|
||||||
(abbreviate-file-name
|
|
||||||
(buffer-file-name (buffer-base-buffer))))))))
|
|
||||||
(t
|
|
||||||
;; Just link to current headline.
|
;; Just link to current headline.
|
||||||
(setq cpltxt (concat "file:"
|
(let ((here (org-link--file-link-to-here)))
|
||||||
(abbreviate-file-name
|
(setq cpltxt (car here))
|
||||||
(buffer-file-name (buffer-base-buffer)))))
|
(setq desc (cdr here)))
|
||||||
;; Add a context search string.
|
(setq link cpltxt)))))
|
||||||
(when (org-xor org-link-context-for-files (equal arg '(4)))
|
|
||||||
(let* ((element (org-element-at-point))
|
|
||||||
(name (org-element-property :name element))
|
|
||||||
(context
|
|
||||||
(cond
|
|
||||||
((let ((region (org-link--context-from-region)))
|
|
||||||
(and region (org-link--normalize-string region t))))
|
|
||||||
(name)
|
|
||||||
((org-before-first-heading-p)
|
|
||||||
(org-link--normalize-string (org-current-line-string) t))
|
|
||||||
(t (org-link-heading-search-string)))))
|
|
||||||
(when (org-string-nw-p context)
|
|
||||||
(setq cpltxt (format "%s::%s" cpltxt context))
|
|
||||||
(setq desc
|
|
||||||
(or name
|
|
||||||
;; Although description is not a search
|
|
||||||
;; string, use `org-link--normalize-string'
|
|
||||||
;; to prettify it (contiguous white spaces)
|
|
||||||
;; and remove volatile contents (statistics
|
|
||||||
;; cookies).
|
|
||||||
(and (not (org-before-first-heading-p))
|
|
||||||
(org-link--normalize-string
|
|
||||||
(org-get-heading t t t t)))
|
|
||||||
"NONE")))))
|
|
||||||
(setq link cpltxt)))))
|
|
||||||
|
|
||||||
|
;; Buffer linked to file, but not an org-mode buffer.
|
||||||
((buffer-file-name (buffer-base-buffer))
|
((buffer-file-name (buffer-base-buffer))
|
||||||
;; Just link to this file here.
|
;; Just link to this file here.
|
||||||
(setq cpltxt (concat "file:"
|
(let ((here (org-link--file-link-to-here)))
|
||||||
(abbreviate-file-name
|
(setq cpltxt (car here))
|
||||||
(buffer-file-name (buffer-base-buffer)))))
|
(setq desc (cdr here)))
|
||||||
;; Add a context search string.
|
(setq link cpltxt))
|
||||||
(when (org-xor org-link-context-for-files (equal arg '(4)))
|
|
||||||
(let ((context (org-link--normalize-string
|
|
||||||
(or (org-link--context-from-region)
|
|
||||||
(org-current-line-string))
|
|
||||||
t)))
|
|
||||||
;; Only use search option if there is some text.
|
|
||||||
(when (org-string-nw-p context)
|
|
||||||
(setq cpltxt (format "%s::%s" cpltxt context))
|
|
||||||
(setq desc "NONE"))))
|
|
||||||
(setq link cpltxt))
|
|
||||||
|
|
||||||
(interactive?
|
(interactive?
|
||||||
(user-error "No method for storing a link from this buffer"))
|
(user-error "No method for storing a link from this buffer"))
|
||||||
|
@ -1767,24 +1892,18 @@ non-nil."
|
||||||
;; Store and return the link
|
;; Store and return the link
|
||||||
(if (not (and interactive? link))
|
(if (not (and interactive? link))
|
||||||
(or agenda-link (and link (org-link-make-string link desc)))
|
(or agenda-link (and link (org-link-make-string link desc)))
|
||||||
(dotimes (_ (if custom-id 2 1)) ; Store 2 links when CUSTOM-ID is non-nil.
|
(org-link--add-to-stored-links link desc)
|
||||||
(cond
|
;; In org buffers, store an additional "human-readable" link
|
||||||
((not (member (list link desc) org-stored-links))
|
;; using custom id, if available.
|
||||||
(push (list link desc) org-stored-links)
|
(when (and (buffer-file-name (buffer-base-buffer))
|
||||||
(message "Stored: %s" (or desc link)))
|
(derived-mode-p 'org-mode)
|
||||||
((equal (list link desc) (car org-stored-links))
|
(org-entry-get nil "CUSTOM_ID"))
|
||||||
(message "This link has already been stored"))
|
(let ((here (org-link--file-link-to-here)))
|
||||||
(t
|
(setq link (car here))
|
||||||
(setq org-stored-links
|
(setq desc (cdr here)))
|
||||||
(delete (list link desc) org-stored-links))
|
(unless (equal (list link desc) (car org-stored-links))
|
||||||
(push (list link desc) org-stored-links)
|
(org-link--add-to-stored-links link desc)))
|
||||||
(message "Link moved to front: %s" (or desc link))))
|
(car org-stored-links)))))
|
||||||
(when custom-id
|
|
||||||
(setq link (concat "file:"
|
|
||||||
(abbreviate-file-name
|
|
||||||
(buffer-file-name (buffer-base-buffer)))
|
|
||||||
"::#" custom-id))))
|
|
||||||
(car org-stored-links)))))
|
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-insert-link (&optional complete-file link-location description)
|
(defun org-insert-link (&optional complete-file link-location description)
|
||||||
|
@ -2078,6 +2197,39 @@ This command can be called in any mode to insert a link in Org syntax."
|
||||||
(org-load-modules-maybe)
|
(org-load-modules-maybe)
|
||||||
(org-run-like-in-org-mode 'org-insert-link))
|
(org-run-like-in-org-mode 'org-insert-link))
|
||||||
|
|
||||||
|
(defun org--re-list-search-forward (regexp-list &optional bound noerror count)
|
||||||
|
"Like `re-search-forward', but REGEXP-LIST is a list of regexps.
|
||||||
|
BOUND, NOERROR, and COUNT are passed to `re-search-forward'."
|
||||||
|
(let (result (min-found most-positive-fixnum)
|
||||||
|
(pos-found nil)
|
||||||
|
(min-found-data nil)
|
||||||
|
(tail regexp-list))
|
||||||
|
(while tail
|
||||||
|
(setq result (save-excursion (re-search-forward (pop tail) bound t count)))
|
||||||
|
(when (and result (< result min-found))
|
||||||
|
(setq min-found result
|
||||||
|
pos-found (match-end 0)
|
||||||
|
min-found-data (match-data))))
|
||||||
|
(if (= most-positive-fixnum min-found)
|
||||||
|
(pcase noerror
|
||||||
|
(`t nil)
|
||||||
|
(_ (re-search-forward (car regexp-list) bound noerror count)))
|
||||||
|
(set-match-data min-found-data)
|
||||||
|
(goto-char pos-found))))
|
||||||
|
|
||||||
|
(defun org--re-list-looking-at (regexp-list &optional inhibit-modify)
|
||||||
|
"Like `looking-at', but REGEXP-LIST is a list of regexps.
|
||||||
|
INHIBIT-MODIFY is passed to `looking-at'."
|
||||||
|
(catch :found
|
||||||
|
(while regexp-list
|
||||||
|
(when
|
||||||
|
(if inhibit-modify
|
||||||
|
(looking-at-p (pop regexp-list))
|
||||||
|
;; FIXME: In Emacs <29, `looking-at' does not accept
|
||||||
|
;; optional INHIBIT-MODIFY argument.
|
||||||
|
(looking-at (pop regexp-list)))
|
||||||
|
(throw :found t)))))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-update-radio-target-regexp ()
|
(defun org-update-radio-target-regexp ()
|
||||||
"Find all radio targets in this file and update the regular expression.
|
"Find all radio targets in this file and update the regular expression.
|
||||||
|
@ -2115,6 +2267,30 @@ Also refresh fontification if needed."
|
||||||
targets
|
targets
|
||||||
"\\|")
|
"\\|")
|
||||||
after-re)))
|
after-re)))
|
||||||
|
(setq org-target-link-regexps nil)
|
||||||
|
(let (current-length sub-targets)
|
||||||
|
(when (<= org-target-link-regexp-limit (length org-target-link-regexp))
|
||||||
|
(while (or targets sub-targets)
|
||||||
|
(when (and sub-targets
|
||||||
|
(or (not targets)
|
||||||
|
(>= (+ current-length (length (car targets)))
|
||||||
|
org-target-link-regexp-limit)))
|
||||||
|
(push (concat before-re
|
||||||
|
(mapconcat
|
||||||
|
(lambda (x)
|
||||||
|
(replace-regexp-in-string
|
||||||
|
" +" "\\s-+" (regexp-quote x) t t))
|
||||||
|
(nreverse sub-targets)
|
||||||
|
"\\|")
|
||||||
|
after-re)
|
||||||
|
org-target-link-regexps)
|
||||||
|
(setq current-length nil
|
||||||
|
sub-targets nil))
|
||||||
|
(unless current-length
|
||||||
|
(setq current-length (+ (length before-re) (length after-re))))
|
||||||
|
(when targets (push (pop targets) sub-targets))
|
||||||
|
(cl-incf current-length (length (car sub-targets))))
|
||||||
|
(setq org-target-link-regexps (nreverse org-target-link-regexps))))
|
||||||
(unless (equal old-regexp org-target-link-regexp)
|
(unless (equal old-regexp org-target-link-regexp)
|
||||||
;; Clean-up cache.
|
;; Clean-up cache.
|
||||||
(let ((regexp (cond ((not old-regexp) org-target-link-regexp)
|
(let ((regexp (cond ((not old-regexp) org-target-link-regexp)
|
||||||
|
@ -2130,9 +2306,11 @@ Also refresh fontification if needed."
|
||||||
after-re)))))
|
after-re)))))
|
||||||
(when (and (featurep 'org-element)
|
(when (and (featurep 'org-element)
|
||||||
(not (bound-and-true-p org-mode-loading)))
|
(not (bound-and-true-p org-mode-loading)))
|
||||||
(org-with-point-at 1
|
(if org-target-link-regexps
|
||||||
(while (re-search-forward regexp nil t)
|
(org-element-cache-reset)
|
||||||
(org-element-cache-refresh (match-beginning 1))))))
|
(org-with-point-at 1
|
||||||
|
(while (re-search-forward regexp nil t)
|
||||||
|
(org-element-cache-refresh (match-beginning 1)))))))
|
||||||
;; Re fontify buffer.
|
;; Re fontify buffer.
|
||||||
(when (memq 'radio org-highlight-links)
|
(when (memq 'radio org-highlight-links)
|
||||||
(org-restart-font-lock)))))
|
(org-restart-font-lock)))))
|
||||||
|
|
|
@ -3148,169 +3148,172 @@ s Search for keywords S Like s, but only TODO entries
|
||||||
'(face bold) header))
|
'(face bold) header))
|
||||||
header)))
|
header)))
|
||||||
(setq header-end (point-marker))
|
(setq header-end (point-marker))
|
||||||
(while t
|
(unwind-protect
|
||||||
(setq custom1 custom)
|
(while t
|
||||||
(when (eq rmheader t)
|
(setq custom1 custom)
|
||||||
(org-goto-line 1)
|
(when (eq rmheader t)
|
||||||
(re-search-forward ":" nil t)
|
(org-goto-line 1)
|
||||||
(delete-region (match-end 0) (line-end-position))
|
(re-search-forward ":" nil t)
|
||||||
(forward-char 1)
|
(delete-region (match-end 0) (line-end-position))
|
||||||
(looking-at "-+")
|
(forward-char 1)
|
||||||
(delete-region (match-end 0) (line-end-position))
|
(looking-at "-+")
|
||||||
(move-marker header-end (match-end 0)))
|
(delete-region (match-end 0) (line-end-position))
|
||||||
(goto-char header-end)
|
(move-marker header-end (match-end 0)))
|
||||||
(delete-region (point) (point-max))
|
(goto-char header-end)
|
||||||
|
(delete-region (point) (point-max))
|
||||||
|
|
||||||
;; Produce all the lines that describe custom commands and prefixes
|
;; Produce all the lines that describe custom commands and prefixes
|
||||||
(setq lines nil)
|
(setq lines nil)
|
||||||
(while (setq entry (pop custom1))
|
(while (setq entry (pop custom1))
|
||||||
(setq key (car entry) desc (nth 1 entry)
|
(setq key (car entry) desc (nth 1 entry)
|
||||||
type (nth 2 entry)
|
type (nth 2 entry)
|
||||||
match (nth 3 entry))
|
match (nth 3 entry))
|
||||||
(if (> (length key) 1)
|
(if (> (length key) 1)
|
||||||
(cl-pushnew (string-to-char key) prefixes :test #'equal)
|
(cl-pushnew (string-to-char key) prefixes :test #'equal)
|
||||||
(setq line
|
(setq line
|
||||||
(format
|
(format
|
||||||
"%-4s%-14s"
|
"%-4s%-14s"
|
||||||
(org-add-props (copy-sequence key)
|
(org-add-props (copy-sequence key)
|
||||||
'(face bold))
|
'(face bold))
|
||||||
(cond
|
(cond
|
||||||
((string-match "\\S-" desc) desc)
|
((string-match "\\S-" desc) desc)
|
||||||
((eq type 'agenda) "Agenda for current week or day")
|
((eq type 'agenda) "Agenda for current week or day")
|
||||||
((eq type 'agenda*) "Appointments for current week or day")
|
((eq type 'agenda*) "Appointments for current week or day")
|
||||||
((eq type 'alltodo) "List of all TODO entries")
|
((eq type 'alltodo) "List of all TODO entries")
|
||||||
((eq type 'search) "Word search")
|
((eq type 'search) "Word search")
|
||||||
((eq type 'stuck) "List of stuck projects")
|
((eq type 'stuck) "List of stuck projects")
|
||||||
((eq type 'todo) "TODO keyword")
|
((eq type 'todo) "TODO keyword")
|
||||||
((eq type 'tags) "Tags query")
|
((eq type 'tags) "Tags query")
|
||||||
((eq type 'tags-todo) "Tags (TODO)")
|
((eq type 'tags-todo) "Tags (TODO)")
|
||||||
((eq type 'tags-tree) "Tags tree")
|
((eq type 'tags-tree) "Tags tree")
|
||||||
((eq type 'todo-tree) "TODO kwd tree")
|
((eq type 'todo-tree) "TODO kwd tree")
|
||||||
((eq type 'occur-tree) "Occur tree")
|
((eq type 'occur-tree) "Occur tree")
|
||||||
((functionp type) (if (symbolp type)
|
((functionp type) (if (symbolp type)
|
||||||
(symbol-name type)
|
(symbol-name type)
|
||||||
"Lambda expression"))
|
"Lambda expression"))
|
||||||
(t "???"))))
|
(t "???"))))
|
||||||
|
(cond
|
||||||
|
((not (org-string-nw-p match)) nil)
|
||||||
|
(org-agenda-menu-show-matcher
|
||||||
|
(setq line
|
||||||
|
(concat line ": "
|
||||||
|
(cond
|
||||||
|
((stringp match)
|
||||||
|
(propertize match 'face 'org-warning))
|
||||||
|
((listp type)
|
||||||
|
(format "set of %d commands" (length type)))))))
|
||||||
|
(t
|
||||||
|
(org-add-props line nil 'help-echo (concat "Matcher: " match))))
|
||||||
|
(push line lines)))
|
||||||
|
(setq lines (nreverse lines))
|
||||||
|
(when prefixes
|
||||||
|
(mapc (lambda (x)
|
||||||
|
(push
|
||||||
|
(format "%s %s"
|
||||||
|
(org-add-props (char-to-string x)
|
||||||
|
nil 'face 'bold)
|
||||||
|
(or (cdr (assoc (concat selstring
|
||||||
|
(char-to-string x))
|
||||||
|
prefix-descriptions))
|
||||||
|
"Prefix key"))
|
||||||
|
lines))
|
||||||
|
prefixes))
|
||||||
|
|
||||||
|
;; Check if we should display in two columns
|
||||||
|
(if org-agenda-menu-two-columns
|
||||||
|
(progn
|
||||||
|
(setq n (length lines)
|
||||||
|
n1 (+ (/ n 2) (mod n 2))
|
||||||
|
right (nthcdr n1 lines)
|
||||||
|
left (copy-sequence lines))
|
||||||
|
(setcdr (nthcdr (1- n1) left) nil))
|
||||||
|
(setq left lines right nil))
|
||||||
|
(while left
|
||||||
|
(insert "\n" (pop left))
|
||||||
|
(when right
|
||||||
|
(if (< (current-column) 40)
|
||||||
|
(move-to-column 40 t)
|
||||||
|
(insert " "))
|
||||||
|
(insert (pop right))))
|
||||||
|
|
||||||
|
;; Make the window the right size
|
||||||
|
(goto-char (point-min))
|
||||||
|
(if second-time
|
||||||
|
(when (not (pos-visible-in-window-p (point-max)))
|
||||||
|
(org-fit-window-to-buffer))
|
||||||
|
(setq second-time t)
|
||||||
|
(org-fit-window-to-buffer))
|
||||||
|
|
||||||
|
;; Hint to navigation if window too small for all information
|
||||||
|
(setq header-line-format
|
||||||
|
(when (not (pos-visible-in-window-p (point-max)))
|
||||||
|
"Use C-v, M-v, C-n or C-p to navigate."))
|
||||||
|
|
||||||
|
;; Ask for selection
|
||||||
|
(cl-loop
|
||||||
|
do (progn
|
||||||
|
(message "Press key for agenda command%s:"
|
||||||
|
(if (or restrict-ok org-agenda-overriding-restriction)
|
||||||
|
(if org-agenda-overriding-restriction
|
||||||
|
" (restriction lock active)"
|
||||||
|
(if restriction
|
||||||
|
(format " (restricted to %s)" restriction)
|
||||||
|
" (unrestricted)"))
|
||||||
|
""))
|
||||||
|
(setq c (read-char-exclusive)))
|
||||||
|
until (not (memq c '(14 16 22 134217846)))
|
||||||
|
do (org-scroll c))
|
||||||
|
|
||||||
|
(message "")
|
||||||
(cond
|
(cond
|
||||||
((not (org-string-nw-p match)) nil)
|
((assoc (char-to-string c) custom)
|
||||||
(org-agenda-menu-show-matcher
|
(setq selstring (concat selstring (char-to-string c)))
|
||||||
(setq line
|
(throw 'exit (cons selstring restriction)))
|
||||||
(concat line ": "
|
((memq c prefixes)
|
||||||
(cond
|
(setq selstring (concat selstring (char-to-string c))
|
||||||
((stringp match)
|
prefixes nil
|
||||||
(propertize match 'face 'org-warning))
|
rmheader (or rmheader t)
|
||||||
((listp type)
|
custom (delq nil (mapcar
|
||||||
(format "set of %d commands" (length type)))))))
|
(lambda (x)
|
||||||
(t
|
(if (or (= (length (car x)) 1)
|
||||||
(org-add-props line nil 'help-echo (concat "Matcher: " match))))
|
(/= (string-to-char (car x)) c))
|
||||||
(push line lines)))
|
nil
|
||||||
(setq lines (nreverse lines))
|
(cons (substring (car x) 1) (cdr x))))
|
||||||
(when prefixes
|
custom))))
|
||||||
(mapc (lambda (x)
|
((eq c ?*)
|
||||||
(push
|
(call-interactively 'org-toggle-sticky-agenda)
|
||||||
(format "%s %s"
|
(sit-for 2))
|
||||||
(org-add-props (char-to-string x)
|
((and (not restrict-ok) (memq c '(?1 ?0 ?<)))
|
||||||
nil 'face 'bold)
|
(message "Restriction is only possible in Org buffers")
|
||||||
(or (cdr (assoc (concat selstring
|
(ding) (sit-for 1))
|
||||||
(char-to-string x))
|
((eq c ?1)
|
||||||
prefix-descriptions))
|
(org-agenda-remove-restriction-lock 'noupdate)
|
||||||
"Prefix key"))
|
(setq restriction 'buffer))
|
||||||
lines))
|
((eq c ?0)
|
||||||
prefixes))
|
(org-agenda-remove-restriction-lock 'noupdate)
|
||||||
|
(setq restriction (if region-p 'region 'subtree)))
|
||||||
|
((eq c ?<)
|
||||||
|
(org-agenda-remove-restriction-lock 'noupdate)
|
||||||
|
(setq restriction
|
||||||
|
(cond
|
||||||
|
((eq restriction 'buffer)
|
||||||
|
(if region-p 'region 'subtree))
|
||||||
|
((memq restriction '(subtree region))
|
||||||
|
nil)
|
||||||
|
(t 'buffer))))
|
||||||
|
((eq c ?>)
|
||||||
|
(org-agenda-remove-restriction-lock 'noupdate)
|
||||||
|
(setq restriction nil))
|
||||||
|
((and (equal selstring "") (memq c '(?s ?S ?a ?t ?m ?L ?C ?e ?T ?M ?# ?! ?/ ??)))
|
||||||
|
(throw 'exit (cons (setq selstring (char-to-string c)) restriction)))
|
||||||
|
((and (> (length selstring) 0) (eq c ?\d))
|
||||||
|
(delete-window)
|
||||||
|
(org-agenda-get-restriction-and-command prefix-descriptions))
|
||||||
|
|
||||||
;; Check if we should display in two columns
|
((equal c ?q) (user-error "Abort"))
|
||||||
(if org-agenda-menu-two-columns
|
(t (user-error "Invalid key %c" c))))
|
||||||
(progn
|
;; Close *Agenda Commands* window.
|
||||||
(setq n (length lines)
|
(quit-window))))))
|
||||||
n1 (+ (/ n 2) (mod n 2))
|
|
||||||
right (nthcdr n1 lines)
|
|
||||||
left (copy-sequence lines))
|
|
||||||
(setcdr (nthcdr (1- n1) left) nil))
|
|
||||||
(setq left lines right nil))
|
|
||||||
(while left
|
|
||||||
(insert "\n" (pop left))
|
|
||||||
(when right
|
|
||||||
(if (< (current-column) 40)
|
|
||||||
(move-to-column 40 t)
|
|
||||||
(insert " "))
|
|
||||||
(insert (pop right))))
|
|
||||||
|
|
||||||
;; Make the window the right size
|
|
||||||
(goto-char (point-min))
|
|
||||||
(if second-time
|
|
||||||
(when (not (pos-visible-in-window-p (point-max)))
|
|
||||||
(org-fit-window-to-buffer))
|
|
||||||
(setq second-time t)
|
|
||||||
(org-fit-window-to-buffer))
|
|
||||||
|
|
||||||
;; Hint to navigation if window too small for all information
|
|
||||||
(setq header-line-format
|
|
||||||
(when (not (pos-visible-in-window-p (point-max)))
|
|
||||||
"Use C-v, M-v, C-n or C-p to navigate."))
|
|
||||||
|
|
||||||
;; Ask for selection
|
|
||||||
(cl-loop
|
|
||||||
do (progn
|
|
||||||
(message "Press key for agenda command%s:"
|
|
||||||
(if (or restrict-ok org-agenda-overriding-restriction)
|
|
||||||
(if org-agenda-overriding-restriction
|
|
||||||
" (restriction lock active)"
|
|
||||||
(if restriction
|
|
||||||
(format " (restricted to %s)" restriction)
|
|
||||||
" (unrestricted)"))
|
|
||||||
""))
|
|
||||||
(setq c (read-char-exclusive)))
|
|
||||||
until (not (memq c '(14 16 22 134217846)))
|
|
||||||
do (org-scroll c))
|
|
||||||
|
|
||||||
(message "")
|
|
||||||
(cond
|
|
||||||
((assoc (char-to-string c) custom)
|
|
||||||
(setq selstring (concat selstring (char-to-string c)))
|
|
||||||
(throw 'exit (cons selstring restriction)))
|
|
||||||
((memq c prefixes)
|
|
||||||
(setq selstring (concat selstring (char-to-string c))
|
|
||||||
prefixes nil
|
|
||||||
rmheader (or rmheader t)
|
|
||||||
custom (delq nil (mapcar
|
|
||||||
(lambda (x)
|
|
||||||
(if (or (= (length (car x)) 1)
|
|
||||||
(/= (string-to-char (car x)) c))
|
|
||||||
nil
|
|
||||||
(cons (substring (car x) 1) (cdr x))))
|
|
||||||
custom))))
|
|
||||||
((eq c ?*)
|
|
||||||
(call-interactively 'org-toggle-sticky-agenda)
|
|
||||||
(sit-for 2))
|
|
||||||
((and (not restrict-ok) (memq c '(?1 ?0 ?<)))
|
|
||||||
(message "Restriction is only possible in Org buffers")
|
|
||||||
(ding) (sit-for 1))
|
|
||||||
((eq c ?1)
|
|
||||||
(org-agenda-remove-restriction-lock 'noupdate)
|
|
||||||
(setq restriction 'buffer))
|
|
||||||
((eq c ?0)
|
|
||||||
(org-agenda-remove-restriction-lock 'noupdate)
|
|
||||||
(setq restriction (if region-p 'region 'subtree)))
|
|
||||||
((eq c ?<)
|
|
||||||
(org-agenda-remove-restriction-lock 'noupdate)
|
|
||||||
(setq restriction
|
|
||||||
(cond
|
|
||||||
((eq restriction 'buffer)
|
|
||||||
(if region-p 'region 'subtree))
|
|
||||||
((memq restriction '(subtree region))
|
|
||||||
nil)
|
|
||||||
(t 'buffer))))
|
|
||||||
((eq c ?>)
|
|
||||||
(org-agenda-remove-restriction-lock 'noupdate)
|
|
||||||
(setq restriction nil))
|
|
||||||
((and (equal selstring "") (memq c '(?s ?S ?a ?t ?m ?L ?C ?e ?T ?M ?# ?! ?/ ??)))
|
|
||||||
(throw 'exit (cons (setq selstring (char-to-string c)) restriction)))
|
|
||||||
((and (> (length selstring) 0) (eq c ?\d))
|
|
||||||
(delete-window)
|
|
||||||
(org-agenda-get-restriction-and-command prefix-descriptions))
|
|
||||||
|
|
||||||
((equal c ?q) (user-error "Abort"))
|
|
||||||
(t (user-error "Invalid key %c" c))))))))
|
|
||||||
|
|
||||||
(defun org-agenda-fit-window-to-buffer ()
|
(defun org-agenda-fit-window-to-buffer ()
|
||||||
"Fit the window to the buffer size."
|
"Fit the window to the buffer size."
|
||||||
|
@ -3638,11 +3641,12 @@ the agenda to write."
|
||||||
(goto-char p)
|
(goto-char p)
|
||||||
(setq m (get-text-property (point) 'org-hd-marker))
|
(setq m (get-text-property (point) 'org-hd-marker))
|
||||||
(when m
|
(when m
|
||||||
(push (with-current-buffer (marker-buffer m)
|
(cl-pushnew (with-current-buffer (marker-buffer m)
|
||||||
(goto-char m)
|
(goto-char m)
|
||||||
(org-copy-subtree 1 nil t t)
|
(org-copy-subtree 1 nil t t)
|
||||||
org-subtree-clip)
|
org-subtree-clip)
|
||||||
content)))
|
content
|
||||||
|
:test #'equal)))
|
||||||
(find-file file)
|
(find-file file)
|
||||||
(erase-buffer)
|
(erase-buffer)
|
||||||
(dolist (s content) (org-paste-subtree 1 s))
|
(dolist (s content) (org-paste-subtree 1 s))
|
||||||
|
@ -6399,14 +6403,14 @@ specification like [h]h:mm."
|
||||||
(org-agenda--timestamp-to-absolute
|
(org-agenda--timestamp-to-absolute
|
||||||
s base 'future (current-buffer) pos)))))
|
s base 'future (current-buffer) pos)))))
|
||||||
(diff (- deadline current))
|
(diff (- deadline current))
|
||||||
(suppress-prewarning
|
(max-warning-days
|
||||||
(let ((scheduled
|
(let ((scheduled
|
||||||
(and org-agenda-skip-deadline-prewarning-if-scheduled
|
(and org-agenda-skip-deadline-prewarning-if-scheduled
|
||||||
(org-element-property
|
(org-element-property
|
||||||
:raw-value
|
:raw-value
|
||||||
(org-element-property :scheduled el)))))
|
(org-element-property :scheduled el)))))
|
||||||
(cond
|
(cond
|
||||||
((not scheduled) nil)
|
((not scheduled) most-positive-fixnum)
|
||||||
;; The current item has a scheduled date, so
|
;; The current item has a scheduled date, so
|
||||||
;; evaluate its prewarning lead time.
|
;; evaluate its prewarning lead time.
|
||||||
((integerp org-agenda-skip-deadline-prewarning-if-scheduled)
|
((integerp org-agenda-skip-deadline-prewarning-if-scheduled)
|
||||||
|
@ -6420,15 +6424,15 @@ specification like [h]h:mm."
|
||||||
org-deadline-warning-days))
|
org-deadline-warning-days))
|
||||||
;; Set pre-warning to deadline.
|
;; Set pre-warning to deadline.
|
||||||
(t 0))))
|
(t 0))))
|
||||||
(wdays (or suppress-prewarning (org-get-wdays s))))
|
(warning-days (min max-warning-days (org-get-wdays s))))
|
||||||
(cond
|
(cond
|
||||||
;; Only display deadlines at their base date, at future
|
;; Only display deadlines at their base date, at future
|
||||||
;; repeat occurrences or in today agenda.
|
;; repeat occurrences or in today agenda.
|
||||||
((= current deadline) nil)
|
((= current deadline) nil)
|
||||||
((= current repeat) nil)
|
((= current repeat) nil)
|
||||||
((not today?) (throw :skip nil))
|
((not today?) (throw :skip nil))
|
||||||
;; Upcoming deadline: display within warning period WDAYS.
|
;; Upcoming deadline: display within warning period WARNING-DAYS.
|
||||||
((> deadline current) (when (> diff wdays) (throw :skip nil)))
|
((> deadline current) (when (> diff warning-days) (throw :skip nil)))
|
||||||
;; Overdue deadline: warn about it for
|
;; Overdue deadline: warn about it for
|
||||||
;; `org-deadline-past-days' duration.
|
;; `org-deadline-past-days' duration.
|
||||||
(t (when (< org-deadline-past-days (- diff)) (throw :skip nil))))
|
(t (when (< org-deadline-past-days (- diff)) (throw :skip nil))))
|
||||||
|
@ -6481,7 +6485,7 @@ specification like [h]h:mm."
|
||||||
'effort-minutes effort-minutes)
|
'effort-minutes effort-minutes)
|
||||||
level category tags time))
|
level category tags time))
|
||||||
(face (org-agenda-deadline-face
|
(face (org-agenda-deadline-face
|
||||||
(- 1 (/ (float diff) (max wdays 1)))))
|
(- 1 (/ (float diff) (max warning-days 1)))))
|
||||||
(upcoming? (and today? (> deadline today)))
|
(upcoming? (and today? (> deadline today)))
|
||||||
(warntime (org-entry-get (point) "APPT_WARNTIME" 'selective)))
|
(warntime (org-entry-get (point) "APPT_WARNTIME" 'selective)))
|
||||||
(org-add-props item props
|
(org-add-props item props
|
||||||
|
@ -6610,13 +6614,13 @@ scheduled items with an hour specification like [h]h:mm."
|
||||||
(futureschedp (> schedule today))
|
(futureschedp (> schedule today))
|
||||||
(habitp (and (fboundp 'org-is-habit-p)
|
(habitp (and (fboundp 'org-is-habit-p)
|
||||||
(string= "habit" (org-element-property :STYLE el))))
|
(string= "habit" (org-element-property :STYLE el))))
|
||||||
(suppress-delay
|
(max-delay-days
|
||||||
(let ((deadline (and org-agenda-skip-scheduled-delay-if-deadline
|
(let ((deadline (and org-agenda-skip-scheduled-delay-if-deadline
|
||||||
(org-element-property
|
(org-element-property
|
||||||
:raw-value
|
:raw-value
|
||||||
(org-element-property :deadline el)))))
|
(org-element-property :deadline el)))))
|
||||||
(cond
|
(cond
|
||||||
((not deadline) nil)
|
((not deadline) most-positive-fixnum)
|
||||||
;; The current item has a deadline date, so
|
;; The current item has a deadline date, so
|
||||||
;; evaluate its delay time.
|
;; evaluate its delay time.
|
||||||
((integerp org-agenda-skip-scheduled-delay-if-deadline)
|
((integerp org-agenda-skip-scheduled-delay-if-deadline)
|
||||||
|
@ -6629,17 +6633,14 @@ scheduled items with an hour specification like [h]h:mm."
|
||||||
(org-agenda--timestamp-to-absolute deadline))
|
(org-agenda--timestamp-to-absolute deadline))
|
||||||
org-scheduled-delay-days))
|
org-scheduled-delay-days))
|
||||||
(t 0))))
|
(t 0))))
|
||||||
(ddays
|
(delay-days
|
||||||
(cond
|
(cond
|
||||||
;; Nullify delay when a repeater triggered already
|
;; Nullify delay when a repeater triggered already
|
||||||
;; and the delay is of the form --Xd.
|
;; and the delay is of the form --Xd.
|
||||||
((and (string-match-p "--[0-9]+[hdwmy]" s)
|
((and (string-match-p "--[0-9]+[hdwmy]" s)
|
||||||
(> schedule (org-agenda--timestamp-to-absolute s)))
|
(> schedule (org-agenda--timestamp-to-absolute s)))
|
||||||
0)
|
0)
|
||||||
(suppress-delay
|
(t (min max-delay-days (org-get-wdays s t))))))
|
||||||
(let ((org-scheduled-delay-days suppress-delay))
|
|
||||||
(org-get-wdays s t t)))
|
|
||||||
(t (org-get-wdays s t)))))
|
|
||||||
;; Display scheduled items at base date (SCHEDULE), today if
|
;; Display scheduled items at base date (SCHEDULE), today if
|
||||||
;; scheduled before the current date, and at any repeat past
|
;; scheduled before the current date, and at any repeat past
|
||||||
;; today. However, skip delayed items and items that have
|
;; today. However, skip delayed items and items that have
|
||||||
|
@ -6647,7 +6648,7 @@ scheduled items with an hour specification like [h]h:mm."
|
||||||
(unless (and todayp
|
(unless (and todayp
|
||||||
habitp
|
habitp
|
||||||
(bound-and-true-p org-habit-show-all-today))
|
(bound-and-true-p org-habit-show-all-today))
|
||||||
(when (or (and (> ddays 0) (< diff ddays))
|
(when (or (and (> delay-days 0) (< diff delay-days))
|
||||||
(> diff (or (and habitp org-habit-scheduled-past-days)
|
(> diff (or (and habitp org-habit-scheduled-past-days)
|
||||||
org-scheduled-past-days))
|
org-scheduled-past-days))
|
||||||
(> schedule current)
|
(> schedule current)
|
||||||
|
@ -6966,6 +6967,7 @@ Any match of REMOVE-RE will be removed from TXT."
|
||||||
(file-name-sans-extension
|
(file-name-sans-extension
|
||||||
(file-name-nondirectory buffer-file-name))
|
(file-name-nondirectory buffer-file-name))
|
||||||
"")))
|
"")))
|
||||||
|
(full-category category)
|
||||||
(category-icon (org-agenda-get-category-icon category))
|
(category-icon (org-agenda-get-category-icon category))
|
||||||
(category-icon (if category-icon
|
(category-icon (if category-icon
|
||||||
(propertize " " 'display category-icon)
|
(propertize " " 'display category-icon)
|
||||||
|
@ -7093,7 +7095,9 @@ Any match of REMOVE-RE will be removed from TXT."
|
||||||
;; And finally add the text properties
|
;; And finally add the text properties
|
||||||
(remove-text-properties 0 (length rtn) '(line-prefix t wrap-prefix t) rtn)
|
(remove-text-properties 0 (length rtn) '(line-prefix t wrap-prefix t) rtn)
|
||||||
(org-add-props rtn nil
|
(org-add-props rtn nil
|
||||||
'org-category category
|
;; CATEGORY might be truncated. Store the full category in
|
||||||
|
;; the properties.
|
||||||
|
'org-category full-category
|
||||||
'tags tags
|
'tags tags
|
||||||
'org-priority-highest org-priority-highest
|
'org-priority-highest org-priority-highest
|
||||||
'org-priority-lowest org-priority-lowest
|
'org-priority-lowest org-priority-lowest
|
||||||
|
|
|
@ -157,7 +157,7 @@ archive location, but not yet deleted from the original file.")
|
||||||
"Splice the archive FILES into the list of files.
|
"Splice the archive FILES into the list of files.
|
||||||
This implies visiting all these files and finding out what the
|
This implies visiting all these files and finding out what the
|
||||||
archive file is."
|
archive file is."
|
||||||
(org-uniquify
|
(seq-uniq
|
||||||
(apply
|
(apply
|
||||||
'append
|
'append
|
||||||
(mapcar
|
(mapcar
|
||||||
|
@ -166,7 +166,9 @@ archive file is."
|
||||||
nil
|
nil
|
||||||
(with-current-buffer (org-get-agenda-file-buffer f)
|
(with-current-buffer (org-get-agenda-file-buffer f)
|
||||||
(cons f (org-all-archive-files)))))
|
(cons f (org-all-archive-files)))))
|
||||||
files))))
|
files))
|
||||||
|
#'file-equal-p
|
||||||
|
))
|
||||||
|
|
||||||
(defun org-all-archive-files ()
|
(defun org-all-archive-files ()
|
||||||
"List of all archive files used in the current buffer."
|
"List of all archive files used in the current buffer."
|
||||||
|
|
|
@ -995,7 +995,7 @@ Store them in the capture property list."
|
||||||
((or `here
|
((or `here
|
||||||
`(here))
|
`(here))
|
||||||
(org-capture-put :exact-position (point) :insert-here t))
|
(org-capture-put :exact-position (point) :insert-here t))
|
||||||
(`(file ,(and path (pred stringp)))
|
(`(file ,path)
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(org-capture-put-target-region-and-position)
|
(org-capture-put-target-region-and-position)
|
||||||
(widen)
|
(widen)
|
||||||
|
@ -1008,7 +1008,7 @@ Store them in the capture property list."
|
||||||
(org-capture-put-target-region-and-position)
|
(org-capture-put-target-region-and-position)
|
||||||
(goto-char position))
|
(goto-char position))
|
||||||
(_ (error "Cannot find target ID \"%s\"" id))))
|
(_ (error "Cannot find target ID \"%s\"" id))))
|
||||||
(`(file+headline ,(and path (pred stringp)) ,(and headline (pred stringp)))
|
(`(file+headline ,path ,(and headline (pred stringp)))
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
;; Org expects the target file to be in Org mode, otherwise
|
;; Org expects the target file to be in Org mode, otherwise
|
||||||
;; it throws an error. However, the default notes files
|
;; it throws an error. However, the default notes files
|
||||||
|
@ -1030,7 +1030,7 @@ Store them in the capture property list."
|
||||||
(unless (bolp) (insert "\n"))
|
(unless (bolp) (insert "\n"))
|
||||||
(insert "* " headline "\n")
|
(insert "* " headline "\n")
|
||||||
(forward-line -1)))
|
(forward-line -1)))
|
||||||
(`(file+olp ,(and path (pred stringp)) . ,(and outline-path (guard outline-path)))
|
(`(file+olp ,path . ,(and outline-path (guard outline-path)))
|
||||||
(let ((m (org-find-olp (cons (org-capture-expand-file path)
|
(let ((m (org-find-olp (cons (org-capture-expand-file path)
|
||||||
outline-path))))
|
outline-path))))
|
||||||
(set-buffer (marker-buffer m))
|
(set-buffer (marker-buffer m))
|
||||||
|
@ -1038,7 +1038,7 @@ Store them in the capture property list."
|
||||||
(widen)
|
(widen)
|
||||||
(goto-char m)
|
(goto-char m)
|
||||||
(set-marker m nil)))
|
(set-marker m nil)))
|
||||||
(`(file+regexp ,(and path (pred stringp)) ,(and regexp (pred stringp)))
|
(`(file+regexp ,path ,(and regexp (pred stringp)))
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(org-capture-put-target-region-and-position)
|
(org-capture-put-target-region-and-position)
|
||||||
(widen)
|
(widen)
|
||||||
|
@ -1051,7 +1051,7 @@ Store them in the capture property list."
|
||||||
(org-capture-put :exact-position (point))
|
(org-capture-put :exact-position (point))
|
||||||
(setq target-entry-p
|
(setq target-entry-p
|
||||||
(and (derived-mode-p 'org-mode) (org-at-heading-p)))))
|
(and (derived-mode-p 'org-mode) (org-at-heading-p)))))
|
||||||
(`(file+olp+datetree ,(and path (pred stringp)) . ,outline-path)
|
(`(file+olp+datetree ,path . ,outline-path)
|
||||||
(let ((m (if outline-path
|
(let ((m (if outline-path
|
||||||
(org-find-olp (cons (org-capture-expand-file path)
|
(org-find-olp (cons (org-capture-expand-file path)
|
||||||
outline-path))
|
outline-path))
|
||||||
|
@ -1106,7 +1106,7 @@ Store them in the capture property list."
|
||||||
;; the following is the keep-restriction argument for
|
;; the following is the keep-restriction argument for
|
||||||
;; org-datetree-find-date-create
|
;; org-datetree-find-date-create
|
||||||
(when outline-path 'subtree-at-point))))
|
(when outline-path 'subtree-at-point))))
|
||||||
(`(file+function ,(and path (pred stringp)) ,(and function (pred functionp)))
|
(`(file+function ,path ,(and function (pred functionp)))
|
||||||
(set-buffer (org-capture-target-buffer path))
|
(set-buffer (org-capture-target-buffer path))
|
||||||
(org-capture-put-target-region-and-position)
|
(org-capture-put-target-region-and-position)
|
||||||
(widen)
|
(widen)
|
||||||
|
@ -1508,7 +1508,7 @@ Of course, if exact position has been required, just put it there."
|
||||||
(condition-case err
|
(condition-case err
|
||||||
(bookmark-set bookmark)
|
(bookmark-set bookmark)
|
||||||
(error
|
(error
|
||||||
(message (format "Bookmark set error: %S" err)))))))
|
(message "Bookmark set error: %S" err))))))
|
||||||
(move-marker org-capture-last-stored-marker (point))))))
|
(move-marker org-capture-last-stored-marker (point))))))
|
||||||
|
|
||||||
(defun org-capture-narrow (beg end)
|
(defun org-capture-narrow (beg end)
|
||||||
|
|
|
@ -1253,9 +1253,9 @@ properties drawers."
|
||||||
;; property `org-summaries', in alist whose key is SPEC.
|
;; property `org-summaries', in alist whose key is SPEC.
|
||||||
(let* ((summary
|
(let* ((summary
|
||||||
(and summarize
|
(and summarize
|
||||||
(let ((values (append (and (/= last-level inminlevel)
|
(let ((values
|
||||||
(aref lvals last-level))
|
(cl-loop for l from (1+ level) to lmax
|
||||||
(aref lvals inminlevel))))
|
append (aref lvals l))))
|
||||||
(and values (funcall summarize values printf))))))
|
(and values (funcall summarize values printf))))))
|
||||||
;; Leaf values are not summaries: do not mark them.
|
;; Leaf values are not summaries: do not mark them.
|
||||||
(when summary
|
(when summary
|
||||||
|
|
|
@ -232,6 +232,18 @@ removed."
|
||||||
default)))
|
default)))
|
||||||
": ")))
|
": ")))
|
||||||
|
|
||||||
|
(if (fboundp 'ensure-list)
|
||||||
|
(defalias 'org-ensure-list #'ensure-list)
|
||||||
|
(defun org-ensure-list (object)
|
||||||
|
"Return OBJECT as a list.
|
||||||
|
If OBJECT is already a list, return OBJECT itself. If it's
|
||||||
|
not a list, return a one-element list containing OBJECT.
|
||||||
|
|
||||||
|
Compatability substitute for `ensure-list' in Emacs 28."
|
||||||
|
(if (listp object)
|
||||||
|
object
|
||||||
|
(list object))))
|
||||||
|
|
||||||
|
|
||||||
;;; Emacs < 27.1 compatibility
|
;;; Emacs < 27.1 compatibility
|
||||||
|
|
||||||
|
@ -655,6 +667,393 @@ This constant, for example, makes the below code not err:
|
||||||
"use `org-element' library"
|
"use `org-element' library"
|
||||||
"9.0")
|
"9.0")
|
||||||
|
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-format-latex-options 'org-latex-preview-appearance-options "9.7")
|
||||||
|
(make-obsolete-variable
|
||||||
|
'org-format-latex-signal-error "no longer used" "9.7")
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-format-latex-header 'org-latex-preview-preamble "9.7")
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-preview-latex-default-process 'org-latex-preview-process-default "9.7")
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-preview-latex-process-alist 'org-latex-preview-process-alist "9.7")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-clear-latex-preview 'org-latex-preview-clear-overlays "9.7")
|
||||||
|
(make-obsolete
|
||||||
|
'org-place-formula-image "no longer used" "9.7")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-latex-color-format 'org-latex-preview--format-color "9.7")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-latex-color 'org-latex-preview--attr-color "9.7")
|
||||||
|
;; MathML related functions from org-latex-preview.el
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-latex-to-mathml-jar-file 'org-mathml-converter-jar-file "9.7")
|
||||||
|
(define-obsolete-variable-alias
|
||||||
|
'org-latex-to-mathml-convert-command 'org-mathml-convert-command "9.7")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-format-latex-mathml-available-p 'org-mathml-converter-available-p "9.7")
|
||||||
|
(define-obsolete-function-alias
|
||||||
|
'org-create-math-formula 'org-mathml-convert-latex "9.7")
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-format-latex-as-mathml (latex-frag latex-frag-type
|
||||||
|
prefix &optional dir)
|
||||||
|
(let* ((absprefix (expand-file-name prefix dir))
|
||||||
|
(print-length nil) (print-level nil)
|
||||||
|
(formula-id (concat
|
||||||
|
"formula-"
|
||||||
|
(sha1
|
||||||
|
(prin1-to-string
|
||||||
|
(list latex-frag
|
||||||
|
org-latex-to-mathml-convert-command)))))
|
||||||
|
(formula-cache (format "%s-%s.mathml" absprefix formula-id))
|
||||||
|
(formula-cache-dir (file-name-directory formula-cache)))
|
||||||
|
(unless (file-directory-p formula-cache-dir)
|
||||||
|
(make-directory formula-cache-dir t))
|
||||||
|
(unless (file-exists-p formula-cache)
|
||||||
|
(org-mathml-convert-latex latex-frag formula-cache))
|
||||||
|
(if (file-exists-p formula-cache)
|
||||||
|
;; Successful conversion. Return the link to MathML file.
|
||||||
|
(org-add-props
|
||||||
|
(format "[[file:%s]]" (file-relative-name formula-cache dir))
|
||||||
|
(list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
|
||||||
|
'org-latex-src-embed-type (if latex-frag-type
|
||||||
|
'paragraph 'character)))
|
||||||
|
;; Failed conversion. Return the LaTeX fragment verbatim
|
||||||
|
latex-frag)))
|
||||||
|
(make-obsolete #'org-format-latex-as-mathml "to be removed" "9.7")
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-dvipng-color (attr)
|
||||||
|
"Return a RGB color specification for dvipng."
|
||||||
|
(org-dvipng-color-format (face-attribute 'default attr nil)))
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-dvipng-color-format (color-name)
|
||||||
|
"Convert COLOR-NAME to a RGB color value for dvipng."
|
||||||
|
(apply #'format "rgb %s %s %s"
|
||||||
|
(mapcar 'org-latex-preview--normalize-color
|
||||||
|
(color-values color-name))))
|
||||||
|
(make-obsolete
|
||||||
|
'org-dvipng-color "to be removed" "9.7")
|
||||||
|
(make-obsolete
|
||||||
|
'org-dvipng-color-format "to be removed" "9.7")
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-normalize-color (value)
|
||||||
|
"Return string to be used as color value for an RGB component."
|
||||||
|
(format "%g" (/ value 65535.0)))
|
||||||
|
(make-obsolete 'org-normalize-color "to be removed" "9.7")
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defcustom org-preview-latex-image-directory "ltximg/"
|
||||||
|
"Path to store latex preview images.
|
||||||
|
A relative path here creates many directories relative to the
|
||||||
|
processed Org files paths. An absolute path puts all preview
|
||||||
|
images at the same place."
|
||||||
|
:group 'org-latex
|
||||||
|
:version "26.1"
|
||||||
|
:package-version '(Org . "9.0")
|
||||||
|
:type 'string)
|
||||||
|
|
||||||
|
(make-obsolete-variable
|
||||||
|
'org-preview-latex-image-directory "replaced with org-persist" "9.7")
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-format-latex
|
||||||
|
(prefix &optional beg end dir overlays msg forbuffer processing-type)
|
||||||
|
"Replace LaTeX fragments with links to an image.
|
||||||
|
|
||||||
|
The function takes care of creating the replacement image.
|
||||||
|
|
||||||
|
Only consider fragments between BEG and END when those are
|
||||||
|
provided.
|
||||||
|
|
||||||
|
When optional argument OVERLAYS is non-nil, display the image on
|
||||||
|
top of the fragment instead of replacing it.
|
||||||
|
|
||||||
|
PROCESSING-TYPE is the conversion method to use, as a symbol.
|
||||||
|
|
||||||
|
Some of the options can be changed using the variable
|
||||||
|
`org-format-latex-options', which see."
|
||||||
|
(when (and overlays (fboundp 'clear-image-cache)) (clear-image-cache))
|
||||||
|
(unless (eq processing-type 'verbatim)
|
||||||
|
(let* ((math-regexp "\\$\\|\\\\[([]\\|^[ \t]*\\\\begin{[A-Za-z0-9*]+}")
|
||||||
|
(cnt 0)
|
||||||
|
checkdir-flag)
|
||||||
|
(goto-char (or beg (point-min)))
|
||||||
|
;; Optimize overlay creation: (info "(elisp) Managing Overlays").
|
||||||
|
(when (and overlays (memq processing-type '(dvipng imagemagick)))
|
||||||
|
(overlay-recenter (or end (point-max))))
|
||||||
|
(while (re-search-forward math-regexp end t)
|
||||||
|
(unless (and overlays
|
||||||
|
(eq (get-char-property (point) 'org-overlay-type)
|
||||||
|
'org-latex-overlay))
|
||||||
|
(let* ((context (org-element-context))
|
||||||
|
(type (org-element-type context)))
|
||||||
|
(when (memq type '(latex-environment latex-fragment))
|
||||||
|
(let ((block-type (eq type 'latex-environment))
|
||||||
|
(value (org-element-property :value context))
|
||||||
|
(beg (org-element-property :begin context))
|
||||||
|
(end (save-excursion
|
||||||
|
(goto-char (org-element-property :end context))
|
||||||
|
(skip-chars-backward " \r\t\n")
|
||||||
|
(point))))
|
||||||
|
(cond
|
||||||
|
((eq processing-type 'mathjax)
|
||||||
|
;; Prepare for MathJax processing.
|
||||||
|
(if (not (string-match "\\`\\$\\$?" value))
|
||||||
|
(goto-char end)
|
||||||
|
(delete-region beg end)
|
||||||
|
(if (string= (match-string 0 value) "$$")
|
||||||
|
(insert "\\[" (substring value 2 -2) "\\]")
|
||||||
|
(insert "\\(" (substring value 1 -1) "\\)"))))
|
||||||
|
((eq processing-type 'html)
|
||||||
|
(goto-char beg)
|
||||||
|
(delete-region beg end)
|
||||||
|
(insert (org-format-latex-as-html value)))
|
||||||
|
((assq processing-type org-preview-latex-process-alist)
|
||||||
|
;; Process to an image.
|
||||||
|
(cl-incf cnt)
|
||||||
|
(goto-char beg)
|
||||||
|
(let* ((processing-info
|
||||||
|
(cdr (assq processing-type org-preview-latex-process-alist)))
|
||||||
|
(face (face-at-point))
|
||||||
|
;; Get the colors from the face at point.
|
||||||
|
(fg
|
||||||
|
(let ((color (plist-get org-format-latex-options
|
||||||
|
:foreground)))
|
||||||
|
(if forbuffer
|
||||||
|
(cond
|
||||||
|
((eq color 'auto)
|
||||||
|
(face-attribute face :foreground nil 'default))
|
||||||
|
((eq color 'default)
|
||||||
|
(face-attribute 'default :foreground nil))
|
||||||
|
(t color))
|
||||||
|
color)))
|
||||||
|
(bg
|
||||||
|
(let ((color (plist-get org-format-latex-options
|
||||||
|
:background)))
|
||||||
|
(if forbuffer
|
||||||
|
(cond
|
||||||
|
((eq color 'auto)
|
||||||
|
(face-attribute face :background nil 'default))
|
||||||
|
((eq color 'default)
|
||||||
|
(face-attribute 'default :background nil))
|
||||||
|
(t color))
|
||||||
|
color)))
|
||||||
|
(hash (sha1 (prin1-to-string
|
||||||
|
(list org-format-latex-header
|
||||||
|
org-latex-default-packages-alist
|
||||||
|
org-latex-packages-alist
|
||||||
|
org-format-latex-options
|
||||||
|
forbuffer value fg bg))))
|
||||||
|
(imagetype (or (plist-get processing-info :image-output-type) "png"))
|
||||||
|
(absprefix (expand-file-name prefix dir))
|
||||||
|
(linkfile (format "%s_%s.%s" prefix hash imagetype))
|
||||||
|
(movefile (format "%s_%s.%s" absprefix hash imagetype))
|
||||||
|
(sep (and block-type "\n\n"))
|
||||||
|
(link (concat sep "[[file:" linkfile "]]" sep))
|
||||||
|
(options
|
||||||
|
(org-combine-plists
|
||||||
|
org-format-latex-options
|
||||||
|
`(:foreground ,fg :background ,bg))))
|
||||||
|
(when msg (message msg cnt))
|
||||||
|
(unless checkdir-flag ; Ensure the directory exists.
|
||||||
|
(setq checkdir-flag t)
|
||||||
|
(let ((todir (file-name-directory absprefix)))
|
||||||
|
(unless (file-directory-p todir)
|
||||||
|
(make-directory todir t))))
|
||||||
|
(unless (file-exists-p movefile)
|
||||||
|
(org-create-formula-image
|
||||||
|
value movefile options forbuffer processing-type))
|
||||||
|
(org-place-formula-image link block-type beg end value overlays movefile imagetype)))
|
||||||
|
((eq processing-type 'mathml)
|
||||||
|
;; Process to MathML.
|
||||||
|
(unless (org-format-latex-mathml-available-p)
|
||||||
|
(user-error "LaTeX to MathML converter not configured"))
|
||||||
|
(cl-incf cnt)
|
||||||
|
(when msg (message msg cnt))
|
||||||
|
(goto-char beg)
|
||||||
|
(delete-region beg end)
|
||||||
|
(insert (org-format-latex-as-mathml
|
||||||
|
value block-type prefix dir)))
|
||||||
|
(t
|
||||||
|
(error "Unknown conversion process %s for LaTeX fragments"
|
||||||
|
processing-type)))))))))))
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-place-formula-image (link block-type beg end value overlays movefile imagetype)
|
||||||
|
"Place an overlay from BEG to END showing MOVEFILE.
|
||||||
|
The overlay will be above BEG if OVERLAYS is non-nil."
|
||||||
|
(if overlays
|
||||||
|
(progn
|
||||||
|
(dolist (o (overlays-in beg end))
|
||||||
|
(when (eq (overlay-get o 'org-overlay-type)
|
||||||
|
'org-latex-overlay)
|
||||||
|
(delete-overlay o)))
|
||||||
|
(let ((ov (make-overlay beg end))
|
||||||
|
(imagetype (or (intern imagetype) 'png)))
|
||||||
|
(overlay-put ov 'org-overlay-type 'org-latex-overlay)
|
||||||
|
(overlay-put ov 'evaporate t)
|
||||||
|
(overlay-put ov
|
||||||
|
'modification-hooks
|
||||||
|
(list (lambda (o _flag _beg _end &optional _l)
|
||||||
|
(delete-overlay o))))
|
||||||
|
(overlay-put ov
|
||||||
|
'display
|
||||||
|
(list 'image :type imagetype :file movefile :ascent 'center)))
|
||||||
|
(goto-char end))
|
||||||
|
(delete-region beg end)
|
||||||
|
(insert
|
||||||
|
(org-add-props link
|
||||||
|
(list 'org-latex-src
|
||||||
|
(replace-regexp-in-string "\"" "" value)
|
||||||
|
'org-latex-src-embed-type
|
||||||
|
(if block-type 'paragraph 'character))))))
|
||||||
|
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-create-formula-image
|
||||||
|
(string tofile options buffer &optional processing-type)
|
||||||
|
"Create an image from LaTeX source using external processes.
|
||||||
|
|
||||||
|
The LaTeX STRING is saved to a temporary LaTeX file, then
|
||||||
|
converted to an image file by process PROCESSING-TYPE defined in
|
||||||
|
`org-preview-latex-process-alist'. A nil value defaults to
|
||||||
|
`org-preview-latex-default-process'.
|
||||||
|
|
||||||
|
The generated image file is eventually moved to TOFILE.
|
||||||
|
|
||||||
|
The OPTIONS argument controls the size, foreground color and
|
||||||
|
background color of the generated image.
|
||||||
|
|
||||||
|
When BUFFER non-nil, this function is used for LaTeX previewing.
|
||||||
|
Otherwise, it is used to deal with LaTeX snippets showed in
|
||||||
|
a HTML file."
|
||||||
|
(let* ((processing-type (or processing-type
|
||||||
|
org-preview-latex-default-process))
|
||||||
|
(processing-info
|
||||||
|
(cdr (assq processing-type org-preview-latex-process-alist)))
|
||||||
|
(programs (plist-get processing-info :programs))
|
||||||
|
(error-message (or (plist-get processing-info :message) ""))
|
||||||
|
(image-input-type (plist-get processing-info :image-input-type))
|
||||||
|
(image-output-type (plist-get processing-info :image-output-type))
|
||||||
|
(post-clean (or (plist-get processing-info :post-clean)
|
||||||
|
'(".dvi" ".xdv" ".pdf" ".tex" ".aux" ".log"
|
||||||
|
".svg" ".png" ".jpg" ".jpeg" ".out")))
|
||||||
|
(latex-header
|
||||||
|
(or (plist-get processing-info :latex-header)
|
||||||
|
(org-latex-make-preamble
|
||||||
|
(org-export-get-environment (org-export-get-backend 'latex))
|
||||||
|
org-format-latex-header
|
||||||
|
'snippet)))
|
||||||
|
(latex-compiler (plist-get processing-info :latex-compiler))
|
||||||
|
(tmpdir temporary-file-directory)
|
||||||
|
(texfilebase (make-temp-name
|
||||||
|
(expand-file-name "orgtex" tmpdir)))
|
||||||
|
(texfile (concat texfilebase ".tex"))
|
||||||
|
(image-size-adjust (or (plist-get processing-info :image-size-adjust)
|
||||||
|
'(1.0 . 1.0)))
|
||||||
|
(scale (* (if buffer (car image-size-adjust) (cdr image-size-adjust))
|
||||||
|
(or (plist-get options (if buffer :scale :html-scale)) 1.0)))
|
||||||
|
(dpi (* scale (if (and buffer (display-graphic-p)) (org--get-display-dpi) 140.0)))
|
||||||
|
(fg (or (plist-get options (if buffer :foreground :html-foreground))
|
||||||
|
"Black"))
|
||||||
|
(bg (or (plist-get options (if buffer :background :html-background))
|
||||||
|
"Transparent"))
|
||||||
|
(image-converter
|
||||||
|
(or (and (string= bg "Transparent")
|
||||||
|
(plist-get processing-info :transparent-image-converter))
|
||||||
|
(plist-get processing-info :image-converter)))
|
||||||
|
(log-buf (get-buffer-create "*Org Preview LaTeX Output*"))
|
||||||
|
(resize-mini-windows nil)) ;Fix Emacs flicker when creating image.
|
||||||
|
(dolist (program programs)
|
||||||
|
(org-check-external-command program error-message))
|
||||||
|
(if (eq fg 'default)
|
||||||
|
(setq fg (org-latex-color :foreground))
|
||||||
|
(setq fg (org-latex-color-format fg)))
|
||||||
|
(setq bg (cond
|
||||||
|
((eq bg 'default) (org-latex-color :background))
|
||||||
|
((string= bg "Transparent") nil)
|
||||||
|
(t (org-latex-color-format bg))))
|
||||||
|
;; Remove TeX \par at end of snippet to avoid trailing space.
|
||||||
|
(if (string-suffix-p string "\n")
|
||||||
|
(aset string (1- (length string)) ?%)
|
||||||
|
(setq string (concat string "%")))
|
||||||
|
(with-temp-file texfile
|
||||||
|
(insert latex-header)
|
||||||
|
(insert "\n\\begin{document}\n"
|
||||||
|
"\\definecolor{fg}{rgb}{" fg "}%\n"
|
||||||
|
(if bg
|
||||||
|
(concat "\\definecolor{bg}{rgb}{" bg "}%\n"
|
||||||
|
"\n\\pagecolor{bg}%\n")
|
||||||
|
"")
|
||||||
|
"\n{\\color{fg}\n"
|
||||||
|
string
|
||||||
|
"\n}\n"
|
||||||
|
"\n\\end{document}\n"))
|
||||||
|
(let* ((err-msg (format "Please adjust `%s' part of \
|
||||||
|
`org-preview-latex-process-alist'."
|
||||||
|
processing-type))
|
||||||
|
(image-input-file
|
||||||
|
(org-compile-file
|
||||||
|
texfile latex-compiler image-input-type err-msg log-buf))
|
||||||
|
(image-output-file
|
||||||
|
(org-compile-file
|
||||||
|
image-input-file image-converter image-output-type err-msg log-buf
|
||||||
|
`((?D . ,(shell-quote-argument (format "%s" dpi)))
|
||||||
|
(?S . ,(shell-quote-argument (format "%s" (/ dpi 140.0))))))))
|
||||||
|
(copy-file image-output-file tofile 'replace)
|
||||||
|
(dolist (e post-clean)
|
||||||
|
(when (file-exists-p (concat texfilebase e))
|
||||||
|
(delete-file (concat texfilebase e))))
|
||||||
|
image-output-file)))
|
||||||
|
|
||||||
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
|
(defun org-html-format-latex (latex-frag processing-type info)
|
||||||
|
"Format a LaTeX fragment LATEX-FRAG into HTML.
|
||||||
|
PROCESSING-TYPE designates the tool used for conversion. It can
|
||||||
|
be `mathjax', `verbatim', `html', nil, t or symbols in
|
||||||
|
`org-preview-latex-process-alist', e.g., `dvipng', `dvisvgm' or
|
||||||
|
`imagemagick'. See `org-html-with-latex' for more information.
|
||||||
|
INFO is a plist containing export properties."
|
||||||
|
(let ((cache-relpath "") (cache-dir ""))
|
||||||
|
(unless (or (eq processing-type 'mathjax)
|
||||||
|
(eq processing-type 'html))
|
||||||
|
(let ((bfn (or (buffer-file-name)
|
||||||
|
(make-temp-name
|
||||||
|
(expand-file-name "latex" temporary-file-directory))))
|
||||||
|
(latex-header
|
||||||
|
(let ((header (plist-get info :latex-header)))
|
||||||
|
(and header
|
||||||
|
(concat (mapconcat
|
||||||
|
(lambda (line) (concat "#+LATEX_HEADER: " line))
|
||||||
|
(org-split-string header "\n")
|
||||||
|
"\n")
|
||||||
|
"\n")))))
|
||||||
|
(setq cache-relpath
|
||||||
|
(concat (file-name-as-directory org-preview-latex-image-directory)
|
||||||
|
(file-name-sans-extension
|
||||||
|
(file-name-nondirectory bfn)))
|
||||||
|
cache-dir (file-name-directory bfn))
|
||||||
|
;; Re-create LaTeX environment from original buffer in
|
||||||
|
;; temporary buffer so that dvipng/imagemagick can properly
|
||||||
|
;; turn the fragment into an image.
|
||||||
|
(setq latex-frag (concat latex-header latex-frag))))
|
||||||
|
(org-export-with-buffer-copy
|
||||||
|
:to-buffer (get-buffer-create " *Org HTML Export LaTeX*")
|
||||||
|
:drop-visibility t :drop-narrowing t :drop-contents t
|
||||||
|
(erase-buffer)
|
||||||
|
(insert latex-frag)
|
||||||
|
(org-format-latex cache-relpath nil nil cache-dir nil
|
||||||
|
"Creating LaTeX Image..." nil processing-type)
|
||||||
|
(buffer-string))))
|
||||||
|
|
||||||
|
(make-obsolete #'org-format-latex "to be removed" "9.7")
|
||||||
|
(make-obsolete #'org-create-formula-image "to be removed" "9.7")
|
||||||
|
(make-obsolete #'org-html-format-latex "to be removed" "9.7")
|
||||||
|
|
||||||
;; FIXME: Unused; obsoleted; to be removed.
|
;; FIXME: Unused; obsoleted; to be removed.
|
||||||
(defun org-let (list &rest body) ;FIXME: So many kittens are suffering here.
|
(defun org-let (list &rest body) ;FIXME: So many kittens are suffering here.
|
||||||
(declare (indent 1) (obsolete cl-progv "2021"))
|
(declare (indent 1) (obsolete cl-progv "2021"))
|
||||||
|
|
|
@ -246,7 +246,7 @@ Assume `epg-context' is set."
|
||||||
;; contents in the buffer.
|
;; contents in the buffer.
|
||||||
(error
|
(error
|
||||||
(insert contents)
|
(insert contents)
|
||||||
(error (error-message-string err)))))
|
(error "%s" (error-message-string err)))))
|
||||||
(when folded-heading
|
(when folded-heading
|
||||||
(goto-char folded-heading)
|
(goto-char folded-heading)
|
||||||
(org-fold-subtree t))
|
(org-fold-subtree t))
|
||||||
|
|
|
@ -116,6 +116,7 @@ than its value."
|
||||||
(const :tag "No limit" nil)
|
(const :tag "No limit" nil)
|
||||||
(integer :tag "Maximum level")))
|
(integer :tag "Maximum level")))
|
||||||
|
|
||||||
|
(defvaralias 'org-hide-block-startup 'org-cycle-hide-block-startup)
|
||||||
(defcustom org-cycle-hide-block-startup nil
|
(defcustom org-cycle-hide-block-startup nil
|
||||||
"Non-nil means entering Org mode will fold all blocks.
|
"Non-nil means entering Org mode will fold all blocks.
|
||||||
This can also be set in on a per-file basis with
|
This can also be set in on a per-file basis with
|
||||||
|
@ -126,6 +127,7 @@ This can also be set in on a per-file basis with
|
||||||
:group 'org-cycle
|
:group 'org-cycle
|
||||||
:type 'boolean)
|
:type 'boolean)
|
||||||
|
|
||||||
|
(defvaralias 'org-hide-drawer-startup 'org-cycle-hide-drawer-startup)
|
||||||
(defcustom org-cycle-hide-drawer-startup t
|
(defcustom org-cycle-hide-drawer-startup t
|
||||||
"Non-nil means entering Org mode will fold all drawers.
|
"Non-nil means entering Org mode will fold all drawers.
|
||||||
This can also be set in on a per-file basis with
|
This can also be set in on a per-file basis with
|
||||||
|
@ -201,6 +203,7 @@ Special case: when 0, never leave empty lines in collapsed view."
|
||||||
:type 'integer)
|
:type 'integer)
|
||||||
(put 'org-cycle-separator-lines 'safe-local-variable 'integerp)
|
(put 'org-cycle-separator-lines 'safe-local-variable 'integerp)
|
||||||
|
|
||||||
|
(defvaralias 'org-pre-cycle-hook 'org-cycle-pre-hook)
|
||||||
(defcustom org-cycle-pre-hook nil
|
(defcustom org-cycle-pre-hook nil
|
||||||
"Hook that is run before visibility cycling is happening.
|
"Hook that is run before visibility cycling is happening.
|
||||||
The function(s) in this hook must accept a single argument which indicates
|
The function(s) in this hook must accept a single argument which indicates
|
||||||
|
@ -241,6 +244,7 @@ normal outline commands like `show-all', but not with the cycling commands."
|
||||||
:package-version '(Org . "9.6")
|
:package-version '(Org . "9.6")
|
||||||
:type 'boolean)
|
:type 'boolean)
|
||||||
|
|
||||||
|
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
|
||||||
(defvar org-cycle-tab-first-hook nil
|
(defvar org-cycle-tab-first-hook nil
|
||||||
"Hook for functions to attach themselves to TAB.
|
"Hook for functions to attach themselves to TAB.
|
||||||
See `org-ctrl-c-ctrl-c-hook' for more information.
|
See `org-ctrl-c-ctrl-c-hook' for more information.
|
||||||
|
|
|
@ -1131,8 +1131,11 @@ The function takes care of setting `:parent' property for NEW."
|
||||||
(eq new-type 'plain-text))
|
(eq new-type 'plain-text))
|
||||||
;; We cannot replace OLD with NEW since strings are not mutable.
|
;; We cannot replace OLD with NEW since strings are not mutable.
|
||||||
;; We take the long path.
|
;; We take the long path.
|
||||||
(progn (org-element-insert-before new old)
|
(progn
|
||||||
(org-element-extract old))
|
(org-element-insert-before new old)
|
||||||
|
(org-element-extract old)
|
||||||
|
;; We will return OLD.
|
||||||
|
(setq old new))
|
||||||
;; Since OLD is going to be changed into NEW by side-effect, first
|
;; Since OLD is going to be changed into NEW by side-effect, first
|
||||||
;; make sure that every element or object within NEW has OLD as
|
;; make sure that every element or object within NEW has OLD as
|
||||||
;; parent.
|
;; parent.
|
||||||
|
|
|
@ -551,6 +551,8 @@ The resulting function can be evaluated at a later time, from
|
||||||
another buffer, effectively cloning the original buffer there.
|
another buffer, effectively cloning the original buffer there.
|
||||||
|
|
||||||
The function assumes BUFFER's major mode is `org-mode'."
|
The function assumes BUFFER's major mode is `org-mode'."
|
||||||
|
(declare-function org-fold-core--update-buffer-folds "org-fold-core" ())
|
||||||
|
(require 'org-fold-core)
|
||||||
(with-current-buffer buffer
|
(with-current-buffer buffer
|
||||||
(let ((str (unless drop-contents (org-with-wide-buffer (buffer-string))))
|
(let ((str (unless drop-contents (org-with-wide-buffer (buffer-string))))
|
||||||
(narrowing
|
(narrowing
|
||||||
|
@ -592,7 +594,7 @@ The function assumes BUFFER's major mode is `org-mode'."
|
||||||
(let ((invis-prop (overlay-get ov 'invisible)))
|
(let ((invis-prop (overlay-get ov 'invisible)))
|
||||||
(when invis-prop
|
(when invis-prop
|
||||||
(push (list (overlay-start ov) (overlay-end ov)
|
(push (list (overlay-start ov) (overlay-end ov)
|
||||||
invis-prop)
|
(overlay-properties ov))
|
||||||
ov-set))))
|
ov-set))))
|
||||||
ov-set))))
|
ov-set))))
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
@ -618,8 +620,12 @@ The function assumes BUFFER's major mode is `org-mode'."
|
||||||
;; Current position of point.
|
;; Current position of point.
|
||||||
(goto-char pos)
|
(goto-char pos)
|
||||||
;; Overlays with invisible property.
|
;; Overlays with invisible property.
|
||||||
(pcase-dolist (`(,start ,end ,invis) ols)
|
(pcase-dolist (`(,start ,end ,props) ols)
|
||||||
(overlay-put (make-overlay start end) 'invisible invis))
|
(let ((ov (make-overlay start end)))
|
||||||
|
(while props
|
||||||
|
(overlay-put ov (pop props) (pop props)))))
|
||||||
|
;; Text property folds.
|
||||||
|
(unless drop-visibility (org-fold-core--update-buffer-folds))
|
||||||
;; Never write the buffer copy to disk, despite
|
;; Never write the buffer copy to disk, despite
|
||||||
;; `buffer-file-name' not being nil.
|
;; `buffer-file-name' not being nil.
|
||||||
(setq write-contents-functions (list (lambda (&rest _) t))))))))
|
(setq write-contents-functions (list (lambda (&rest _) t))))))))
|
||||||
|
@ -3842,7 +3848,9 @@ Assume point is at the beginning of the link."
|
||||||
;; Type 1: Text targeted from a radio target.
|
;; Type 1: Text targeted from a radio target.
|
||||||
((and org-target-link-regexp
|
((and org-target-link-regexp
|
||||||
(save-excursion (or (bolp) (backward-char))
|
(save-excursion (or (bolp) (backward-char))
|
||||||
(looking-at org-target-link-regexp)))
|
(if org-target-link-regexps
|
||||||
|
(org--re-list-looking-at org-target-link-regexps)
|
||||||
|
(looking-at org-target-link-regexp))))
|
||||||
(setq type "radio")
|
(setq type "radio")
|
||||||
(setq format 'plain)
|
(setq format 'plain)
|
||||||
(setq link-end (match-end 1))
|
(setq link-end (match-end 1))
|
||||||
|
@ -5215,7 +5223,10 @@ to an appropriate container (e.g., a paragraph)."
|
||||||
((not (memq 'link restriction)) nil)
|
((not (memq 'link restriction)) nil)
|
||||||
((progn
|
((progn
|
||||||
(unless (bolp) (forward-char -1))
|
(unless (bolp) (forward-char -1))
|
||||||
(not (re-search-forward org-target-link-regexp nil t)))
|
(not
|
||||||
|
(if org-target-link-regexps
|
||||||
|
(org--re-list-search-forward org-target-link-regexps nil t)
|
||||||
|
(re-search-forward org-target-link-regexp nil t))))
|
||||||
nil)
|
nil)
|
||||||
;; Since we moved backward, we do not want to
|
;; Since we moved backward, we do not want to
|
||||||
;; match again an hypothetical 1-character long
|
;; match again an hypothetical 1-character long
|
||||||
|
@ -5224,8 +5235,11 @@ to an appropriate container (e.g., a paragraph)."
|
||||||
;; beginning of line, we prevent this here.
|
;; beginning of line, we prevent this here.
|
||||||
((and (= start (1+ (line-beginning-position)))
|
((and (= start (1+ (line-beginning-position)))
|
||||||
(= start (match-end 1)))
|
(= start (match-end 1)))
|
||||||
(and (re-search-forward org-target-link-regexp nil t)
|
(and
|
||||||
(1+ (match-beginning 1))))
|
(if org-target-link-regexps
|
||||||
|
(org--re-list-search-forward org-target-link-regexps nil t)
|
||||||
|
(re-search-forward org-target-link-regexp nil t))
|
||||||
|
(1+ (match-beginning 1))))
|
||||||
(t (1+ (match-beginning 1))))))
|
(t (1+ (match-beginning 1))))))
|
||||||
found)
|
found)
|
||||||
(save-excursion
|
(save-excursion
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
(require 'org-macs)
|
(require 'org-macs)
|
||||||
(org-assert-version)
|
(org-assert-version)
|
||||||
|
(require 'seq) ; Emacs 27 does not preload seq.el; for `seq-every-p'.
|
||||||
|
|
||||||
(declare-function org-mode "org" ())
|
(declare-function org-mode "org" ())
|
||||||
(declare-function org-toggle-pretty-entities "org" ())
|
(declare-function org-toggle-pretty-entities "org" ())
|
||||||
|
|
|
@ -634,6 +634,10 @@ unless RETURN-ONLY is non-nil."
|
||||||
text-property-default-nonsticky
|
text-property-default-nonsticky
|
||||||
full-prop-list))))))))))))))
|
full-prop-list))))))))))))))
|
||||||
|
|
||||||
|
(defun org-fold-core--update-buffer-folds ()
|
||||||
|
"Copy folding state in a new buffer with text copied from old buffer."
|
||||||
|
(org-fold-core--property-symbol-get-create (car (org-fold-core-folding-spec-list))))
|
||||||
|
|
||||||
(defun org-fold-core-decouple-indirect-buffer-folds ()
|
(defun org-fold-core-decouple-indirect-buffer-folds ()
|
||||||
"Copy and decouple folding state in a newly created indirect buffer.
|
"Copy and decouple folding state in a newly created indirect buffer.
|
||||||
This function is mostly intended to be used in
|
This function is mostly intended to be used in
|
||||||
|
@ -641,7 +645,7 @@ This function is mostly intended to be used in
|
||||||
(when (and (buffer-base-buffer)
|
(when (and (buffer-base-buffer)
|
||||||
(eq org-fold-core-style 'text-properties)
|
(eq org-fold-core-style 'text-properties)
|
||||||
(not (memql 'ignore-indirect org-fold-core--optimise-for-huge-buffers)))
|
(not (memql 'ignore-indirect org-fold-core--optimise-for-huge-buffers)))
|
||||||
(org-fold-core--property-symbol-get-create (car (org-fold-core-folding-spec-list)))))
|
(org-fold-core--update-buffer-folds)))
|
||||||
|
|
||||||
;;; API
|
;;; API
|
||||||
|
|
||||||
|
@ -784,19 +788,16 @@ corresponding folding spec (if the text is folded using that spec)."
|
||||||
(when (and spec (not (eq spec 'all))) (org-fold-core--check-spec spec))
|
(when (and spec (not (eq spec 'all))) (org-fold-core--check-spec spec))
|
||||||
(org-with-point-at pom
|
(org-with-point-at pom
|
||||||
(cond
|
(cond
|
||||||
((eq spec 'all)
|
((or (null spec) (eq spec 'all))
|
||||||
(let ((result))
|
(catch :single-spec
|
||||||
(dolist (spec (org-fold-core-folding-spec-list))
|
(let ((result))
|
||||||
(let ((val (if (eq org-fold-core-style 'text-properties)
|
(dolist (lspec (org-fold-core-folding-spec-list))
|
||||||
(get-text-property (point) (org-fold-core--property-symbol-get-create spec nil t))
|
(let ((val (if (eq org-fold-core-style 'text-properties)
|
||||||
(get-char-property (point) (org-fold-core--property-symbol-get-create spec nil t)))))
|
(get-text-property (point) (org-fold-core--property-symbol-get-create lspec nil t))
|
||||||
(when val (push val result))))
|
(get-char-property (point) (org-fold-core--property-symbol-get-create lspec nil t)))))
|
||||||
(reverse result)))
|
(when (and val (null spec)) (throw :single-spec val))
|
||||||
((null spec)
|
(when val (push val result))))
|
||||||
(let ((result (if (eq org-fold-core-style 'text-properties)
|
(reverse result))))
|
||||||
(get-text-property (point) 'invisible)
|
|
||||||
(get-char-property (point) 'invisible))))
|
|
||||||
(when (org-fold-core-folding-spec-p result) result)))
|
|
||||||
(t (if (eq org-fold-core-style 'text-properties)
|
(t (if (eq org-fold-core-style 'text-properties)
|
||||||
(get-text-property (point) (org-fold-core--property-symbol-get-create spec nil t))
|
(get-text-property (point) (org-fold-core--property-symbol-get-create spec nil t))
|
||||||
(get-char-property (point) (org-fold-core--property-symbol-get-create spec nil t))))))))
|
(get-char-property (point) (org-fold-core--property-symbol-get-create spec nil t))))))))
|
||||||
|
@ -1034,26 +1035,23 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
|
||||||
(when spec (org-fold-core--check-spec spec))
|
(when spec (org-fold-core--check-spec spec))
|
||||||
(with-silent-modifications
|
(with-silent-modifications
|
||||||
(org-with-wide-buffer
|
(org-with-wide-buffer
|
||||||
;; Arrange face property of newlines after all the folds
|
;; Arrange fontifying newlines after all the folds between FROM
|
||||||
;; between FROM and TO to match the first character before the
|
;; and TO to match the first character before the fold; not the
|
||||||
;; fold; not the last as per Emacs defaults. This makes
|
;; last as per Emacs defaults. This makes :extend faces span
|
||||||
;; :extend faces span past the ellipsis.
|
;; past the ellipsis. See bug#65896. The face properties are
|
||||||
;; See bug#65896.
|
;; assigned via `org-activate-folds'.
|
||||||
(if flag ; folding
|
(when (equal ?\n (char-after from))
|
||||||
(when (equal ?\n (char-after to))
|
(font-lock-flush from (1+ from)))
|
||||||
(put-text-property to (1+ to) 'face (get-text-property from 'face)))
|
(when (equal ?\n (char-after to))
|
||||||
;; unfolding
|
(font-lock-flush to (1+ to)))
|
||||||
(dolist (region (org-fold-core-get-regions :from from :to to :specs spec))
|
(dolist (region (org-fold-core-get-regions :from from :to to :specs spec))
|
||||||
(when (equal ?\n (char-after (cadr region)))
|
(when (equal ?\n (char-after (cadr region)))
|
||||||
(if-let ((specs (remq spec (org-fold-core-get-folding-spec 'all (1- (cadr region))))))
|
(font-lock-flush (cadr region) (1+ (cadr region))))
|
||||||
;; There are multiple folds, re-apply 'face according
|
;; Re-fontify beginning of the fold - we may
|
||||||
;; to the folds that will stay after unfolding SPEC.
|
;; unfold inside an existing fold, with FROM begin a newline
|
||||||
(put-text-property
|
;; after spliced fold.
|
||||||
(cadr region) (1+ (cadr region)) 'face
|
(when (equal ?\n (char-after (car region)))
|
||||||
(get-text-property
|
(font-lock-flush (car region) (1+ (car region)))))
|
||||||
(car (org-fold-core-get-region-at-point (car specs) (1- (cadr region))))
|
|
||||||
'face))
|
|
||||||
(font-lock-flush (cadr region) (1+ (cadr region)))))))
|
|
||||||
(when (eq org-fold-core-style 'overlays)
|
(when (eq org-fold-core-style 'overlays)
|
||||||
(if org-fold-core--keep-overlays
|
(if org-fold-core--keep-overlays
|
||||||
(mapc
|
(mapc
|
||||||
|
@ -1113,14 +1111,12 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
|
||||||
(setq pos next))
|
(setq pos next))
|
||||||
(setq pos (next-single-char-property-change pos 'invisible nil to)))))))
|
(setq pos (next-single-char-property-change pos 'invisible nil to)))))))
|
||||||
(when (eq org-fold-core-style 'text-properties)
|
(when (eq org-fold-core-style 'text-properties)
|
||||||
(remove-text-properties from to (list (org-fold-core--property-symbol-get-create spec) nil)))
|
(remove-text-properties from to (list (org-fold-core--property-symbol-get-create spec) nil)))))
|
||||||
;; FROM..TO may represent only a part of the fold. Transfer
|
;; Re-calculate trailing faces for all the folds revealed
|
||||||
;; 'face from the new char before fold, if there is any.
|
;; by unfolding or created by folding.
|
||||||
(when-let ((trailing-fold (org-fold-core-get-region-at-point spec to)))
|
(dolist (region (org-fold-core-get-regions :from from :to to :specs spec))
|
||||||
(when (equal ?\n (char-after (cdr trailing-fold)))
|
(when (equal ?\n (char-after (cadr region)))
|
||||||
(put-text-property
|
(font-lock-flush (cadr region) (1+ (cadr region)))))))))
|
||||||
(cdr trailing-fold) (1+ (cdr trailing-fold))
|
|
||||||
'face (get-text-property to 'face))))))))))
|
|
||||||
|
|
||||||
(cl-defmacro org-fold-core-regions (regions &key override clean-markers relative)
|
(cl-defmacro org-fold-core-regions (regions &key override clean-markers relative)
|
||||||
"Fold every region in REGIONS list in current buffer.
|
"Fold every region in REGIONS list in current buffer.
|
||||||
|
|
|
@ -102,7 +102,11 @@ When nil, you can use these keybindings to navigate the buffer:
|
||||||
mouse-drag-region universal-argument org-occur)))
|
mouse-drag-region universal-argument org-occur)))
|
||||||
(dolist (cmd cmds)
|
(dolist (cmd cmds)
|
||||||
(substitute-key-definition cmd cmd map global-map)))
|
(substitute-key-definition cmd cmd map global-map)))
|
||||||
(suppress-keymap map)
|
(if org-goto-auto-isearch
|
||||||
|
;; Suppress 0-9 interpreted as digital arguments.
|
||||||
|
;; Make them initiate isearch instead.
|
||||||
|
(suppress-keymap map t)
|
||||||
|
(suppress-keymap map))
|
||||||
(org-defkey map "\C-m" 'org-goto-ret)
|
(org-defkey map "\C-m" 'org-goto-ret)
|
||||||
(org-defkey map [(return)] 'org-goto-ret)
|
(org-defkey map [(return)] 'org-goto-ret)
|
||||||
(org-defkey map [(left)] 'org-goto-left)
|
(org-defkey map [(left)] 'org-goto-left)
|
||||||
|
|
178
lisp/org-id.el
178
lisp/org-id.el
|
@ -129,6 +129,46 @@ nil Never use an ID to make a link, instead link using a text search for
|
||||||
(const :tag "Only use existing" use-existing)
|
(const :tag "Only use existing" use-existing)
|
||||||
(const :tag "Do not use ID to create link" nil)))
|
(const :tag "Do not use ID to create link" nil)))
|
||||||
|
|
||||||
|
(defcustom org-id-link-consider-parent-id nil
|
||||||
|
"Non-nil means storing a link to an Org entry considers inherited IDs.
|
||||||
|
|
||||||
|
When this option is non-nil and `org-id-link-use-context' is
|
||||||
|
enabled, ID properties inherited from parent entries will be
|
||||||
|
considered when storing an ID link. If no ID is found in this
|
||||||
|
way, a new one may be created as normal (see
|
||||||
|
`org-id-link-to-org-use-id').
|
||||||
|
|
||||||
|
For example, given this org file:
|
||||||
|
|
||||||
|
* Parent
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: abc
|
||||||
|
:END:
|
||||||
|
** Child 1
|
||||||
|
** Child 2
|
||||||
|
|
||||||
|
With `org-id-link-consider-parent-id' and
|
||||||
|
`org-id-link-use-context' both enabled, storing a link with point
|
||||||
|
at \"Child 1\" will produce a link \"<id:abc::*Child 1>\". This
|
||||||
|
allows linking to uniquely-named sub-entries within a parent
|
||||||
|
entry with an ID, without requiring every sub-entry to have its
|
||||||
|
own ID."
|
||||||
|
:group 'org-link-store
|
||||||
|
:group 'org-id
|
||||||
|
:package-version '(Org . "9.7")
|
||||||
|
:type 'boolean)
|
||||||
|
|
||||||
|
(defcustom org-id-link-use-context t
|
||||||
|
"Non-nil means enables search string context in org-id links.
|
||||||
|
|
||||||
|
Search strings are added by `org-id-store-link' when both the
|
||||||
|
general option `org-link-context-for-files' and the org-id option
|
||||||
|
`org-id-link-use-context' are non-nil."
|
||||||
|
:group 'org-link-store
|
||||||
|
:group 'org-id
|
||||||
|
:package-version '(Org . "9.7")
|
||||||
|
:type 'boolean)
|
||||||
|
|
||||||
(defcustom org-id-uuid-program "uuidgen"
|
(defcustom org-id-uuid-program "uuidgen"
|
||||||
"The uuidgen program."
|
"The uuidgen program."
|
||||||
:group 'org-id
|
:group 'org-id
|
||||||
|
@ -280,15 +320,21 @@ This is useful when working with contents in a temporary buffer
|
||||||
that will be copied back to the original.")
|
that will be copied back to the original.")
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-id-get (&optional epom create prefix)
|
(defun org-id-get (&optional epom create prefix inherit)
|
||||||
"Get the ID property of the entry at EPOM.
|
"Get the ID of the entry at EPOM.
|
||||||
EPOM is an element, marker, or buffer position.
|
|
||||||
If EPOM is nil, refer to the entry at point.
|
EPOM is an element, marker, or buffer position. If EPOM is nil,
|
||||||
If the entry does not have an ID, the function returns nil.
|
refer to the entry at point.
|
||||||
However, when CREATE is non-nil, create an ID if none is present already.
|
|
||||||
PREFIX will be passed through to `org-id-new'.
|
If INHERIT is non-nil, ID properties inherited from parent
|
||||||
In any case, the ID of the entry is returned."
|
entries are considered. Otherwise, only ID properties on the
|
||||||
(let ((id (org-entry-get epom "ID")))
|
entry itself are considered.
|
||||||
|
|
||||||
|
When CREATE is nil, return the ID of the entry if found,
|
||||||
|
otherwise nil. When CREATE is non-nil, create an ID if none has
|
||||||
|
been found, and return the new ID. PREFIX will be passed through
|
||||||
|
to `org-id-new'."
|
||||||
|
(let ((id (org-entry-get epom "ID" (and inherit t))))
|
||||||
(cond
|
(cond
|
||||||
((and id (stringp id) (string-match "\\S-" id))
|
((and id (stringp id) (string-match "\\S-" id))
|
||||||
id)
|
id)
|
||||||
|
@ -700,21 +746,56 @@ optional argument MARKERP, return the position as a new marker."
|
||||||
|
|
||||||
;; id link type
|
;; id link type
|
||||||
|
|
||||||
;; Calling the following function is hard-coded into `org-store-link',
|
(defun org-id--get-id-to-store-link (&optional create)
|
||||||
;; so we do have to add it to `org-store-link-functions'.
|
"Get or create the relevant ID for storing a link.
|
||||||
|
|
||||||
|
Optional argument CREATE is passed to `org-id-get'.
|
||||||
|
|
||||||
|
Inherited IDs are only considered when
|
||||||
|
`org-id-link-consider-parent-id', `org-id-link-use-context' and
|
||||||
|
`org-link-context-for-files' are all enabled, since inherited IDs
|
||||||
|
are confusing without the additional search string context.
|
||||||
|
|
||||||
|
Note that this function resets the
|
||||||
|
`org-entry-property-inherited-from' marker: it will either point
|
||||||
|
to nil (if the id was not inherited) or to the point it was
|
||||||
|
inherited from."
|
||||||
|
(let* ((inherit-id (and org-id-link-consider-parent-id
|
||||||
|
org-id-link-use-context
|
||||||
|
org-link-context-for-files)))
|
||||||
|
(move-marker org-entry-property-inherited-from nil)
|
||||||
|
(org-id-get nil create nil inherit-id)))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-id-store-link ()
|
(defun org-id-store-link ()
|
||||||
"Store a link to the current entry, using its ID.
|
"Store a link to the current entry, using its ID.
|
||||||
|
|
||||||
If before first heading store first title-keyword as description
|
The link description is based on the heading, or if before the
|
||||||
or filename if no title."
|
first heading, the title keyword if available, or else the
|
||||||
|
filename.
|
||||||
|
|
||||||
|
When `org-link-context-for-files' and `org-id-link-use-context'
|
||||||
|
are non-nil, add a search string to the link. The link
|
||||||
|
description is then based on the search string target.
|
||||||
|
|
||||||
|
When in addition `org-id-link-consider-parent-id' is non-nil, the
|
||||||
|
ID can be inherited from a parent entry, with the search string
|
||||||
|
used to still link to the current location."
|
||||||
(interactive)
|
(interactive)
|
||||||
(when (and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode))
|
(when (and (buffer-file-name (buffer-base-buffer))
|
||||||
(let* ((link (concat "id:" (org-id-get-create)))
|
(derived-mode-p 'org-mode))
|
||||||
|
;; Get the precise target first, in case looking for an id causes
|
||||||
|
;; a properties drawer to be added at the current location.
|
||||||
|
(let* ((precise-target (and org-link-context-for-files
|
||||||
|
org-id-link-use-context
|
||||||
|
(org-link-precise-link-target)))
|
||||||
|
(link (concat "id:" (org-id--get-id-to-store-link 'create)))
|
||||||
|
(id-location (or (and org-entry-property-inherited-from
|
||||||
|
(marker-position org-entry-property-inherited-from))
|
||||||
|
(save-excursion (org-back-to-heading-or-point-min t) (point))))
|
||||||
(case-fold-search nil)
|
(case-fold-search nil)
|
||||||
(desc (save-excursion
|
(desc (save-excursion
|
||||||
(org-back-to-heading-or-point-min t)
|
(goto-char id-location)
|
||||||
(cond ((org-before-first-heading-p)
|
(cond ((org-before-first-heading-p)
|
||||||
(let ((keywords (org-collect-keywords '("TITLE"))))
|
(let ((keywords (org-collect-keywords '("TITLE"))))
|
||||||
(if keywords
|
(if keywords
|
||||||
|
@ -726,14 +807,59 @@ or filename if no title."
|
||||||
(match-string 4)
|
(match-string 4)
|
||||||
(match-string 0)))
|
(match-string 0)))
|
||||||
(t link)))))
|
(t link)))))
|
||||||
|
;; Precise targets should be after id-location to avoid
|
||||||
|
;; duplicating the current headline as a search string
|
||||||
|
(when (and precise-target
|
||||||
|
(> (nth 2 precise-target) id-location))
|
||||||
|
(setq link (concat link "::" (nth 0 precise-target)))
|
||||||
|
(setq desc (nth 1 precise-target)))
|
||||||
(org-link-store-props :link link :description desc :type "id")
|
(org-link-store-props :link link :description desc :type "id")
|
||||||
link)))
|
link)))
|
||||||
|
|
||||||
(defun org-id-open (id _)
|
;;;###autoload
|
||||||
"Go to the entry with id ID."
|
(defun org-id-store-link-maybe (&optional interactive?)
|
||||||
(org-mark-ring-push)
|
"Store a link to the current entry using its ID if enabled.
|
||||||
(let ((m (org-id-find id 'marker))
|
|
||||||
cmd)
|
The value of `org-id-link-to-org-use-id' determines whether an ID
|
||||||
|
link should be stored, using `org-id-store-link'.
|
||||||
|
|
||||||
|
Assume the function is called interactively if INTERACTIVE? is
|
||||||
|
non-nil."
|
||||||
|
(when (and (buffer-file-name (buffer-base-buffer))
|
||||||
|
(derived-mode-p 'org-mode)
|
||||||
|
(or (eq org-id-link-to-org-use-id t)
|
||||||
|
(and interactive?
|
||||||
|
(or (eq org-id-link-to-org-use-id 'create-if-interactive)
|
||||||
|
(and (eq org-id-link-to-org-use-id
|
||||||
|
'create-if-interactive-and-no-custom-id)
|
||||||
|
(not (org-entry-get nil "CUSTOM_ID")))))
|
||||||
|
;; 'use-existing
|
||||||
|
(and org-id-link-to-org-use-id
|
||||||
|
(org-id--get-id-to-store-link))))
|
||||||
|
(org-id-store-link)))
|
||||||
|
|
||||||
|
(defun org-id-open (link _)
|
||||||
|
"Go to the entry indicated by id link LINK.
|
||||||
|
|
||||||
|
The link can include a search string after \"::\", which is
|
||||||
|
passed to `org-link-search'.
|
||||||
|
|
||||||
|
For backwards compatibility with IDs that contain \"::\", if no
|
||||||
|
match is found for the ID, the full link string including \"::\"
|
||||||
|
will be tried as an ID."
|
||||||
|
(let* ((option (and (string-match "::\\(.*\\)\\'" link)
|
||||||
|
(match-string 1 link)))
|
||||||
|
(id (if (not option) link
|
||||||
|
(substring link 0 (match-beginning 0))))
|
||||||
|
m cmd)
|
||||||
|
(org-mark-ring-push)
|
||||||
|
(setq m (org-id-find id 'marker))
|
||||||
|
(when (and (not m) option)
|
||||||
|
;; Backwards compatibility: if id is not found, try treating
|
||||||
|
;; whole link as an id.
|
||||||
|
(setq m (org-id-find link 'marker))
|
||||||
|
(when m
|
||||||
|
(setq option nil)))
|
||||||
(unless m
|
(unless m
|
||||||
(error "Cannot find entry with ID \"%s\"" id))
|
(error "Cannot find entry with ID \"%s\"" id))
|
||||||
;; Use a buffer-switching command in analogy to finding files
|
;; Use a buffer-switching command in analogy to finding files
|
||||||
|
@ -750,9 +876,17 @@ or filename if no title."
|
||||||
(funcall cmd (marker-buffer m)))
|
(funcall cmd (marker-buffer m)))
|
||||||
(goto-char m)
|
(goto-char m)
|
||||||
(move-marker m nil)
|
(move-marker m nil)
|
||||||
|
(when option
|
||||||
|
(save-restriction
|
||||||
|
(unless (org-before-first-heading-p)
|
||||||
|
(org-narrow-to-subtree))
|
||||||
|
(org-link-search option nil nil
|
||||||
|
(org-element-lineage (org-element-at-point) 'headline t))))
|
||||||
(org-fold-show-context)))
|
(org-fold-show-context)))
|
||||||
|
|
||||||
(org-link-set-parameters "id" :follow #'org-id-open)
|
(org-link-set-parameters "id"
|
||||||
|
:follow #'org-id-open
|
||||||
|
:store #'org-id-store-link-maybe)
|
||||||
|
|
||||||
(provide 'org-id)
|
(provide 'org-id)
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -65,6 +65,7 @@
|
||||||
;; - special properties in properties drawers,
|
;; - special properties in properties drawers,
|
||||||
;; - obsolete syntax for properties drawers,
|
;; - obsolete syntax for properties drawers,
|
||||||
;; - invalid duration in EFFORT property,
|
;; - invalid duration in EFFORT property,
|
||||||
|
;; - invalid ID property with a double colon,
|
||||||
;; - missing definition for footnote references,
|
;; - missing definition for footnote references,
|
||||||
;; - missing reference for footnote definitions,
|
;; - missing reference for footnote definitions,
|
||||||
;; - non-footnote definitions in footnote section,
|
;; - non-footnote definitions in footnote section,
|
||||||
|
@ -686,6 +687,16 @@ Use :header-args: instead"
|
||||||
(list (org-element-begin p)
|
(list (org-element-begin p)
|
||||||
(format "Invalid effort duration format: %S" value))))))))
|
(format "Invalid effort duration format: %S" value))))))))
|
||||||
|
|
||||||
|
(defun org-lint-invalid-id-property (ast)
|
||||||
|
(org-element-map ast 'node-property
|
||||||
|
(lambda (p)
|
||||||
|
(when (equal "ID" (org-element-property :key p))
|
||||||
|
(let ((value (org-element-property :value p)))
|
||||||
|
(and (org-string-nw-p value)
|
||||||
|
(string-match-p "::" value)
|
||||||
|
(list (org-element-begin p)
|
||||||
|
(format "IDs should not include \"::\": %S" value))))))))
|
||||||
|
|
||||||
(defun org-lint-link-to-local-file (ast)
|
(defun org-lint-link-to-local-file (ast)
|
||||||
(org-element-map ast 'link
|
(org-element-map ast 'link
|
||||||
(lambda (l)
|
(lambda (l)
|
||||||
|
@ -1507,6 +1518,19 @@ AST is the buffer parse tree."
|
||||||
((memq (org-element-property :type deadline) '(inactive inactive-range))
|
((memq (org-element-property :type deadline) '(inactive inactive-range))
|
||||||
(list (org-element-begin planning) "Inactive timestamp in DEADLINE will not appear in agenda."))
|
(list (org-element-begin planning) "Inactive timestamp in DEADLINE will not appear in agenda."))
|
||||||
(t nil))))))
|
(t nil))))))
|
||||||
|
|
||||||
|
(defvar org-beamer-frame-environment) ; defined in ox-beamer.el
|
||||||
|
(defun org-lint-beamer-frame (ast)
|
||||||
|
"Check for occurrences of begin or end frame."
|
||||||
|
(require 'ox-beamer)
|
||||||
|
(org-with-point-at ast
|
||||||
|
(goto-char (point-min))
|
||||||
|
(let (result)
|
||||||
|
(while (re-search-forward
|
||||||
|
(concat "\\\\\\(begin\\|end\\){" org-beamer-frame-environment "}") nil t)
|
||||||
|
(push (list (match-beginning 0) "Beamer frame name may cause error when exporting. Consider customizing `org-beamer-frame-environment'.") result))
|
||||||
|
result)))
|
||||||
|
|
||||||
|
|
||||||
;;; Checkers declaration
|
;;; Checkers declaration
|
||||||
|
|
||||||
|
@ -1684,6 +1708,11 @@ AST is the buffer parse tree."
|
||||||
#'org-lint-invalid-effort-property
|
#'org-lint-invalid-effort-property
|
||||||
:categories '(properties))
|
:categories '(properties))
|
||||||
|
|
||||||
|
(org-lint-add-checker 'invalid-id-property
|
||||||
|
"Report search string delimiter \"::\" in ID property"
|
||||||
|
#'org-lint-invalid-id-property
|
||||||
|
:categories '(properties))
|
||||||
|
|
||||||
(org-lint-add-checker 'undefined-footnote-reference
|
(org-lint-add-checker 'undefined-footnote-reference
|
||||||
"Report missing definition for footnote references"
|
"Report missing definition for footnote references"
|
||||||
#'org-lint-undefined-footnote-reference
|
#'org-lint-undefined-footnote-reference
|
||||||
|
@ -1787,6 +1816,10 @@ AST is the buffer parse tree."
|
||||||
"Report $ that might be treated as LaTeX fragment boundary."
|
"Report $ that might be treated as LaTeX fragment boundary."
|
||||||
#'org-lint-LaTeX-$-ambiguous
|
#'org-lint-LaTeX-$-ambiguous
|
||||||
:categories '(markup) :trust 'low)
|
:categories '(markup) :trust 'low)
|
||||||
|
(org-lint-add-checker 'beamer-frame
|
||||||
|
"Report that frame text contains beamer frame environment."
|
||||||
|
#'org-lint-beamer-frame
|
||||||
|
:categories '(export) :trust 'low)
|
||||||
(org-lint-add-checker 'timestamp-syntax
|
(org-lint-add-checker 'timestamp-syntax
|
||||||
"Report malformed timestamps."
|
"Report malformed timestamps."
|
||||||
#'org-lint-timestamp-syntax
|
#'org-lint-timestamp-syntax
|
||||||
|
|
274
lisp/org-macs.el
274
lisp/org-macs.el
|
@ -344,6 +344,278 @@ If EXCLUDE-TMP is non-nil, ignore temporary buffers."
|
||||||
nil))
|
nil))
|
||||||
(buffer-list)))))
|
(buffer-list)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Async stack
|
||||||
|
|
||||||
|
(defvar org-async--stack nil
|
||||||
|
"List of async currently running task forms.
|
||||||
|
Each running task is represented by a list with the following structure:
|
||||||
|
(%PROCESS :success %FUN :failure %FUN
|
||||||
|
:filter %FUN :buffer %BUFFER
|
||||||
|
:timeout %FLOAT :start-time %FLOAT
|
||||||
|
:info %SEXP)")
|
||||||
|
|
||||||
|
(defvar org-async--wait-queue nil
|
||||||
|
"List of async queued task forms.
|
||||||
|
Each queued task is represented by a list with the following structure:
|
||||||
|
(%PROCESS :success %FUN :failure %FUN
|
||||||
|
:filter %FUN :buffer %BUFFER
|
||||||
|
:info %SEXP :dir %STRING
|
||||||
|
:timeout %FLOAT :coding %SYMBOL)")
|
||||||
|
|
||||||
|
(defvar org-async-process-limit 4
|
||||||
|
"Maximum number of processes to run at once.")
|
||||||
|
|
||||||
|
(defvar org-async-timeout 120
|
||||||
|
"Default timeout for a process started via `org-async-queue'.")
|
||||||
|
|
||||||
|
(defvar org-async-check-timeout-interval 1
|
||||||
|
"Check for processes which have exceeded their timeout every this many seconds.")
|
||||||
|
|
||||||
|
(defvar org-async--counter 0)
|
||||||
|
|
||||||
|
(cl-defun org-async-call (proc &key success failure filter buffer info timeout now process-variables
|
||||||
|
(dir default-directory) (coding 'utf-8))
|
||||||
|
"Start PROC and register it with callbacks SUCCESS and FAILURE.
|
||||||
|
|
||||||
|
PROC can be a process, string, or list. A string will be run as
|
||||||
|
a shell command, with `start-process-shell-command' and a list
|
||||||
|
run using `start-process' with the car as the command and the cdr
|
||||||
|
as the arguments. The process will be executed in DIR (if set)
|
||||||
|
or `default-directory'.
|
||||||
|
|
||||||
|
There is also a \"special form\" of PROC, namely a list where the
|
||||||
|
first item is the symbol org-async-task, and the rest constitutes
|
||||||
|
an argument list for `org-async-call'. This form allows for easy
|
||||||
|
specification of callbacks that are themselves async tasks, e.g.
|
||||||
|
(org-async-call \\='(\"sleep 1\")
|
||||||
|
:success \\='(org-async-task (\"notify-send\" \"done\")))
|
||||||
|
When using this form, all other arguments are ignored.
|
||||||
|
|
||||||
|
When BUFFER is provided, the output of PROC will be directed to it.
|
||||||
|
Shoud BUFFER be t, then a temp buffer will be created and removed
|
||||||
|
during `org-async--cleanup-process'.
|
||||||
|
|
||||||
|
SUCCESS and FAILURE can be any form accepted by `org-async--execute-callback',
|
||||||
|
namely:
|
||||||
|
- A string, which is used a `message' string with the exit-code,
|
||||||
|
process-buffer, and INFO as arguments.
|
||||||
|
- A function, which is called with exit-code, process-buffer,
|
||||||
|
and INFO as arguments.
|
||||||
|
- A list, which is used as an argument list for a new `org-async-call' call.
|
||||||
|
- nil, which does nothing.
|
||||||
|
|
||||||
|
When PROC succeeds by exiting with an exit code of zero, the SUCCESS
|
||||||
|
callback will be run. Should PROC fail, or be killed, or the process
|
||||||
|
runs for more than TIMEOUT seconds, the FAILURE callback will be run.
|
||||||
|
|
||||||
|
A function FILTER can be provided, in which case it will be
|
||||||
|
called in the same manner as a normal procecss filter, however
|
||||||
|
the function FILTER will be called with INFO as a third argument.
|
||||||
|
i.e. the call signature is (content new-content-string INFO)
|
||||||
|
When BUFFER is non-nil, there are two other major differences:
|
||||||
|
- The new content is silently inserted before FILTER is called
|
||||||
|
- Note that `point' is left alone and is not moved by this.
|
||||||
|
- The process buffer is the current buffer when FILTER is called.
|
||||||
|
|
||||||
|
When CODING is non-nil, both the process encode and decode system
|
||||||
|
will be set to CODING. If unset, UTF-8 is used.
|
||||||
|
|
||||||
|
When NOW is non-nil, the PROC is started immediately, regardless
|
||||||
|
of `org-async-process-limit'.
|
||||||
|
|
||||||
|
For improved performance, PROCESS-VARIABLES is a list of
|
||||||
|
let-style bindings that should be applied to the process.
|
||||||
|
Variables are supported on an individual basis (i.e. only certain
|
||||||
|
variables can be set), with the default value being equivalent to:
|
||||||
|
|
||||||
|
:process-variables ((process-adaptive-read-buffering nil)
|
||||||
|
(process-connection-type nil)
|
||||||
|
(read-process-output-max 65536))"
|
||||||
|
(cond
|
||||||
|
;; Called with a task (as can be used with callbacks), so re-call
|
||||||
|
;; with expanded arguments.
|
||||||
|
((and (consp proc)
|
||||||
|
(eq (car proc) 'org-async-task))
|
||||||
|
(apply #'org-async-call (cdr proc)))
|
||||||
|
;; Start the async process now.
|
||||||
|
((or now (< (length org-async--stack) org-async-process-limit))
|
||||||
|
(let ((proc
|
||||||
|
(let ((default-directory (or dir default-directory))
|
||||||
|
(process-adaptive-read-buffering ; No by default
|
||||||
|
(cadr (or (assoc 'process-adaptive-read-buffering process-variables) nil)))
|
||||||
|
(process-connection-type ; Use a pipe by default
|
||||||
|
(cadr (or (assoc 'process-connection-type process-variables) nil)))
|
||||||
|
(read-process-output-max ; Can be worth changing depending on the process
|
||||||
|
(or (assq 'read-process-output-max process-variables) read-process-output-max)))
|
||||||
|
(cond ((processp proc) proc)
|
||||||
|
((stringp proc)
|
||||||
|
(start-process-shell-command
|
||||||
|
(format "org-async-%d" (cl-incf org-async--counter))
|
||||||
|
buffer proc))
|
||||||
|
((consp proc)
|
||||||
|
(apply #'start-process
|
||||||
|
(format "org-async-%s-%d"
|
||||||
|
(car proc) (cl-incf org-async--counter))
|
||||||
|
buffer proc))
|
||||||
|
(t (error "Async process input %S not a recognised format"
|
||||||
|
proc)))))
|
||||||
|
(timeout (or timeout org-async-timeout)))
|
||||||
|
(set-process-sentinel proc #'org-async--sentinel)
|
||||||
|
(when filter
|
||||||
|
(set-process-filter proc #'org-async--filter))
|
||||||
|
(when coding
|
||||||
|
(set-process-coding-system proc coding coding))
|
||||||
|
(push (list proc
|
||||||
|
:success success
|
||||||
|
:failure failure
|
||||||
|
:filter filter
|
||||||
|
:buffer (if (eq buffer t)
|
||||||
|
(cons :temp (generate-new-buffer " *temp*" t))
|
||||||
|
buffer)
|
||||||
|
:info info
|
||||||
|
:timeout timeout
|
||||||
|
:start-time (float-time))
|
||||||
|
org-async--stack)
|
||||||
|
(org-async--monitor t)
|
||||||
|
(car org-async--stack)))
|
||||||
|
;; Queue the task to be run later.
|
||||||
|
(t
|
||||||
|
(setq org-async--wait-queue
|
||||||
|
(append org-async--wait-queue
|
||||||
|
(list (list proc
|
||||||
|
:success success
|
||||||
|
:failure failure
|
||||||
|
:filter filter
|
||||||
|
:buffer buffer
|
||||||
|
:info info
|
||||||
|
:dir dir
|
||||||
|
:timeout timeout
|
||||||
|
:coding coding))))
|
||||||
|
(last org-async--wait-queue))))
|
||||||
|
|
||||||
|
(defvar org-async--blocking-tasks nil
|
||||||
|
"List of async tasks currently being waited on.")
|
||||||
|
|
||||||
|
(defun org-async-wait-for (&rest tasks)
|
||||||
|
"Block until every task of TASKS has finished (including callback tasks)."
|
||||||
|
(setq org-async--blocking-tasks tasks)
|
||||||
|
(while org-async--blocking-tasks
|
||||||
|
(dolist (task org-async--blocking-tasks)
|
||||||
|
(accept-process-output (car task)))))
|
||||||
|
|
||||||
|
(defun org-async--filter (process string)
|
||||||
|
"After PROCESS recieves STRING, call the async filter.
|
||||||
|
This is implementated to satisfy the filter function documentation in
|
||||||
|
`org-async-call'."
|
||||||
|
(when-let ((proc-info (alist-get process org-async--stack)))
|
||||||
|
(let ((filter (plist-get proc-info :filter))
|
||||||
|
(buffer (plist-get proc-info :buffer)))
|
||||||
|
(if buffer
|
||||||
|
(with-current-buffer buffer
|
||||||
|
(save-excursion
|
||||||
|
(goto-char (point-max))
|
||||||
|
(insert string))
|
||||||
|
(funcall filter process string (plist-get proc-info :info)))
|
||||||
|
(funcall filter process string (plist-get proc-info :info))))))
|
||||||
|
|
||||||
|
(defun org-async--sentinel (process _signal)
|
||||||
|
"Watch PROCESS for death, and cleanup accordingly.
|
||||||
|
When a signal is recieved, the status of PROCESS is checked.
|
||||||
|
Should the it have an exit status, with status code 0,
|
||||||
|
`org-async--cleanup-process' is run with the \"failed\" argument
|
||||||
|
unset. Should the process have finished in any other manner,
|
||||||
|
`org-async--cleanup-process' is run with the \"failed\" argument."
|
||||||
|
(pcase (process-status process)
|
||||||
|
((and 'exit (guard (= 0 (process-exit-status process))))
|
||||||
|
(org-async--cleanup-process process))
|
||||||
|
((or 'exit 'signal 'failed)
|
||||||
|
(org-async--cleanup-process process 'failed))))
|
||||||
|
|
||||||
|
(defun org-async--cleanup-process (process &optional failed)
|
||||||
|
"Remove PROCESS from the async stack, and run its callback.
|
||||||
|
If the exit code of PROCESS is zero and FAILED is non-nil, then
|
||||||
|
the success callback is run (via `org-async--execute-callback').
|
||||||
|
Otherwise, the failure callback is run."
|
||||||
|
(when (assq process org-async--stack)
|
||||||
|
(let* ((proc-info (cdr (assq process org-async--stack)))
|
||||||
|
(buffer-val (plist-get proc-info :buffer))
|
||||||
|
(proc-buf (if (consp buffer-val) (cdr buffer-val) buffer-val))
|
||||||
|
(blocking-p (cl-member process org-async--blocking-tasks :key #'car)))
|
||||||
|
(setq org-async--stack
|
||||||
|
(delq (assq process org-async--stack) org-async--stack))
|
||||||
|
;; Ensure that any filter is called on the final output
|
||||||
|
;; prior to the callbacks.
|
||||||
|
(while (accept-process-output process))
|
||||||
|
(org-async--execute-callback
|
||||||
|
(plist-get
|
||||||
|
proc-info
|
||||||
|
(if (and (not failed)
|
||||||
|
(= 0 (process-exit-status process)))
|
||||||
|
:success :failure))
|
||||||
|
(process-exit-status process)
|
||||||
|
proc-buf
|
||||||
|
(plist-get proc-info :info)
|
||||||
|
blocking-p)
|
||||||
|
(when blocking-p
|
||||||
|
(setq org-async--blocking-tasks
|
||||||
|
(cl-delete process org-async--blocking-tasks :key #'car)))
|
||||||
|
(when (and (consp buffer-val) (eq :temp (car buffer-val)))
|
||||||
|
(kill-buffer proc-buf)))
|
||||||
|
(when (and org-async--wait-queue
|
||||||
|
(< org-async-process-limit (length org-async--stack)))
|
||||||
|
(apply #'org-async-call (pop org-async--wait-queue)))))
|
||||||
|
|
||||||
|
(defun org-async--execute-callback (callback exit-code process-buffer info &optional blocking)
|
||||||
|
"Run CALLBACK with EXIT-CODE, PROCESS-BUFFER, and INFO.
|
||||||
|
CALLBACK can take one of four forms:
|
||||||
|
- A string, which is used a `message' string with EXIT-CODE,
|
||||||
|
PROCESS-BUFFER, and INFO as arguments.
|
||||||
|
- A function, which is called with EXIT-CODE, PROCESS-BUFFER,
|
||||||
|
and INFO as arguments.
|
||||||
|
- A list, which is either:
|
||||||
|
- An (org-async-task ...) structure, which passed to an
|
||||||
|
`org-async-call' invocation.
|
||||||
|
- A list of callbacks, which are individually evaluated.
|
||||||
|
- nil, which does nothing.
|
||||||
|
|
||||||
|
When BLOCKING is set, all callback tasks are made blocking."
|
||||||
|
(cond
|
||||||
|
((stringp callback)
|
||||||
|
(message callback exit-code process-buffer info))
|
||||||
|
((functionp callback)
|
||||||
|
(funcall callback exit-code process-buffer info))
|
||||||
|
((consp callback)
|
||||||
|
(if (eq (car callback) 'org-async-task)
|
||||||
|
(if blocking
|
||||||
|
(push (org-async-call callback) org-async--blocking-tasks)
|
||||||
|
(org-async-call callback))
|
||||||
|
(dolist (clbk callback)
|
||||||
|
(org-async--execute-callback clbk exit-code process-buffer info blocking))))
|
||||||
|
((null callback)) ; Do nothing.
|
||||||
|
(t (message "Ignoring invalid `org-async-call' callback: %S" callback))))
|
||||||
|
|
||||||
|
(defvar org-async--monitor-scheduled nil)
|
||||||
|
(defun org-async--monitor (&optional force)
|
||||||
|
"Check each process against their timeouts, and kill any overdue.
|
||||||
|
The only runs when `org-async--monitor-scheduled' is nil, unless FORCE is set.
|
||||||
|
Should any processes still be alive after checking the stack, this will run
|
||||||
|
itself using a timer in `org-async-check-timeout-interval' seconds."
|
||||||
|
(when (or force (null org-async--monitor-scheduled))
|
||||||
|
(dolist (stack-proc org-async--stack)
|
||||||
|
(if (process-live-p (car stack-proc))
|
||||||
|
(let ((timeout (plist-get (cdr stack-proc) :timeout)))
|
||||||
|
(when (and (numberp timeout)
|
||||||
|
(< 0 timeout
|
||||||
|
(- (float-time)
|
||||||
|
(plist-get (cdr stack-proc) :start-time))))
|
||||||
|
(kill-process (car stack-proc))))
|
||||||
|
(org-async--cleanup-process (car stack-proc))))
|
||||||
|
(if org-async--stack
|
||||||
|
(setq org-async--monitor-scheduled
|
||||||
|
(run-at-time org-async-check-timeout-interval
|
||||||
|
nil #'org-async--monitor t))
|
||||||
|
(setq org-async--monitor-scheduled nil))))
|
||||||
|
|
||||||
|
|
||||||
;;; File
|
;;; File
|
||||||
|
@ -991,7 +1263,7 @@ Otherwise, return nil."
|
||||||
"Splits STRING into substrings at SEPARATORS.
|
"Splits STRING into substrings at SEPARATORS.
|
||||||
|
|
||||||
SEPARATORS is a regular expression. When nil, it defaults to
|
SEPARATORS is a regular expression. When nil, it defaults to
|
||||||
\"[ \f\t\n\r\v]+\".
|
\"[ \\f\\t\\n\\r\\v]+\".
|
||||||
|
|
||||||
Unlike `split-string', matching SEPARATORS at the beginning and
|
Unlike `split-string', matching SEPARATORS at the beginning and
|
||||||
end of string are ignored."
|
end of string are ignored."
|
||||||
|
|
|
@ -470,7 +470,7 @@ agenda view showing the flagged items."
|
||||||
(insert "#+TAGS: " (mapconcat 'identity tags " ") "\n")
|
(insert "#+TAGS: " (mapconcat 'identity tags " ") "\n")
|
||||||
(insert "#+ALLPRIORITIES: " org-mobile-allpriorities "\n")
|
(insert "#+ALLPRIORITIES: " org-mobile-allpriorities "\n")
|
||||||
(when (file-exists-p (expand-file-name
|
(when (file-exists-p (expand-file-name
|
||||||
org-mobile-directory "agendas.org"))
|
"agendas.org" org-mobile-directory))
|
||||||
(insert "* [[file:agendas.org][Agenda Views]]\n"))
|
(insert "* [[file:agendas.org][Agenda Views]]\n"))
|
||||||
(pcase-dolist (`(,_ . ,link-name) files-alist)
|
(pcase-dolist (`(,_ . ,link-name) files-alist)
|
||||||
(insert (format "* [[file:%s][%s]]\n" link-name link-name)))
|
(insert (format "* [[file:%s][%s]]\n" link-name link-name)))
|
||||||
|
|
|
@ -389,7 +389,7 @@ The keys are conses of (container . associated).")
|
||||||
(defvar org-persist--index-age nil
|
(defvar org-persist--index-age nil
|
||||||
"The modification time of the index file, when it was loaded.")
|
"The modification time of the index file, when it was loaded.")
|
||||||
|
|
||||||
(defvar org-persist--report-time 0.5
|
(defvar org-persist--report-time nil
|
||||||
"Whether to report read/write time.
|
"Whether to report read/write time.
|
||||||
|
|
||||||
When the value is a number, it is a threshold number of seconds. If
|
When the value is a number, it is a threshold number of seconds. If
|
||||||
|
@ -602,22 +602,27 @@ MISC, if non-nil will be appended to the collection. It must be a plist."
|
||||||
|
|
||||||
;;;; Reading container data.
|
;;;; Reading container data.
|
||||||
|
|
||||||
|
(defvar org-persist--inhibit-container-normalization nil
|
||||||
|
"Prevent `org-persist--normalize-container' from doing anything.")
|
||||||
|
|
||||||
(defun org-persist--normalize-container (container &optional inner)
|
(defun org-persist--normalize-container (container &optional inner)
|
||||||
"Normalize CONTAINER representation into (type . settings).
|
"Normalize CONTAINER representation into (type . settings).
|
||||||
|
|
||||||
When INNER is non-nil, do not try to match as list of containers."
|
When INNER is non-nil, do not try to match as list of containers."
|
||||||
(pcase container
|
(if org-persist--inhibit-container-normalization
|
||||||
((or `elisp `elisp-data `version `file `index `url)
|
container
|
||||||
`(,container nil))
|
(pcase container
|
||||||
((or (pred keywordp) (pred stringp) `(quote . ,_))
|
((or `elisp `elisp-data `version `file `index `url)
|
||||||
`(elisp-data ,container))
|
`(,container nil))
|
||||||
((pred symbolp)
|
((or (pred keywordp) (pred stringp) `(quote . ,_))
|
||||||
`(elisp ,container))
|
`(elisp-data ,container))
|
||||||
(`(,(or `elisp `elisp-data `version `file `index `url) . ,_)
|
((pred symbolp)
|
||||||
container)
|
`(elisp ,container))
|
||||||
((and (pred listp) (guard (not inner)))
|
(`(,(or `elisp `elisp-data `version `file `index `url) . ,_)
|
||||||
(mapcar (lambda (c) (org-persist--normalize-container c 'inner)) container))
|
container)
|
||||||
(_ (error "org-persist: Unknown container type: %S" container))))
|
((and (pred listp) (guard (not inner)))
|
||||||
|
(mapcar (lambda (c) (org-persist--normalize-container c 'inner)) container))
|
||||||
|
(_ (error "org-persist: Unknown container type: %S" container)))))
|
||||||
|
|
||||||
(defvar org-persist--associated-buffer-cache (make-hash-table :weakness 'key)
|
(defvar org-persist--associated-buffer-cache (make-hash-table :weakness 'key)
|
||||||
"Buffer hash cache.")
|
"Buffer hash cache.")
|
||||||
|
@ -823,20 +828,23 @@ COLLECTION is the plist holding data collection."
|
||||||
(defun org-persist-write:file (c collection)
|
(defun org-persist-write:file (c collection)
|
||||||
"Write file container C according to COLLECTION."
|
"Write file container C according to COLLECTION."
|
||||||
(org-persist-collection-let collection
|
(org-persist-collection-let collection
|
||||||
(when (or (and path (file-exists-p path))
|
(if (or (and path (file-exists-p path))
|
||||||
(and (stringp (cadr c)) (file-exists-p (cadr c))))
|
(and (stringp (cadr c)) (file-exists-p (cadr c))))
|
||||||
(when (and (stringp (cadr c)) (file-exists-p (cadr c)))
|
(progn
|
||||||
(setq path (cadr c)))
|
(when (and (stringp (cadr c)) (file-exists-p (cadr c)))
|
||||||
(let* ((persist-file (plist-get collection :persist-file))
|
(setq path (cadr c)))
|
||||||
(ext (file-name-extension path))
|
(let* ((persist-file (plist-get collection :persist-file))
|
||||||
(file-copy (org-file-name-concat
|
(ext (file-name-extension path))
|
||||||
org-persist-directory
|
(file-copy (org-file-name-concat
|
||||||
(format "%s-%s.%s" persist-file (md5 path) ext))))
|
org-persist-directory
|
||||||
(unless (file-exists-p file-copy)
|
(format "%s-%s.%s" persist-file (md5 path) ext))))
|
||||||
(unless (file-exists-p (file-name-directory file-copy))
|
(unless (file-exists-p file-copy)
|
||||||
(make-directory (file-name-directory file-copy) t))
|
(unless (file-exists-p (file-name-directory file-copy))
|
||||||
(copy-file path file-copy 'overwrite))
|
(make-directory (file-name-directory file-copy) t))
|
||||||
(format "%s-%s.%s" persist-file (md5 path) ext)))))
|
(copy-file path file-copy 'overwrite))
|
||||||
|
(format "%s-%s.%s" persist-file (md5 path) ext)))
|
||||||
|
(when-let ((file-copy (org-persist-read c associated)))
|
||||||
|
(file-relative-name file-copy org-persist-directory)))))
|
||||||
|
|
||||||
(defun org-persist-write:url (c collection)
|
(defun org-persist-write:url (c collection)
|
||||||
"Write url container C according to COLLECTION."
|
"Write url container C according to COLLECTION."
|
||||||
|
@ -941,26 +949,27 @@ VALUE pairs.
|
||||||
When WRITE-IMMEDIATELY is non-nil, the return value will be the same
|
When WRITE-IMMEDIATELY is non-nil, the return value will be the same
|
||||||
with `org-persist-write'."
|
with `org-persist-write'."
|
||||||
(unless org-persist--index (org-persist--load-index))
|
(unless org-persist--index (org-persist--load-index))
|
||||||
(setq container (org-persist--normalize-container container))
|
(let ((container (org-persist--normalize-container container))
|
||||||
(when inherit
|
(inherit (and inherit (org-persist--normalize-container inherit)))
|
||||||
(setq inherit (org-persist--normalize-container inherit))
|
(org-persist--inhibit-container-normalization t))
|
||||||
(let ((inherited-collection (org-persist--get-collection inherit associated))
|
(when inherit
|
||||||
new-collection)
|
(let ((inherited-collection (org-persist--get-collection inherit associated))
|
||||||
(unless (member container (plist-get inherited-collection :container))
|
new-collection)
|
||||||
(setq new-collection
|
(unless (member container (plist-get inherited-collection :container))
|
||||||
(plist-put (copy-sequence inherited-collection) :container
|
(setq new-collection
|
||||||
(cons container (plist-get inherited-collection :container))))
|
(plist-put (copy-sequence inherited-collection) :container
|
||||||
(org-persist--remove-from-index inherited-collection)
|
(cons container (plist-get inherited-collection :container))))
|
||||||
(org-persist--add-to-index new-collection))))
|
(org-persist--remove-from-index inherited-collection)
|
||||||
(let ((collection (org-persist--get-collection container associated misc)))
|
(org-persist--add-to-index new-collection))))
|
||||||
(when (and expiry (not inherit))
|
(let ((collection (org-persist--get-collection container associated misc)))
|
||||||
(when expiry (plist-put collection :expiry expiry))))
|
(when (and expiry (not inherit))
|
||||||
(when (or (bufferp associated) (bufferp (plist-get associated :buffer)))
|
(when expiry (plist-put collection :expiry expiry))))
|
||||||
(with-current-buffer (if (bufferp associated)
|
(when (or (bufferp associated) (bufferp (plist-get associated :buffer)))
|
||||||
associated
|
(with-current-buffer (if (bufferp associated)
|
||||||
(plist-get associated :buffer))
|
associated
|
||||||
(add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local)))
|
(plist-get associated :buffer))
|
||||||
(when write-immediately (org-persist-write container associated)))
|
(add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local)))
|
||||||
|
(when write-immediately (org-persist-write container associated))))
|
||||||
|
|
||||||
(cl-defun org-persist-unregister (container &optional associated &key remove-related)
|
(cl-defun org-persist-unregister (container &optional associated &key remove-related)
|
||||||
"Unregister CONTAINER in ASSOCIATED to be persistent.
|
"Unregister CONTAINER in ASSOCIATED to be persistent.
|
||||||
|
@ -1075,6 +1084,9 @@ have the same meaning as in `org-persist-read'."
|
||||||
"Call `org-persist-load-all' in current buffer."
|
"Call `org-persist-load-all' in current buffer."
|
||||||
(org-persist-load-all (current-buffer)))
|
(org-persist-load-all (current-buffer)))
|
||||||
|
|
||||||
|
(defvar org-persist--inhibit-write nil
|
||||||
|
"Whether `org-persist-write' should be inhibited.")
|
||||||
|
|
||||||
(defun org-persist-write (container &optional associated ignore-return)
|
(defun org-persist-write (container &optional associated ignore-return)
|
||||||
"Save CONTAINER according to ASSOCIATED.
|
"Save CONTAINER according to ASSOCIATED.
|
||||||
ASSOCIATED can be a plist, a buffer, or a string.
|
ASSOCIATED can be a plist, a buffer, or a string.
|
||||||
|
@ -1084,31 +1096,33 @@ The return value is nil when writing fails and the written value (as
|
||||||
returned by `org-persist-read') on success.
|
returned by `org-persist-read') on success.
|
||||||
When IGNORE-RETURN is non-nil, just return t on success without calling
|
When IGNORE-RETURN is non-nil, just return t on success without calling
|
||||||
`org-persist-read'."
|
`org-persist-read'."
|
||||||
(setq associated (org-persist--normalize-associated associated))
|
(unless org-persist--inhibit-write
|
||||||
;; Update hash
|
(setq associated (org-persist--normalize-associated associated))
|
||||||
(when (and (plist-get associated :file)
|
;; Update hash
|
||||||
(plist-get associated :hash)
|
(when (and (plist-get associated :file)
|
||||||
(get-file-buffer (plist-get associated :file)))
|
(plist-get associated :hash)
|
||||||
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
|
(get-file-buffer (plist-get associated :file)))
|
||||||
(let ((collection (org-persist--get-collection container associated)))
|
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
|
||||||
(setf collection (plist-put collection :associated associated))
|
(let ((collection (org-persist--get-collection container associated))
|
||||||
(unless (or
|
(org-persist--inhibit-write t))
|
||||||
;; Prevent data leakage from encrypted files.
|
(setf collection (plist-put collection :associated associated))
|
||||||
;; We do it in somewhat paranoid manner and do not
|
(unless (or
|
||||||
;; allow anything related to encrypted files to be
|
;; Prevent data leakage from encrypted files.
|
||||||
;; written.
|
;; We do it in somewhat paranoid manner and do not
|
||||||
(and (plist-get associated :file)
|
;; allow anything related to encrypted files to be
|
||||||
(string-match-p epa-file-name-regexp (plist-get associated :file)))
|
;; written.
|
||||||
(seq-find (lambda (v)
|
(and (plist-get associated :file)
|
||||||
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
|
(string-match-p epa-file-name-regexp (plist-get associated :file)))
|
||||||
(plist-get collection :container)))
|
(cl-some (lambda (v)
|
||||||
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
|
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
|
||||||
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
|
(plist-get collection :container)))
|
||||||
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
|
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
|
||||||
(plist-get collection :container))))
|
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
|
||||||
(puthash file data org-persist--write-cache)
|
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
|
||||||
(org-persist--write-elisp-file file data)
|
(plist-get collection :container))))
|
||||||
(or ignore-return (org-persist-read container associated)))))))
|
(puthash file data org-persist--write-cache)
|
||||||
|
(org-persist--write-elisp-file file data)
|
||||||
|
(or ignore-return (org-persist-read container associated))))))))
|
||||||
|
|
||||||
(defun org-persist-write-all (&optional associated)
|
(defun org-persist-write-all (&optional associated)
|
||||||
"Save all the persistent data.
|
"Save all the persistent data.
|
||||||
|
|
|
@ -809,9 +809,42 @@ as `org-src-fontify-natively' is non-nil."
|
||||||
(1+ pt) (1- (point)) 'face 'org-inline-src-block)))
|
(1+ pt) (1- (point)) 'face 'org-inline-src-block)))
|
||||||
(font-lock-append-text-property
|
(font-lock-append-text-property
|
||||||
(1- (point)) (point) 'face '(org-inline-src-block shadow))
|
(1- (point)) (point) 'face '(org-inline-src-block shadow))
|
||||||
(setq pt (point)))))
|
(setq pt (point)))
|
||||||
|
(when (and org-inline-src-prettify-results
|
||||||
|
(re-search-forward "\\= {{{results(" limit t))
|
||||||
|
(font-lock-append-text-property pt (1+ pt) 'face 'org-inline-src-block)
|
||||||
|
(goto-char pt))))
|
||||||
t)))
|
t)))
|
||||||
|
|
||||||
|
(defun org-fontify-inline-src-results (limit)
|
||||||
|
"Apply prettify-symbols modifications to inline results blocks.
|
||||||
|
Performed according to `org-inline-src-prettify-results'."
|
||||||
|
(when (and org-inline-src-prettify-results
|
||||||
|
(re-search-forward "{{{results(\\(.+?\\))}}}" limit t))
|
||||||
|
(remove-list-of-text-properties (match-beginning 0) (point)
|
||||||
|
'(composition
|
||||||
|
prettify-symbols-start
|
||||||
|
prettify-symbols-end))
|
||||||
|
(font-lock-append-text-property (match-beginning 0) (match-end 0)
|
||||||
|
'face 'org-block)
|
||||||
|
(let ((start (match-beginning 0)) (end (match-beginning 1)))
|
||||||
|
(with-silent-modifications
|
||||||
|
(compose-region start end (if (eq org-inline-src-prettify-results t)
|
||||||
|
"(" (car org-inline-src-prettify-results)))
|
||||||
|
(add-text-properties start end `(prettify-symbols-start ,start prettify-symbols-end ,end))))
|
||||||
|
(let ((start (match-end 1)) (end (point)))
|
||||||
|
(with-silent-modifications
|
||||||
|
(compose-region start end (if (eq org-inline-src-prettify-results t)
|
||||||
|
")" (cdr org-inline-src-prettify-results)))
|
||||||
|
(add-text-properties start end `(prettify-symbols-start ,start prettify-symbols-end ,end))))
|
||||||
|
t))
|
||||||
|
|
||||||
|
(defun org-toggle-inline-results-display ()
|
||||||
|
"Toggle the literal or contracted display of inline src blocks results."
|
||||||
|
(interactive)
|
||||||
|
(setq org-inline-src-prettify-results (not org-inline-src-prettify-results))
|
||||||
|
(org-restart-font-lock))
|
||||||
|
|
||||||
|
|
||||||
;;; Escape contents
|
;;; Escape contents
|
||||||
|
|
||||||
|
|
|
@ -5494,25 +5494,38 @@ for a horizontal separator line, or a list of field values as strings.
|
||||||
The table is taken from the parameter TXT, or from the buffer at point."
|
The table is taken from the parameter TXT, or from the buffer at point."
|
||||||
(if txt
|
(if txt
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
|
(buffer-disable-undo)
|
||||||
(insert txt)
|
(insert txt)
|
||||||
(goto-char (point-min))
|
(goto-char (point-min))
|
||||||
(org-table-to-lisp))
|
(org-table-to-lisp))
|
||||||
(save-excursion
|
(save-excursion
|
||||||
(goto-char (org-table-begin))
|
(goto-char (org-table-begin))
|
||||||
(let ((table nil))
|
(let (table)
|
||||||
(while (re-search-forward "\\=[ \t]*|" nil t)
|
(while (progn (skip-chars-forward " \t")
|
||||||
(let ((row nil))
|
(eq (following-char) ?|))
|
||||||
(if (looking-at "-")
|
(forward-char)
|
||||||
(push 'hline table)
|
(push
|
||||||
(while (not (progn (skip-chars-forward " \t") (eolp)))
|
(if (eq (following-char) ?-)
|
||||||
(push (buffer-substring
|
'hline
|
||||||
(point)
|
(let (row)
|
||||||
(progn (re-search-forward "[ \t]*\\(|\\|$\\)")
|
(while (progn
|
||||||
(match-beginning 0)))
|
(skip-chars-forward " \t")
|
||||||
row))
|
(not (eolp)))
|
||||||
(push (nreverse row) table)))
|
(let ((q (point)))
|
||||||
|
(skip-chars-forward "^|\n")
|
||||||
|
(goto-char
|
||||||
|
(prog1
|
||||||
|
(let ((p (point)))
|
||||||
|
(unless (eolp) (setq p (1+ p)))
|
||||||
|
p)
|
||||||
|
(skip-chars-backward " \t" q)
|
||||||
|
;; Preserve text properties. They are used when
|
||||||
|
;; calculating cell width.
|
||||||
|
(push (buffer-substring q (point)) row)))))
|
||||||
|
(nreverse row)))
|
||||||
|
table)
|
||||||
(forward-line))
|
(forward-line))
|
||||||
(nreverse table)))))
|
(nreverse table)))))
|
||||||
|
|
||||||
(defun org-table-collapse-header (table &optional separator max-header-lines)
|
(defun org-table-collapse-header (table &optional separator max-header-lines)
|
||||||
"Collapse the lines before `hline' into a single header.
|
"Collapse the lines before `hline' into a single header.
|
||||||
|
|
891
lisp/org.el
891
lisp/org.el
File diff suppressed because it is too large
Load Diff
|
@ -148,6 +148,22 @@ which is replaced with the subtitle."
|
||||||
:package-version '(Org . "8.3")
|
:package-version '(Org . "8.3")
|
||||||
:type '(string :tag "Format string"))
|
:type '(string :tag "Format string"))
|
||||||
|
|
||||||
|
(defcustom org-beamer-frame-environment "orgframe"
|
||||||
|
"Name of the alternative beamer frame environment.
|
||||||
|
In frames marked as fragile, this environment is used in place of
|
||||||
|
the usual frame environment.
|
||||||
|
|
||||||
|
This permits insertion of a beamer frame inside example blocks,
|
||||||
|
working around beamer limitations. See
|
||||||
|
https://list.orgmode.org/87a5nux3zr.fsf@t14.reltub.ca/T/#mc7221e93f138bdd56c916b194b9230d3a6c3de09
|
||||||
|
|
||||||
|
This option may need to be changed when \"\\end{orgframe}\" string is
|
||||||
|
used inside beamer slides."
|
||||||
|
:group 'org-export-beamer
|
||||||
|
:package-version '(Org . "9.7")
|
||||||
|
:type '(string :tag "Beamer frame")
|
||||||
|
:safe (lambda (str) (string-match-p "^[A-Za-z]+$" str)))
|
||||||
|
|
||||||
|
|
||||||
;;; Internal Variables
|
;;; Internal Variables
|
||||||
|
|
||||||
|
@ -408,12 +424,14 @@ used as a communication channel."
|
||||||
"Format HEADLINE as a frame.
|
"Format HEADLINE as a frame.
|
||||||
CONTENTS holds the contents of the headline. INFO is a plist
|
CONTENTS holds the contents of the headline. INFO is a plist
|
||||||
used as a communication channel."
|
used as a communication channel."
|
||||||
(let ((fragilep
|
(let* ((fragilep
|
||||||
;; FRAGILEP is non-nil when HEADLINE contains an element
|
;; FRAGILEP is non-nil when HEADLINE contains an element
|
||||||
;; among `org-beamer-verbatim-elements'.
|
;; among `org-beamer-verbatim-elements'.
|
||||||
(org-element-map headline org-beamer-verbatim-elements 'identity
|
(org-element-map headline org-beamer-verbatim-elements 'identity
|
||||||
info 'first-match)))
|
info 'first-match))
|
||||||
(concat "\\begin{frame}"
|
(frame (or (and fragilep org-beamer-frame-environment)
|
||||||
|
"frame")))
|
||||||
|
(concat "\\begin{" frame "}"
|
||||||
;; Overlay specification, if any. When surrounded by
|
;; Overlay specification, if any. When surrounded by
|
||||||
;; square brackets, consider it as a default
|
;; square brackets, consider it as a default
|
||||||
;; specification.
|
;; specification.
|
||||||
|
@ -480,7 +498,7 @@ used as a communication channel."
|
||||||
;; output.
|
;; output.
|
||||||
(if (not fragilep) contents
|
(if (not fragilep) contents
|
||||||
(replace-regexp-in-string "\\`\n*" "\\& " (or contents "")))
|
(replace-regexp-in-string "\\`\n*" "\\& " (or contents "")))
|
||||||
"\\end{frame}")))
|
"\\end{" frame "}")))
|
||||||
|
|
||||||
(defun org-beamer--format-block (headline contents info)
|
(defun org-beamer--format-block (headline contents info)
|
||||||
"Format HEADLINE as a block.
|
"Format HEADLINE as a block.
|
||||||
|
@ -814,7 +832,6 @@ contextual information."
|
||||||
(org-export-get-reference radio-target info)
|
(org-export-get-reference radio-target info)
|
||||||
text))
|
text))
|
||||||
|
|
||||||
|
|
||||||
;;;; Template
|
;;;; Template
|
||||||
;;
|
;;
|
||||||
;; Template used is similar to the one used in `latex' backend,
|
;; Template used is similar to the one used in `latex' backend,
|
||||||
|
@ -830,10 +847,12 @@ holding export options."
|
||||||
;; Timestamp.
|
;; Timestamp.
|
||||||
(and (plist-get info :time-stamp-file)
|
(and (plist-get info :time-stamp-file)
|
||||||
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
|
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
|
||||||
;; LaTeX compiler
|
;; Document class, packages, and some configuration.
|
||||||
(org-latex--insert-compiler info)
|
|
||||||
;; Document class and packages.
|
|
||||||
(org-latex-make-preamble info)
|
(org-latex-make-preamble info)
|
||||||
|
;; Define the alternative frame environment.
|
||||||
|
(unless (equal "frame" org-beamer-frame-environment)
|
||||||
|
(format "\\newenvironment<>{%s}[1][]{\\begin{frame}#2[environment=%1$s,#1]}{\\end{frame}}\n"
|
||||||
|
org-beamer-frame-environment))
|
||||||
;; Insert themes.
|
;; Insert themes.
|
||||||
(let ((format-theme
|
(let ((format-theme
|
||||||
(lambda (prop command)
|
(lambda (prop command)
|
||||||
|
@ -881,12 +900,6 @@ holding export options."
|
||||||
(let ((template (plist-get info :latex-hyperref-template)))
|
(let ((template (plist-get info :latex-hyperref-template)))
|
||||||
(and (stringp template)
|
(and (stringp template)
|
||||||
(format-spec template (org-latex--format-spec info))))
|
(format-spec template (org-latex--format-spec info))))
|
||||||
;; engrave-faces-latex preamble
|
|
||||||
(when (and (eq (plist-get info :latex-src-block-backend) 'engraved)
|
|
||||||
(org-element-map (plist-get info :parse-tree)
|
|
||||||
'(src-block inline-src-block) #'identity
|
|
||||||
info t))
|
|
||||||
(org-latex-generate-engraved-preamble info))
|
|
||||||
;; Document start.
|
;; Document start.
|
||||||
"\\begin{document}\n\n"
|
"\\begin{document}\n\n"
|
||||||
;; Title command.
|
;; Title command.
|
||||||
|
|
371
lisp/ox-html.el
371
lisp/ox-html.el
|
@ -39,6 +39,8 @@
|
||||||
(require 'ox)
|
(require 'ox)
|
||||||
(require 'ox-publish)
|
(require 'ox-publish)
|
||||||
(require 'table)
|
(require 'table)
|
||||||
|
(require 'org-latex-preview)
|
||||||
|
(require 'ox-mathml)
|
||||||
|
|
||||||
|
|
||||||
;;; Function Declarations
|
;;; Function Declarations
|
||||||
|
@ -107,7 +109,8 @@
|
||||||
(verbatim . org-html-verbatim)
|
(verbatim . org-html-verbatim)
|
||||||
(verse-block . org-html-verse-block))
|
(verse-block . org-html-verse-block))
|
||||||
:filters-alist '((:filter-options . org-html-infojs-install-script)
|
:filters-alist '((:filter-options . org-html-infojs-install-script)
|
||||||
(:filter-parse-tree . org-html-image-link-filter)
|
(:filter-parse-tree org-html-image-link-filter
|
||||||
|
org-html-prepare-latex-images)
|
||||||
(:filter-final-output . org-html-final-function))
|
(:filter-final-output . org-html-final-function))
|
||||||
:menu-entry
|
:menu-entry
|
||||||
'(?h "Export to HTML"
|
'(?h "Export to HTML"
|
||||||
|
@ -155,6 +158,7 @@
|
||||||
(:html-infojs-template nil nil org-html-infojs-template)
|
(:html-infojs-template nil nil org-html-infojs-template)
|
||||||
(:html-inline-image-rules nil nil org-html-inline-image-rules)
|
(:html-inline-image-rules nil nil org-html-inline-image-rules)
|
||||||
(:html-link-org-files-as-html nil nil org-html-link-org-files-as-html)
|
(:html-link-org-files-as-html nil nil org-html-link-org-files-as-html)
|
||||||
|
(:html-latex-image-options nil nil org-html-latex-image-options)
|
||||||
(:html-mathjax-options nil nil org-html-mathjax-options)
|
(:html-mathjax-options nil nil org-html-mathjax-options)
|
||||||
(:html-mathjax-template nil nil org-html-mathjax-template)
|
(:html-mathjax-template nil nil org-html-mathjax-template)
|
||||||
(:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format)
|
(:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format)
|
||||||
|
@ -319,7 +323,7 @@ This affects IDs that are determined from the ID property.")
|
||||||
pre.src-awk:before { content: 'Awk'; }
|
pre.src-awk:before { content: 'Awk'; }
|
||||||
pre.src-authinfo::before { content: 'Authinfo'; }
|
pre.src-authinfo::before { content: 'Authinfo'; }
|
||||||
pre.src-C:before { content: 'C'; }
|
pre.src-C:before { content: 'C'; }
|
||||||
/* pre.src-C++ doesn't work in CSS */
|
pre.src-C\\+\\+:before { content: 'C++'; }
|
||||||
pre.src-clojure:before { content: 'Clojure'; }
|
pre.src-clojure:before { content: 'Clojure'; }
|
||||||
pre.src-css:before { content: 'CSS'; }
|
pre.src-css:before { content: 'CSS'; }
|
||||||
pre.src-D:before { content: 'D'; }
|
pre.src-D:before { content: 'D'; }
|
||||||
|
@ -822,7 +826,7 @@ e.g. \"tex:mathjax\". Allowed values are:
|
||||||
be loaded.
|
be loaded.
|
||||||
`html' Use `org-latex-to-html-convert-command' to convert
|
`html' Use `org-latex-to-html-convert-command' to convert
|
||||||
LaTeX fragments to HTML.
|
LaTeX fragments to HTML.
|
||||||
SYMBOL Any symbol defined in `org-preview-latex-process-alist',
|
SYMBOL Any symbol defined in `org-latex-preview-process-alist',
|
||||||
e.g., `dvipng'."
|
e.g., `dvipng'."
|
||||||
:group 'org-export-html
|
:group 'org-export-html
|
||||||
:version "24.4"
|
:version "24.4"
|
||||||
|
@ -1168,6 +1172,25 @@ See `format-time-string' for more information on its components."
|
||||||
:package-version '(Org . "8.0")
|
:package-version '(Org . "8.0")
|
||||||
:type 'string)
|
:type 'string)
|
||||||
|
|
||||||
|
(defcustom org-html-latex-image-options
|
||||||
|
'(:foreground "Black" :background "Transparent"
|
||||||
|
:page-width 1.0 :scale 1.0 :image-dir "ltximg" :inline nil)
|
||||||
|
"LaTeX preview options that apply to generated images.
|
||||||
|
This is a HTML-specific counterpart to
|
||||||
|
`org-latex-preview-appearance-options', which see.
|
||||||
|
|
||||||
|
This supports two extra properties,
|
||||||
|
:image-dir an html-export counterpart of `org-latex-preview-cache', and
|
||||||
|
:inline a list of image formats (or single format symbol) that
|
||||||
|
should not be saved according to :image-dir, but instead
|
||||||
|
inlined in the generated HTML. Valid format symbols are:
|
||||||
|
- png, to inline png images using <img> with a data URI
|
||||||
|
- svg, to inline svg images using <img> with a data URI
|
||||||
|
- svg-embed, to inline svg images using an <svg> element"
|
||||||
|
:group 'org-export-html
|
||||||
|
:package-version '(Org . "9.7")
|
||||||
|
:type 'plist)
|
||||||
|
|
||||||
;;;; Template :: Mathjax
|
;;;; Template :: Mathjax
|
||||||
|
|
||||||
(defcustom org-html-mathjax-options
|
(defcustom org-html-mathjax-options
|
||||||
|
@ -1653,6 +1676,36 @@ https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag"
|
||||||
:package-version '(Org . "9.1")
|
:package-version '(Org . "9.1")
|
||||||
:type 'string)
|
:type 'string)
|
||||||
|
|
||||||
|
;;;; LaTeX Fragments
|
||||||
|
|
||||||
|
(defcustom org-latex-to-html-convert-command nil
|
||||||
|
"Shell command to convert LaTeX fragments to HTML.
|
||||||
|
This command is very open-ended: the output of the command will
|
||||||
|
directly replace the LaTeX fragment in the resulting HTML.
|
||||||
|
Replace format-specifiers in the command as noted below and use
|
||||||
|
`shell-command' to convert LaTeX to HTML.
|
||||||
|
%i: The LaTeX fragment to be converted.
|
||||||
|
|
||||||
|
For example, this could be used with LaTeXML as
|
||||||
|
\"latexmlc \\='literal:%i\\=' --profile=math --preload=siunitx.sty 2>/dev/null\".
|
||||||
|
|
||||||
|
The LaTeX fragment is replaced as is, without escaping special shell
|
||||||
|
syntax. It may be necessary to use single-quotes around \\='%i\\=', not
|
||||||
|
double-quotes. Else a math fragment such as \"$y = 200$\" may be
|
||||||
|
expanded to \" = 200\"."
|
||||||
|
:group 'org-latex
|
||||||
|
:package-version '(Org . "9.4")
|
||||||
|
:type '(choice
|
||||||
|
(const :tag "None" nil)
|
||||||
|
(string :tag "Shell command")))
|
||||||
|
|
||||||
|
(defun org-format-latex-as-html (latex-fragment)
|
||||||
|
"Convert LATEX-FRAGMENT to HTML.
|
||||||
|
This uses `org-latex-to-html-convert-command', which see."
|
||||||
|
(let ((cmd (format-spec org-latex-to-html-convert-command
|
||||||
|
`((?i . ,latex-fragment)))))
|
||||||
|
(message "Running %s" cmd)
|
||||||
|
(shell-command-to-string cmd)))
|
||||||
|
|
||||||
;;;; Todos
|
;;;; Todos
|
||||||
|
|
||||||
|
@ -1777,11 +1830,7 @@ a communication channel."
|
||||||
(org-html--make-attribute-string
|
(org-html--make-attribute-string
|
||||||
(org-combine-plists
|
(org-combine-plists
|
||||||
(list :src source
|
(list :src source
|
||||||
:alt (if (string-match-p
|
:alt (file-name-nondirectory source))
|
||||||
(concat "^" org-preview-latex-image-directory) source)
|
|
||||||
(org-html-encode-plain-text
|
|
||||||
(org-find-text-property-in-string 'org-latex-src source))
|
|
||||||
(file-name-nondirectory source)))
|
|
||||||
(if (string= "svg" (file-name-extension source))
|
(if (string= "svg" (file-name-extension source))
|
||||||
(org-combine-plists '(:class "org-svg") attributes '(:fallback nil))
|
(org-combine-plists '(:class "org-svg") attributes '(:fallback nil))
|
||||||
attributes)))
|
attributes)))
|
||||||
|
@ -2994,58 +3043,102 @@ CONTENTS is nil. INFO is a plist holding contextual information."
|
||||||
|
|
||||||
;;;; LaTeX Environment
|
;;;; LaTeX Environment
|
||||||
|
|
||||||
(defun org-html-format-latex (latex-frag processing-type info)
|
(defun org-html-prepare-latex-images (parse-tree _backend info)
|
||||||
"Format a LaTeX fragment LATEX-FRAG into HTML.
|
"Make sure that appropriate preview images exist for all LaTeX.
|
||||||
PROCESSING-TYPE designates the tool used for conversion. It can
|
TODO."
|
||||||
be `mathjax', `verbatim', `html', nil, t or symbols in
|
(when (assq (plist-get info :with-latex) org-latex-preview-process-alist)
|
||||||
`org-preview-latex-process-alist', e.g., `dvipng', `dvisvgm' or
|
(let* ((latex-preamble
|
||||||
`imagemagick'. See `org-html-with-latex' for more information.
|
(or org-latex-preview--preamble-content
|
||||||
INFO is a plist containing export properties."
|
(setq org-latex-preview--preamble-content
|
||||||
(let ((cache-relpath "") (cache-dir ""))
|
(org-latex-preview--get-preamble))))
|
||||||
(unless (or (eq processing-type 'mathjax)
|
(elements
|
||||||
(eq processing-type 'html))
|
(org-element-map parse-tree
|
||||||
(let ((bfn (or (buffer-file-name)
|
'(latex-fragment latex-environment)
|
||||||
(make-temp-name
|
#'identity
|
||||||
(expand-file-name "latex" temporary-file-directory))))
|
info))
|
||||||
(latex-header
|
(entries-and-numbering
|
||||||
(let ((header (plist-get info :latex-header)))
|
(org-latex-preview--construct-entries
|
||||||
(and header
|
elements t parse-tree))
|
||||||
(concat (mapconcat
|
(processing-type (plist-get info :with-latex))
|
||||||
(lambda (line) (concat "#+LATEX_HEADER: " line))
|
(processing-info
|
||||||
(org-split-string header "\n")
|
(cdr (assq processing-type org-latex-preview-process-alist)))
|
||||||
"\n")
|
(imagetype (or (plist-get processing-info :image-output-type) "png"))
|
||||||
"\n")))))
|
(numbering-offsets (cons nil (cadr entries-and-numbering)))
|
||||||
(setq cache-relpath
|
(html-options (plist-get info :html-latex-image-options))
|
||||||
(concat (file-name-as-directory org-preview-latex-image-directory)
|
(element-hash-table (make-hash-table :test #'eq :size (length elements)))
|
||||||
(file-name-sans-extension
|
fragment-info prev-fg prev-bg)
|
||||||
(file-name-nondirectory bfn)))
|
(cl-loop
|
||||||
cache-dir (file-name-directory bfn))
|
for entry in (car entries-and-numbering)
|
||||||
;; Re-create LaTeX environment from original buffer in
|
for element in elements
|
||||||
;; temporary buffer so that dvipng/imagemagick can properly
|
do
|
||||||
;; turn the fragment into an image.
|
(pcase-let* ((`(,beg ,end ,provided-value) entry)
|
||||||
(setq latex-frag (concat latex-header latex-frag))))
|
(value (or provided-value
|
||||||
(org-export-with-buffer-copy
|
(buffer-substring-no-properties beg end)))
|
||||||
:to-buffer (get-buffer-create " *Org HTML Export LaTeX*")
|
(fg (plist-get html-options :foreground))
|
||||||
:drop-visibility t :drop-narrowing t :drop-contents t
|
(bg (plist-get html-options :background))
|
||||||
(erase-buffer)
|
(number (car (setq numbering-offsets (cdr numbering-offsets))))
|
||||||
(insert latex-frag)
|
(hash (org-latex-preview--hash
|
||||||
(org-format-latex cache-relpath nil nil cache-dir nil
|
processing-type latex-preamble value imagetype fg bg number))
|
||||||
"Creating LaTeX Image..." nil processing-type)
|
(options (org-combine-plists
|
||||||
(buffer-string))))
|
org-latex-preview-appearance-options
|
||||||
|
html-options
|
||||||
|
(list :number number
|
||||||
|
:continue-color
|
||||||
|
(and (equal prev-bg bg)
|
||||||
|
(equal prev-fg fg))))))
|
||||||
|
(puthash element hash element-hash-table)
|
||||||
|
(unless (org-latex-preview--get-cached hash)
|
||||||
|
(push (list :string (org-latex-preview--tex-styled
|
||||||
|
processing-type value options)
|
||||||
|
:overlay (org-latex-preview--ensure-overlay beg end)
|
||||||
|
:key hash)
|
||||||
|
fragment-info))
|
||||||
|
(setq prev-fg fg prev-bg bg)))
|
||||||
|
(when fragment-info
|
||||||
|
(apply #'org-async-wait-for
|
||||||
|
(org-latex-preview--create-image-async
|
||||||
|
processing-type
|
||||||
|
(nreverse fragment-info)
|
||||||
|
:latex-preamble latex-preamble
|
||||||
|
:appearance-options html-options)))
|
||||||
|
(plist-put info :html-latex-preview-hash-table element-hash-table)
|
||||||
|
nil)))
|
||||||
|
|
||||||
(defun org-html--wrap-latex-environment (contents _ &optional caption label)
|
(defun org-html--as-latex (element info &optional content)
|
||||||
|
(let ((content (or content (org-element-property :value element))))
|
||||||
|
(pcase (plist-get info :with-latex)
|
||||||
|
('verbatim ; Do nothing.
|
||||||
|
content)
|
||||||
|
((or 't 'mathjax)
|
||||||
|
(cond ; Prepare for MathJax processing.
|
||||||
|
((string-match-p "\\`\\$\\$" content)
|
||||||
|
(concat "\\[" (substring content 2 -2) "\\]"))
|
||||||
|
((string-match-p "\\`\\$" content)
|
||||||
|
(concat "\\(" (substring content 1 -1) "\\)"))
|
||||||
|
(t content)))
|
||||||
|
('html
|
||||||
|
(org-format-latex-as-html content))
|
||||||
|
('mathml
|
||||||
|
(if-let ((path (org-mathml-convert-latex-cached content)))
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents path)
|
||||||
|
(buffer-string))
|
||||||
|
content))
|
||||||
|
((and ptype (guard (assq ptype org-latex-preview-process-alist)))
|
||||||
|
(org-html-latex-image element info))
|
||||||
|
(processing-type
|
||||||
|
(warn "LaTeX fragment processor `%s' is unknown" processing-type)
|
||||||
|
content))))
|
||||||
|
|
||||||
|
(defun org-html--wrap-latex-environment (contents &optional label)
|
||||||
"Wrap CONTENTS string within appropriate environment for equations.
|
"Wrap CONTENTS string within appropriate environment for equations.
|
||||||
When optional arguments CAPTION and LABEL are given, use them for
|
When optional arguments CAPTION and LABEL are given, use them for
|
||||||
caption and \"id\" attribute."
|
caption and \"id\" attribute."
|
||||||
(format "\n<div%s class=\"equation-container\">\n%s%s\n</div>"
|
(format "\n<div%s class=\"equation-container\">\n%s\n</div>"
|
||||||
;; ID.
|
;; ID.
|
||||||
(if (org-string-nw-p label) (format " id=\"%s\"" label) "")
|
(if (org-string-nw-p label) (format " id=\"%s\"" label) "")
|
||||||
;; Contents.
|
;; Contents.
|
||||||
(format "<span class=\"equation\">\n%s\n</span>" contents)
|
(format "<span class=\"equation\">\n%s\n</span>" contents)))
|
||||||
;; Caption.
|
|
||||||
(if (not (org-string-nw-p caption)) ""
|
|
||||||
(format "\n<span class=\"equation-label\">\n%s\n</span>"
|
|
||||||
caption))))
|
|
||||||
|
|
||||||
(defun org-html--math-environment-p (element &optional _)
|
(defun org-html--math-environment-p (element &optional _)
|
||||||
"Non-nil when ELEMENT is a LaTeX math environment.
|
"Non-nil when ELEMENT is a LaTeX math environment.
|
||||||
|
@ -3077,57 +3170,141 @@ For instance, change an `equation' environment to `equation*'."
|
||||||
"Transcode a LATEX-ENVIRONMENT element from Org to HTML.
|
"Transcode a LATEX-ENVIRONMENT element from Org to HTML.
|
||||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||||
(let ((processing-type (plist-get info :with-latex))
|
(let ((processing-type (plist-get info :with-latex))
|
||||||
(latex-frag (org-remove-indentation
|
(latex-frag (org-remove-indentation
|
||||||
(org-element-property :value latex-environment)))
|
(org-element-property :value latex-environment)))
|
||||||
(attributes (org-export-read-attribute :attr_html latex-environment))
|
(label (org-html--reference latex-environment info t)))
|
||||||
(label (org-html--reference latex-environment info t))
|
(if (memq processing-type '(t mathjax))
|
||||||
(caption (and (org-html--latex-environment-numbered-p latex-environment)
|
(org-html--as-latex
|
||||||
(number-to-string
|
latex-environment info
|
||||||
(org-export-get-ordinal
|
(if (org-string-nw-p label)
|
||||||
latex-environment info nil
|
(replace-regexp-in-string "\\`.*"
|
||||||
(lambda (l _)
|
(format "\\&\n\\\\label{%s}" label)
|
||||||
(and (org-html--math-environment-p l)
|
latex-frag)
|
||||||
(org-html--latex-environment-numbered-p l))))))))
|
latex-frag))
|
||||||
(cond
|
(org-html--wrap-latex-environment
|
||||||
((memq processing-type '(t mathjax))
|
(org-html--as-latex latex-environment info latex-frag)
|
||||||
(org-html-format-latex
|
label))))
|
||||||
(if (org-string-nw-p label)
|
|
||||||
(replace-regexp-in-string "\\`.*"
|
|
||||||
(format "\\&\n\\\\label{%s}" label)
|
|
||||||
latex-frag)
|
|
||||||
latex-frag)
|
|
||||||
'mathjax info))
|
|
||||||
((assq processing-type org-preview-latex-process-alist)
|
|
||||||
(let ((formula-link
|
|
||||||
(org-html-format-latex
|
|
||||||
(org-html--unlabel-latex-environment latex-frag)
|
|
||||||
processing-type info)))
|
|
||||||
(when (and formula-link (string-match "file:\\([^]]*\\)" formula-link))
|
|
||||||
(let ((source (org-export-file-uri (match-string 1 formula-link))))
|
|
||||||
(org-html--wrap-latex-environment
|
|
||||||
(org-html--format-image source attributes info)
|
|
||||||
info caption label)))))
|
|
||||||
(t (org-html--wrap-latex-environment latex-frag info caption label)))))
|
|
||||||
|
|
||||||
;;;; LaTeX Fragment
|
;;;; LaTeX Fragment
|
||||||
|
|
||||||
(defun org-html-latex-fragment (latex-fragment _contents info)
|
(defun org-html-latex-fragment (latex-fragment _contents info)
|
||||||
"Transcode a LATEX-FRAGMENT object from Org to HTML.
|
"Transcode a LATEX-FRAGMENT object from Org to HTML.
|
||||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||||
(let ((latex-frag (org-element-property :value latex-fragment))
|
(org-html--as-latex latex-fragment info))
|
||||||
(processing-type (plist-get info :with-latex)))
|
|
||||||
|
(defun org-html-latex-image (element info)
|
||||||
|
"Transcode the LaTeX fragment or environment ELEMENT from Org to HTML.
|
||||||
|
INFO is a plist holding contextual information, and it is assumed
|
||||||
|
that an image for ELEMENT already exists within it."
|
||||||
|
(let* ((hash (or (gethash element (plist-get info :html-latex-preview-hash-table))
|
||||||
|
(error "Expected LaTeX preview hash to exist for element, but none found")))
|
||||||
|
(path-info (or (org-latex-preview--get-cached hash)
|
||||||
|
(error "Expected LaTeX preview %S to exist in the cache" hash)))
|
||||||
|
(image-options (plist-get info :html-latex-image-options))
|
||||||
|
(block-p (memq (aref (org-element-property :value element) 1) '(?$ ?\[)))
|
||||||
|
(image-source
|
||||||
|
(org-html-latex-image--data path-info hash info block-p)))
|
||||||
|
(unless (and (plist-get (cdr path-info) :height)
|
||||||
|
(plist-get (cdr path-info) :depth))
|
||||||
|
(error "Something went wrong during image generation"))
|
||||||
|
(if (and (eq (plist-get image-options :inline) 'svg-embed)
|
||||||
|
(eq (plist-get (cdr path-info) :image-type) 'svg))
|
||||||
|
image-source
|
||||||
|
(let ((scaling (org-html-latex-image--scaling path-info info)))
|
||||||
|
(org-html-close-tag
|
||||||
|
"img"
|
||||||
|
(org-html--make-attribute-string
|
||||||
|
(list :src image-source
|
||||||
|
:alt (org-html-encode-plain-text
|
||||||
|
(org-element-property :value element))
|
||||||
|
:style (if block-p
|
||||||
|
(format "height: %.4fem; display: block" (plist-get scaling :height))
|
||||||
|
(format "height: %.4fem; vertical-align: -%.4fem; display: inline-block"
|
||||||
|
(plist-get scaling :height) (plist-get scaling :depth)))
|
||||||
|
:class (format "org-latex org-latex-%s" (if block-p "block" "inline"))))
|
||||||
|
info)))))
|
||||||
|
|
||||||
|
(defun org-html-latex-image--scaling (image-path-info info)
|
||||||
|
"Determine the appropriate (<height> . <depth>) of IMAGE-PATH-INFO given INFO."
|
||||||
|
(let* ((image-options (plist-get info :html-latex-image-options))
|
||||||
|
(rescale-factor (if (eq (plist-get (cdr image-path-info) :image-type) 'svg)
|
||||||
|
(plist-get image-options :scale)
|
||||||
|
1)))
|
||||||
|
(list :height (* rescale-factor (plist-get (cdr image-path-info) :height))
|
||||||
|
:depth (* rescale-factor (plist-get (cdr image-path-info) :depth)))))
|
||||||
|
|
||||||
|
(defun org-html-latex-image--data (image-path-info hash info &optional block-p)
|
||||||
|
"Obtaine the image source for IMAGE-PATH-INFO as a string.
|
||||||
|
This can take the form of a path, data URI, or <svg> element
|
||||||
|
depending on HASH and INFO. BLOCK-P signals that the image
|
||||||
|
should be a block element."
|
||||||
|
(let* ((image-options (plist-get info :html-latex-image-options))
|
||||||
|
(inline-condition (plist-get image-options :inline))
|
||||||
|
(image-dir (plist-get image-options :image-dir))
|
||||||
|
(image-format (plist-get (cdr image-path-info) :image-type))
|
||||||
|
(source-file (car image-path-info)))
|
||||||
(cond
|
(cond
|
||||||
((memq processing-type '(t mathjax))
|
((or inline-condition
|
||||||
(org-html-format-latex latex-frag 'mathjax info))
|
(member (file-name-extension source-file)
|
||||||
((memq processing-type '(t html))
|
(org-ensure-list inline-condition)))
|
||||||
(org-html-format-latex latex-frag 'html info))
|
(let ((coding-system-for-read 'utf-8)
|
||||||
((assq processing-type org-preview-latex-process-alist)
|
(file-name-handler-alist nil))
|
||||||
(let ((formula-link
|
(with-temp-buffer
|
||||||
(org-html-format-latex latex-frag processing-type info)))
|
(insert-file-contents-literally source-file)
|
||||||
(when (and formula-link (string-match "file:\\([^]]*\\)" formula-link))
|
(cond
|
||||||
(let ((source (org-export-file-uri (match-string 1 formula-link))))
|
((and (eq inline-condition 'svg-embed)
|
||||||
(org-html--format-image source nil info)))))
|
(eq image-format 'svg))
|
||||||
(t latex-frag))))
|
(goto-char (point-min))
|
||||||
|
(let ((svg-closing-tag (and (search-forward "<svg" nil t)
|
||||||
|
(search-forward ">" nil t))))
|
||||||
|
|
||||||
|
(dolist (search '("<!-- This file was generated by dvisvgm [^\n]+ -->"
|
||||||
|
" height=['\"][^\"']+[\"']"
|
||||||
|
" width=['\"][^\"']+[\"']"))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when (re-search-forward search svg-closing-tag t)
|
||||||
|
(replace-match "")))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when (re-search-forward "viewBox=['\"][^\"']+[\"']" svg-closing-tag t)
|
||||||
|
(insert
|
||||||
|
" style=\""
|
||||||
|
(let ((scaling (org-html-latex-image--scaling image-path-info info)))
|
||||||
|
(if block-p
|
||||||
|
(format "height: %.4fem; display: block" (plist-get scaling :height))
|
||||||
|
(format "height: %.4fem; vertical-align: -%.4fem; display: inline-block"
|
||||||
|
(plist-get scaling :height) (plist-get scaling :depth))))
|
||||||
|
"\" class=\"org-latex org-latex-"
|
||||||
|
(if block-p "block" "inline")
|
||||||
|
"\"")))
|
||||||
|
(buffer-string))
|
||||||
|
((eq image-format 'svg)
|
||||||
|
;; Modelled after <https://codepen.io/tigt/post/optimizing-svgs-in-data-uris>.
|
||||||
|
(concat "data:image/svg+xml,"
|
||||||
|
(url-hexify-string
|
||||||
|
(subst-char-in-string ?\" ?\' (buffer-string))
|
||||||
|
'(?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n
|
||||||
|
?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z ?A ?B
|
||||||
|
?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P
|
||||||
|
?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z ?0 ?1 ?2 ?3
|
||||||
|
?4 ?5 ?6 ?7 ?8 ?9 ?- ?_ ?. ?~
|
||||||
|
;;Special additions
|
||||||
|
?\s ?= ?: ?/))))
|
||||||
|
(t
|
||||||
|
(base64-encode-region (point-min) (point-max))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(insert "data:image/" (symbol-name image-format) ";base64,")
|
||||||
|
(buffer-string))))))
|
||||||
|
((stringp image-dir)
|
||||||
|
(let* ((image-dir (expand-file-name image-dir))
|
||||||
|
(image-path (file-name-with-extension
|
||||||
|
(file-name-concat image-dir (substring hash 0 11))
|
||||||
|
(file-name-extension source-file))))
|
||||||
|
(unless (file-directory-p image-dir)
|
||||||
|
(mkdir image-dir t))
|
||||||
|
(unless (file-exists-p image-path)
|
||||||
|
(copy-file source-file image-path))
|
||||||
|
image-path))
|
||||||
|
(t source-file))))
|
||||||
|
|
||||||
;;;; Line Break
|
;;;; Line Break
|
||||||
|
|
||||||
|
|
676
lisp/ox-latex.el
676
lisp/ox-latex.el
|
@ -48,6 +48,7 @@
|
||||||
(defvar engrave-faces-latex-output-style)
|
(defvar engrave-faces-latex-output-style)
|
||||||
(defvar engrave-faces-current-preset-style)
|
(defvar engrave-faces-current-preset-style)
|
||||||
(defvar engrave-faces-latex-mathescape)
|
(defvar engrave-faces-latex-mathescape)
|
||||||
|
(defvar engrave-faces-latex-colorbox-strut)
|
||||||
|
|
||||||
|
|
||||||
;;; Define Backend
|
;;; Define Backend
|
||||||
|
@ -110,9 +111,9 @@
|
||||||
(?l "As LaTeX file" org-latex-export-to-latex)
|
(?l "As LaTeX file" org-latex-export-to-latex)
|
||||||
(?p "As PDF file" org-latex-export-to-pdf)
|
(?p "As PDF file" org-latex-export-to-pdf)
|
||||||
(?o "As PDF file and open"
|
(?o "As PDF file and open"
|
||||||
(lambda (a s v b)
|
(lambda (async subtreep visable-only body-only)
|
||||||
(if a (org-latex-export-to-pdf t s v b)
|
(if async (org-latex-export-to-pdf t subtreep visable-only body-only)
|
||||||
(org-open-file (org-latex-export-to-pdf nil s v b)))))))
|
(org-latex-export-to-pdf-and-open nil subtreep visable-only body-only))))))
|
||||||
:filters-alist '((:filter-options . org-latex-math-block-options-filter)
|
:filters-alist '((:filter-options . org-latex-math-block-options-filter)
|
||||||
(:filter-paragraph . org-latex-clean-invalid-line-breaks)
|
(:filter-paragraph . org-latex-clean-invalid-line-breaks)
|
||||||
(:filter-parse-tree org-latex-math-block-tree-filter
|
(:filter-parse-tree org-latex-math-block-tree-filter
|
||||||
|
@ -170,7 +171,83 @@
|
||||||
(:latex-toc-command nil nil org-latex-toc-command)
|
(:latex-toc-command nil nil org-latex-toc-command)
|
||||||
(:latex-compiler "LATEX_COMPILER" nil org-latex-compiler)
|
(:latex-compiler "LATEX_COMPILER" nil org-latex-compiler)
|
||||||
;; Redefine regular options.
|
;; Redefine regular options.
|
||||||
(:date "DATE" nil "\\today" parse)))
|
(:date "DATE" nil "\\today" parse))
|
||||||
|
:feature-conditions-alist
|
||||||
|
`((t !announce-start !announce-end
|
||||||
|
!guess-pollyglossia !guess-babel !guess-inputenc)
|
||||||
|
(,(lambda (info)
|
||||||
|
;; Since amsmath is added unconditionally when using
|
||||||
|
;; xelatex/lualatex (see `org-latex-default-packages-alist'),
|
||||||
|
;; and amssymb is not needed, we need not bother when using
|
||||||
|
;; thoese compilers.
|
||||||
|
(and (not (member (plist-get info :latex-compiler) '("xelatex" "lualatex")))
|
||||||
|
(org-element-map (plist-get info :parse-tree)
|
||||||
|
'(latex-fragment latex-environment) #'identity info t)))
|
||||||
|
maths)
|
||||||
|
(,(lambda (info)
|
||||||
|
(org-element-map (plist-get info :parse-tree)
|
||||||
|
'underline #'identity info t))
|
||||||
|
underline)
|
||||||
|
("\\\\uu?line\\|\\\\uwave\\|\\\\sout\\|\\\\xout\\|\\\\dashuline\\|\\dotuline\\|\\markoverwith"
|
||||||
|
underline)
|
||||||
|
(,(lambda (info)
|
||||||
|
(org-element-map (plist-get info :parse-tree)
|
||||||
|
'link
|
||||||
|
(lambda (link)
|
||||||
|
(and (member (org-element-property :type link)
|
||||||
|
'("http" "https" "ftp" "file"))
|
||||||
|
(file-name-extension (org-element-property :path link))
|
||||||
|
(equal (downcase (file-name-extension
|
||||||
|
(org-element-property :path link)))
|
||||||
|
"svg")))
|
||||||
|
info t))
|
||||||
|
svg)
|
||||||
|
(org-latex-tables-booktabs booktabs)
|
||||||
|
(,(lambda (info)
|
||||||
|
(equal (plist-get info :latex-default-table-environment)
|
||||||
|
"longtable"))
|
||||||
|
longtable)
|
||||||
|
("^[ \t]*\\+attr_latex: .*:environment +longtable"
|
||||||
|
longtable)
|
||||||
|
(,(lambda (info)
|
||||||
|
(eq (plist-get info :latex-src-block-backend) 'engraved))
|
||||||
|
engraved-code)
|
||||||
|
("^[ \t]*#\\+attr_latex: .*:float +wrap"
|
||||||
|
float-wrap)
|
||||||
|
("^[ \t]*#\\+attr_latex: .*:float +sideways"
|
||||||
|
rotate)
|
||||||
|
("^[ \t]*#\\+caption\\(?:\\[.*\\]\\)?:\\|\\\\caption{" caption))
|
||||||
|
:feature-implementations-alist
|
||||||
|
`((!announce-start
|
||||||
|
:snippet ,(lambda (info)
|
||||||
|
(with-temp-buffer
|
||||||
|
(setq-local left-margin 2)
|
||||||
|
(insert (string-join
|
||||||
|
(mapcar #'symbol-name
|
||||||
|
(plist-get info :features))
|
||||||
|
", ")
|
||||||
|
".")
|
||||||
|
(fill-region-as-paragraph (point-min) (point-max))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(insert "%% ox-latex features:\n% ")
|
||||||
|
(while (search-forward "\n" nil t)
|
||||||
|
(insert "%"))
|
||||||
|
(buffer-string)))
|
||||||
|
:order -100)
|
||||||
|
(maths :snippet "\\usepackage{amsmath}\n\\usepackage{amssymb}" :order 0.2)
|
||||||
|
(underline :snippet "\\usepackage[normalem]{ulem}" :order 0.5)
|
||||||
|
(image :snippet "\\usepackage{graphicx}" :order 2)
|
||||||
|
(svg :snippet "\\usepackage[inkscapelatex=false]{svg}" :order 2 :when image)
|
||||||
|
(longtable :snippet "\\usepackage{longtable}" :when table :order 2)
|
||||||
|
(booktabs :snippet "\\usepackage{booktabs}" :when table :order 2)
|
||||||
|
(float-wrap :snippet "\\usepackage{wrapfig}" :order 2)
|
||||||
|
(rotate :snippet "\\usepackage{rotating}" :order 2)
|
||||||
|
(caption :snippet "\\usepackage{capt-of}")
|
||||||
|
(engraved-code :when code :snippet org-latex-generate-engraved-preamble)
|
||||||
|
(!guess-pollyglossia :snippet org-latex-guess-polyglossia-language)
|
||||||
|
(!guess-babel :snippet org-latex-guess-babel-language)
|
||||||
|
(!guess-inputenc :snippet org-latex-guess-inputenc)
|
||||||
|
(!announce-end :snippet "%% end ox-latex features\n" :order 100)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1200,9 +1277,10 @@ will produce
|
||||||
|
|
||||||
% Define a Code environment to prettily wrap the fontified code.
|
% Define a Code environment to prettily wrap the fontified code.
|
||||||
\\usepackage[breakable,xparse]{tcolorbox}
|
\\usepackage[breakable,xparse]{tcolorbox}
|
||||||
|
\\providecommand{\\codefont}{\\footnotesize}
|
||||||
\\DeclareTColorBox[]{Code}{o}%
|
\\DeclareTColorBox[]{Code}{o}%
|
||||||
{colback=EfD!98!EFD, colframe=EfD!95!EFD,
|
{colback=EfD!98!EFD, colframe=EfD!95!EFD,
|
||||||
fontupper=\\footnotesize\\setlength{\\fboxsep}{0pt},
|
fontupper=\\setlength{\\fboxsep}{0pt}\\codefont,
|
||||||
colupper=EFD,
|
colupper=EFD,
|
||||||
IfNoValueTF={#1}%
|
IfNoValueTF={#1}%
|
||||||
{boxsep=2pt, arc=2.5pt, outer arc=2.5pt,
|
{boxsep=2pt, arc=2.5pt, outer arc=2.5pt,
|
||||||
|
@ -1226,7 +1304,9 @@ as long as it:
|
||||||
In the default value the colors \"EFD\" and \"EfD\" are provided
|
In the default value the colors \"EFD\" and \"EfD\" are provided
|
||||||
as they are respectively the foreground and background colors,
|
as they are respectively the foreground and background colors,
|
||||||
just in case they aren't provided by the generated preamble, so
|
just in case they aren't provided by the generated preamble, so
|
||||||
we can assume they are always set.
|
we can assume they are always set. The command \"\\codefont\" is
|
||||||
|
also provided (defaulting to \"\\footnotesize\"), to allow the
|
||||||
|
font used in \"Code\" environments to be easily tweaked.
|
||||||
|
|
||||||
Within this preamble there are two recognized macro-like placeholders:
|
Within this preamble there are two recognized macro-like placeholders:
|
||||||
|
|
||||||
|
@ -1323,7 +1403,19 @@ default values of which are given by `org-latex-engraved-preamble' and
|
||||||
(alist-get 'default
|
(alist-get 'default
|
||||||
(if theme
|
(if theme
|
||||||
(engrave-faces-get-theme (intern theme))
|
(engrave-faces-get-theme (intern theme))
|
||||||
engrave-faces-current-preset-style)))))))
|
engrave-faces-current-preset-style))))))
|
||||||
|
(gen-theme-command
|
||||||
|
(lambda (theme)
|
||||||
|
(format "\n\\newcommand{\\engravedtheme%s}{%%\n%s\n}"
|
||||||
|
(replace-regexp-in-string
|
||||||
|
"[^A-Za-z]" "" (symbol-name theme))
|
||||||
|
(replace-regexp-in-string
|
||||||
|
"\\\\newcommand\\\\efstrut[^\n]*\n" ""
|
||||||
|
(replace-regexp-in-string
|
||||||
|
"\\\\newcommand{\\(.+?\\)}\\[1\\]" "\\\\long\\\\def\\1##1"
|
||||||
|
(replace-regexp-in-string
|
||||||
|
"#1" "##1"
|
||||||
|
(funcall gen-theme-spec theme))))))))
|
||||||
(when (stringp engraved-theme)
|
(when (stringp engraved-theme)
|
||||||
(setq engraved-theme (intern engraved-theme)))
|
(setq engraved-theme (intern engraved-theme)))
|
||||||
(when (string-match "^[ \t]*\\[FVEXTRA-SETUP\\][ \t]*\n?" engraved-preamble)
|
(when (string-match "^[ \t]*\\[FVEXTRA-SETUP\\][ \t]*\n?" engraved-preamble)
|
||||||
|
@ -1353,37 +1445,57 @@ default values of which are given by `org-latex-engraved-preamble' and
|
||||||
t t
|
t t
|
||||||
engraved-preamble)))
|
engraved-preamble)))
|
||||||
(concat
|
(concat
|
||||||
"\n% Setup for code blocks [1/2]\n\n"
|
"% Setup for code blocks [1/2]\n\n"
|
||||||
engraved-preamble
|
engraved-preamble
|
||||||
"\n\n% Setup for code blocks [2/2]: syntax highlighting colors\n\n"
|
"\n\n% Setup for code blocks [2/2]: syntax highlighting colors\n\n"
|
||||||
(if (require 'engrave-faces-latex nil t)
|
(if (require 'engrave-faces-latex nil t)
|
||||||
(if engraved-themes
|
(if engraved-themes
|
||||||
(concat
|
(concat
|
||||||
|
;; We don't want to re-define the efstrut, so we now need to
|
||||||
|
;; define it seperately.
|
||||||
|
(format "\\newcommand\\efstrut{%s}\n\n"
|
||||||
|
engrave-faces-latex-colorbox-strut)
|
||||||
|
;; Define default theme
|
||||||
|
(funcall gen-theme-command engraved-theme)
|
||||||
|
"\n"
|
||||||
|
;; Define other themes
|
||||||
(mapconcat
|
(mapconcat
|
||||||
(lambda (theme)
|
gen-theme-command
|
||||||
(format
|
(cl-remove-if (lambda (theme) (string= theme (symbol-name engraved-theme)))
|
||||||
"\n\\newcommand{\\engravedtheme%s}{%%\n%s\n}"
|
engraved-themes)
|
||||||
(replace-regexp-in-string "[^A-Za-z]" "" (symbol-name theme))
|
|
||||||
(replace-regexp-in-string
|
|
||||||
"newcommand" "renewcommand"
|
|
||||||
(replace-regexp-in-string
|
|
||||||
"#" "##"
|
|
||||||
(funcall gen-theme-spec theme)))))
|
|
||||||
engraved-themes
|
|
||||||
"\n")
|
"\n")
|
||||||
"\n\n"
|
;; Load the default theme
|
||||||
(cond
|
"\n\n\\engravedtheme"
|
||||||
((memq engraved-theme engraved-themes)
|
(replace-regexp-in-string
|
||||||
(concat "\\engravedtheme"
|
"[^A-Za-z]" "" (symbol-name engraved-theme))
|
||||||
(replace-regexp-in-string
|
"\n")
|
||||||
"[^A-Za-z]" "" engraved-theme)
|
|
||||||
"\n"))
|
|
||||||
(t (funcall gen-theme-spec engraved-theme))))
|
|
||||||
(funcall gen-theme-spec engraved-theme))
|
(funcall gen-theme-spec engraved-theme))
|
||||||
(warn "Cannot engrave source blocks. Consider installing `engrave-faces'.")
|
(warn "Cannot engrave source blocks. Consider installing `engrave-faces'.")
|
||||||
"% WARNING syntax highlighting unavailable as engrave-faces-latex was missing.\n")
|
"% WARNING syntax highlighting unavailable as engrave-faces-latex was missing.\n")
|
||||||
"\n")))
|
"\n")))
|
||||||
|
|
||||||
|
;; Citation features
|
||||||
|
|
||||||
|
(org-export-update-features 'latex
|
||||||
|
(bibliography-csl
|
||||||
|
:condition (eq (org-cite-processor info) 'csl)
|
||||||
|
:when bibliography
|
||||||
|
:snippet org-cite-csl--generate-latex-preamble)
|
||||||
|
(bibliography-biblatex
|
||||||
|
:condition (eq (org-cite-processor info) 'biblatex)
|
||||||
|
:when bibliography
|
||||||
|
:snippet org-cite-biblatex--generate-latex-usepackage)
|
||||||
|
(bibliography-biblatex-resources
|
||||||
|
:condition (eq (org-cite-processor info) 'biblatex)
|
||||||
|
:when bibliography
|
||||||
|
:snippet org-cite-biblatex--generate-latex-bibresources
|
||||||
|
:no-precompile t
|
||||||
|
:order 90)
|
||||||
|
(bibliography-natbib
|
||||||
|
:condition (eq (org-cite-processor info) 'natbib)
|
||||||
|
:when bibliography
|
||||||
|
:snippet org-cite-natbib--generate-latex-preamble))
|
||||||
|
|
||||||
;;;; Compilation
|
;;;; Compilation
|
||||||
|
|
||||||
(defcustom org-latex-compiler-file-string "%% Intended LaTeX compiler: %s\n"
|
(defcustom org-latex-compiler-file-string "%% Intended LaTeX compiler: %s\n"
|
||||||
|
@ -1627,88 +1739,84 @@ For non-floats, see `org-latex--wrap-label'."
|
||||||
(org-trim label)
|
(org-trim label)
|
||||||
(org-export-data main info))))))
|
(org-export-data main info))))))
|
||||||
|
|
||||||
(defun org-latex-guess-inputenc (header)
|
(defun org-latex-guess-inputenc (info)
|
||||||
"Set the coding system in inputenc to what the buffer is.
|
"Set the coding system in inputenc to what the buffer is.
|
||||||
|
|
||||||
HEADER is the LaTeX header string. This function only applies
|
INFO is the plist used as a communication channel.
|
||||||
when specified inputenc option is \"AUTO\".
|
This function only applies when specified inputenc option is \"AUTO\".
|
||||||
|
|
||||||
Return the new header, as a string."
|
Return the new header, as a string."
|
||||||
(let* ((cs (or (ignore-errors
|
(let ((header (plist-get info :latex-full-header))
|
||||||
(latexenc-coding-system-to-inputenc
|
(cs (or (ignore-errors
|
||||||
(or org-export-coding-system buffer-file-coding-system)))
|
(latexenc-coding-system-to-inputenc
|
||||||
"utf8")))
|
(or org-export-coding-system buffer-file-coding-system)))
|
||||||
(if (not cs) header
|
"utf8")))
|
||||||
|
(when (and cs (string-match "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}" header))
|
||||||
;; First translate if that is requested.
|
;; First translate if that is requested.
|
||||||
(setq cs (or (cdr (assoc cs org-latex-inputenc-alist)) cs))
|
(setq cs (or (cdr (assoc cs org-latex-inputenc-alist)) cs))
|
||||||
;; Then find the \usepackage statement and replace the option.
|
(plist-put info :latex-full-header
|
||||||
(replace-regexp-in-string "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}"
|
(replace-match cs t t header 1))))
|
||||||
cs header t nil 1))))
|
nil)
|
||||||
|
|
||||||
(defun org-latex-guess-babel-language (header info)
|
(defun org-latex-guess-babel-language (info)
|
||||||
"Set Babel's language according to LANGUAGE keyword.
|
"Set Babel's language according to LANGUAGE keyword.
|
||||||
|
|
||||||
HEADER is the LaTeX header string. INFO is the plist used as
|
INFO is the plist used as a communication channel.
|
||||||
a communication channel.
|
|
||||||
|
|
||||||
Insertion of guessed language only happens when Babel package has
|
Insertion of guessed language only happens when Babel package has
|
||||||
explicitly been loaded. Then it is added to the rest of
|
explicitly been loaded. Then it is added to the rest of
|
||||||
package's options.
|
package's options.
|
||||||
|
|
||||||
The optional argument to Babel or the mandatory argument to
|
The optional argument to Babel or the mandatory argument to
|
||||||
`\babelprovide' command may be \"AUTO\" which is then replaced
|
`\\babelprovide' command may be \"AUTO\" which is then replaced
|
||||||
with the language of the document or
|
with the language of the document or
|
||||||
`org-export-default-language' unless language in question is
|
`org-export-default-language' unless language in question is
|
||||||
already loaded.
|
already loaded.
|
||||||
|
|
||||||
Return the new header."
|
Return the new header."
|
||||||
(let* ((language-code (plist-get info :language))
|
(let* ((language-code (plist-get info :language))
|
||||||
(plist (cdr
|
(plist (cdr (assoc language-code org-latex-language-alist)))
|
||||||
(assoc language-code org-latex-language-alist)))
|
(language (plist-get plist :babel))
|
||||||
(language (plist-get plist :babel))
|
(header (plist-get info :latex-full-header))
|
||||||
(language-ini-only (plist-get plist :babel-ini-only))
|
(language-ini-only (plist-get plist :babel-ini-only))
|
||||||
(language-ini-alt (plist-get plist :babel-ini-alt))
|
(language-ini-alt (plist-get plist :babel-ini-alt))
|
||||||
;; If no language is set, or Babel package is not loaded, or
|
(babel-header-options
|
||||||
;; LANGUAGE keyword value is a language served by Babel
|
;; If no language is set, or Babel package is not loaded, or
|
||||||
;; exclusively through ini files, return HEADER as-is.
|
;; LANGUAGE keyword value is a language served by Babel
|
||||||
(header (if (or language-ini-only
|
;; exclusively through ini files, return HEADER as-is.
|
||||||
(not (stringp language-code))
|
(and (not language-ini-only)
|
||||||
(not (string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header)))
|
(stringp language-code)
|
||||||
header
|
(string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header)
|
||||||
(let ((options (save-match-data
|
(let ((options (save-match-data
|
||||||
(org-split-string (match-string 1 header) ",[ \t]*"))))
|
(org-split-string (match-string 1 header) ",[ \t]*"))))
|
||||||
;; If LANGUAGE is already loaded, return header
|
(cond ((member language options) (delete "AUTO" options))
|
||||||
;; without AUTO. Otherwise, replace AUTO with language or
|
((member "AUTO" options) options)
|
||||||
;; append language if AUTO is not present. Languages that are
|
(t (append options (list language))))))))
|
||||||
;; served in Babel exclusively through ini files are not added
|
(when babel-header-options
|
||||||
;; to the babel argument, and must be loaded using
|
;; If AUTO is present in the header options, replace it with `language'.
|
||||||
;; `\babelprovide'.
|
(setq header
|
||||||
(replace-match
|
(replace-match
|
||||||
(mapconcat (lambda (option) (if (equal "AUTO" option) language option))
|
(mapconcat (lambda (option) (if (equal "AUTO" option) language option))
|
||||||
(cond ((member language options) (delete "AUTO" options))
|
babel-header-options
|
||||||
((member "AUTO" options) options)
|
", ")
|
||||||
(t (append options (list language))))
|
t nil header 1)))
|
||||||
", ")
|
|
||||||
t nil header 1)))))
|
|
||||||
;; If `\babelprovide[args]{AUTO}' is present, AUTO is
|
;; If `\babelprovide[args]{AUTO}' is present, AUTO is
|
||||||
;; replaced by LANGUAGE.
|
;; replaced by LANGUAGE.
|
||||||
(if (not (string-match "\\\\babelprovide\\[.*\\]{\\(.+\\)}" header))
|
(when (string-match "\\\\babelprovide\\[.*\\]{AUTO}" header)
|
||||||
header
|
(setq header
|
||||||
(let ((prov (match-string 1 header)))
|
(replace-regexp-in-string
|
||||||
(if (equal "AUTO" prov)
|
(format
|
||||||
(replace-regexp-in-string (format
|
"\\(\\\\babelprovide\\[.*\\]\\)\\({\\)%s}" babel-header-options)
|
||||||
"\\(\\\\babelprovide\\[.*\\]\\)\\({\\)%s}" prov)
|
(format "\\1\\2%s}" (if language-ini-alt language-ini-alt
|
||||||
(format "\\1\\2%s}"
|
(or language language-ini-only)))
|
||||||
(if language-ini-alt language-ini-alt
|
header t)))
|
||||||
(or language language-ini-only)))
|
(plist-put info :latex-full-header header))
|
||||||
header t)
|
nil)
|
||||||
header)))))
|
|
||||||
|
|
||||||
(defun org-latex-guess-polyglossia-language (header info)
|
(defun org-latex-guess-polyglossia-language (info)
|
||||||
"Set the Polyglossia language according to the LANGUAGE keyword.
|
"Set the Polyglossia language according to the LANGUAGE keyword.
|
||||||
|
|
||||||
HEADER is the LaTeX header string. INFO is the plist used as
|
INFO is the plist used as a communication channel.
|
||||||
a communication channel.
|
|
||||||
|
|
||||||
Insertion of guessed language only happens when the Polyglossia
|
Insertion of guessed language only happens when the Polyglossia
|
||||||
package has been explicitly loaded.
|
package has been explicitly loaded.
|
||||||
|
@ -1719,48 +1827,50 @@ replaced with the language of the document or
|
||||||
using \setdefaultlanguage and not as an option to the package.
|
using \setdefaultlanguage and not as an option to the package.
|
||||||
|
|
||||||
Return the new header."
|
Return the new header."
|
||||||
(let* ((language (plist-get info :language)))
|
(let ((header (plist-get info :latex-full-header))
|
||||||
|
(language (plist-get info :language)))
|
||||||
;; If no language is set or Polyglossia is not loaded, return
|
;; If no language is set or Polyglossia is not loaded, return
|
||||||
;; HEADER as-is.
|
;; HEADER as-is.
|
||||||
(if (or (not (stringp language))
|
(when (and (stringp language)
|
||||||
(not (string-match
|
(string-match
|
||||||
"\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n"
|
"\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n"
|
||||||
header)))
|
header))
|
||||||
header
|
|
||||||
(let* ((options (org-string-nw-p (match-string 1 header)))
|
(let* ((options (org-string-nw-p (match-string 1 header)))
|
||||||
(languages (and options
|
(languages (and options
|
||||||
;; Reverse as the last loaded language is
|
;; Reverse as the last loaded language is
|
||||||
;; the main language.
|
;; the main language.
|
||||||
(nreverse
|
(nreverse
|
||||||
(delete-dups
|
(delete-dups
|
||||||
(save-match-data
|
(save-match-data
|
||||||
(org-split-string
|
(org-split-string
|
||||||
(replace-regexp-in-string
|
(replace-regexp-in-string
|
||||||
"AUTO" language options t)
|
"AUTO" language options t)
|
||||||
",[ \t]*"))))))
|
",[ \t]*"))))))
|
||||||
(main-language-set
|
(main-language-set
|
||||||
(string-match-p "\\\\setmainlanguage{.*?}" header)))
|
(string-match-p "\\\\setmainlanguage{.*?}" header))
|
||||||
(replace-match
|
(polyglossia-modified-header
|
||||||
(concat "\\usepackage{polyglossia}\n"
|
(replace-match
|
||||||
(mapconcat
|
(concat "\\usepackage{polyglossia}\n"
|
||||||
(lambda (l)
|
(mapconcat
|
||||||
(let* ((plist (cdr
|
(lambda (l)
|
||||||
(assoc language org-latex-language-alist)))
|
(let* ((plist (cdr (assoc language org-latex-language-alist)))
|
||||||
(polyglossia-variant (plist-get plist :polyglossia-variant))
|
(polyglossia-variant (plist-get plist :polyglossia-variant))
|
||||||
(polyglossia-lang (plist-get plist :polyglossia))
|
(polyglossia-lang (plist-get plist :polyglossia))
|
||||||
(l (if (equal l language)
|
(l (if (equal l language)
|
||||||
polyglossia-lang
|
polyglossia-lang
|
||||||
l)))
|
l)))
|
||||||
(format (if main-language-set (format "\\setotherlanguage{%s}\n" l)
|
(format (if main-language-set (format "\\setotherlanguage{%s}\n" l)
|
||||||
(setq main-language-set t)
|
(setq main-language-set t)
|
||||||
"\\setmainlanguage%s{%s}\n")
|
"\\setmainlanguage%s{%s}\n")
|
||||||
(if polyglossia-variant
|
(if polyglossia-variant
|
||||||
(format "[variant=%s]" polyglossia-variant)
|
(format "[variant=%s]" polyglossia-variant)
|
||||||
"")
|
"")
|
||||||
l)))
|
l)))
|
||||||
languages
|
languages
|
||||||
""))
|
""))
|
||||||
t t header 0)))))
|
t t header 0)))
|
||||||
|
(plist-put info :latex-full-header polyglossia-modified-header))))
|
||||||
|
nil)
|
||||||
|
|
||||||
(defun org-latex--remove-packages (pkg-alist info)
|
(defun org-latex--remove-packages (pkg-alist info)
|
||||||
"Remove packages based on the current LaTeX compiler.
|
"Remove packages based on the current LaTeX compiler.
|
||||||
|
@ -1965,32 +2075,103 @@ non-nil, only includes packages relevant to image generation, as
|
||||||
specified in `org-latex-default-packages-alist' or
|
specified in `org-latex-default-packages-alist' or
|
||||||
`org-latex-packages-alist'."
|
`org-latex-packages-alist'."
|
||||||
(let* ((class (plist-get info :latex-class))
|
(let* ((class (plist-get info :latex-class))
|
||||||
(class-template
|
(class-template
|
||||||
(or template
|
(or template
|
||||||
(let* ((class-options (plist-get info :latex-class-options))
|
(let* ((class-options (plist-get info :latex-class-options))
|
||||||
(header (nth 1 (assoc class (plist-get info :latex-classes)))))
|
(header (nth 1 (assoc class (plist-get info :latex-classes)))))
|
||||||
(and (stringp header)
|
(and (stringp header)
|
||||||
(if (not class-options) header
|
(if (not class-options) header
|
||||||
(replace-regexp-in-string
|
(replace-regexp-in-string
|
||||||
"^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
|
"^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
|
||||||
class-options header t nil 1))))
|
class-options header t nil 1))))
|
||||||
(user-error "Unknown LaTeX class `%s'" class))))
|
(user-error "Unknown LaTeX class `%s'" class)))
|
||||||
(org-latex-guess-polyglossia-language
|
(header-split (format "\n%%--org-latex-header-temp-split-%d--\n" (random 10000)))
|
||||||
(org-latex-guess-babel-language
|
(header (concat (org-element-normalize-string (plist-get info :latex-header))
|
||||||
(org-latex-guess-inputenc
|
header-split
|
||||||
(org-element-normalize-string
|
(and (not snippet?)
|
||||||
(org-splice-latex-header
|
(org-element-normalize-string
|
||||||
class-template
|
(plist-get info :latex-header-extra)))))
|
||||||
(org-latex--remove-packages org-latex-default-packages-alist info)
|
generated-preamble preamble-nonprecompilable)
|
||||||
(org-latex--remove-packages org-latex-packages-alist info)
|
(plist-put info :latex-full-header
|
||||||
snippet?
|
(org-element-normalize-string
|
||||||
(mapconcat #'org-element-normalize-string
|
(org-splice-latex-header
|
||||||
(list (plist-get info :latex-header)
|
class-template
|
||||||
(and (not snippet?)
|
(org-latex--remove-packages org-latex-default-packages-alist info)
|
||||||
(plist-get info :latex-header-extra)))
|
(org-latex--remove-packages org-latex-packages-alist info)
|
||||||
""))))
|
snippet?
|
||||||
info)
|
header)))
|
||||||
info)))
|
(if snippet?
|
||||||
|
(setq generated-preamble
|
||||||
|
(progn
|
||||||
|
(org-latex-guess-inputenc info)
|
||||||
|
(org-latex-guess-babel-language info)
|
||||||
|
(org-latex-guess-polyglossia-language info)
|
||||||
|
"\n% Generated preamble omitted for snippets."))
|
||||||
|
(let (impl-precomp impl-noprecomp)
|
||||||
|
(dolist (impl (plist-get info :feature-implementations))
|
||||||
|
(message "LaTeX feature: %s" (car impl))
|
||||||
|
(if (or impl-noprecomp (plist-get (cdr impl) :no-precompile))
|
||||||
|
(push impl impl-noprecomp)
|
||||||
|
(push impl impl-precomp)))
|
||||||
|
(message "Precomp feats: %S" (reverse (mapcar #'car impl-precomp)))
|
||||||
|
(message "No precomp feats: %S" (reverse (mapcar #'car impl-noprecomp)))
|
||||||
|
(setq generated-preamble
|
||||||
|
(string-join
|
||||||
|
(org-export-expand-feature-snippets
|
||||||
|
info (nreverse impl-precomp))
|
||||||
|
"\n\n")
|
||||||
|
preamble-nonprecompilable
|
||||||
|
(string-join
|
||||||
|
(org-export-expand-feature-snippets
|
||||||
|
info (nreverse impl-noprecomp))
|
||||||
|
"\n\n"))))
|
||||||
|
(let* ((header-parts
|
||||||
|
(split-string (plist-get info :latex-full-header)
|
||||||
|
(regexp-quote header-split)))
|
||||||
|
(header-main (car header-parts))
|
||||||
|
(header-extra (cadr header-parts))
|
||||||
|
(preamble
|
||||||
|
(concat
|
||||||
|
(org-latex--insert-compiler info)
|
||||||
|
header-main
|
||||||
|
"\n"
|
||||||
|
generated-preamble
|
||||||
|
"\n"))
|
||||||
|
(format-file
|
||||||
|
(and org-latex-precompile
|
||||||
|
;; Precompilation is disabled for xelatex/lualatex for now.
|
||||||
|
(if (member (plist-get info :latex-compiler)
|
||||||
|
'("xelatex" "lualatex"))
|
||||||
|
(progn
|
||||||
|
(display-warning
|
||||||
|
'(org latex-export disable-local-precompile)
|
||||||
|
(format "%s does not support precompilation, disabling LaTeX precompile in this buffer.\n To re-enable, run `(setq-local org-latex-precompile t)' or reopen this buffer."
|
||||||
|
(plist-get info :latex-compiler)))
|
||||||
|
(setf (buffer-local-value
|
||||||
|
'org-latex-precompile (get-buffer (plist-get info :input-buffer)))
|
||||||
|
nil))
|
||||||
|
(org-latex--precompile
|
||||||
|
info preamble
|
||||||
|
(string-match-p "\\(?:\\\\input{\\|\\\\include{\\)[^/]" preamble))))))
|
||||||
|
(when (and format-file (not snippet?))
|
||||||
|
(let ((preamble-parts (split-string preamble (regexp-quote header-split))))
|
||||||
|
(setq preamble (car preamble-parts)
|
||||||
|
preamble-nonprecompilable
|
||||||
|
(concat preamble-nonprecompilable
|
||||||
|
(cadr preamble-parts)))))
|
||||||
|
(concat (and format-file
|
||||||
|
(concat "%& " (file-name-sans-extension format-file) "\n"))
|
||||||
|
(and (plist-get info :time-stamp-file)
|
||||||
|
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
|
||||||
|
preamble
|
||||||
|
(and format-file
|
||||||
|
"\n% end precompiled preamble\n\\ifcsname endofdump\\endcsname\\endofdump\\fi\n")
|
||||||
|
"\n"
|
||||||
|
preamble-nonprecompilable
|
||||||
|
(and preamble-nonprecompilable
|
||||||
|
(not (string-empty-p preamble-nonprecompilable))
|
||||||
|
"\n")
|
||||||
|
header-extra))))
|
||||||
|
|
||||||
(defun org-latex-template (contents info)
|
(defun org-latex-template (contents info)
|
||||||
"Return complete document string after LaTeX conversion.
|
"Return complete document string after LaTeX conversion.
|
||||||
|
@ -1999,12 +2180,7 @@ holding export options."
|
||||||
(let ((title (org-export-data (plist-get info :title) info))
|
(let ((title (org-export-data (plist-get info :title) info))
|
||||||
(spec (org-latex--format-spec info)))
|
(spec (org-latex--format-spec info)))
|
||||||
(concat
|
(concat
|
||||||
;; Timestamp.
|
;; Timestamp, compiler statement, document class and packages.
|
||||||
(and (plist-get info :time-stamp-file)
|
|
||||||
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
|
|
||||||
;; LaTeX compiler.
|
|
||||||
(org-latex--insert-compiler info)
|
|
||||||
;; Document class and packages.
|
|
||||||
(org-latex-make-preamble info)
|
(org-latex-make-preamble info)
|
||||||
;; Possibly limit depth for headline numbering.
|
;; Possibly limit depth for headline numbering.
|
||||||
(let ((sec-num (plist-get info :section-numbers)))
|
(let ((sec-num (plist-get info :section-numbers)))
|
||||||
|
@ -2041,12 +2217,6 @@ holding export options."
|
||||||
(let ((template (plist-get info :latex-hyperref-template)))
|
(let ((template (plist-get info :latex-hyperref-template)))
|
||||||
(and (stringp template)
|
(and (stringp template)
|
||||||
(format-spec template spec)))
|
(format-spec template spec)))
|
||||||
;; engrave-faces-latex preamble
|
|
||||||
(when (and (eq (plist-get info :latex-src-block-backend) 'engraved)
|
|
||||||
(org-element-map (plist-get info :parse-tree)
|
|
||||||
'(src-block inline-src-block) #'identity
|
|
||||||
info t))
|
|
||||||
(org-latex-generate-engraved-preamble info))
|
|
||||||
;; Document start.
|
;; Document start.
|
||||||
"\\begin{document}\n\n"
|
"\\begin{document}\n\n"
|
||||||
;; Title command.
|
;; Title command.
|
||||||
|
@ -2074,6 +2244,118 @@ holding export options."
|
||||||
;; Document end.
|
;; Document end.
|
||||||
"\\end{document}")))
|
"\\end{document}")))
|
||||||
|
|
||||||
|
(defvar org-latex-precompile t
|
||||||
|
"Precompile the preamble during export.
|
||||||
|
This requires the LaTeX package \"mylatexformat\" to be installed.")
|
||||||
|
|
||||||
|
(defconst org-latex--precompile-log "*Org LaTeX Precompilation*")
|
||||||
|
|
||||||
|
(defvar org-latex-precompile-command
|
||||||
|
"%l -output-directory %o -ini -jobname=%b \"&%L\" mylatexformat.ltx %f")
|
||||||
|
|
||||||
|
(defvar org-latex-precompile-compiler-map
|
||||||
|
'(("pdflatex" . "latex")
|
||||||
|
("xelatex" . "xelatex -no-pdf")
|
||||||
|
("lualatex" . "dvilualatex")))
|
||||||
|
|
||||||
|
(defun org-latex--precompile (info preamble &optional tempfile-p)
|
||||||
|
"Precompile/dump LaTeX PREAMBLE text.
|
||||||
|
|
||||||
|
The path to the format file (.fmt) is returned. If the format
|
||||||
|
file could not be found in the persist cache, it is generated
|
||||||
|
according to PROCESSING-INFO and stored. INFO is a plist used as
|
||||||
|
a communication channel.
|
||||||
|
|
||||||
|
If TEMPFILE-P is non-nil, then it is assumed the preamble does
|
||||||
|
not contain any relative references to other files.
|
||||||
|
|
||||||
|
This is intended to speed up Org's LaTeX preview generation
|
||||||
|
process."
|
||||||
|
(let ((preamble-hash
|
||||||
|
(thread-first
|
||||||
|
preamble
|
||||||
|
(concat
|
||||||
|
(plist-get info :latex-compiler)
|
||||||
|
(if tempfile-p "-temp"
|
||||||
|
default-directory))
|
||||||
|
(sha1)))
|
||||||
|
(default-directory
|
||||||
|
(if tempfile-p temporary-file-directory default-directory)))
|
||||||
|
(or (cadr
|
||||||
|
(org-persist-read "LaTeX format file cache"
|
||||||
|
(list :key preamble-hash)
|
||||||
|
nil nil :read-related t))
|
||||||
|
(when-let ((dump-file
|
||||||
|
(org-latex--precompile-preamble
|
||||||
|
info preamble (expand-file-name preamble-hash temporary-file-directory))))
|
||||||
|
(cadr
|
||||||
|
(org-persist-register `(,"LaTeX format file cache"
|
||||||
|
(file ,dump-file))
|
||||||
|
(list :key preamble-hash)
|
||||||
|
:write-immediately t))))))
|
||||||
|
|
||||||
|
(defun org-latex--remove-cached-preamble (latex-compiler preamble &optional tempfile-p)
|
||||||
|
"Remove the cached preamble file for PREAMBLE compiled with LATEX-COMPILER.
|
||||||
|
TEMPFILE-P should be set to mirror the caching `org-latex--precompile' call
|
||||||
|
which is intended to be evicted from the cache."
|
||||||
|
(let ((preamble-hash
|
||||||
|
(thread-first
|
||||||
|
preamble
|
||||||
|
(concat
|
||||||
|
latex-compiler
|
||||||
|
(if tempfile-p "-temp"
|
||||||
|
default-directory))
|
||||||
|
(sha1))))
|
||||||
|
(org-persist-unregister "LaTeX format file cache"
|
||||||
|
(list :key preamble-hash)
|
||||||
|
:remove-related t)))
|
||||||
|
|
||||||
|
(defun org-latex--precompile-preamble (info preamble basepath)
|
||||||
|
"Precompile PREAMBLE with \"mylatexformat\".
|
||||||
|
The PREAMBLE string is placed in BASEPATH.tex and compiled
|
||||||
|
according to PROCESSING-INFO. If compilation and dumping
|
||||||
|
succeeded, BASEPATH.fmt will be returned.
|
||||||
|
|
||||||
|
Should any errors occur during compilation, nil will be returned,
|
||||||
|
and appropriate warnings may be emitted."
|
||||||
|
(let ((dump-file (concat basepath ".fmt"))
|
||||||
|
(preamble-file (concat basepath ".tex"))
|
||||||
|
(precompile-buffer
|
||||||
|
(with-current-buffer
|
||||||
|
(get-buffer-create org-latex--precompile-log)
|
||||||
|
(erase-buffer)
|
||||||
|
(current-buffer))))
|
||||||
|
(with-temp-file preamble-file
|
||||||
|
(insert preamble "\n\\endofdump\n"))
|
||||||
|
(message "Precompiling Org LaTeX preamble...")
|
||||||
|
(condition-case nil
|
||||||
|
(org-compile-file
|
||||||
|
preamble-file (list org-latex-precompile-command)
|
||||||
|
"fmt" nil precompile-buffer
|
||||||
|
(or (plist-get info :precompile-format-spec)
|
||||||
|
`((?l . ,(plist-get info :latex-compiler))
|
||||||
|
(?L . ,(plist-get info :latex-compiler)))))
|
||||||
|
(:success
|
||||||
|
(kill-buffer precompile-buffer)
|
||||||
|
(delete-file preamble-file)
|
||||||
|
dump-file)
|
||||||
|
(error
|
||||||
|
(unless (= 0 (call-process "kpsewhich" nil nil nil "mylatexformat.ltx"))
|
||||||
|
(display-warning
|
||||||
|
'(org latex-preview preamble-precompilation)
|
||||||
|
"The LaTeX package \"mylatexformat\" is required for precompilation, but could not be found")
|
||||||
|
:warning)
|
||||||
|
(unless (= 0 (call-process "kpsewhich" nil nil nil "preview.sty"))
|
||||||
|
(display-warning
|
||||||
|
'(org latex-preview preamble-precompilation)
|
||||||
|
"The LaTeX package \"preview\" is required for precompilation, but could not be found")
|
||||||
|
:warning)
|
||||||
|
(display-warning
|
||||||
|
'(org latex-preview preamble-precompilation)
|
||||||
|
(format "Failed to precompile preamble (%s), see the \"%s\" buffer."
|
||||||
|
preamble-file precompile-buffer)
|
||||||
|
:warning)
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
|
||||||
;;; Transcode Functions
|
;;; Transcode Functions
|
||||||
|
@ -2451,7 +2733,7 @@ contextual information."
|
||||||
(let ((code (org-element-property :value inline-src-block))
|
(let ((code (org-element-property :value inline-src-block))
|
||||||
(lang (org-element-property :language inline-src-block)))
|
(lang (org-element-property :language inline-src-block)))
|
||||||
(pcase (plist-get info :latex-src-block-backend)
|
(pcase (plist-get info :latex-src-block-backend)
|
||||||
(`verbatim (org-latex--text-markup code 'code info))
|
((or `verbatim (guard (not lang))) (org-latex--text-markup code 'code info))
|
||||||
(`minted (org-latex-inline-src-block--minted info code lang))
|
(`minted (org-latex-inline-src-block--minted info code lang))
|
||||||
(`engraved (org-latex-inline-src-block--engraved info code lang))
|
(`engraved (org-latex-inline-src-block--engraved info code lang))
|
||||||
(`listings (org-latex-inline-src-block--listings info code lang))
|
(`listings (org-latex-inline-src-block--listings info code lang))
|
||||||
|
@ -2478,8 +2760,12 @@ INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
|
||||||
(defun org-latex-inline-src-block--engraved (info code lang)
|
(defun org-latex-inline-src-block--engraved (info code lang)
|
||||||
"Transcode an inline src block's content from Org to LaTeX, using engrave-faces.
|
"Transcode an inline src block's content from Org to LaTeX, using engrave-faces.
|
||||||
INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
|
INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
|
||||||
(org-latex-src--engrave-code
|
(let ((engraved-theme (plist-get info :latex-engraved-theme)))
|
||||||
code lang nil (plist-get info :latex-engraved-options) t))
|
(when (stringp engraved-theme)
|
||||||
|
(setq engraved-theme (intern engraved-theme)))
|
||||||
|
(org-latex-src--engrave-code
|
||||||
|
code lang engraved-theme nil
|
||||||
|
(plist-get info :latex-engraved-options) t)))
|
||||||
|
|
||||||
(defun org-latex-inline-src-block--listings (info code lang)
|
(defun org-latex-inline-src-block--listings (info code lang)
|
||||||
"Transcode an inline src block's content from Org to LaTeX, using lstlistings.
|
"Transcode an inline src block's content from Org to LaTeX, using lstlistings.
|
||||||
|
@ -3532,7 +3818,7 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
|
||||||
(when (eq mathescape 'yes)
|
(when (eq mathescape 'yes)
|
||||||
(or engrave-faces-latex-mathescape t)))))
|
(or engrave-faces-latex-mathescape t)))))
|
||||||
|
|
||||||
(defun org-latex-src--engrave-code (content lang &optional theme options inline)
|
(defun org-latex-src--engrave-code (content lang &optional theme explicit-theme-p options inline)
|
||||||
"Engrave CONTENT to LaTeX in a LANG-mode buffer, and give the result.
|
"Engrave CONTENT to LaTeX in a LANG-mode buffer, and give the result.
|
||||||
When the THEME symbol is non-nil, that theme will be used.
|
When the THEME symbol is non-nil, that theme will be used.
|
||||||
|
|
||||||
|
@ -3573,12 +3859,12 @@ to the Verbatim environment or Verb command."
|
||||||
(concat "\\begin{Code}\n\\begin{Verbatim}" engraved-options "\n"
|
(concat "\\begin{Code}\n\\begin{Verbatim}" engraved-options "\n"
|
||||||
engraved-code "\n\\end{Verbatim}\n\\end{Code}"))))
|
engraved-code "\n\\end{Verbatim}\n\\end{Code}"))))
|
||||||
(kill-buffer engraved-buffer)
|
(kill-buffer engraved-buffer)
|
||||||
(if theme
|
(if (and theme explicit-theme-p)
|
||||||
(concat "{\\engravedtheme"
|
(concat "\\begingroup\\engravedtheme"
|
||||||
(replace-regexp-in-string "[^A-Za-z]" ""
|
(replace-regexp-in-string "[^A-Za-z]" ""
|
||||||
(symbol-name theme))
|
(symbol-name theme))
|
||||||
engraved-wrapped
|
engraved-wrapped
|
||||||
"}")
|
"\\endgroup")
|
||||||
engraved-wrapped))
|
engraved-wrapped))
|
||||||
(user-error "Cannot engrave code as `engrave-faces-latex' is unavailable.")))
|
(user-error "Cannot engrave code as `engrave-faces-latex' is unavailable.")))
|
||||||
|
|
||||||
|
@ -3612,7 +3898,16 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
|
||||||
`(("linenos")
|
`(("linenos")
|
||||||
("firstnumber" ,(number-to-string (1+ num-start)))))
|
("firstnumber" ,(number-to-string (1+ num-start)))))
|
||||||
(and local-options `((,local-options))))))
|
(and local-options `((,local-options))))))
|
||||||
(engraved-theme (plist-get attributes :engraved-theme))
|
(intern-safe
|
||||||
|
(lambda (name)
|
||||||
|
(if (stringp name)
|
||||||
|
(intern name)
|
||||||
|
name)))
|
||||||
|
(engraved-doc-theme
|
||||||
|
(funcall intern-safe (plist-get info :latex-engraved-theme)))
|
||||||
|
(engraved-theme
|
||||||
|
(or (funcall intern-safe (plist-get attributes :engraved-theme))
|
||||||
|
engraved-doc-theme))
|
||||||
(content
|
(content
|
||||||
(let* ((code-info (org-export-unravel-code src-block))
|
(let* ((code-info (org-export-unravel-code src-block))
|
||||||
(max-width
|
(max-width
|
||||||
|
@ -3638,7 +3933,8 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
|
||||||
(org-latex-src--engrave-mathescape-p info options)))
|
(org-latex-src--engrave-mathescape-p info options)))
|
||||||
(org-latex-src--engrave-code
|
(org-latex-src--engrave-code
|
||||||
content lang
|
content lang
|
||||||
(when engraved-theme (intern engraved-theme))
|
engraved-theme
|
||||||
|
(not (eq engraved-theme engraved-doc-theme))
|
||||||
options))))
|
options))))
|
||||||
(concat (car float-env) body (cdr float-env))))
|
(concat (car float-env) body (cdr float-env))))
|
||||||
|
|
||||||
|
@ -4352,13 +4648,21 @@ Return PDF file's name."
|
||||||
async subtreep visible-only body-only ext-plist
|
async subtreep visible-only body-only ext-plist
|
||||||
#'org-latex-compile)))
|
#'org-latex-compile)))
|
||||||
|
|
||||||
(defun org-latex-compile (texfile &optional snippet)
|
(defun org-latex-export-to-pdf-and-open (&optional async subtreep visible-only body-only ext-plist)
|
||||||
|
(let ((outfile (org-export-output-file-name ".tex" subtreep)))
|
||||||
|
(org-export-to-file 'latex outfile
|
||||||
|
async subtreep visible-only body-only ext-plist
|
||||||
|
(lambda (f) (org-latex-compile f nil t)))))
|
||||||
|
|
||||||
|
(defun org-latex-compile (texfile &optional snippet open-pdf)
|
||||||
"Compile a TeX file.
|
"Compile a TeX file.
|
||||||
|
|
||||||
TEXFILE is the name of the file being compiled. Processing is
|
TEXFILE is the name of the file being compiled. Processing is
|
||||||
done through the command specified in `org-latex-pdf-process',
|
done through the command specified in `org-latex-pdf-process',
|
||||||
which see. Output is redirected to \"*Org PDF LaTeX Output*\"
|
which see. Output is redirected to \"*Org PDF LaTeX Output*\"
|
||||||
buffer.
|
buffer. When the process is a shell command (and not a
|
||||||
|
function), it will be run asyncronously via `org-async-call',
|
||||||
|
unless the current session is `noninteractive'.
|
||||||
|
|
||||||
When optional argument SNIPPET is non-nil, TEXFILE is a temporary
|
When optional argument SNIPPET is non-nil, TEXFILE is a temporary
|
||||||
file used to preview a LaTeX snippet. In this case, do not
|
file used to preview a LaTeX snippet. In this case, do not
|
||||||
|
@ -4393,16 +4697,34 @@ produced."
|
||||||
(?L . ,(shell-quote-argument compiler))))
|
(?L . ,(shell-quote-argument compiler))))
|
||||||
(log-buf-name "*Org PDF LaTeX Output*")
|
(log-buf-name "*Org PDF LaTeX Output*")
|
||||||
(log-buf (and (not snippet) (get-buffer-create log-buf-name)))
|
(log-buf (and (not snippet) (get-buffer-create log-buf-name)))
|
||||||
outfile)
|
(outfile (expand-file-name (concat (file-name-base texfile) ".pdf")
|
||||||
;; Erase compile buffer at the start.
|
(file-name-directory texfile))))
|
||||||
(with-current-buffer log-buf
|
(with-current-buffer log-buf
|
||||||
(erase-buffer))
|
(erase-buffer))
|
||||||
(setq outfile
|
(if (or noninteractive (functionp process))
|
||||||
(org-compile-file
|
(progn
|
||||||
texfile process "pdf"
|
(org-compile-file texfile process "pdf"
|
||||||
(format "See %S for details" log-buf-name)
|
(format "See %S for details" log-buf-name)
|
||||||
log-buf spec))
|
log-buf spec)
|
||||||
(org-latex-compile--postprocess outfile log-buf snippet)
|
(and open-pdf (org-open-file outfile))
|
||||||
|
(org-latex-compile--postprocess outfile log-buf snippet))
|
||||||
|
(let ((failure-msg (format "File %S wasn't produced (exit code %%d). See %%s for details."
|
||||||
|
outfile))
|
||||||
|
async-call-spec)
|
||||||
|
(dolist (cmd (reverse (org-compile-file-commands
|
||||||
|
texfile process "pdf" spec)))
|
||||||
|
(setq async-call-spec
|
||||||
|
(list cmd
|
||||||
|
:buffer log-buf
|
||||||
|
:success
|
||||||
|
(or async-call-spec
|
||||||
|
(list (and open-pdf (lambda (_ _ _) (org-open-file outfile)))
|
||||||
|
(lambda (_ buf _)
|
||||||
|
(org-latex-compile--postprocess outfile buf snippet))))
|
||||||
|
:failure
|
||||||
|
failure-msg)))
|
||||||
|
(message "Compiling %s..." texfile)
|
||||||
|
(apply #'org-async-call async-call-spec)))
|
||||||
;; Return output file name.
|
;; Return output file name.
|
||||||
outfile))
|
outfile))
|
||||||
|
|
||||||
|
|
|
@ -510,8 +510,9 @@ contextual information."
|
||||||
(expand-file-name "reshilite" tmpdir)))
|
(expand-file-name "reshilite" tmpdir)))
|
||||||
(org-lang (org-element-property :language inline-src-block))
|
(org-lang (org-element-property :language inline-src-block))
|
||||||
(lst-lang
|
(lst-lang
|
||||||
(cadr (assq (intern org-lang)
|
(and org-lang
|
||||||
(plist-get info :man-source-highlight-langs))))
|
(cadr (assq (intern org-lang)
|
||||||
|
(plist-get info :man-source-highlight-langs)))))
|
||||||
|
|
||||||
(cmd (concat (expand-file-name "source-highlight")
|
(cmd (concat (expand-file-name "source-highlight")
|
||||||
" -s " lst-lang
|
" -s " lst-lang
|
||||||
|
@ -757,8 +758,9 @@ contextual information."
|
||||||
(code (org-element-property :value src-block))
|
(code (org-element-property :value src-block))
|
||||||
(org-lang (org-element-property :language src-block))
|
(org-lang (org-element-property :language src-block))
|
||||||
(lst-lang
|
(lst-lang
|
||||||
(cadr (assq (intern org-lang)
|
(and org-lang
|
||||||
(plist-get info :man-source-highlight-langs))))
|
(cadr (assq (intern org-lang)
|
||||||
|
(plist-get info :man-source-highlight-langs)))))
|
||||||
(cmd (concat "source-highlight"
|
(cmd (concat "source-highlight"
|
||||||
" -s " lst-lang
|
" -s " lst-lang
|
||||||
" -f groff_man "
|
" -f groff_man "
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
;;; ox-mathml.el --- Support for MathML exports -*- lexical-binding: t; -*-
|
||||||
|
;;
|
||||||
|
;; Copyright (C) 2023 TEC
|
||||||
|
;;
|
||||||
|
;; Author: TEC <contact@tecosaur.net>
|
||||||
|
;; Maintainer: TEC <contact@tecosaur.net>
|
||||||
|
;; Created: February 27, 2023
|
||||||
|
;; Modified: February 27, 2023
|
||||||
|
;; Version: 0.0.1
|
||||||
|
;; Keywords: abbrev bib c calendar comm convenience data docs emulations extensions faces files frames games hardware help hypermedia i18n internal languages lisp local maint mail matching mouse multimedia news outlines processes terminals tex tools unix vc wp
|
||||||
|
;; Homepage: https://github.com/tecosaur/ox-mathml
|
||||||
|
;; Package-Requires: ((emacs "24.3"))
|
||||||
|
;;
|
||||||
|
;; This file is not part of GNU Emacs.
|
||||||
|
;;
|
||||||
|
;;; Commentary:
|
||||||
|
;;
|
||||||
|
;; Support for MathML exports
|
||||||
|
;;
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
(defgroup org-mathml nil
|
||||||
|
"Options for generation of MathML representations of LaTeX math."
|
||||||
|
:tag "Org MathML export"
|
||||||
|
:group 'org-export)
|
||||||
|
|
||||||
|
(defcustom org-mathml-converter-jar-file nil
|
||||||
|
"Value of\"%j\" in `org-mathml-convert-command'.
|
||||||
|
Use this to specify additional executable file say a jar file.
|
||||||
|
|
||||||
|
When using MathToWeb as the converter, specify the full-path to
|
||||||
|
your mathtoweb.jar file."
|
||||||
|
:group 'org-mathml
|
||||||
|
:version "24.1"
|
||||||
|
:type '(choice
|
||||||
|
(const :tag "None" nil)
|
||||||
|
(file :tag "JAR file" :must-match t)))
|
||||||
|
|
||||||
|
(defcustom org-mathml-convert-command nil
|
||||||
|
"Command to convert LaTeX fragments to MathML.
|
||||||
|
Replace format-specifiers in the command as noted below and use
|
||||||
|
`shell-command' to convert LaTeX to MathML.
|
||||||
|
%j: Executable file in fully expanded form as specified by
|
||||||
|
`org-latex-to-mathml-jar-file'.
|
||||||
|
%I: Input LaTeX file in fully expanded form.
|
||||||
|
%i: The latex fragment to be converted.
|
||||||
|
%o: Output MathML file.
|
||||||
|
|
||||||
|
This command is used by `org-mathml-convert-latex'.
|
||||||
|
|
||||||
|
When using MathToWeb as the converter, set this option to
|
||||||
|
\"java -jar %j -unicode -force -df %o %I\".
|
||||||
|
|
||||||
|
When using LaTeXML set this option to
|
||||||
|
\"latexmlmath \"%i\" --preload=amsmath.sty --preload=amssymb.sty --presentationmathml=%o\"."
|
||||||
|
:group 'org-mathml
|
||||||
|
:version "24.1"
|
||||||
|
:type '(choice
|
||||||
|
(const :tag "None" nil)
|
||||||
|
(string :tag "\nShell command")))
|
||||||
|
|
||||||
|
(defun org-mathml-converter-available-p ()
|
||||||
|
"Return t if `org-mathml-convert-command' is usable."
|
||||||
|
(save-match-data
|
||||||
|
(when (and (boundp 'org-mathml-convert-command)
|
||||||
|
org-mathml-convert-command)
|
||||||
|
(let ((executable (car (split-string
|
||||||
|
org-mathml-convert-command))))
|
||||||
|
(when (executable-find executable)
|
||||||
|
(if (string-match
|
||||||
|
"%j" org-mathml-convert-command)
|
||||||
|
(file-readable-p org-mathml-converter-jar-file)
|
||||||
|
t))))))
|
||||||
|
|
||||||
|
(defun org-mathml-convert-latex (latex-frag &optional mathml-file)
|
||||||
|
"Convert LATEX-FRAG to MathML and store it in MATHML-FILE.
|
||||||
|
Use `org-latex-to-mathml-convert-command'. If the conversion is
|
||||||
|
successful, return the portion between \"<math...> </math>\"
|
||||||
|
elements otherwise return nil. When MATHML-FILE is specified,
|
||||||
|
write the results in to that file. When invoked as an
|
||||||
|
interactive command, prompt for LATEX-FRAG, with initial value
|
||||||
|
set to the current active region and echo the results for user
|
||||||
|
inspection."
|
||||||
|
(interactive (list (let ((frag (when (org-region-active-p)
|
||||||
|
(buffer-substring-no-properties
|
||||||
|
(region-beginning) (region-end)))))
|
||||||
|
(read-string "LaTeX Fragment: " frag nil frag))))
|
||||||
|
(unless latex-frag (user-error "Invalid LaTeX fragment"))
|
||||||
|
(let* ((tmp-in-file
|
||||||
|
(let ((file (file-relative-name
|
||||||
|
(make-temp-name (expand-file-name "ltxmathml-in")))))
|
||||||
|
(write-region latex-frag nil file)
|
||||||
|
file))
|
||||||
|
(tmp-out-file (file-relative-name
|
||||||
|
(make-temp-name (expand-file-name "ltxmathml-out"))))
|
||||||
|
(cmd (format-spec
|
||||||
|
org-mathml-convert-command
|
||||||
|
`((?j . ,(and org-mathml-converter-jar-file
|
||||||
|
(shell-quote-argument
|
||||||
|
(expand-file-name
|
||||||
|
org-mathml-converter-jar-file))))
|
||||||
|
(?I . ,(shell-quote-argument tmp-in-file))
|
||||||
|
(?i . ,latex-frag)
|
||||||
|
(?o . ,(shell-quote-argument tmp-out-file)))))
|
||||||
|
mathml shell-command-output)
|
||||||
|
(when (called-interactively-p 'any)
|
||||||
|
(unless (org-mathml-converter-available-p)
|
||||||
|
(user-error "LaTeX to MathML converter not configured")))
|
||||||
|
(message "Running %s" cmd)
|
||||||
|
(setq shell-command-output (shell-command-to-string cmd))
|
||||||
|
(setq mathml
|
||||||
|
(when (file-readable-p tmp-out-file)
|
||||||
|
(with-current-buffer (find-file-noselect tmp-out-file t)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when (re-search-forward
|
||||||
|
(format "<math[^>]*?%s[^>]*?>\\(.\\|\n\\)*</math>"
|
||||||
|
(regexp-quote
|
||||||
|
"xmlns=\"http://www.w3.org/1998/Math/MathML\""))
|
||||||
|
nil t)
|
||||||
|
(prog1 (match-string 0) (kill-buffer))))))
|
||||||
|
(cond
|
||||||
|
(mathml
|
||||||
|
(setq mathml
|
||||||
|
(concat "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" mathml))
|
||||||
|
(when mathml-file
|
||||||
|
(write-region mathml nil mathml-file))
|
||||||
|
(when (called-interactively-p 'any)
|
||||||
|
(message mathml)))
|
||||||
|
((warn "LaTeX to MathML conversion failed")
|
||||||
|
(message shell-command-output)))
|
||||||
|
(delete-file tmp-in-file)
|
||||||
|
(when (file-exists-p tmp-out-file)
|
||||||
|
(delete-file tmp-out-file))
|
||||||
|
mathml))
|
||||||
|
|
||||||
|
(defun org-mathml-convert-latex-cached (latex-frag)
|
||||||
|
"Use `org-mathml-convert-latex' but check local cache first."
|
||||||
|
(let ((latex-hash-path
|
||||||
|
(expand-file-name
|
||||||
|
(concat
|
||||||
|
"org-mathml-formula-"
|
||||||
|
(sha1
|
||||||
|
(prin1-to-string
|
||||||
|
(list latex-frag
|
||||||
|
org-mathml-convert-command)))
|
||||||
|
".mathml")
|
||||||
|
temporary-file-directory))
|
||||||
|
print-length print-level)
|
||||||
|
(unless (file-exists-p latex-hash-path)
|
||||||
|
(org-mathml-convert-latex latex-frag latex-hash-path))
|
||||||
|
(and (file-exists-p latex-hash-path)
|
||||||
|
latex-hash-path)))
|
||||||
|
|
||||||
|
(provide 'ox-mathml)
|
||||||
|
;;; ox-mathml.el ends here
|
252
lisp/ox-odt.el
252
lisp/ox-odt.el
|
@ -34,6 +34,7 @@
|
||||||
(require 'org-macs)
|
(require 'org-macs)
|
||||||
(require 'ox)
|
(require 'ox)
|
||||||
(require 'table nil 'noerror)
|
(require 'table nil 'noerror)
|
||||||
|
(require 'ox-mathml)
|
||||||
|
|
||||||
(declare-function org-at-heading-p "org" (&optional _))
|
(declare-function org-at-heading-p "org" (&optional _))
|
||||||
(declare-function org-back-to-heading "org" (&optional invisible-ok))
|
(declare-function org-back-to-heading "org" (&optional invisible-ok))
|
||||||
|
@ -3111,7 +3112,7 @@ and prefix with \"OrgSrc\". For example,
|
||||||
(defun org-odt-do-format-code
|
(defun org-odt-do-format-code
|
||||||
(code info &optional lang refs retain-labels num-start)
|
(code info &optional lang refs retain-labels num-start)
|
||||||
(let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
|
(let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
|
||||||
(lang-mode (and lang (intern (format "%s-mode" lang))))
|
(lang-mode (if lang (intern (format "%s-mode" lang)) #'ignore))
|
||||||
(code-lines (org-split-string code "\n"))
|
(code-lines (org-split-string code "\n"))
|
||||||
(code-length (length code-lines))
|
(code-length (length code-lines))
|
||||||
(use-htmlfontify-p (and (functionp lang-mode)
|
(use-htmlfontify-p (and (functionp lang-mode)
|
||||||
|
@ -3712,114 +3713,147 @@ contextual information."
|
||||||
|
|
||||||
(defun org-odt--translate-latex-fragments (tree _backend info)
|
(defun org-odt--translate-latex-fragments (tree _backend info)
|
||||||
(let ((processing-type (plist-get info :with-latex))
|
(let ((processing-type (plist-get info :with-latex))
|
||||||
(count 0)
|
(count 0)
|
||||||
(warning nil))
|
(warning nil))
|
||||||
;; Normalize processing-type to one of dvipng, mathml or verbatim.
|
;; MathML will be handled seperately.
|
||||||
;; If the desired converter is not available, force verbatim
|
(if (and (memq processing-type '(t mathml))
|
||||||
;; processing.
|
(fboundp 'org-format-latex-mathml-available-p)
|
||||||
(cl-case processing-type
|
(org-format-latex-mathml-available-p)
|
||||||
((t mathml)
|
(plist-put info :with-latex 'mathml))
|
||||||
(if (and (fboundp 'org-format-latex-mathml-available-p)
|
(org-element-map tree '(latex-fragment latex-environment)
|
||||||
(org-format-latex-mathml-available-p))
|
(lambda (latex)
|
||||||
(setq processing-type 'mathml)
|
(cl-incf count)
|
||||||
(setq warning "`org-odt-with-latex': LaTeX to MathML converter not available. Falling back to verbatim.")
|
(if-let ((latex-frag (org-element-property :value latex))
|
||||||
(setq processing-type 'verbatim)))
|
(path (org-mathml-convert-latex-cached latex-frag))
|
||||||
((dvipng imagemagick)
|
(link (list 'link
|
||||||
(unless (and (org-check-external-command "latex" "" t)
|
(list :type "file"
|
||||||
(org-check-external-command
|
:path path
|
||||||
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
|
:format 'bracket
|
||||||
(setq warning "`org-odt-with-latex': LaTeX to PNG converter not available. Falling back to verbatim.")
|
:raw-link (format "file:%s" path))))
|
||||||
(setq processing-type 'verbatim)))
|
(replacement
|
||||||
(otherwise
|
(if (eq (org-element-type latex) 'latex-environment)
|
||||||
(setq warning "`org-odt-with-latex': Unknown LaTeX option. Forcing verbatim.")
|
;;LaTeX environment. Mimic a "standalone image
|
||||||
(setq processing-type 'verbatim)))
|
;; or formula" by enclosing the `link' in
|
||||||
|
;; a `paragraph'. Copy over original
|
||||||
;; Display warning if the selected PROCESSING-TYPE is not
|
;; attributes, captions to the enclosing
|
||||||
;; available, but there are fragments to be converted.
|
;; paragraph.
|
||||||
(when warning
|
(org-element-adopt-elements
|
||||||
(org-element-map tree '(latex-fragment latex-environment)
|
(list 'paragraph
|
||||||
(lambda (_) (warn warning))
|
(list :style "OrgFormula"
|
||||||
info 'first-match nil t))
|
:name
|
||||||
|
(org-element-property :name latex)
|
||||||
;; Store normalized value for later use.
|
:caption
|
||||||
(when (plist-get info :with-latex)
|
(org-element-property :caption latex)))
|
||||||
(plist-put info :with-latex processing-type))
|
link)
|
||||||
(message "Formatting LaTeX using %s" processing-type)
|
link)))
|
||||||
|
(progn
|
||||||
;; Convert `latex-fragment's and `latex-environment's.
|
;; Note down the object that link replaces.
|
||||||
(when (memq processing-type '(mathml dvipng imagemagick))
|
(org-element-put-property replacement :replaces
|
||||||
(org-element-map tree '(latex-fragment latex-environment)
|
(list (org-element-type latex)
|
||||||
(lambda (latex-*)
|
(list :value latex-frag)))
|
||||||
(cl-incf count)
|
;; Restore blank after initial element or object.
|
||||||
(let* ((latex-frag (org-element-property :value latex-*))
|
(org-element-put-property
|
||||||
(input-file (plist-get info :input-file))
|
replacement :post-blank
|
||||||
(cache-dir (file-name-directory input-file))
|
(org-element-property :post-blank latex))
|
||||||
(cache-subdir (concat
|
;; Replace now.
|
||||||
(cl-case processing-type
|
(org-element-set-element latex replacement))
|
||||||
((dvipng imagemagick)
|
(setq warning "Conversion of LaTeX to MathML failed. Falling back to verbatim.")))
|
||||||
org-preview-latex-image-directory)
|
info nil nil)
|
||||||
(mathml "ltxmathml/"))
|
;; Normalize processing-type to one of dvipng or verbatim.
|
||||||
(file-name-sans-extension
|
;; If the desired converter is not available, force verbatim
|
||||||
(file-name-nondirectory input-file))))
|
;; processing.
|
||||||
(display-msg
|
(cl-case processing-type
|
||||||
(cl-case processing-type
|
((t mathml)
|
||||||
((dvipng imagemagick)
|
(setq warning "LaTeX to MathML converter not available. Falling back to verbatim."
|
||||||
(format "Creating LaTeX Image %d..." count))
|
processing-type 'verbatim))
|
||||||
(mathml (format "Creating MathML snippet %d..." count))))
|
((dvipng imagemagick)
|
||||||
;; Get an Org-style link to PNG image or the MathML
|
(unless (and (org-check-external-command "latex" "" t)
|
||||||
;; file.
|
(org-check-external-command
|
||||||
(link
|
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
|
||||||
(with-temp-buffer
|
(setq warning "LaTeX to PNG converter not available. Falling back to verbatim."
|
||||||
(insert latex-frag)
|
processing-type 'verbatim)))
|
||||||
(delay-mode-hooks (let ((org-inhibit-startup t)) (org-mode)))
|
(otherwise
|
||||||
;; When converting to a PNG image, make sure to
|
(setq warning "Unknown LaTeX option. Forcing verbatim."
|
||||||
;; copy all LaTeX header specifications from the
|
processing-type 'verbatim)))
|
||||||
;; Org source.
|
;; Display warning if the selected PROCESSING-TYPE is not
|
||||||
(unless (eq processing-type 'mathml)
|
;; available, but there are fragments to be converted.
|
||||||
(let ((h (plist-get info :latex-header)))
|
(when warning
|
||||||
(when h
|
(org-element-map tree '(latex-fragment latex-environment)
|
||||||
(insert "\n"
|
(lambda (_) (warn warning))
|
||||||
(replace-regexp-in-string
|
info 'first-match nil t))
|
||||||
"^" "#+LATEX_HEADER: " h)))))
|
;; Store normalized value for later use.
|
||||||
(org-format-latex cache-subdir nil nil cache-dir
|
(when (plist-get info :with-latex)
|
||||||
nil display-msg nil
|
(plist-put info :with-latex processing-type))
|
||||||
processing-type)
|
(message "Formatting LaTeX using %s" processing-type)
|
||||||
(goto-char (point-min))
|
;; Convert `latex-fragment's and `latex-environment's.
|
||||||
(skip-chars-forward " \t\n")
|
(when (memq processing-type '(dvipng imagemagick))
|
||||||
(org-element-link-parser))))
|
(org-element-map tree '(latex-fragment latex-environment)
|
||||||
(if (not (org-element-type-p link 'link))
|
(lambda (latex-*)
|
||||||
(message "LaTeX Conversion failed.")
|
(cl-incf count)
|
||||||
;; Conversion succeeded. Parse above Org-style link to
|
(let* ((latex-frag (org-element-property :value latex-*))
|
||||||
;; a `link' object.
|
(input-file (plist-get info :input-file))
|
||||||
(let ((replacement
|
(cache-dir (file-name-directory input-file))
|
||||||
(cl-case (org-element-type latex-*)
|
(cache-subdir (concat
|
||||||
;;LaTeX environment. Mimic a "standalone image
|
(cl-case processing-type
|
||||||
;; or formula" by enclosing the `link' in
|
((dvipng imagemagick)
|
||||||
;; a `paragraph'. Copy over original
|
org-preview-latex-image-directory))
|
||||||
;; attributes, captions to the enclosing
|
(file-name-sans-extension
|
||||||
;; paragraph.
|
(file-name-nondirectory input-file))))
|
||||||
(latex-environment
|
(display-msg
|
||||||
(org-element-adopt
|
(cl-case processing-type
|
||||||
(list 'paragraph
|
((dvipng imagemagick)
|
||||||
(list :style "OrgFormula"
|
(format "Creating LaTeX Image %d..." count))))
|
||||||
:name
|
;; Get an Org-style link to PNG image.
|
||||||
(org-element-property :name latex-*)
|
(link
|
||||||
:caption
|
(with-temp-buffer
|
||||||
(org-element-property :caption latex-*)))
|
(insert latex-frag)
|
||||||
link))
|
(delay-mode-hooks (let ((org-inhibit-startup t)) (org-mode)))
|
||||||
;; LaTeX fragment. No special action.
|
;; When converting to a PNG image, make sure to
|
||||||
(latex-fragment link))))
|
;; copy all LaTeX header specifications from the
|
||||||
;; Note down the object that link replaces.
|
;; Org source.
|
||||||
(org-element-put-property replacement :replaces
|
(let ((h (plist-get info :latex-header)))
|
||||||
(list (org-element-type latex-*)
|
(when h
|
||||||
(list :value latex-frag)))
|
(insert "\n"
|
||||||
;; Restore blank after initial element or object.
|
(replace-regexp-in-string
|
||||||
(org-element-put-property
|
"^" "#+LATEX_HEADER: " h))))
|
||||||
replacement :post-blank
|
(org-latex-preview-replace-fragments
|
||||||
(org-element-property :post-blank latex-*))
|
cache-subdir processing-type cache-dir display-msg)
|
||||||
;; Replace now.
|
(goto-char (point-min))
|
||||||
(org-element-set latex-* replacement)))))
|
(skip-chars-forward " \t\n")
|
||||||
info nil nil t)))
|
(org-element-link-parser))))
|
||||||
|
(if (not (eq 'link (org-element-type link)))
|
||||||
|
(message "LaTeX Conversion failed.")
|
||||||
|
;; Conversion succeeded. Parse above Org-style link to
|
||||||
|
;; a `link' object.
|
||||||
|
(let ((replacement
|
||||||
|
(cl-case (org-element-type latex-*)
|
||||||
|
;;LaTeX environment. Mimic a "standalone image
|
||||||
|
;; or formula" by enclosing the `link' in
|
||||||
|
;; a `paragraph'. Copy over original
|
||||||
|
;; attributes, captions to the enclosing
|
||||||
|
;; paragraph.
|
||||||
|
(latex-environment
|
||||||
|
(org-element-adopt-elements
|
||||||
|
(list 'paragraph
|
||||||
|
(list :style "OrgFormula"
|
||||||
|
:name
|
||||||
|
(org-element-property :name latex-*)
|
||||||
|
:caption
|
||||||
|
(org-element-property :caption latex-*)))
|
||||||
|
link))
|
||||||
|
;; LaTeX fragment. No special action.
|
||||||
|
(latex-fragment link))))
|
||||||
|
;; Note down the object that link replaces.
|
||||||
|
(org-element-put-property replacement :replaces
|
||||||
|
(list (org-element-type latex-*)
|
||||||
|
(list :value latex-frag)))
|
||||||
|
;; Restore blank after initial element or object.
|
||||||
|
(org-element-put-property
|
||||||
|
replacement :post-blank
|
||||||
|
(org-element-property :post-blank latex-*))
|
||||||
|
;; Replace now.
|
||||||
|
(org-element-set-element latex-* replacement)))))
|
||||||
|
info nil nil t))))
|
||||||
tree)
|
tree)
|
||||||
|
|
||||||
|
|
||||||
|
@ -4146,7 +4180,7 @@ MathML source to kill ring depending on the value of
|
||||||
(save-buffer-coding-system 'utf-8))
|
(save-buffer-coding-system 'utf-8))
|
||||||
(set-buffer buffer)
|
(set-buffer buffer)
|
||||||
(set-buffer-file-coding-system coding-system-for-write)
|
(set-buffer-file-coding-system coding-system-for-write)
|
||||||
(let ((mathml (org-create-math-formula latex-frag)))
|
(let ((mathml (org-mathml-convert-latex-cached latex-frag)))
|
||||||
(unless mathml (error "No Math formula created"))
|
(unless mathml (error "No Math formula created"))
|
||||||
(insert mathml)
|
(insert mathml)
|
||||||
;; Add MathML to kill ring, if needed.
|
;; Add MathML to kill ring, if needed.
|
||||||
|
|
|
@ -1681,8 +1681,9 @@ as a communication channel."
|
||||||
"Transcode a SRC-BLOCK element from Org to Texinfo.
|
"Transcode a SRC-BLOCK element from Org to Texinfo.
|
||||||
CONTENTS holds the contents of the item. INFO is a plist holding
|
CONTENTS holds the contents of the item. INFO is a plist holding
|
||||||
contextual information."
|
contextual information."
|
||||||
(let* ((lisp (string-match-p "lisp"
|
(let* ((lisp (string-match-p
|
||||||
(org-element-property :language src-block)))
|
"lisp"
|
||||||
|
(or (org-element-property :language src-block) "")))
|
||||||
(code (org-texinfo--sanitize-content
|
(code (org-texinfo--sanitize-content
|
||||||
(org-export-format-code-default src-block info)))
|
(org-export-format-code-default src-block info)))
|
||||||
(value (format
|
(value (format
|
||||||
|
|
620
lisp/ox.el
620
lisp/ox.el
|
@ -1060,7 +1060,7 @@ mode."
|
||||||
|
|
||||||
(cl-defstruct (org-export-backend (:constructor org-export-create-backend)
|
(cl-defstruct (org-export-backend (:constructor org-export-create-backend)
|
||||||
(:copier nil))
|
(:copier nil))
|
||||||
name parent transcoders options filters blocks menu)
|
name parent transcoders options filters blocks menu feature-conditions feature-implementations)
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(defun org-export-get-backend (name)
|
(defun org-export-get-backend (name)
|
||||||
|
@ -1166,6 +1166,62 @@ returns filters inherited from parent backends, if any."
|
||||||
(setq filters (append filters (org-export-backend-filters backend))))
|
(setq filters (append filters (org-export-backend-filters backend))))
|
||||||
filters)))
|
filters)))
|
||||||
|
|
||||||
|
(defvar org-export-conditional-features)
|
||||||
|
|
||||||
|
(defun org-export-get-all-feature-conditions (backend)
|
||||||
|
"Return full feature condition alist for BACKEND.
|
||||||
|
|
||||||
|
BACKEND is an export back-end, as return by, e.g,,
|
||||||
|
`org-export-create-backend'. Return value is an alist where keys
|
||||||
|
are feature conditions, and values are feature symbols.
|
||||||
|
|
||||||
|
Unlike `org-export-backend-feature-conditions', this function
|
||||||
|
also returns conditions inherited from parent back-ends, if any."
|
||||||
|
(when (symbolp backend) (setq backend (org-export-get-backend backend)))
|
||||||
|
(and backend
|
||||||
|
(let ((conditions (org-export-backend-feature-conditions backend))
|
||||||
|
parent)
|
||||||
|
(while (setq parent (org-export-backend-parent backend))
|
||||||
|
(setq backend (org-export-get-backend parent))
|
||||||
|
(dolist (condition (org-export-backend-feature-conditions backend))
|
||||||
|
(push condition conditions)))
|
||||||
|
(dolist (condition org-export-conditional-features)
|
||||||
|
(unless (assq (car condition) conditions)
|
||||||
|
(push condition conditions)))
|
||||||
|
conditions)))
|
||||||
|
|
||||||
|
(defun org-export-get-all-feature-implementations (backend)
|
||||||
|
"Return full feature implementation alist for BACKEND.
|
||||||
|
|
||||||
|
BACKEND is an export back-end, as return by, e.g,,
|
||||||
|
`org-export-create-backend'. Return value is an alist where keys
|
||||||
|
are feature symbols, and values are an implementation
|
||||||
|
specification plist.
|
||||||
|
|
||||||
|
Unlike `org-export-backend-feature-implementations', this function
|
||||||
|
also returns implementations inherited from parent back-ends, if any."
|
||||||
|
(when (symbolp backend) (setq backend (org-export-get-backend backend)))
|
||||||
|
(and backend
|
||||||
|
(let ((implementations (org-export-backend-feature-implementations backend))
|
||||||
|
parent)
|
||||||
|
(while (setq parent (org-export-backend-parent backend))
|
||||||
|
(setq backend (org-export-get-backend parent))
|
||||||
|
(dolist (implementation (org-export-backend-feature-implementations backend))
|
||||||
|
(unless (assq (car implementation) implementations)
|
||||||
|
(push implementation implementations))))
|
||||||
|
implementations)))
|
||||||
|
|
||||||
|
(defun org-export-install-features (info)
|
||||||
|
"Install feature conditions and implementations in the communication channel.
|
||||||
|
INFO is a plist containing the current communication channel.
|
||||||
|
Return the updated communication channel."
|
||||||
|
(plist-put info :feature-conditions
|
||||||
|
(org-export-get-all-feature-conditions
|
||||||
|
(plist-get info :back-end)))
|
||||||
|
(plist-put info :feature-implementations
|
||||||
|
(org-export-get-all-feature-implementations
|
||||||
|
(plist-get info :back-end))))
|
||||||
|
|
||||||
(defun org-export-define-backend (backend transcoders &rest body)
|
(defun org-export-define-backend (backend transcoders &rest body)
|
||||||
"Define a new backend BACKEND.
|
"Define a new backend BACKEND.
|
||||||
|
|
||||||
|
@ -1277,20 +1333,24 @@ keywords are understood:
|
||||||
`org-export-options-alist' for more information about
|
`org-export-options-alist' for more information about
|
||||||
structure of the values."
|
structure of the values."
|
||||||
(declare (indent 1))
|
(declare (indent 1))
|
||||||
(let (filters menu-entry options)
|
(let (filters menu-entry options feature-conditions feature-implementations)
|
||||||
(while (keywordp (car body))
|
(while (keywordp (car body))
|
||||||
(let ((keyword (pop body)))
|
(let ((keyword (pop body)))
|
||||||
(pcase keyword
|
(pcase keyword
|
||||||
(:filters-alist (setq filters (pop body)))
|
(:filters-alist (setq filters (pop body)))
|
||||||
(:menu-entry (setq menu-entry (pop body)))
|
(:menu-entry (setq menu-entry (pop body)))
|
||||||
(:options-alist (setq options (pop body)))
|
(:options-alist (setq options (pop body)))
|
||||||
|
(:feature-conditions-alist (setq feature-conditions (pop body)))
|
||||||
|
(:feature-implementations-alist (setq feature-implementations (pop body)))
|
||||||
(_ (error "Unknown keyword: %s" keyword)))))
|
(_ (error "Unknown keyword: %s" keyword)))))
|
||||||
(org-export-register-backend
|
(org-export-register-backend
|
||||||
(org-export-create-backend :name backend
|
(org-export-create-backend :name backend
|
||||||
:transcoders transcoders
|
:transcoders transcoders
|
||||||
:options options
|
:options options
|
||||||
:filters filters
|
:filters filters
|
||||||
:menu menu-entry))))
|
:menu menu-entry
|
||||||
|
:feature-conditions feature-conditions
|
||||||
|
:feature-implementations feature-implementations))))
|
||||||
|
|
||||||
(defun org-export-define-derived-backend (child parent &rest body)
|
(defun org-export-define-derived-backend (child parent &rest body)
|
||||||
"Create a new backend as a variant of an existing one.
|
"Create a new backend as a variant of an existing one.
|
||||||
|
@ -1337,7 +1397,7 @@ The backend could then be called with, for example:
|
||||||
|
|
||||||
(org-export-to-buffer \\='my-latex \"*Test my-latex*\")"
|
(org-export-to-buffer \\='my-latex \"*Test my-latex*\")"
|
||||||
(declare (indent 2))
|
(declare (indent 2))
|
||||||
(let (filters menu-entry options transcoders)
|
(let (filters menu-entry options transcoders feature-conditions feature-implementations)
|
||||||
(while (keywordp (car body))
|
(while (keywordp (car body))
|
||||||
(let ((keyword (pop body)))
|
(let ((keyword (pop body)))
|
||||||
(pcase keyword
|
(pcase keyword
|
||||||
|
@ -1345,6 +1405,8 @@ The backend could then be called with, for example:
|
||||||
(:menu-entry (setq menu-entry (pop body)))
|
(:menu-entry (setq menu-entry (pop body)))
|
||||||
(:options-alist (setq options (pop body)))
|
(:options-alist (setq options (pop body)))
|
||||||
(:translate-alist (setq transcoders (pop body)))
|
(:translate-alist (setq transcoders (pop body)))
|
||||||
|
(:feature-conditions-alist (setq feature-conditions (pop body)))
|
||||||
|
(:feature-implementations-alist (setq feature-implementations (pop body)))
|
||||||
(_ (error "Unknown keyword: %s" keyword)))))
|
(_ (error "Unknown keyword: %s" keyword)))))
|
||||||
(org-export-register-backend
|
(org-export-register-backend
|
||||||
(org-export-create-backend :name child
|
(org-export-create-backend :name child
|
||||||
|
@ -1352,7 +1414,9 @@ The backend could then be called with, for example:
|
||||||
:transcoders transcoders
|
:transcoders transcoders
|
||||||
:options options
|
:options options
|
||||||
:filters filters
|
:filters filters
|
||||||
:menu menu-entry))))
|
:menu menu-entry
|
||||||
|
:feature-conditions feature-conditions
|
||||||
|
:feature-implementations feature-implementations))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2055,6 +2119,550 @@ keywords before output."
|
||||||
(funcall (intern (format "org-element-%s-interpreter" type))
|
(funcall (intern (format "org-element-%s-interpreter" type))
|
||||||
blob contents))))
|
blob contents))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Conditional/Generated Features
|
||||||
|
;;
|
||||||
|
;; Many formats have some version of a preamble, whether it be HTML's
|
||||||
|
;; <head>...</head> or the content before LaTeX's \begin{document}.
|
||||||
|
;; Depending on the particular features in the Org document being
|
||||||
|
;; exported, different setup snippets will be needed. There's the
|
||||||
|
;; "everything and the kitchen sink" approach of adding absolutely
|
||||||
|
;; everything that might be needed, and the post-translation editing
|
||||||
|
;; with filters approach, but neither really solve this problem nicely.
|
||||||
|
;;
|
||||||
|
;; The conditional/generated preamble defines mechanisms of detecting
|
||||||
|
;; which "export features" are used in a document, handles
|
||||||
|
;; interactions between features, and provides/generates content to
|
||||||
|
;; support the features.
|
||||||
|
;;
|
||||||
|
;; Each export feature condition takes the form of a
|
||||||
|
;; (CONDITION . FEATURES) cons cell (see `org-export-detect-features'),
|
||||||
|
;; and each implementation takes the form of a (FEATURE . (:KEY VALUE ...))
|
||||||
|
;; associated plist (see `org-export-resolve-feature-implementations'
|
||||||
|
;; and `org-export-expand-feature-snippets').
|
||||||
|
;;
|
||||||
|
;; This functionality is applied during export as follows:
|
||||||
|
;; 1. The export feature conditions and implementations are installed
|
||||||
|
;; into the INFO plist with `org-export-install-features'.
|
||||||
|
;; This simply applies `org-export-get-all-feature-conditions' and
|
||||||
|
;; `org-export-get-all-feature-implementations', which merges the
|
||||||
|
;; backend's conditions/implementations with all of it's parents and
|
||||||
|
;; finally the global condition list
|
||||||
|
;; `org-export-conditional-features'.
|
||||||
|
;; 2. The "export features" used in a document are detected with
|
||||||
|
;; `org-export-detect-features'.
|
||||||
|
;; 3. The interaction between different feature implementations is
|
||||||
|
;; resolved with `org-export-resolve-feature-implementations',
|
||||||
|
;; producing an ordered list of implementations to be actually used
|
||||||
|
;; in an export.
|
||||||
|
;; 4. The feature implementation's snippets are transformed into strings
|
||||||
|
;; to be inserted with `org-export-expand-feature-snippets'.
|
||||||
|
|
||||||
|
(defcustom org-export-conditional-features
|
||||||
|
`(("^[ \t]*#\\+print_bibliography:" bibliography)
|
||||||
|
(,(lambda (info)
|
||||||
|
(org-element-map (plist-get info :parse-tree)
|
||||||
|
'link
|
||||||
|
(lambda (link)
|
||||||
|
(and (member (org-element-property :type link)
|
||||||
|
'("http" "https" "ftp" "file"))
|
||||||
|
(file-name-extension (org-element-property :path link))
|
||||||
|
(member (downcase (file-name-extension
|
||||||
|
(org-element-property :path link)))
|
||||||
|
image-file-name-extensions)))
|
||||||
|
info t))
|
||||||
|
image)
|
||||||
|
(,(lambda (info)
|
||||||
|
(org-element-map (plist-get info :parse-tree)
|
||||||
|
'table #'identity info t))
|
||||||
|
table)
|
||||||
|
(,(lambda (info)
|
||||||
|
(org-element-map (plist-get info :parse-tree)
|
||||||
|
'(src-block inline-src-block) #'identity info t))
|
||||||
|
code))
|
||||||
|
"Org feature tests and associated feature flags.
|
||||||
|
|
||||||
|
Alist where the car is a test for the presense of the feature,
|
||||||
|
and the CDR is either a single feature symbol or a list of
|
||||||
|
feature symbols.
|
||||||
|
|
||||||
|
See `org-export-detect-features' for how this is processed."
|
||||||
|
:group 'org-export-general
|
||||||
|
:type '(alist :key-type
|
||||||
|
(choice (regexp :tag "Feature test regexp")
|
||||||
|
(variable :tag "Feature variable")
|
||||||
|
(function :tag "Feature test function"))
|
||||||
|
:value-type
|
||||||
|
(repeat symbol :tag "Feature symbols")))
|
||||||
|
|
||||||
|
(defun org-export-detect-features (info)
|
||||||
|
"Detect features from `org-export-conditional-features' in INFO.
|
||||||
|
|
||||||
|
More specifically, for each (CONDITION . FEATURES) cons cell of
|
||||||
|
the :feature-conditions list in INFO, the CONDITION is evaluated
|
||||||
|
in two phases.
|
||||||
|
|
||||||
|
In phase one, CONDITION is transformed like so:
|
||||||
|
- If a variable symbol, the value is fetched
|
||||||
|
- If a function symbol, the function is called with INFO as the
|
||||||
|
sole argument
|
||||||
|
- If a string, passed on unmodified
|
||||||
|
|
||||||
|
In phase two, if the CONDITION result is a string, it is used as
|
||||||
|
a case-sensitive regexp search in the buffer. The regexp
|
||||||
|
matching is taken as confirmation of the existance of FEATURES.
|
||||||
|
Any other non-nil value indicates the existance of FEATURES.
|
||||||
|
|
||||||
|
A list of all detected feature symbols is returned.
|
||||||
|
|
||||||
|
This function should be run in the processed export Org buffer,
|
||||||
|
after includes have been expanded and commented trees removed."
|
||||||
|
(delete-dups
|
||||||
|
(cl-loop
|
||||||
|
for (condition . features) in (plist-get info :feature-conditions)
|
||||||
|
for matcher =
|
||||||
|
(cond
|
||||||
|
((stringp condition) condition)
|
||||||
|
((functionp condition) (funcall condition info))
|
||||||
|
((symbolp condition) (symbol-value condition))
|
||||||
|
(t (error "org-export: Feature condition %s (for %s) unable to be used"
|
||||||
|
condition features)))
|
||||||
|
for active-features =
|
||||||
|
(and (if (stringp matcher)
|
||||||
|
(save-excursion
|
||||||
|
(goto-char (point-min))
|
||||||
|
(re-search-forward matcher nil t))
|
||||||
|
matcher)
|
||||||
|
(copy-sequence features))
|
||||||
|
when active-features
|
||||||
|
nconc active-features)))
|
||||||
|
|
||||||
|
(define-error 'org-missing-feature-dependency
|
||||||
|
"A feature was asked for, but is not availible")
|
||||||
|
|
||||||
|
(define-error 'org-circular-feature-dependency
|
||||||
|
"There was a circular dependency between some features")
|
||||||
|
|
||||||
|
(defun org-export-resolve-feature-implementations (info &optional features implementations)
|
||||||
|
"Resolve the IMPLEMENTATIONS of FEATURES, of INFO.
|
||||||
|
|
||||||
|
FEATURES should be a list of all feature symbols to be resolved,
|
||||||
|
and defaults to (plist-get info :features). IMPLEMENTATIONS
|
||||||
|
should be an alist of feature symbols and specification plists,
|
||||||
|
and defaults to (plist-get info :feature-implementations).
|
||||||
|
|
||||||
|
The following keys of the each implementation plist are recognised:
|
||||||
|
- :snippet, which is either,
|
||||||
|
- A string, which should be included in the preamble verbatim.
|
||||||
|
- A variable, the value of which should be included in the preamble.
|
||||||
|
- A function, which is called with two arguments — the export info,
|
||||||
|
and the list of feature flags. The returned value is included in
|
||||||
|
the preamble.
|
||||||
|
- :requires, a feature or list of features this feature will enable.
|
||||||
|
- :when, a feature or list of features which are required for this
|
||||||
|
feature to be active.
|
||||||
|
- :prevents, a feature or list of features that should be masked.
|
||||||
|
- :order, for when inclusion order matters. Feature implementations
|
||||||
|
with a lower order appear first. The default is 0.
|
||||||
|
- :after, a feature or list of features that must be preceding.
|
||||||
|
- :before, a feature or list of features that must be succeeding.
|
||||||
|
|
||||||
|
This function processes :requires, :when, and :prevents in turn,
|
||||||
|
sorting according by :order both before processing :requires and
|
||||||
|
after processing :prevents. The final implementation list is
|
||||||
|
returned."
|
||||||
|
(let* ((explicit-features (or features (plist-get info :features)))
|
||||||
|
(implementations (or implementations
|
||||||
|
(plist-get info :feature-implementations)))
|
||||||
|
(current-implementations
|
||||||
|
(sort (cl-loop for feat in explicit-features
|
||||||
|
collect (assq feat implementations))
|
||||||
|
(lambda (a b)
|
||||||
|
(< (or (plist-get (cdr a) :order) 0)
|
||||||
|
(or (plist-get (cdr b) :order) 0)))))
|
||||||
|
;; require-records serves to record /why/ a particular implementation
|
||||||
|
;; is used. It takes the form of an alist with feature symbols as the
|
||||||
|
;; keys, and a list of features that ask for that feature as values.
|
||||||
|
;; A t value is used to indicate the feature has been explicitly
|
||||||
|
;; required.
|
||||||
|
(require-records
|
||||||
|
(cl-loop for feat in explicit-features
|
||||||
|
collect (list feat t))))
|
||||||
|
;; * Process ~:requires~
|
||||||
|
;; Here we temporarily treat current-implementations as a queue of
|
||||||
|
;; unproceesed implementations, and for each implemention move
|
||||||
|
;; it to processed-implementations if not already present.
|
||||||
|
;; :requires are processed by being added to the current-implementations
|
||||||
|
;; stack as they are seen. Along the way require-records is built for
|
||||||
|
;; the sake of the subsequent :prevents processing.
|
||||||
|
(let ((impl-queue-last (last current-implementations))
|
||||||
|
processed-implementations impl)
|
||||||
|
(while current-implementations
|
||||||
|
(setq impl (pop current-implementations))
|
||||||
|
(unless (memq impl processed-implementations)
|
||||||
|
(push impl processed-implementations)
|
||||||
|
(dolist (req (org-ensure-list
|
||||||
|
(plist-get (cdar processed-implementations) :requires)))
|
||||||
|
(unless (assq req processed-implementations)
|
||||||
|
(let ((required-impl (assq req implementations)))
|
||||||
|
(unless required-impl
|
||||||
|
(signal 'org-missing-feature-dependency
|
||||||
|
(format "The feature `%s' was asked for but could not be found"
|
||||||
|
req)))
|
||||||
|
(setq impl-queue-last
|
||||||
|
(if current-implementations
|
||||||
|
(setcdr impl-queue-last (list required-impl))
|
||||||
|
(setq current-implementations (list required-impl))))
|
||||||
|
(push (car impl) (alist-get req require-records)))))))
|
||||||
|
(setq current-implementations
|
||||||
|
(nreverse (delq nil processed-implementations))))
|
||||||
|
;; * Process ~:when~
|
||||||
|
;; More specifically, remove features with unfulfilled :when conditions.
|
||||||
|
;; To correctly resolve all the various :when conditions,
|
||||||
|
;; do not make any assumptions about which features are active.
|
||||||
|
;; Initially only consider non-:when implementations to be
|
||||||
|
;; active, then run through the list of unconfirmed :when
|
||||||
|
;; implementations and check their conditions against the list
|
||||||
|
;; of confirmed features. Continue doing this until no more
|
||||||
|
;; features are confirmed.
|
||||||
|
(let ((processing t)
|
||||||
|
(confirmed-features
|
||||||
|
(cl-remove-if ; Count unimplemented features as present.
|
||||||
|
(lambda (feat) (assq feat current-implementations))
|
||||||
|
explicit-features))
|
||||||
|
conditional-implementations when)
|
||||||
|
;; Sort all features by the presense of :when.
|
||||||
|
(dolist (impl current-implementations)
|
||||||
|
(if (plist-get (cdr impl) :when)
|
||||||
|
(push impl conditional-implementations)
|
||||||
|
(push (car impl) confirmed-features)))
|
||||||
|
(while processing
|
||||||
|
(setq processing nil)
|
||||||
|
;; Check for implementations which have satisfied :when
|
||||||
|
;; contions.
|
||||||
|
(dolist (impl conditional-implementations)
|
||||||
|
(setq when (plist-get (cdr impl) :when))
|
||||||
|
(when (cond
|
||||||
|
((symbolp when)
|
||||||
|
(memq when confirmed-features))
|
||||||
|
((consp when)
|
||||||
|
(not (cl-set-difference when confirmed-features))))
|
||||||
|
(push (car impl) confirmed-features)
|
||||||
|
(setq conditional-implementations
|
||||||
|
(delq impl conditional-implementations)
|
||||||
|
processing t))))
|
||||||
|
;; Now all that remains is implementations with unsatisfiable
|
||||||
|
;; :when conditions.
|
||||||
|
(dolist (impl conditional-implementations)
|
||||||
|
(setq current-implementations
|
||||||
|
(delq impl current-implementations))))
|
||||||
|
;; * Process ~:prevents~
|
||||||
|
;; Go through every implementation and for prevented features
|
||||||
|
;; 1. Remove them from current-implementations
|
||||||
|
;; 2. Go through require-records and remove them from the cdrs.
|
||||||
|
;; By modifying require-records in this way, features that are
|
||||||
|
;; only present due to a now-prevented feature will have a
|
||||||
|
;; nil cdr. We can then (recursively) check for these features
|
||||||
|
;; with `rassq' and remove them.
|
||||||
|
;; Since we used a queue rather than a stack when processing
|
||||||
|
;; :requires, we know that second order requires (i.e. :requires
|
||||||
|
;; of :requires) will come after after first order requires.
|
||||||
|
;; This means that should a n-th order require be prevented by
|
||||||
|
;; (n-1)-th order require, it will be removed before being
|
||||||
|
;; processed, and hence handled correctly.
|
||||||
|
(let (feats-to-remove removed null-require)
|
||||||
|
(dolist (impl current-implementations)
|
||||||
|
(setq feats-to-remove (org-ensure-list (plist-get (cdr impl) :prevents)))
|
||||||
|
(while feats-to-remove
|
||||||
|
;; Remove each of feats-to-remove.
|
||||||
|
(dolist (feat feats-to-remove)
|
||||||
|
(unless (memq feat removed)
|
||||||
|
(push feat removed)
|
||||||
|
(setq current-implementations
|
||||||
|
(delq (assq feat current-implementations)
|
||||||
|
current-implementations))
|
||||||
|
(when (assq feat require-records)
|
||||||
|
(setq require-records
|
||||||
|
(delq (assq feat require-records) require-records)))))
|
||||||
|
(dolist (rec require-records)
|
||||||
|
(setcdr rec (cl-set-difference (cdr rec) feats-to-remove)))
|
||||||
|
;; The features have now been removed.
|
||||||
|
(setq feats-to-remove nil)
|
||||||
|
;; Look for orphan requires.
|
||||||
|
(when (setq null-require (rassq nil require-records))
|
||||||
|
(push (car null-require) feats-to-remove)))))
|
||||||
|
;; Re-sort by ~:order~, to position reqirued features correctly.
|
||||||
|
(setq current-implementations
|
||||||
|
(sort current-implementations
|
||||||
|
(lambda (a b)
|
||||||
|
(< (or (plist-get (cdr a) :order) 0)
|
||||||
|
(or (plist-get (cdr b) :order) 0)))))
|
||||||
|
;; * Processing ~:before~ and ~:after~
|
||||||
|
;; To resolve dependency order, we will now perform a stable topological
|
||||||
|
;; sort on any DAGs that exist within current-implementations.
|
||||||
|
(org-export--feature-implementation-toposort
|
||||||
|
current-implementations)))
|
||||||
|
|
||||||
|
(defun org-export--feature-implementation-toposort (implementations)
|
||||||
|
"Perform a stable topological sort of IMPLEMENTATIONS.
|
||||||
|
The sort is performed based on the :before and :after properties.
|
||||||
|
|
||||||
|
See <https://en.wikipedia.org/wiki/Topological_sorting> for more information
|
||||||
|
on what this entails."
|
||||||
|
(let ((feature-indicies
|
||||||
|
(cl-loop
|
||||||
|
for elt in implementations
|
||||||
|
and index from 0
|
||||||
|
collect (cons (car elt) index)))
|
||||||
|
resolved-implementations
|
||||||
|
adj-list node-stack)
|
||||||
|
;; Build an adjacency list from :before and :after.
|
||||||
|
(dolist (impl implementations)
|
||||||
|
(push (list (car impl)) adj-list))
|
||||||
|
(dolist (impl implementations)
|
||||||
|
(let ((before (org-ensure-list (plist-get (cdr impl) :before)))
|
||||||
|
(after (org-ensure-list (plist-get (cdr impl) :after))))
|
||||||
|
(dolist (child before)
|
||||||
|
(push (car impl) (cdr (assq child adj-list))))
|
||||||
|
(when after
|
||||||
|
(setcdr (assq (car impl) adj-list)
|
||||||
|
(nconc (cdr (assq (car impl) adj-list))
|
||||||
|
after)))))
|
||||||
|
;; Initialise the node stack with the first implementation.
|
||||||
|
(setq node-stack (list (car implementations))
|
||||||
|
;; Make the order of adj-list match implementations.
|
||||||
|
adj-list
|
||||||
|
(mapcar
|
||||||
|
(lambda (entry)
|
||||||
|
(cons (car entry)
|
||||||
|
;; Sort edges according to feature order, to do
|
||||||
|
;; the DFS in order and make the result stable.
|
||||||
|
(sort (cdr entry)
|
||||||
|
(lambda (a b)
|
||||||
|
(< (or (alist-get a feature-indicies)
|
||||||
|
most-positive-fixnum)
|
||||||
|
(or (alist-get b feature-indicies)
|
||||||
|
most-positive-fixnum))))))
|
||||||
|
(nreverse adj-list)))
|
||||||
|
(while adj-list
|
||||||
|
(let ((deps (alist-get (caar node-stack) adj-list))
|
||||||
|
new-dep-found)
|
||||||
|
;; Look for any unresolved dependencies.
|
||||||
|
(while (and deps (not new-dep-found))
|
||||||
|
(if (not (assq (car deps) adj-list))
|
||||||
|
(setq deps (cdr deps))
|
||||||
|
;; Check the unresolved dependency is not part of a cycle.
|
||||||
|
(when (assq (car deps) node-stack)
|
||||||
|
(signal 'org-circular-feature-dependency
|
||||||
|
(format "Found a cycle in the feature dependency graph: %S"
|
||||||
|
(cons (car deps)
|
||||||
|
(nreverse (memq (car deps)
|
||||||
|
(nreverse
|
||||||
|
(mapcar #'car node-stack))))))))
|
||||||
|
;; Push the unresolved dependency to the top of the stack.
|
||||||
|
(push (assq (car deps) implementations)
|
||||||
|
node-stack)
|
||||||
|
(setq new-dep-found t)))
|
||||||
|
(unless new-dep-found
|
||||||
|
;; The top item of the stack has no unresolved dependencies.
|
||||||
|
;; Move it to the resolved list, and remove its entry from
|
||||||
|
;; adj-list to both mark it as such and ensure that
|
||||||
|
;; node-stack will not be incremented to it when/if the
|
||||||
|
;; stack is emptied.
|
||||||
|
(push (car node-stack) resolved-implementations)
|
||||||
|
(setq adj-list
|
||||||
|
(delq (assq (caar node-stack) adj-list) adj-list)
|
||||||
|
node-stack
|
||||||
|
(or (cdr node-stack)
|
||||||
|
(list (assq (caar adj-list) implementations)))))))
|
||||||
|
(nreverse resolved-implementations)))
|
||||||
|
|
||||||
|
(defun org-export-expand-feature-snippets (info &rest feature-implementations)
|
||||||
|
"Expand each of the feature :snippet keys in FEATURE-IMPLEMENTATIONS.
|
||||||
|
FEATURE-IMPLEMENTATIONS is expected to be a list of implementation
|
||||||
|
plists, if not provided explicitly it is extracted from the
|
||||||
|
:feature-implementations key of INFO. Note that an explicitly
|
||||||
|
provided nil FEATURE-IMPLEMENTATIONS is interpreted as no features.
|
||||||
|
|
||||||
|
Each implementation plist's :snippet value is expanded in order, in
|
||||||
|
the following manner:
|
||||||
|
- nil values are ignored
|
||||||
|
- functions are called with INFO, and must produce a string or nil
|
||||||
|
- variable symbols use the value, which must be a string or nil
|
||||||
|
- strings are included verbatim
|
||||||
|
- all other values throw an `error'.
|
||||||
|
|
||||||
|
\(fn INFO &optional FEATURE-IMPLEMENTATIONS)"
|
||||||
|
(let ((feat-impls
|
||||||
|
(cond
|
||||||
|
((not feature-implementations)
|
||||||
|
(plist-get info :feature-implementations))
|
||||||
|
((= (length feature-implementations) 1)
|
||||||
|
(car feature-implementations))
|
||||||
|
(t (signal 'wrong-number-of-arguments
|
||||||
|
`(org-export-expand-feature-snippets
|
||||||
|
,(1+ (length feature-implementations)))))))
|
||||||
|
expanded-snippets snippet value)
|
||||||
|
(dolist (impl feat-impls)
|
||||||
|
(setq snippet (plist-get (cdr impl) :snippet)
|
||||||
|
value (cond
|
||||||
|
((null snippet) nil)
|
||||||
|
((functionp snippet) (funcall snippet info))
|
||||||
|
((symbolp snippet) (symbol-value snippet))
|
||||||
|
((stringp snippet) snippet)
|
||||||
|
(t (error "org-export: The %s feature snippet %S is invalid (must be either nil, a function/variable symbol, or a string)"
|
||||||
|
(car impl) snippet))))
|
||||||
|
(cond
|
||||||
|
((stringp value)
|
||||||
|
(push value expanded-snippets))
|
||||||
|
(value ; Non-string value, could come from function or variable.
|
||||||
|
(error "org-export: The %s feature snippet %s must give nil or a string, but instead gave %S"
|
||||||
|
(car impl)
|
||||||
|
(cond
|
||||||
|
((and (functionp snippet) (symbolp snippet))
|
||||||
|
(format "function (`%s')" snippet))
|
||||||
|
((functionp snippet) "anonymous function")
|
||||||
|
(t (format "variable (`%s')" snippet)))
|
||||||
|
value))))
|
||||||
|
(nreverse expanded-snippets)))
|
||||||
|
|
||||||
|
(defun org-export-process-features (info)
|
||||||
|
"Install feature conditions/implementations in INFO, and resolve them.
|
||||||
|
See `org-export-detect-features' and `org-export-resolve-feature-implementations' for
|
||||||
|
more information on what this entails."
|
||||||
|
(org-export-install-features info)
|
||||||
|
(let* ((exp-features (org-export-detect-features info))
|
||||||
|
(resolved-implementations
|
||||||
|
(org-export-resolve-feature-implementations info exp-features)))
|
||||||
|
(plist-put info :feature-implementations resolved-implementations)
|
||||||
|
(plist-put info :features (mapcar #'car resolved-implementations))))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(defmacro org-export-update-features (backend &rest feature-property-value-lists)
|
||||||
|
"For BACKEND's export spec, set each FEATURE's :PROPERTY to VALUE.
|
||||||
|
|
||||||
|
The behaviour of this macro is best behaved with an example.
|
||||||
|
For instance, to add some preamble content from the variable
|
||||||
|
\"my-org-beamer-metropolis-tweaks\" when using the metropolis theme
|
||||||
|
with beamer export:
|
||||||
|
|
||||||
|
(org-export-update-features \\='beamer
|
||||||
|
(beamer-metropolis
|
||||||
|
:condition (string-match-p \"metropolis$\" (plist-get info :beamer-theme))
|
||||||
|
:snippet my-org-beamer-metropolis-tweaks
|
||||||
|
:order 3))
|
||||||
|
|
||||||
|
The modifies the beamer backend, either creating or updating the
|
||||||
|
\"beamer-metropolis\" feature. The :condition property adds a
|
||||||
|
condition which detects the feature, and all other properties are
|
||||||
|
applied to the feature's implementation plist. Setting
|
||||||
|
:condition to t means the feature will always be enabled, and
|
||||||
|
conversely setting :condition to nil means the feature will never
|
||||||
|
be enabled.
|
||||||
|
|
||||||
|
When setting the :condition and :snippet properties, any sexp is
|
||||||
|
is implicitly converted to,
|
||||||
|
(lambda (info) SEXPR)
|
||||||
|
|
||||||
|
Each (FEATURE . (:PROPERTY VALUE)) form that is processed is
|
||||||
|
taken from the single &rest argument
|
||||||
|
FEATURE-PROPERTY-VALUE-LISTS.
|
||||||
|
|
||||||
|
\(fn BACKEND &rest (FEATURE . (:PROPERTY VALUE)...)...)"
|
||||||
|
(declare (indent 1))
|
||||||
|
(org-with-gensyms (backend-struct the-entry the-condition the-feat-impl cond-feat)
|
||||||
|
(let ((backend-expr
|
||||||
|
(if (and (eq (car-safe backend) 'quote)
|
||||||
|
(symbolp (cadr backend))
|
||||||
|
(not (cddr backend)))
|
||||||
|
`(org-export-get-backend ',(cadr backend))
|
||||||
|
`(if (symbolp ,backend)
|
||||||
|
(org-export-get-backend ,backend)
|
||||||
|
backend)))
|
||||||
|
(backend-impls
|
||||||
|
(list 'aref backend-struct
|
||||||
|
(cl-struct-slot-offset 'org-export-backend 'feature-implementations)))
|
||||||
|
(backend-conds
|
||||||
|
(list 'aref backend-struct
|
||||||
|
(cl-struct-slot-offset 'org-export-backend 'feature-conditions)))
|
||||||
|
body condition-set-p implementation-set-p)
|
||||||
|
(dolist (feature-property-value-set feature-property-value-lists)
|
||||||
|
(when (eq (car feature-property-value-set) 'quote)
|
||||||
|
(pop feature-property-value-set))
|
||||||
|
(let ((features (car feature-property-value-set))
|
||||||
|
(property-value-pairs (cdr feature-property-value-set))
|
||||||
|
let-body property value)
|
||||||
|
(while property-value-pairs
|
||||||
|
(setq property (pop property-value-pairs)
|
||||||
|
value (pop property-value-pairs))
|
||||||
|
(cond
|
||||||
|
((consp value)
|
||||||
|
(unless (memq (car value) '(function quote))
|
||||||
|
(if (and (memq property '(:condition :snippet))
|
||||||
|
(not (functionp value)))
|
||||||
|
(setq value `(lambda (info) ,value))
|
||||||
|
(setq value (list 'quote value)))))
|
||||||
|
((memq value '(nil t))) ; Leave unmodified.
|
||||||
|
((symbolp value)
|
||||||
|
(setq value (list 'quote value))))
|
||||||
|
(if (eq property :condition)
|
||||||
|
(progn
|
||||||
|
(unless condition-set-p
|
||||||
|
(setq condition-set-p t))
|
||||||
|
(push
|
||||||
|
(if value
|
||||||
|
(let ((the-features (org-ensure-list features)))
|
||||||
|
`(let* ((,the-condition ,value)
|
||||||
|
(,the-entry (assoc ,the-condition ,backend-conds)))
|
||||||
|
(if ,the-entry
|
||||||
|
(setcdr ,the-entry
|
||||||
|
(append ',the-features (cdr ,the-entry)))
|
||||||
|
(push (cons ,the-condition ',the-features)
|
||||||
|
,backend-conds))))
|
||||||
|
(let ((single-feature
|
||||||
|
(if (consp features)
|
||||||
|
(intern (string-join (mapcar #'symbol-name features)
|
||||||
|
"-and-"))
|
||||||
|
features)))
|
||||||
|
`(dolist (,cond-feat ,backend-conds)
|
||||||
|
(cond
|
||||||
|
((equal (cdr ,cond-feat) (list ',single-feature))
|
||||||
|
(setf ,backend-conds (delq ,cond-feat ,backend-conds)))
|
||||||
|
((memq ',single-feature (cdr ,cond-feat))
|
||||||
|
(setcdr ,cond-feat
|
||||||
|
(delq ',single-feature (cdr ,cond-feat))))))))
|
||||||
|
body))
|
||||||
|
(unless implementation-set-p
|
||||||
|
(setq implementation-set-p t))
|
||||||
|
(push
|
||||||
|
(if let-body
|
||||||
|
`(plist-put (cdr ,the-feat-impl) ,property ,value)
|
||||||
|
`(setcdr ,the-feat-impl
|
||||||
|
(plist-put (cdr ,the-feat-impl) ,property ,value)))
|
||||||
|
let-body)))
|
||||||
|
(when let-body
|
||||||
|
(let ((the-feature
|
||||||
|
(if (consp features)
|
||||||
|
(intern (string-join (mapcar #'symbol-name features)
|
||||||
|
"-and-"))
|
||||||
|
features)))
|
||||||
|
(when (consp features)
|
||||||
|
(push
|
||||||
|
`(plist-put (cdr ,the-feat-impl) :when ',features)
|
||||||
|
let-body))
|
||||||
|
(push
|
||||||
|
`(let ((,the-feat-impl
|
||||||
|
(or (assoc ',the-feature ,backend-impls)
|
||||||
|
(car (push (list ',the-feature ,property nil)
|
||||||
|
,backend-impls)))))
|
||||||
|
,@(nreverse let-body))
|
||||||
|
body)))))
|
||||||
|
`(let ((,backend-struct ,backend-expr))
|
||||||
|
,@(and (not (org-export-backend-p backend-expr))
|
||||||
|
`((unless (org-export-backend-p ,backend-struct)
|
||||||
|
(error "`%s' is not a loaded export backend" ,backend))))
|
||||||
|
,@(nreverse body)
|
||||||
|
nil))))
|
||||||
|
|
||||||
|
|
||||||
;;; The Filter System
|
;;; The Filter System
|
||||||
|
@ -3129,6 +3737,8 @@ still inferior to file-local settings."
|
||||||
(when (plist-get info :with-cite-processors)
|
(when (plist-get info :with-cite-processors)
|
||||||
(org-cite-process-citations info)
|
(org-cite-process-citations info)
|
||||||
(org-cite-process-bibliography info))
|
(org-cite-process-bibliography info))
|
||||||
|
;; Install all the feature conditions and implementations.
|
||||||
|
(org-export-process-features info)
|
||||||
info))
|
info))
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
|
|
|
@ -7,3 +7,14 @@
|
||||||
doi = {10.1007/978-3-642-23816-1},
|
doi = {10.1007/978-3-642-23816-1},
|
||||||
isbn = {9783642238161}
|
isbn = {9783642238161}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InCollection{Geyer2011,
|
||||||
|
author = {Geyer, Charles J},
|
||||||
|
title = {{Introduction to Markov\plus Chain Monte Carlo}},
|
||||||
|
year = 2011,
|
||||||
|
booktitle = {{Handbook of Markov Chain Monte Carlo}},
|
||||||
|
editor = {Brooks, Steve and Gelman, Andrew and Jones, Galin and Meng,
|
||||||
|
Xiao-Li},
|
||||||
|
publisher = {CRC press},
|
||||||
|
pages = 45,
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#+latex_header: \usepackage{amsmath}
|
||||||
|
#+latex_header: \usepackage{amssymb}
|
||||||
|
|
||||||
|
* Inline fragments and LaTeX environments
|
||||||
|
:PROPERTIES:
|
||||||
|
:ID: 0b3807b3-69af-40cb-a27a-b380d54879cc
|
||||||
|
:END:
|
||||||
|
|
||||||
|
The LQR problem for a time-periodic system of the form
|
||||||
|
\begin{align}
|
||||||
|
\dot{x} = A(t) x + B(t) u, \quad t \in [0, \infty), \quad x(0) = x_i \label{eq:time-varying-system}\\
|
||||||
|
A(t+T) = A(t),\ B(t + T) = B(t) \nonumber
|
||||||
|
\end{align}
|
||||||
|
is as follows. With a quadratic form defined on \( (x,u) \) pairs
|
||||||
|
\begin{align}
|
||||||
|
\label{eq:quadratic-form}
|
||||||
|
\mathbf{q}(x, u) := \lim_{t_f \to \infty} \int_{0}^{t_f} \begin{bmatrix} x \\ u \end{bmatrix}^{\star} \begin{bmatrix}
|
||||||
|
Q & 0 \\
|
||||||
|
0 & r
|
||||||
|
\end{bmatrix} \begin{bmatrix} x \\ u \end{bmatrix} =: \lim_{t_f \to \infty} \int_{0}^{t_f} q(x,u) dt
|
||||||
|
\end{align}
|
||||||
|
with \( q \ge 0 \) and \( r \ge 0 \), find the infimum of the quadratic form \( \mathbf{q} \) subject to the dynamics:
|
||||||
|
\[
|
||||||
|
\inf_{x,u} \mathbf{q}(x,u).
|
||||||
|
\]
|
||||||
|
\begin{align}
|
||||||
|
\label{eq:lqr-inf-via-duality}
|
||||||
|
\inf_{x, u} \mathbf{q}(x, u) = x_i^{\star} \bar{\lambda}(0) x_i,
|
||||||
|
\end{align}
|
||||||
|
where \( \bar{\lambda} \) is the maximal solution of the differential linear matrix inequality over \( [0, t] \).
|
|
@ -2545,6 +2545,225 @@ abc
|
||||||
(lambda (&rest _) (error "No warnings should occur"))))
|
(lambda (&rest _) (error "No warnings should occur"))))
|
||||||
(org-babel-import-elisp-from-file (buffer-file-name))))))
|
(org-babel-import-elisp-from-file (buffer-file-name))))))
|
||||||
|
|
||||||
|
(ert-deftest test-ob/demarcate-block-split-duplication ()
|
||||||
|
"Test duplication of language, body, switches, and headers in splitting."
|
||||||
|
(let ((caption "#+caption: caption.")
|
||||||
|
(regexp (rx "#+caption: caption."))
|
||||||
|
(org-adapt-indentation nil))
|
||||||
|
(org-test-with-temp-text (format "
|
||||||
|
%s
|
||||||
|
#+header: :var edge=\"also duplicated\"
|
||||||
|
#+header: :wrap \"src any-spanish -n\"
|
||||||
|
#+name: Nobody
|
||||||
|
#+begin_src any-english -i -n :var here=\"duplicated\" :wrap \"src any-english -n\"
|
||||||
|
|
||||||
|
above split
|
||||||
|
<point>
|
||||||
|
below split
|
||||||
|
|
||||||
|
#+end_src
|
||||||
|
do not org-indent-block text here
|
||||||
|
" caption)
|
||||||
|
(let ((wrap-val "src any-spanish -n") above below avars bvars)
|
||||||
|
(org-babel-demarcate-block)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(org-babel-next-src-block) ;; upper source block
|
||||||
|
(setq above (org-babel-get-src-block-info))
|
||||||
|
(setq avars (org-babel--get-vars (nth 2 above)))
|
||||||
|
(org-babel-next-src-block) ;; lower source block
|
||||||
|
(setq below (org-babel-get-src-block-info))
|
||||||
|
(setq bvars (org-babel--get-vars (nth 2 below)))
|
||||||
|
;; duplicated multi-line header arguments:
|
||||||
|
(should (string= "also duplicated" (cdr (assq 'edge avars))))
|
||||||
|
(should (string= "also duplicated" (cdr (assq 'edge bvars))))
|
||||||
|
(should (string= wrap-val (cdr (assq :wrap (nth 2 above)))))
|
||||||
|
(should (string= wrap-val (cdr (assq :wrap (nth 2 below)))))
|
||||||
|
;; duplicated language, other header arguments, and switches:
|
||||||
|
(should (string= "any-english" (nth 0 above)))
|
||||||
|
(should (string= "any-english" (nth 0 below)))
|
||||||
|
(should (string= "above split" (org-trim (nth 1 above))))
|
||||||
|
(should (string= "below split" (org-trim (nth 1 below))))
|
||||||
|
(should (string= "duplicated" (cdr (assq 'here avars))))
|
||||||
|
(should (string= "duplicated" (cdr (assq 'here bvars))))
|
||||||
|
(should (string= "-i -n" (nth 3 above)))
|
||||||
|
(should (string= "-i -n" (nth 3 below)))
|
||||||
|
;; non-duplication of name and caption, which is not in above/below.
|
||||||
|
(should (string= "Nobody" (nth 4 above)))
|
||||||
|
(should-not (string= "" (nth 4 below)))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(should (re-search-forward regexp))
|
||||||
|
(should-not (re-search-forward regexp nil 'noerror))))))
|
||||||
|
|
||||||
|
(ert-deftest test-ob/demarcate-block-split-prefix-point ()
|
||||||
|
"Test prefix argument point splitting."
|
||||||
|
(let ((org-adapt-indentation t)
|
||||||
|
(org-edit-src-content-indentation 2)
|
||||||
|
(org-src-preserve-indentation nil)
|
||||||
|
(ok-col 11)
|
||||||
|
(stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*"))
|
||||||
|
(org-test-with-temp-text "
|
||||||
|
********** 10 stars with point between two lines
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
;; to upper block
|
||||||
|
<point>
|
||||||
|
;; to lower block
|
||||||
|
#+end_src
|
||||||
|
"
|
||||||
|
(org-babel-demarcate-block 'a-prefix-arg)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(dolist (regexp `(,stars
|
||||||
|
"#\\+beg" ";; to upper block" "#\\+end"
|
||||||
|
,stars
|
||||||
|
"#\\+beg" ";; to lower block" "#\\+end"))
|
||||||
|
(should (re-search-forward regexp))
|
||||||
|
(goto-char (match-beginning 0))
|
||||||
|
(cond ((string= regexp stars)
|
||||||
|
(should (= 0 (current-column))))
|
||||||
|
((string-prefix-p ";;" regexp)
|
||||||
|
(should (= (+ ok-col org-edit-src-content-indentation)
|
||||||
|
(current-column))))
|
||||||
|
(t (should (= ok-col (current-column)))))))))
|
||||||
|
|
||||||
|
(ert-deftest test-ob/demarcate-block-split-prefix-region ()
|
||||||
|
"Test prefix argument region splitting."
|
||||||
|
(let ((org-adapt-indentation t)
|
||||||
|
(org-edit-src-content-indentation 2)
|
||||||
|
(org-src-preserve-indentation nil)
|
||||||
|
(ok-col 11)
|
||||||
|
(stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*")
|
||||||
|
(parts '("to upper block" "mark those words as region" "to lower block")))
|
||||||
|
(org-test-with-temp-text (format "
|
||||||
|
********** 10 stars with region between two lines
|
||||||
|
#+header: :var b=\"also seen\"
|
||||||
|
#+begin_src any-language -i -n :var a=\"seen\"
|
||||||
|
%s
|
||||||
|
<point>%s
|
||||||
|
%s
|
||||||
|
#+end_src
|
||||||
|
" (nth 0 parts) (nth 1 parts) (nth 2 parts))
|
||||||
|
(let ((n 0) info vars)
|
||||||
|
(transient-mark-mode 1)
|
||||||
|
(push-mark (point) t t)
|
||||||
|
(search-forward (nth 1 parts))
|
||||||
|
(org-babel-demarcate-block 'a-prefix-argument)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(while (< n (length parts))
|
||||||
|
(org-babel-next-src-block)
|
||||||
|
(setq info (org-babel-get-src-block-info))
|
||||||
|
(setq vars (org-babel--get-vars (nth 2 info)))
|
||||||
|
(should (string= "any-language" (nth 0 info)))
|
||||||
|
(should (string= (nth n parts) (org-trim (nth 1 info))))
|
||||||
|
(should (string= "seen" (cdr (assq 'a vars))))
|
||||||
|
(should (string= "also seen" (cdr (assq 'b vars))))
|
||||||
|
(should (string= "-i -n" (nth 3 info)))
|
||||||
|
(cl-incf n)))
|
||||||
|
(goto-char (point-min))
|
||||||
|
(dolist (regexp `(,stars
|
||||||
|
"#\\+beg" ,(nth 0 parts) "#\\+end"
|
||||||
|
,stars
|
||||||
|
"#\\+beg" ,(nth 1 parts) "#\\+end"
|
||||||
|
,stars
|
||||||
|
"#\\+beg" ,(nth 2 parts) "#\\+end"))
|
||||||
|
(should (re-search-forward regexp))
|
||||||
|
(goto-char (match-beginning 0))
|
||||||
|
(cond ((string= regexp stars)
|
||||||
|
(should (= 0 (current-column))))
|
||||||
|
((memq regexp parts)
|
||||||
|
(should (= (+ ok-col org-edit-src-content-indentation)
|
||||||
|
(current-column))))
|
||||||
|
(t (should (= ok-col (current-column)))))))))
|
||||||
|
|
||||||
|
(ert-deftest test-ob/demarcate-block-split-user-errors ()
|
||||||
|
"Test for `user-error's in splitting"
|
||||||
|
(let ((org-adapt-indentation t)
|
||||||
|
(org-edit-src-content-indentation 2)
|
||||||
|
(org-src-preserve-indentation))
|
||||||
|
(let* ((caption "#+caption: caption.")
|
||||||
|
(within-body ";; within-body")
|
||||||
|
(below-block "# below block")
|
||||||
|
(template "
|
||||||
|
%s%s
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
%s%s
|
||||||
|
"))
|
||||||
|
;; Test point at caption.
|
||||||
|
(org-test-with-temp-text
|
||||||
|
(format template "<point>" caption within-body below-block "")
|
||||||
|
(should-error (org-babel-demarcate-block) :type 'user-error))
|
||||||
|
;; Test region from below the block (mark) to within the body (point).
|
||||||
|
(org-test-with-temp-text
|
||||||
|
(format template "" caption within-body below-block "<point>")
|
||||||
|
;; Set mark.
|
||||||
|
(transient-mark-mode 1)
|
||||||
|
(push-mark (point) t t)
|
||||||
|
;; Set point.
|
||||||
|
(should (search-backward within-body nil 'noerror))
|
||||||
|
(goto-char (match-beginning 0))
|
||||||
|
(should-error (org-babel-demarcate-block) :type 'user-error)))))
|
||||||
|
|
||||||
|
(ert-deftest test-ob/demarcate-block-wrap-point ()
|
||||||
|
"Test wrapping point in blank lines below a source block."
|
||||||
|
(org-test-with-temp-text "
|
||||||
|
#+begin_src any-language -i -n :var here=\"not duplicated\"
|
||||||
|
to upper block
|
||||||
|
#+end_src
|
||||||
|
<point>
|
||||||
|
"
|
||||||
|
(let (info vars)
|
||||||
|
(org-babel-demarcate-block)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(org-babel-next-src-block)
|
||||||
|
(setq info (org-babel-get-src-block-info)) ;; upper source block info
|
||||||
|
(setq vars (org-babel--get-vars (nth 2 info)))
|
||||||
|
(should (string= "any-language" (nth 0 info)))
|
||||||
|
(should (string= "to upper block" (org-trim (nth 1 info))))
|
||||||
|
(should (string= "not duplicated" (cdr (assq 'here vars))))
|
||||||
|
(should (string= "-i -n" (nth 3 info)))
|
||||||
|
(org-babel-next-src-block)
|
||||||
|
(setq info (org-babel-get-src-block-info)) ;; lower source block info
|
||||||
|
(setq vars (org-babel--get-vars (nth 2 info)))
|
||||||
|
(should (string= "any-language" (nth 0 info)))
|
||||||
|
(should (string= "" (org-trim (nth 1 info))))
|
||||||
|
(should-not vars)
|
||||||
|
(should (string= "" (nth 3 info))))))
|
||||||
|
|
||||||
|
(ert-deftest test-ob/demarcate-block-wrap-region ()
|
||||||
|
"Test wrapping region in blank lines below a source block."
|
||||||
|
(let ((region-text "mark this line as region leaving point in blank lines"))
|
||||||
|
(org-test-with-temp-text (format "
|
||||||
|
#+begin_src any-language -i -n :var here=\"not duplicated\"
|
||||||
|
to upper block
|
||||||
|
#+end_src
|
||||||
|
<point>
|
||||||
|
%s
|
||||||
|
" region-text)
|
||||||
|
(let (info vars)
|
||||||
|
(transient-mark-mode 1)
|
||||||
|
(push-mark (point) t t)
|
||||||
|
(search-forward region-text)
|
||||||
|
(exchange-point-and-mark)
|
||||||
|
(org-babel-demarcate-block)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(org-babel-next-src-block)
|
||||||
|
(setq info (org-babel-get-src-block-info)) ;; upper source block info
|
||||||
|
(setq vars (org-babel--get-vars (nth 2 info)))
|
||||||
|
(should (string= "any-language" (nth 0 info)))
|
||||||
|
(should (string= "to upper block" (org-trim (nth 1 info))))
|
||||||
|
(should (string= "not duplicated" (cdr (assq 'here vars))))
|
||||||
|
(should (string= "-i -n" (nth 3 info)))
|
||||||
|
(org-babel-next-src-block)
|
||||||
|
(setq info (org-babel-get-src-block-info)) ;; lower source block info
|
||||||
|
(setq vars (org-babel--get-vars (nth 2 info)))
|
||||||
|
(should (string= "any-language" (nth 0 info)))
|
||||||
|
(should (string= region-text (org-trim (nth 1 info))))
|
||||||
|
(should-not vars)
|
||||||
|
(should (string= "" (nth 3 info)))))))
|
||||||
|
|
||||||
(provide 'test-ob)
|
(provide 'test-ob)
|
||||||
|
|
||||||
;;; test-ob ends here
|
;;; test-ob ends here
|
||||||
|
|
|
@ -156,5 +156,24 @@ Text: [cite/t: Citing ; @friends; and @friends also; is duplication.]"
|
||||||
(search-forward "Text: Citing van Dongen, M.R.C. (2012), and van Dongen, M.R.C. (2012)
|
(search-forward "Text: Citing van Dongen, M.R.C. (2012), and van Dongen, M.R.C. (2012)
|
||||||
also is duplication." nil t)))))))
|
also is duplication." nil t)))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-cite-basic/export-bibliography ()
|
||||||
|
"Test `org-cite-basic-export-bibliography'."
|
||||||
|
;; Drop {...} Bibtex brackets and render entities.
|
||||||
|
(org-test-with-temp-text
|
||||||
|
(format
|
||||||
|
"#+bibliography: %s
|
||||||
|
#+cite_export: basic
|
||||||
|
Foo [cite/plain:@Geyer2011]
|
||||||
|
#+print_bibliography:"
|
||||||
|
(expand-file-name "examples/Basic.bib" org-test-dir))
|
||||||
|
(let ((export-buffer "*Test ASCII Export*")
|
||||||
|
(org-export-show-temporary-export-buffer nil))
|
||||||
|
(org-export-to-buffer 'ascii export-buffer)
|
||||||
|
(with-current-buffer export-buffer
|
||||||
|
(let ((case-fold-search t))
|
||||||
|
(should
|
||||||
|
;; Rendered from {Introduction to Markov\plus Chain Monte Carlo}
|
||||||
|
(search-forward "Introduction to Markov+ Chain Monte Carlo" nil t)))))))
|
||||||
|
|
||||||
(provide 'test-oc-basic)
|
(provide 'test-oc-basic)
|
||||||
;;; test-oc-basic.el ends here
|
;;; test-oc-basic.el ends here
|
||||||
|
|
|
@ -381,6 +381,128 @@ See https://github.com/yantar92/org/issues/4."
|
||||||
(equal (format "[[file:%s::*foo bar][foo bar]]" file file)
|
(equal (format "[[file:%s::*foo bar][foo bar]]" file file)
|
||||||
(org-store-link nil)))))))
|
(org-store-link nil)))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-link/precise-link-target ()
|
||||||
|
"Test `org-link-precise-link-target` specifications."
|
||||||
|
(org-test-with-temp-text "* H1<point>\n* H2\n"
|
||||||
|
(should
|
||||||
|
(equal '("*H1" "H1" 1)
|
||||||
|
(org-link-precise-link-target))))
|
||||||
|
(org-test-with-temp-text "* H1\n#+name: foo<point>\n#+begin_example\nhi\n#+end_example\n"
|
||||||
|
(should
|
||||||
|
(equal '("foo" "foo" 6)
|
||||||
|
(org-link-precise-link-target))))
|
||||||
|
(org-test-with-temp-text "\nText<point>\n* H1\n"
|
||||||
|
(should
|
||||||
|
(equal '("Text" nil 2)
|
||||||
|
(org-link-precise-link-target))))
|
||||||
|
(org-test-with-temp-text "\n<point>\n* H1\n"
|
||||||
|
(should
|
||||||
|
(equal nil (org-link-precise-link-target)))))
|
||||||
|
|
||||||
|
(defmacro test-ol-stored-link-with-text (text &rest body)
|
||||||
|
"Return :link and :description from link stored in body."
|
||||||
|
(declare (indent 1))
|
||||||
|
`(let (org-store-link-plist)
|
||||||
|
(org-test-with-temp-text-in-file ,text
|
||||||
|
,@body
|
||||||
|
(list (plist-get org-store-link-plist :link)
|
||||||
|
(plist-get org-store-link-plist :description)))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-link/id-store-link ()
|
||||||
|
"Test `org-id-store-link' specifications."
|
||||||
|
(let ((org-id-link-to-org-use-id nil))
|
||||||
|
(should
|
||||||
|
(equal '(nil nil)
|
||||||
|
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n"
|
||||||
|
(org-id-store-link-maybe t)))))
|
||||||
|
;; On a headline, link to that headline's ID. Use heading as the
|
||||||
|
;; description of the link.
|
||||||
|
(let ((org-id-link-to-org-use-id t))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc" "H1")
|
||||||
|
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n"
|
||||||
|
(org-id-store-link-maybe t)))))
|
||||||
|
;; Remove TODO keywords etc from description of the link.
|
||||||
|
(let ((org-id-link-to-org-use-id t))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc" "H1")
|
||||||
|
(test-ol-stored-link-with-text "* TODO [#A] H1 :tag:\n:PROPERTIES:\n:ID: abc\n:END:\n"
|
||||||
|
(org-id-store-link-maybe t)))))
|
||||||
|
;; create-if-interactive
|
||||||
|
(let ((org-id-link-to-org-use-id 'create-if-interactive))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc" "H1")
|
||||||
|
(cl-letf (((symbol-function 'org-id-new)
|
||||||
|
(lambda (&rest _rest) "abc")))
|
||||||
|
(test-ol-stored-link-with-text "* H1\n"
|
||||||
|
(org-id-store-link-maybe t)))))
|
||||||
|
(should
|
||||||
|
(equal '(nil nil)
|
||||||
|
(test-ol-stored-link-with-text "* H1\n"
|
||||||
|
(org-id-store-link-maybe nil)))))
|
||||||
|
;; create-if-interactive-and-no-custom-id
|
||||||
|
(let ((org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc" "H1")
|
||||||
|
(cl-letf (((symbol-function 'org-id-new)
|
||||||
|
(lambda (&rest _rest) "abc")))
|
||||||
|
(test-ol-stored-link-with-text "* H1\n"
|
||||||
|
(org-id-store-link-maybe t)))))
|
||||||
|
(should
|
||||||
|
(equal '(nil nil)
|
||||||
|
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:CUSTOM_ID: xyz\n:END:\n"
|
||||||
|
(org-id-store-link-maybe t))))
|
||||||
|
(should
|
||||||
|
(equal '(nil nil)
|
||||||
|
(test-ol-stored-link-with-text "* H1\n"
|
||||||
|
(org-id-store-link-maybe nil)))))
|
||||||
|
;; use-context should have no effect when on the headline with an id
|
||||||
|
(let ((org-id-link-to-org-use-id t)
|
||||||
|
(org-id-link-use-context t))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc" "H2")
|
||||||
|
(test-ol-stored-link-with-text "* H1\n** H2<point>\n:PROPERTIES:\n:ID: abc\n:END:\n"
|
||||||
|
;; simulate previously getting an inherited value
|
||||||
|
(move-marker org-entry-property-inherited-from 1)
|
||||||
|
(org-id-store-link-maybe t))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-link/id-store-link-using-parent ()
|
||||||
|
"Test `org-id-store-link' specifications with `org-id-link-consider-parent-id` set."
|
||||||
|
;; when using context to still find specific heading
|
||||||
|
(let ((org-id-link-to-org-use-id t)
|
||||||
|
(org-id-link-consider-parent-id t)
|
||||||
|
(org-id-link-use-context t))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc::*H2" "H2")
|
||||||
|
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n** H2\n<point>"
|
||||||
|
(org-id-store-link))))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc::name" "name")
|
||||||
|
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n\n#+name: name\n<point>#+begin_example\nhi\n#+end_example\n"
|
||||||
|
(org-id-store-link))))
|
||||||
|
(should
|
||||||
|
(equal '("id:abc" "H1")
|
||||||
|
(test-ol-stored-link-with-text "* H1<point>\n:PROPERTIES:\n:ID: abc\n:END:\n** H2\n"
|
||||||
|
(org-id-store-link))))
|
||||||
|
;; should not use newly added ids as search string, e.g. in an empty file
|
||||||
|
(should
|
||||||
|
(let (name result)
|
||||||
|
(setq result
|
||||||
|
(cl-letf (((symbol-function 'org-id-new)
|
||||||
|
(lambda (&rest _rest) "abc")))
|
||||||
|
(test-ol-stored-link-with-text "<point>"
|
||||||
|
(setq name (buffer-name))
|
||||||
|
(org-id-store-link))))
|
||||||
|
(equal `("id:abc" ,name) result))))
|
||||||
|
;; should not find targets in the next section
|
||||||
|
(let ((org-id-link-to-org-use-id 'use-existing)
|
||||||
|
(org-id-link-consider-parent-id t)
|
||||||
|
(org-id-link-use-context t))
|
||||||
|
(should
|
||||||
|
(equal '(nil nil)
|
||||||
|
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n* H2\n** <point>Target\n"
|
||||||
|
(org-id-store-link-maybe t))))))
|
||||||
|
|
||||||
|
|
||||||
;;; Radio Targets
|
;;; Radio Targets
|
||||||
|
|
||||||
|
|
|
@ -687,6 +687,47 @@ Sunday 7 January 2024
|
||||||
(should-not (org-agenda-files)))
|
(should-not (org-agenda-files)))
|
||||||
(org-test-agenda--kill-all-agendas))
|
(org-test-agenda--kill-all-agendas))
|
||||||
|
|
||||||
|
(ert-deftest test-org-agenda/skip-deadline-prewarning-if-scheduled ()
|
||||||
|
"Test `org-agenda-skip-deadline-prewarning-if-scheduled'."
|
||||||
|
(org-test-at-time
|
||||||
|
"2024-01-15"
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled t))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should-not (search-forward "In " nil t))))
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should (search-forward "In " nil t))))
|
||||||
|
;; Custom prewarning cookie "-3d", so there should be no warning anyway.
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat -3d> SCHEDULED: <2024-01-19 Fri>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should-not (search-forward "In " nil t))))
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 3))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should-not (search-forward "In " nil t))))
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled nil))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should (search-forward "In " nil t))))
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-16 Tue>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should-not (search-forward "In " nil t))))
|
||||||
|
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
|
||||||
|
(org-test-agenda-with-agenda
|
||||||
|
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-15 Mon>"
|
||||||
|
(org-agenda-list nil nil 1)
|
||||||
|
(should (search-forward "In " nil t))))))
|
||||||
|
|
||||||
|
|
||||||
;; agenda redo
|
;; agenda redo
|
||||||
|
|
||||||
|
|
|
@ -755,6 +755,15 @@ Some other text
|
||||||
(text (org-element-map tree 'plain-text 'identity nil t)))
|
(text (org-element-map tree 'plain-text 'identity nil t)))
|
||||||
(org-element-set text "b")
|
(org-element-set text "b")
|
||||||
(org-element-map tree 'plain-text 'identity nil t)))))
|
(org-element-map tree 'plain-text 'identity nil t)))))
|
||||||
|
;; Replace string inside anonymous element with another string.
|
||||||
|
(let* ((parent (org-element-create 'anonymous nil "test"))
|
||||||
|
(str (car (org-element-contents parent))))
|
||||||
|
(let ((return (org-element-set str "repl"))
|
||||||
|
(new (car (org-element-contents parent))))
|
||||||
|
;; Return the modified value.
|
||||||
|
(should (eq return new))
|
||||||
|
(should (equal new "repl"))
|
||||||
|
(should (eq (org-element-parent new) parent))))
|
||||||
;; KEEP-PROPS
|
;; KEEP-PROPS
|
||||||
(should
|
(should
|
||||||
(org-element-property
|
(org-element-property
|
||||||
|
@ -2378,6 +2387,23 @@ e^{i\\pi}+1=0
|
||||||
(org-element-property
|
(org-element-property
|
||||||
:type
|
:type
|
||||||
(org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))
|
(org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))
|
||||||
|
(should
|
||||||
|
(equal
|
||||||
|
"radio"
|
||||||
|
(org-test-with-temp-text "<<<radio>>><<<radio2>>><<<foo>>>A radio link"
|
||||||
|
(org-update-radio-target-regexp)
|
||||||
|
(org-element-property
|
||||||
|
:type
|
||||||
|
(org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))
|
||||||
|
(should
|
||||||
|
(equal
|
||||||
|
"radio"
|
||||||
|
(let ((org-target-link-regexp-limit 9))
|
||||||
|
(org-test-with-temp-text "<<<radio>>><<<radio2>>><<<foo>>>A radio link"
|
||||||
|
(org-update-radio-target-regexp)
|
||||||
|
(org-element-property
|
||||||
|
:type
|
||||||
|
(org-element-map (org-element-parse-buffer) 'link #'identity nil t))))))
|
||||||
;; Pathological case: radio target of length 1 at beginning of line
|
;; Pathological case: radio target of length 1 at beginning of line
|
||||||
;; not followed by spaces.
|
;; not followed by spaces.
|
||||||
(should
|
(should
|
||||||
|
|
|
@ -0,0 +1,344 @@
|
||||||
|
;;; test-org-latex-preview.el --- tests for org-latex-preview.el -*- lexical-binding: t; -*-
|
||||||
|
|
||||||
|
;; Copyright (c) 2023 Karthik Chikmagalur
|
||||||
|
;; Authors: Karthik Chikmagalur
|
||||||
|
|
||||||
|
;; This file is not part of GNU Emacs.
|
||||||
|
|
||||||
|
;; This program is free software; you can redistribute it and/or modify
|
||||||
|
;; it under the terms of the GNU General Public License as published by
|
||||||
|
;; the Free Software Foundation, either version 3 of the License, or
|
||||||
|
;; (at your option) any later version.
|
||||||
|
|
||||||
|
;; This program is distributed in the hope that it will be useful,
|
||||||
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;; GNU General Public License for more details.
|
||||||
|
|
||||||
|
;; You should have received a copy of the GNU General Public License
|
||||||
|
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
(unless (featurep 'org-latex-preview)
|
||||||
|
(signal 'missing-test-dependency "Support for LaTeX previews"))
|
||||||
|
(require 'org-latex-preview)
|
||||||
|
(require 'cl-lib)
|
||||||
|
|
||||||
|
(ert-deftest test-org-latex-preview/assert ()
|
||||||
|
(should t))
|
||||||
|
|
||||||
|
|
||||||
|
;; Test for executables required for preview generation
|
||||||
|
(org-test-for-executable "latex")
|
||||||
|
(org-test-for-executable "dvisvgm")
|
||||||
|
(org-test-for-executable "dvipng")
|
||||||
|
;; Should we test imagemagick?
|
||||||
|
;; (org-test-for-executable "convert")
|
||||||
|
|
||||||
|
|
||||||
|
;; fragment pre-processing tests
|
||||||
|
|
||||||
|
;;; Collect fragments
|
||||||
|
(ert-deftest test-org-latex-preview/collect-fragments-inline ()
|
||||||
|
"Test LaTeX fragment collection"
|
||||||
|
(let ((elements
|
||||||
|
'((latex-fragment
|
||||||
|
(:value "\\( q \\ge 0 \\)" :begin 770 :end 784 :post-blank 1 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( r \\ge 0 \\)" :begin 788 :end 801 :post-blank 0 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments")))))))))))
|
||||||
|
(org-test-at-id
|
||||||
|
"0b3807b3-69af-40cb-a27a-b380d54879cc"
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview-collect-fragments 765 807) elements))
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview--construct-entries elements)
|
||||||
|
'(((770 783 "\\( q \\ge 0 \\)")
|
||||||
|
(788 801 "\\( r \\ge 0 \\)"))
|
||||||
|
nil)))
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview--construct-entries elements)
|
||||||
|
(org-latex-preview--construct-entries elements 'with-numbering))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-latex-preview/collect-fragments-environment ()
|
||||||
|
"Test LaTeX fragment collection"
|
||||||
|
(let ((elements
|
||||||
|
'((latex-environment
|
||||||
|
(:begin 241 :end 412 :value "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n" :post-blank 0 :post-affiliated 241 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments")))))))))
|
||||||
|
(org-test-at-id
|
||||||
|
"0b3807b3-69af-40cb-a27a-b380d54879cc"
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview-collect-fragments 186 426) elements))
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview--construct-entries elements)
|
||||||
|
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n"))
|
||||||
|
nil)))
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview--construct-entries elements 'with-numbering)
|
||||||
|
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n"))
|
||||||
|
(1)))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-latex-preview/collect-fragments-all ()
|
||||||
|
"Test LaTeX fragment collection"
|
||||||
|
(let ((elements
|
||||||
|
'((latex-environment
|
||||||
|
(:begin 241 :end 412 :value "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n" :post-blank 0 :post-affiliated 241 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( (x,u) \\)" :begin 460 :end 472 :post-blank 1 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 412 :end 478 :contents-begin 412 :contents-end 478 :post-blank 0 :post-affiliated 412 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-environment
|
||||||
|
(:begin 478 :end 765 :value "\\begin{align}\n\\label{eq:quadratic-form}\n\\mathbf{q}(x, u) := \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} \\begin{bmatrix} x \\\\ u \\end{bmatrix}^{\\star} \\begin{bmatrix}\nQ & 0 \\\\\n0 & r\n \\end{bmatrix} \\begin{bmatrix} x \\\\ u \\end{bmatrix} =: \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} q(x,u) dt\n\\end{align}\n" :post-blank 0 :post-affiliated 478 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( q \\ge 0 \\)" :begin 770 :end 784 :post-blank 1 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( r \\ge 0 \\)" :begin 788 :end 801 :post-blank 0 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( \\mathbf{q} \\)" :begin 842 :end 859 :post-blank 1 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\[\n\\inf_{x,u} \\mathbf{q}(x,u).\n\\]" :begin 884 :end 917 :post-blank 0 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-environment
|
||||||
|
(:begin 918 :end 1040 :value "\\begin{align}\n\\label{eq:lqr-inf-via-duality}\n\\inf_{x, u} \\mathbf{q}(x, u) = x_i^{\\star} \\bar{\\lambda}(0) x_i,\n\\end{align}\n" :post-blank 0 :post-affiliated 918 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( \\bar{\\lambda} \\)" :begin 1046 :end 1066 :post-blank 1 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 1040 :end 1154 :contents-begin 1040 :contents-end 1154 :post-blank 0 :post-affiliated 1040 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
|
||||||
|
(latex-fragment
|
||||||
|
(:value "\\( [0, t] \\)" :begin 1140 :end 1152 :post-blank 0 :parent
|
||||||
|
(paragraph
|
||||||
|
(:begin 1040 :end 1154 :contents-begin 1040 :contents-end 1154 :post-blank 0 :post-affiliated 1040 :mode nil :granularity element :parent
|
||||||
|
(section
|
||||||
|
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
|
||||||
|
(headline
|
||||||
|
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments")))))))))))
|
||||||
|
(org-test-at-id
|
||||||
|
"0b3807b3-69af-40cb-a27a-b380d54879cc"
|
||||||
|
(should
|
||||||
|
(equal (org-latex-preview-collect-fragments) elements))
|
||||||
|
(should (equal (org-latex-preview--construct-entries elements)
|
||||||
|
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n")
|
||||||
|
(460 471 "\\( (x,u) \\)")
|
||||||
|
(478 764 "\\begin{align}\n\\label{eq:quadratic-form}\n\\mathbf{q}(x, u) := \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} \\begin{bmatrix} x \\\\ u \\end{bmatrix}^{\\star} \\begin{bmatrix}\nQ & 0 \\\\\n0 & r\n \\end{bmatrix} \\begin{bmatrix} x \\\\ u \\end{bmatrix} =: \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} q(x,u) dt\n\\end{align}\n")
|
||||||
|
(770 783 "\\( q \\ge 0 \\)")
|
||||||
|
(788 801 "\\( r \\ge 0 \\)")
|
||||||
|
(842 858 "\\( \\mathbf{q} \\)")
|
||||||
|
(884 917 "\\[\n\\inf_{x,u} \\mathbf{q}(x,u).\n\\]")
|
||||||
|
(918 1039 "\\begin{align}\n\\label{eq:lqr-inf-via-duality}\n\\inf_{x, u} \\mathbf{q}(x, u) = x_i^{\\star} \\bar{\\lambda}(0) x_i,\n\\end{align}\n")
|
||||||
|
(1046 1065 "\\( \\bar{\\lambda} \\)")
|
||||||
|
(1140 1152 "\\( [0, t] \\)"))
|
||||||
|
nil)))
|
||||||
|
(should (equal (org-latex-preview--construct-entries elements 'with-numbering)
|
||||||
|
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n")
|
||||||
|
(460 471 "\\( (x,u) \\)")
|
||||||
|
(478 764 "\\begin{align}\n\\label{eq:quadratic-form}\n\\mathbf{q}(x, u) := \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} \\begin{bmatrix} x \\\\ u \\end{bmatrix}^{\\star} \\begin{bmatrix}\nQ & 0 \\\\\n0 & r\n \\end{bmatrix} \\begin{bmatrix} x \\\\ u \\end{bmatrix} =: \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} q(x,u) dt\n\\end{align}\n")
|
||||||
|
(770 783 "\\( q \\ge 0 \\)")
|
||||||
|
(788 801 "\\( r \\ge 0 \\)")
|
||||||
|
(842 858 "\\( \\mathbf{q} \\)")
|
||||||
|
(884 917 "\\[\n\\inf_{x,u} \\mathbf{q}(x,u).\n\\]")
|
||||||
|
(918 1039 "\\begin{align}\n\\label{eq:lqr-inf-via-duality}\n\\inf_{x, u} \\mathbf{q}(x, u) = x_i^{\\star} \\bar{\\lambda}(0) x_i,\n\\end{align}\n")
|
||||||
|
(1046 1065 "\\( \\bar{\\lambda} \\)")
|
||||||
|
(1140 1152 "\\( [0, t] \\)"))
|
||||||
|
(1 nil 2 nil nil nil nil 4 nil nil)))))))
|
||||||
|
|
||||||
|
;;; Set up overlays
|
||||||
|
(ert-deftest test-org-latex-preview/ensure-overlay ()
|
||||||
|
(org-test-at-id "0b3807b3-69af-40cb-a27a-b380d54879cc"
|
||||||
|
(org-latex-preview-clear-overlays (point-min) (point-max))
|
||||||
|
(let* ((elements (org-latex-preview-collect-fragments))
|
||||||
|
(entries (car (org-latex-preview--construct-entries elements)))
|
||||||
|
(ov) (all-ovs))
|
||||||
|
(pcase-dolist(`(,beg ,end ,value) entries)
|
||||||
|
(setq ov (org-latex-preview--ensure-overlay beg end))
|
||||||
|
(push ov all-ovs)
|
||||||
|
(should (eq (get-char-property beg 'org-overlay-type) 'org-latex-overlay))
|
||||||
|
(should (eq (overlay-get ov 'org-overlay-type) 'org-latex-overlay))
|
||||||
|
(should (eq (overlay-get ov 'evaporate) t))
|
||||||
|
(should (eq (overlay-get ov 'priority) org-latex-preview--overlay-priority))
|
||||||
|
(should (equal (overlay-get ov 'modification-hooks)
|
||||||
|
(list #'org-latex-preview-auto--mark-overlay-modified)))
|
||||||
|
(should (equal (overlay-get ov 'insert-in-front-hooks)
|
||||||
|
(list #'org-latex-preview-auto--insert-front-handler)))
|
||||||
|
(should (equal (overlay-get ov 'insert-behind-hooks)
|
||||||
|
(list #'org-latex-preview-auto--insert-behind-handler))))
|
||||||
|
(should (eq (length all-ovs) (length elements))))
|
||||||
|
(org-latex-preview-clear-overlays (point-min) (point-max))))
|
||||||
|
|
||||||
|
|
||||||
|
;; Precompilation test
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; Process fragments
|
||||||
|
|
||||||
|
;;; No precompilation, no caching
|
||||||
|
(ert-deftest test-org-latex-preview/place-previews-1 ()
|
||||||
|
(org-test-at-id "0b3807b3-69af-40cb-a27a-b380d54879cc"
|
||||||
|
(let ((org-latex-preview-process-precompiled nil)
|
||||||
|
(org-latex-preview-cache 'temp)
|
||||||
|
(org-latex-preview-process-default 'dvisvgm))
|
||||||
|
(org-latex-preview-auto-mode -1)
|
||||||
|
(goto-char 255)
|
||||||
|
(let ((element (org-element-context)))
|
||||||
|
(should (eq (org-element-type element) 'latex-environment))
|
||||||
|
(org-latex-preview-clear-cache (org-element-property :begin element)
|
||||||
|
(org-element-property :end element))
|
||||||
|
(apply #'org-async-wait-for
|
||||||
|
(org-latex-preview--place-from-elements
|
||||||
|
org-latex-preview-process-default
|
||||||
|
(list element)))
|
||||||
|
(let ((ov (cl-some (lambda (o) (and (eq (overlay-get o 'org-overlay-type)
|
||||||
|
'org-latex-overlay)
|
||||||
|
o))
|
||||||
|
(overlays-at (point)))))
|
||||||
|
(should (overlayp ov))
|
||||||
|
(let ((display (overlay-get ov 'display))
|
||||||
|
(face (overlay-get ov 'face))
|
||||||
|
(hidden-face (overlay-get ov 'hidden-face))
|
||||||
|
(preview-image (overlay-get ov 'preview-image)))
|
||||||
|
;; Image properties
|
||||||
|
(should (consp display))
|
||||||
|
(should (eq (car display) 'image))
|
||||||
|
(should (eq (plist-get (cdr display) :type) 'svg))
|
||||||
|
(should (stringp (plist-get (cdr display) :file)))
|
||||||
|
(should (string-suffix-p ".svg" (plist-get (cdr display) :file)))
|
||||||
|
(should (eq preview-image display))
|
||||||
|
;; Face properties
|
||||||
|
(should (eq face 'default))
|
||||||
|
(should (eq hidden-face face))))))))
|
||||||
|
|
||||||
|
(ert-deftest test-org-latex-preview/place-previews-all ()
|
||||||
|
(org-test-at-id "0b3807b3-69af-40cb-a27a-b380d54879cc"
|
||||||
|
(let ((org-latex-preview-process-precompiled nil)
|
||||||
|
(org-latex-preview-cache 'temp)
|
||||||
|
(org-latex-preview-process-default 'dvisvgm))
|
||||||
|
(org-latex-preview-auto-mode -1)
|
||||||
|
(org-latex-preview-clear-cache (point-min) (point-max))
|
||||||
|
(let ((elements (org-latex-preview-collect-fragments)))
|
||||||
|
(apply #'org-async-wait-for
|
||||||
|
(org-latex-preview--place-from-elements
|
||||||
|
org-latex-preview-process-default
|
||||||
|
elements))
|
||||||
|
(dolist (element elements)
|
||||||
|
(let ((ov (cl-some (lambda (o) (and (eq (overlay-get o 'org-overlay-type)
|
||||||
|
'org-latex-overlay)
|
||||||
|
o))
|
||||||
|
(overlays-at (org-element-property :begin element)))))
|
||||||
|
(should (overlayp ov))
|
||||||
|
(let ((display (overlay-get ov 'display))
|
||||||
|
(face (overlay-get ov 'face))
|
||||||
|
(hidden-face (overlay-get ov 'hidden-face))
|
||||||
|
(preview-image (overlay-get ov 'preview-image)))
|
||||||
|
;; Image properties
|
||||||
|
(should (consp display))
|
||||||
|
(should (eq (car display) 'image))
|
||||||
|
(should (eq (plist-get (cdr display) :type) 'svg))
|
||||||
|
(should (stringp (plist-get (cdr display) :file)))
|
||||||
|
(should (string-suffix-p ".svg" (plist-get (cdr display) :file)))
|
||||||
|
(should (eq preview-image display))
|
||||||
|
;; Face properties
|
||||||
|
(should (eq face 'default))
|
||||||
|
(should (eq hidden-face face)))))
|
||||||
|
(org-latex-preview-clear-cache (point-min) (point-max))))))
|
||||||
|
|
||||||
|
;; TODO: Test with precompilation
|
||||||
|
;; TODO: Test with caching
|
||||||
|
|
||||||
|
|
||||||
|
;; dvisvgm filter+callback test
|
||||||
|
;; dvipng filter+callback test
|
||||||
|
;; imagemagick filter+callback test
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; Color extraction
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; Scaling
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; Caching
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
;;; clear-cache test
|
||||||
|
|
||||||
|
|
||||||
|
;; Numbering
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; org-latex-preview-auto-mode
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; live previews
|
||||||
|
;; TODO
|
||||||
|
|
||||||
|
|
||||||
|
;; lualatex-specific tests
|
||||||
|
|
||||||
|
|
||||||
|
;; xelatex-specific tests
|
||||||
|
|
||||||
|
(provide 'test-org-latex-preview)
|
||||||
|
;;; test-org-latex-preview.el ends here
|
|
@ -1849,7 +1849,21 @@ See also `test-org-table/copy-field'."
|
||||||
(should
|
(should
|
||||||
(org-test-with-temp-text "|-<point>--|---------|\n|---|---|-----|"
|
(org-test-with-temp-text "|-<point>--|---------|\n|---|---|-----|"
|
||||||
(org-table-align)
|
(org-table-align)
|
||||||
t)))
|
t))
|
||||||
|
;; Adjust table width.
|
||||||
|
(should
|
||||||
|
(equal
|
||||||
|
(let ((org-link-descriptive t))
|
||||||
|
(org-test-with-temp-text "
|
||||||
|
| a | b |
|
||||||
|
|----------+---|
|
||||||
|
| [[c][c]] | d |<point>"
|
||||||
|
(org-table-align)
|
||||||
|
(buffer-string)))
|
||||||
|
"
|
||||||
|
| a | b |
|
||||||
|
|---+---|
|
||||||
|
| [[c][c]] | d |")))
|
||||||
|
|
||||||
(ert-deftest test-org-table/align-buffer-tables ()
|
(ert-deftest test-org-table/align-buffer-tables ()
|
||||||
"Align all tables when updating buffer."
|
"Align all tables when updating buffer."
|
||||||
|
|
|
@ -1980,8 +1980,30 @@ CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46"
|
||||||
(let ((org-insert-heading-respect-content nil))
|
(let ((org-insert-heading-respect-content nil))
|
||||||
(org-insert-heading '(16)))
|
(org-insert-heading '(16)))
|
||||||
(buffer-string))))
|
(buffer-string))))
|
||||||
;; When optional TOP-LEVEL argument is non-nil, always insert
|
;; When optional LEVEL argument is a number, insert a heading at
|
||||||
;; a level 1 heading.
|
;; that level.
|
||||||
|
(should
|
||||||
|
(equal "* H1\n** H2\n* "
|
||||||
|
(org-test-with-temp-text "* H1\n** H2<point>"
|
||||||
|
(org-insert-heading nil nil 1)
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H1\n** H2\n** "
|
||||||
|
(org-test-with-temp-text "* H1\n** H2<point>"
|
||||||
|
(org-insert-heading nil nil 2)
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H1\n** H2\n*** "
|
||||||
|
(org-test-with-temp-text "* H1\n** H2<point>"
|
||||||
|
(org-insert-heading nil nil 3)
|
||||||
|
(buffer-string))))
|
||||||
|
(should
|
||||||
|
(equal "* H1\n- item\n* "
|
||||||
|
(org-test-with-temp-text "* H1\n- item<point>"
|
||||||
|
(org-insert-heading nil nil 1)
|
||||||
|
(buffer-string))))
|
||||||
|
;; When optional LEVEL argument is non-nil, always insert a level 1
|
||||||
|
;; heading.
|
||||||
(should
|
(should
|
||||||
(equal "* H1\n** H2\n* "
|
(equal "* H1\n** H2\n* "
|
||||||
(org-test-with-temp-text "* H1\n** H2<point>"
|
(org-test-with-temp-text "* H1\n** H2<point>"
|
||||||
|
@ -2436,6 +2458,13 @@ Test
|
||||||
** H2<point>"
|
** H2<point>"
|
||||||
(should (= 1 (org-up-heading-safe)))
|
(should (= 1 (org-up-heading-safe)))
|
||||||
(should (looking-at-p "^\\* H1")))
|
(should (looking-at-p "^\\* H1")))
|
||||||
|
;; Return true level. Ignore `org-odd-levels-only'.
|
||||||
|
(let ((org-odd-levels-only t))
|
||||||
|
(org-test-with-temp-text "
|
||||||
|
*** H1
|
||||||
|
***** H2<point>"
|
||||||
|
(should (= 3 (org-up-heading-safe)))
|
||||||
|
(should (looking-at-p "^\\*\\{3\\} H1"))))
|
||||||
;; Do not jump beyond the level 1 heading.
|
;; Do not jump beyond the level 1 heading.
|
||||||
(org-test-with-temp-text "
|
(org-test-with-temp-text "
|
||||||
Text.
|
Text.
|
||||||
|
|
|
@ -2140,6 +2140,120 @@ Footnotes[fn:2], foot[fn:test] and [fn:inline:inline footnote]
|
||||||
(bold . (lambda (bold contents info) (concat contents "!")))))
|
(bold . (lambda (bold contents info) (concat contents "!")))))
|
||||||
'(:with-emphasize t)))))
|
'(:with-emphasize t)))))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Export features
|
||||||
|
|
||||||
|
(ert-deftest test-org-export/feature-resolution ()
|
||||||
|
"Test the behaviour of `org-export-resolve-feature-implementations'"
|
||||||
|
;; Check implementations for listed features are given.
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a) (b) (c)))
|
||||||
|
'((a) (b) (c))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a)))
|
||||||
|
'((a))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a) '((a) (b) (c)))
|
||||||
|
'((a))))
|
||||||
|
;; Check depencency resolution.
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a) '((a :requires b) (b) (c)))
|
||||||
|
'((a :requires b) (b))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a) '((a :requires (b c)) (b) (c)))
|
||||||
|
'((a :requires (b c)) (b) (c))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a) '((a :requires b) (b :requires c) (c)))
|
||||||
|
'((a :requires b) (b :requires c) (c))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :prevents b) (b) (c)))
|
||||||
|
'((a :prevents b) (c))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :prevents (b c)) (b) (c)))
|
||||||
|
'((a :prevents (b c)))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c d) '((a :requires (b c)) (b) (c) (d :prevents b)))
|
||||||
|
'((a :requires (b c)) (c) (d :prevents b))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c d) '((a :requires (b c)) (b) (c :prevents b)))
|
||||||
|
'((a :requires (b c)) (c :prevents b))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a d) '((a :requires b) (b :requires c) (c) (d :prevents a)))
|
||||||
|
'((d :prevents a))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c d) '((a :requires b) (b :requires c) (c) (d :prevents a)))
|
||||||
|
'((b :requires c) (c) (d :prevents a))))
|
||||||
|
(should-error
|
||||||
|
(org-export-resolve-feature-implementations
|
||||||
|
nil '(a) '((a :requires b)))
|
||||||
|
:type 'org-missing-feature-dependency)
|
||||||
|
;; Check application of the :when condition.
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :when b) (b)))
|
||||||
|
'((a :when b) (b))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :when (b c)) (b) (c)))
|
||||||
|
'((a :when (b c)) (b) (c))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a) (b) (c :when (a b))))
|
||||||
|
'((a) (b) (c :when (a b)))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :when b) (b :when c) (c)))
|
||||||
|
'((a :when b) (b :when c) (c))))
|
||||||
|
;; Check simple ordering.
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :order 3) (b :order 1) (c :order 2)))
|
||||||
|
'((b :order 1) (c :order 2) (a :order 3))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :order 1) (b) (c :order -1)))
|
||||||
|
'((c :order -1) (b) (a :order 1))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a) '((a :order 1 :requires b) (b :requires c) (c :order -1)))
|
||||||
|
'((c :order -1) (b :requires c) (a :order 1 :requires b))))
|
||||||
|
;; Check before/after ordering.
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :after (b c)) (b) (c)))
|
||||||
|
'((b) (c) (a :after (b c)))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a :after b) (b :after c) (c)))
|
||||||
|
'((c) (b :after c) (a :after b))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a) (b) (c :before (a b))))
|
||||||
|
'((c :before (a b)) (a) (b))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a) (b :before a) (c :before b)))
|
||||||
|
'((c :before b) (b :before a) (a))))
|
||||||
|
(should
|
||||||
|
(equal (org-export-resolve-feature-implementations
|
||||||
|
nil '(a b c) '((a) (b :after c :before a) (c)))
|
||||||
|
'((c) (b :after c :before a) (a))))
|
||||||
|
(should-error ; Circular dependency
|
||||||
|
(org-export-resolve-feature-implementations
|
||||||
|
nil '(a b) '((a :after b) (b :after a)))
|
||||||
|
:type 'org-circular-feature-dependency))
|
||||||
|
|
||||||
|
|
||||||
;;; Comments
|
;;; Comments
|
||||||
|
|
Loading…
Reference in New Issue