forked from mirrors/org-mode
Compare commits
86 Commits
Author | SHA1 | Date |
---|---|---|
Max Nikulin | b4d17c0629 | |
Ihor Radchenko | f326cd58ba | |
Ihor Radchenko | aa71facf6d | |
Ihor Radchenko | 62356cb44b | |
Ihor Radchenko | fffb87174e | |
Lee Thompson | 1a7d7a5a5b | |
Ihor Radchenko | bc51dfcb74 | |
Ihor Radchenko | db5478108c | |
Ihor Radchenko | e36a4fe20e | |
Ihor Radchenko | 4131926984 | |
Ihor Radchenko | 4deb06ea0a | |
Ihor Radchenko | f64c8a5a5b | |
Ihor Radchenko | 3bf33c0d7b | |
Ihor Radchenko | e380684c1a | |
Ihor Radchenko | 31748e67f0 | |
Ihor Radchenko | c6849a2bc8 | |
Ihor Radchenko | 96b3129265 | |
Ihor Radchenko | bd5665e017 | |
Ihor Radchenko | 24c4a40f79 | |
Ihor Radchenko | 773bba92a8 | |
Max Nikulin | 51aff8a11b | |
Ihor Radchenko | 48169da265 | |
Ihor Radchenko | c8bd2092b1 | |
Ihor Radchenko | 56992b221e | |
Ihor Radchenko | 24feef95e4 | |
Andras Simonyi | 288e0a11c2 | |
Ihor Radchenko | 43ba00b2b1 | |
Ihor Radchenko | e09ad15a71 | |
Ihor Radchenko | 0227e12605 | |
Ihor Radchenko | 105dedd0d9 | |
Ihor Radchenko | edb5eaaac3 | |
Ihor Radchenko | 1523e21d82 | |
Ihor Radchenko | 0d24857ccf | |
Ihor Radchenko | dbea900d60 | |
Cook, Malcolm | 09520a056c | |
Ihor Radchenko | 2028bb15cd | |
Ihor Radchenko | 5da0eb6ea7 | |
Ihor Radchenko | 3d0deadf41 | |
Ihor Radchenko | 1cafe3e8e4 | |
Rudolf Adamkovič | 7cdf835c71 | |
Ihor Radchenko | fede1c9909 | |
Ihor Radchenko | 55795376b6 | |
Ihor Radchenko | d8ac958836 | |
Max Nikulin | ac7c5c5d88 | |
Ihor Radchenko | dd12e9c763 | |
Ihor Radchenko | a2e5685e49 | |
Morgan Smith | 646f6ec133 | |
Max Nikulin | c6bbde4c78 | |
Max Nikulin | 3c01767f7e | |
Max Nikulin | 0f0019e326 | |
Max Nikulin | badb09d679 | |
Max Nikulin | 735334445f | |
Ihor Radchenko | 3e11b2eb88 | |
Ihor Radchenko | 89c68683f9 | |
Ihor Radchenko | a2514c97de | |
Ihor Radchenko | 36a9f6f185 | |
Ihor Radchenko | e0f24a3f6d | |
Ihor Radchenko | 065af4b42a | |
Ihor Radchenko | ba747598c6 | |
Ihor Radchenko | 64049aaddc | |
Alexander Adolf | fbf613ece9 | |
Alexander Adolf | 4e6fa96e26 | |
Alexander Adolf | 5a98b4c563 | |
Pedro A. Aranda | f124b616d9 | |
Ihor Radchenko | e4ab416fcc | |
Ihor Radchenko | 510e8f9cc8 | |
Ihor Radchenko | 68d592bae4 | |
Ihor Radchenko | 0e743d0bfc | |
Rudolf Adamkovič | 252cc0be07 | |
Ihor Radchenko | 9e88e1c822 | |
Ihor Radchenko | d6353b283a | |
Ihor Radchenko | 80b474db00 | |
Ihor Radchenko | c6c5474b7e | |
Arash Esbati | ff9d00c9c3 | |
Alexander Gogl | 46e13c3ebd | |
Ihor Radchenko | 1ad03e77b1 | |
Ihor Radchenko | 52bde22b9d | |
Ihor Radchenko | a9275d5fd1 | |
Ihor Radchenko | dba92f72d3 | |
Ihor Radchenko | 0db82ee8f8 | |
Ihor Radchenko | 5b0b7f2924 | |
Ihor Radchenko | 36d0928043 | |
Ihor Radchenko | 769018718c | |
Morgan Smith | 942a7320d0 | |
Ihor Radchenko | d73688faa4 | |
Ihor Radchenko | 037700c411 |
File diff suppressed because it is too large
Load Diff
363
etc/ORG-NEWS
363
etc/ORG-NEWS
|
@ -13,6 +13,49 @@ Please send Org bug reports to mailto:emacs-orgmode@gnu.org.
|
|||
|
||||
* Version 9.7 (not released yet)
|
||||
** Important announcements and breaking changes
|
||||
*** ~org-auto-align-tags~ is now respected universally
|
||||
|
||||
Previously, only a subset of Org editing commands respected
|
||||
~org-auto-align-tags~ option. Now, it is no longer the case. All the
|
||||
editing commands, including typing (~org-self-insert-command~) and
|
||||
deletion respect the option.
|
||||
|
||||
~org-auto-align-tags~ is still enabled by default. For users who
|
||||
customized ~org-auto-align-tags~ to nil, ~org-edit-headline~,
|
||||
~org-priority~, ~org-set-tags~, ~org-entry-put~, ~org-kill-line~, and
|
||||
typing/deleting in headlines will no longer unconditionally auto-align
|
||||
the tags.
|
||||
|
||||
*** ~org-create-file-search-functions~ can use ~org-list-store-props~ to suggest link description
|
||||
|
||||
In Org <9.0, ~org-create-file-search-functions~ could set ~description~
|
||||
variable to suggest link description for the stored link. However,
|
||||
this feature stopped working since Org 9.0 switched to lexical binding.
|
||||
|
||||
Now, it is again possible for ~org-create-file-search-functions~ to
|
||||
supply link descriptions using ~(org-list-store-props :description
|
||||
"suggested description")~ in the search function body.
|
||||
|
||||
*** Inline image width value in =#+attr_org= is preferred over other =#+attr_...= keywords
|
||||
|
||||
Previously, when ~org-image-actual-width~ is a list or nil, Org used the
|
||||
first =#+attr_...= keyword containing =:width ...= to compute the inline
|
||||
image width. Now, =#+attr_org=, if present, takes precedence.
|
||||
In the following example the image preview has width of 75%
|
||||
while earlier versions pick 33%.
|
||||
|
||||
: #+attr_html: :width 33%
|
||||
: #+attr_org: :width 0.75
|
||||
: [[image.png]]
|
||||
|
||||
*** =ox-html=: When exporting footnotes with custom non-number names, the names are used as link anchors
|
||||
|
||||
Previously, link anchors for footnote references and footnote
|
||||
definitions were based on the footnote number: =fn.1=, =fnr.15=, etc.
|
||||
|
||||
Now, when the footnote has a non-number name, it is used as an anchor:
|
||||
=fn.name=, =fnr.name=.
|
||||
|
||||
*** Underline syntax now takes priority over subscript when both are applicable
|
||||
|
||||
Previously, Org mode interpreted =(_text_)= as subscript.
|
||||
|
@ -83,6 +126,11 @@ transcoders.
|
|||
|
||||
Users can disable citations processors by customizing new
|
||||
~org-export-process-citations~ option.
|
||||
*** =ox-org= preserves header non-default arguments in src blocks
|
||||
|
||||
Previously, all the header arguments where stripped from src blocks
|
||||
during export. Now, header arguments are preserved as long as their
|
||||
values are not equal to the default header argument values.
|
||||
|
||||
*** =ox-org= disables citation processors by default
|
||||
|
||||
|
@ -314,7 +362,43 @@ Now, ~org-store-link~ moves the stored link to front of the list of
|
|||
stored links. This way, the link will show up first in the completion
|
||||
and when inserting all the stored links with ~org-insert-all-links~.
|
||||
|
||||
*** Major changes and additions to Org API
|
||||
*** Major changes and additions to Org element API
|
||||
**** Diary type timestamps now support optional time/timerange
|
||||
|
||||
Previously, diary type timestamps could not specify time.
|
||||
Now, it is allowed to add a time or time range:
|
||||
|
||||
: <%%(diary-float t 4 2) 22:00-23:00>
|
||||
: <%%(diary-float t 4 2) 10:30>
|
||||
|
||||
The parsed representation of such timestamps will have ~:hour-start~,
|
||||
~:minute-start~, ~:hour-end~, ~:minute-end~, and ~:range-type~
|
||||
properties set appropriately. In addition, a new ~:diary-sexp~
|
||||
property will store the diary sexp value.
|
||||
|
||||
For example,
|
||||
|
||||
: <%%(diary-float t 4 2) 22:00-23:00>
|
||||
|
||||
will have the following properties
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
:type: diary
|
||||
:range-type: timerange
|
||||
:raw-value: "<%%(diary-float t 4 2) 22:00-23:00>"
|
||||
:year-start: nil
|
||||
:month-start: nil
|
||||
:day-start: nil
|
||||
:hour-start: 22
|
||||
:minute-start: 0
|
||||
:year-end: nil
|
||||
:month-end: nil
|
||||
:day-end: nil
|
||||
:hour-end: 23
|
||||
:minute-end: 0
|
||||
:diary-sexp: "(diary-float t 4 2)"
|
||||
#+end_src
|
||||
|
||||
**** New term: "syntax node"
|
||||
|
||||
To reduce confusion with "element" referring to both "syntax element"
|
||||
|
@ -569,157 +653,6 @@ constant.
|
|||
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
|
||||
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
|
||||
|
||||
|
@ -787,7 +720,57 @@ 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.
|
||||
|
||||
*** ~org-ctags~ is not activated by default any more
|
||||
|
||||
To follow Emacs [[info:elisp#Coding Conventions][coding conventions]] and to avoid confusion of users
|
||||
who accidentally get ~org-ctags~ autoloaded due to help completion,
|
||||
the library does not modify ~org-open-link-functions~ during loading
|
||||
any more. Run ~org-ctags-enable~ to setup hooks and advices:
|
||||
|
||||
#+begin_src emacs-lisp
|
||||
(with-eval-after-load "org-ctags"
|
||||
(org-ctags-enable))
|
||||
#+end_src
|
||||
|
||||
** New and changed options
|
||||
*** New option controlling how Org mode sorts things ~org-sort-function~
|
||||
|
||||
Sorting of agenda items, tables, menus, headlines, etc can now be
|
||||
controlled using a new custom option ~org-sort-function~.
|
||||
|
||||
By default, Org mode sorts things according to the operation system
|
||||
language. However, language sorting rules may or may not produce good
|
||||
results depending on the use case. For example, multi-language
|
||||
documents may be sorted weirdly when sorting rules for system language
|
||||
are applied on the text written using different language. Also, some
|
||||
operations systems (e.g. MacOS), do not provide accurate string
|
||||
sorting rules.
|
||||
|
||||
Org mode provides 3 possible values for ~org-sort-function~:
|
||||
1. (default) Sort using system language rules.
|
||||
2. Sort using string comparison (~compare-strings~), making use of UTF
|
||||
case conversion. This may work better for mixed-language documents
|
||||
and on MacOS.
|
||||
3. Custom function, if the above does not fit the needs.
|
||||
|
||||
*** =ob-latex= now uses a new option ~org-babel-latex-process-alist~ to generate png output
|
||||
|
||||
Previously, =ob-latex= used ~org-preview-latex-default-process~ from
|
||||
~org-preview-latex-process-alist~ to produce png output. Now, the
|
||||
process settings are separated into a new dedicated option
|
||||
~org-babel-latex-process-alist~.
|
||||
|
||||
The default value is pulled from =dvipng= process type from
|
||||
~org-preview-latex-process-alist~, preserving the existing behavior.
|
||||
However, the output is now immune to changes in
|
||||
~org-preview-latex-default-process~ and can be customized
|
||||
independently of the image preview settings.
|
||||
|
||||
*** ~org-babel-lua-multiple-values-separator~
|
||||
|
||||
The string that separates the values of multi-valued results returned
|
||||
from Lua code blocks.
|
||||
|
||||
*** =.avif= images are now recognized in ~org-html-inline-image-rules~
|
||||
|
||||
In =ox-html=, =.avif= image links are now inlined by default.
|
||||
|
@ -1150,7 +1133,58 @@ 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 option ~latex-default-footnote-command~ to customize the LaTeX footnote command
|
||||
|
||||
This new option allows you to define the LaTeX command the Org mode
|
||||
footnotes are converted to (for example ~\sidenote{%s%s}~ instead of
|
||||
the default ~\footnote{%s%s}~).
|
||||
|
||||
The option can be customized either by
|
||||
|
||||
1. setting the global variable in the ~org-export-latex~ customization
|
||||
group or
|
||||
2. by setting the file local keyword =LATEX_FOOTNOTE_COMMAND=
|
||||
|
||||
*** Options for ~#+cite_export: biblatex~ can use the package's option syntax
|
||||
|
||||
When using =biblatex= to export bibliographies, you can use the format
|
||||
as specified in the =biblatex= package documentation as
|
||||
=key=val,key=val,...=
|
||||
|
||||
*** New option ~org-columns-dblock-formatter~
|
||||
|
||||
=colview= dynamic blocks now understand a new ~:formatter~ parameter
|
||||
to use a specific function for formatting and inserting the contents
|
||||
of the dynamic block. This new option can be used to set the global
|
||||
default formatting function that will be used for =colview= dynamic
|
||||
blocks that do not specify any ~:formatter~ parameter. Its default
|
||||
value (the new function ~org-columns-dblock-write-default~) yields the
|
||||
previous (fixed) formatting behaviour.
|
||||
|
||||
*** New allowed value of ~org-md-headline-style~ to mix ATX and Setext style headlines
|
||||
|
||||
Setting ~org-md-headline-style~ to ~'mixed~ will export headline
|
||||
levels one and two as Setext style headlines, and headline levels
|
||||
three through six will be exported as ATX style headlines.
|
||||
|
||||
** New features
|
||||
*** =ob-lua=: Support all types and multiple values in results
|
||||
|
||||
Lua code blocks can now return values of any type and can also return
|
||||
multiple values. Previously, values of certain types were incorrectly
|
||||
converted to the empty string =""=, which broke HTML export for inline
|
||||
code blocks, and multiple values were incorrectly concatenated, where
|
||||
~return 1, 2, 3~ was evaluated as =123=.
|
||||
|
||||
Multiple values are comma-separated by default, so that they work well
|
||||
with inline code blocks. To change the string used as the separator,
|
||||
customize ~org-babel-lua-multiple-values-separator~.
|
||||
|
||||
*** ~org-paste-subtree~ now handles =C-u= and =C-u C-u= prefix arguments specially
|
||||
|
||||
With =C-u= prefix argument, force inserting a sibling heading below.
|
||||
With =C-u C-u= prefix argument, force inserting a child heading.
|
||||
|
||||
*** =colview= dynamic block now writes column width specifications
|
||||
|
||||
When column format contains width specifications, =colview= dynamic
|
||||
|
@ -1181,6 +1215,31 @@ Example:
|
|||
: | PROYECTO EMACS |
|
||||
: #+END:
|
||||
|
||||
*** =colview= dynamic block supports custom formatting function
|
||||
|
||||
The =colview= dynamic block understands a new ~:formatter~ parameter,
|
||||
which specifies a user-supplied function to format and insert the data
|
||||
in the dynamic block.
|
||||
|
||||
A global default formatting function for =colview= dynamic blocks can
|
||||
be set via the new option ~org-columns-dblock-formatter~ which
|
||||
defaults to the new function ~org-columns-dblock-write-default~, that
|
||||
implements the previous (fixed) formatting behaviour. Hence, the
|
||||
default behaviour is identical to previous versions.
|
||||
|
||||
The global default function can be overridden for any given =colview=
|
||||
dynamic block individually by specifying a custom formatter function
|
||||
using the new ~:formatter~ parameter on the block's =BEGIN= line.
|
||||
|
||||
This new feature replicates the ~:formatter~ option already available
|
||||
for =clocktable= dynamic blocks.
|
||||
|
||||
*** =colview= dynamic block can link to headlines
|
||||
|
||||
The =colview= dynamic block understands a new ~:link~ parameter, which
|
||||
when non-~nil~ causes =ITEM= headlines in the table to be linked to
|
||||
their origins.
|
||||
|
||||
*** =ob-tangle.el=: New flag to remove tangle targets before writing
|
||||
|
||||
When ~org-babel-tangle-remove-file-before-write~ is set to ~t~ the
|
||||
|
|
|
@ -66,17 +66,21 @@
|
|||
(lambda (pair)
|
||||
(let ((val (cdr pair)))
|
||||
(calc-push-list
|
||||
;; For a vector, Calc follows the format (vec 1 2 3 ...) so
|
||||
;; a matrix becomes (vec (vec 1 2 3) (vec 4 5 6) ...). See
|
||||
;; the comments in "Arithmetic routines." section of
|
||||
;; calc.el.
|
||||
(list (if (listp val)
|
||||
(cons 'vec
|
||||
(if (null (cdr val))
|
||||
(car val)
|
||||
(mapcar (lambda (x) (if (listp x) (cons 'vec x) x))
|
||||
val)))
|
||||
val))))
|
||||
(list
|
||||
(cond
|
||||
;; For a vector, Calc follows the format (vec 1 2 3 ...) so
|
||||
;; a matrix becomes (vec (vec 1 2 3) (vec 4 5 6) ...). See
|
||||
;; the comments in "Arithmetic routines." section of
|
||||
;; calc.el.
|
||||
((listp val)
|
||||
(cons 'vec
|
||||
(if (null (cdr val))
|
||||
(car val)
|
||||
(mapcar (lambda (x) (if (listp x) (cons 'vec x) x))
|
||||
val))))
|
||||
((numberp val)
|
||||
(math-read-number (number-to-string val)))
|
||||
(t val)))))
|
||||
(calc-store-into (car pair)))
|
||||
vars)
|
||||
(mapc
|
||||
|
|
102
lisp/ob-core.el
102
lisp/ob-core.el
|
@ -3136,47 +3136,47 @@ block but are passed literally to the \"example-block\"."
|
|||
(with-current-buffer parent-buffer
|
||||
(buffer-chars-modified-tick)))))
|
||||
(cl-macrolet ((c-wrap
|
||||
(s)
|
||||
;; Comment string S, according to LANG mode. Return new
|
||||
;; string.
|
||||
`(unless org-babel-tangle-uncomment-comments
|
||||
(with-temp-buffer
|
||||
(funcall (org-src-get-lang-mode lang))
|
||||
(comment-region (point)
|
||||
(progn (insert ,s) (point)))
|
||||
(org-trim (buffer-string)))))
|
||||
(s)
|
||||
;; Comment string S, according to LANG mode. Return new
|
||||
;; string.
|
||||
`(unless org-babel-tangle-uncomment-comments
|
||||
(with-temp-buffer
|
||||
(funcall (org-src-get-lang-mode lang))
|
||||
(comment-region (point)
|
||||
(progn (insert ,s) (point)))
|
||||
(org-trim (buffer-string)))))
|
||||
(expand-body
|
||||
(i)
|
||||
;; Expand body of code represented by block info I.
|
||||
`(let ((b (if (org-babel-noweb-p (nth 2 ,i) :eval)
|
||||
(org-babel-expand-noweb-references ,i)
|
||||
(nth 1 ,i))))
|
||||
(if (not comment) b
|
||||
(let ((cs (org-babel-tangle-comment-links ,i)))
|
||||
(concat (c-wrap (car cs)) "\n"
|
||||
b "\n"
|
||||
(c-wrap (cadr cs)))))))
|
||||
(i)
|
||||
;; Expand body of code represented by block info I.
|
||||
`(let ((b (if (org-babel-noweb-p (nth 2 ,i) :eval)
|
||||
(org-babel-expand-noweb-references ,i)
|
||||
(nth 1 ,i))))
|
||||
(if (not comment) b
|
||||
(let ((cs (org-babel-tangle-comment-links ,i)))
|
||||
(concat (c-wrap (car cs)) "\n"
|
||||
b "\n"
|
||||
(c-wrap (cadr cs)) "\n")))))
|
||||
(expand-references
|
||||
(ref)
|
||||
`(pcase (gethash ,ref org-babel-expand-noweb-references--cache)
|
||||
(`(,last . ,previous)
|
||||
;; Ignore separator for last block.
|
||||
(let ((strings (list (expand-body last))))
|
||||
(dolist (i previous)
|
||||
(let ((parameters (nth 2 i)))
|
||||
;; Since we're operating in reverse order, first
|
||||
;; push separator, then body.
|
||||
(push (or (cdr (assq :noweb-sep parameters)) "\n")
|
||||
strings)
|
||||
(push (expand-body i) strings)))
|
||||
(mapconcat #'identity strings "")))
|
||||
;; Raise an error about missing reference, or return the
|
||||
;; empty string.
|
||||
((guard (or org-babel-noweb-error-all-langs
|
||||
(member lang org-babel-noweb-error-langs)))
|
||||
(error "Cannot resolve %s (see `org-babel-noweb-error-langs')"
|
||||
(org-babel-noweb-wrap ,ref)))
|
||||
(_ ""))))
|
||||
(ref)
|
||||
`(pcase (gethash ,ref org-babel-expand-noweb-references--cache)
|
||||
(`(,last . ,previous)
|
||||
;; Ignore separator for last block.
|
||||
(let ((strings (list (expand-body last))))
|
||||
(dolist (i previous)
|
||||
(let ((parameters (nth 2 i)))
|
||||
;; Since we're operating in reverse order, first
|
||||
;; push separator, then body.
|
||||
(push (or (cdr (assq :noweb-sep parameters)) "\n")
|
||||
strings)
|
||||
(push (expand-body i) strings)))
|
||||
(mapconcat #'identity strings "")))
|
||||
;; Raise an error about missing reference, or return the
|
||||
;; empty string.
|
||||
((guard (or org-babel-noweb-error-all-langs
|
||||
(member lang org-babel-noweb-error-langs)))
|
||||
(error "Cannot resolve %s (see `org-babel-noweb-error-langs')"
|
||||
(org-babel-noweb-wrap ,ref)))
|
||||
(_ ""))))
|
||||
(replace-regexp-in-string
|
||||
noweb-re
|
||||
(lambda (m)
|
||||
|
@ -3360,10 +3360,22 @@ situations in which is it not appropriate."
|
|||
(string= cell "*this*")))
|
||||
;; FIXME: Arbitrary code evaluation.
|
||||
(eval (read cell) t))
|
||||
((save-match-data
|
||||
(and (string-match "^[[:space:]]*\"\\(.*\\)\"[[:space:]]*$" cell)
|
||||
(not (string-match "[^\\]\"" (match-string 1 cell)))))
|
||||
(read cell))
|
||||
((let (read-val)
|
||||
(and (string-match-p
|
||||
(rx bos (0+ (any space ?\n))
|
||||
?\" (0+ anychar) ?\"
|
||||
(0+ (any space ?\n)) eos)
|
||||
cell)
|
||||
;; CELL is a single string
|
||||
(with-temp-buffer
|
||||
(insert cell)
|
||||
(goto-char 1)
|
||||
(when (setq read-val
|
||||
(ignore-errors
|
||||
(read (current-buffer))))
|
||||
(skip-chars-forward "[:space:]")
|
||||
(eobp)))
|
||||
read-val)))
|
||||
(t (org-no-properties cell))))
|
||||
|
||||
(defun org-babel--string-to-number (string)
|
||||
|
@ -3389,7 +3401,9 @@ SEPARATOR is passed to `org-table-convert-region', which see."
|
|||
;; If the file was empty, don't bother trying to
|
||||
;; convert the table.
|
||||
(when (> pmax 1)
|
||||
(org-table-convert-region (point-min) pmax separator)
|
||||
(org-table-convert-region
|
||||
(point-min) pmax
|
||||
(or separator 'babel-auto))
|
||||
(delq nil
|
||||
(mapcar (lambda (row)
|
||||
(and (not (eq row 'hline))
|
||||
|
|
|
@ -238,7 +238,10 @@ this template."
|
|||
((not (string= replacement
|
||||
(buffer-substring begin end)))
|
||||
(delete-region begin end)
|
||||
(insert replacement))))))
|
||||
(insert replacement))
|
||||
;; Replacement is the same as the source
|
||||
;; block. Continue onwards.
|
||||
(t (goto-char end))))))
|
||||
((or `babel-call `inline-babel-call)
|
||||
(org-babel-exp-do-export
|
||||
(or (org-babel-lob-get-info element)
|
||||
|
@ -367,7 +370,7 @@ The function respects the value of the :exports header argument."
|
|||
nil))))
|
||||
|
||||
(defcustom org-babel-exp-code-template
|
||||
"#+begin_src %lang%switches%flags\n%body\n#+end_src"
|
||||
"#+begin_src %lang%switches%flags%header-args\n%body\n#+end_src"
|
||||
"Template used to export the body of code blocks.
|
||||
This template may be customized to include additional information
|
||||
such as the code block name, or the values of particular header
|
||||
|
@ -379,6 +382,7 @@ and the following %keys may be used.
|
|||
body ------ the body of the code block
|
||||
switches -- the switches associated to the code block
|
||||
flags ----- the flags passed to the code block
|
||||
header-args the non-default header arguments of the code block
|
||||
|
||||
In addition to the keys mentioned above, every header argument
|
||||
defined for the code block may be used as a key and will be
|
||||
|
@ -388,7 +392,7 @@ replaced with its value."
|
|||
:package-version '(Org . "9.6"))
|
||||
|
||||
(defcustom org-babel-exp-inline-code-template
|
||||
"src_%lang[%switches%flags]{%body}"
|
||||
"src_%lang[%switches%flags%header-args]{%body}"
|
||||
"Template used to export the body of inline code blocks.
|
||||
This template may be customized to include additional information
|
||||
such as the code block name, or the values of particular header
|
||||
|
@ -400,6 +404,7 @@ and the following %keys may be used.
|
|||
body ------ the body of the code block
|
||||
switches -- the switches associated to the code block
|
||||
flags ----- the flags passed to the code block
|
||||
header-args the non-default header arguments of the code block
|
||||
|
||||
In addition to the keys mentioned above, every header argument
|
||||
defined for the code block may be used as a key and will be
|
||||
|
@ -432,6 +437,41 @@ replaced with its value."
|
|||
(and (org-string-nw-p f) (concat " " f))))
|
||||
("flags" . ,(let ((f (assq :flags (nth 2 info))))
|
||||
(and f (concat " " (cdr f)))))
|
||||
("header-args"
|
||||
.
|
||||
,(let* ((header-args
|
||||
(mapcar
|
||||
(lambda (pair)
|
||||
;; Do no include special parameters, parameters with
|
||||
;; their values equal to defaults.
|
||||
(unless (or
|
||||
;; Special parameters that are not real header
|
||||
;; arguments.
|
||||
(memq (car pair)
|
||||
'( :result-params :result-type
|
||||
;; This is an obsolete parameter still
|
||||
;; used in some tests.
|
||||
:flags))
|
||||
;; Global defaults.
|
||||
(equal (cdr pair)
|
||||
(alist-get
|
||||
(car pair)
|
||||
(if (eq type 'inline) org-babel-default-inline-header-args
|
||||
org-babel-default-header-args)))
|
||||
;; Per-language defaults.
|
||||
(let ((lang-headers
|
||||
(intern
|
||||
(concat "org-babel-default-header-args:"
|
||||
(nth 0 info)))))
|
||||
(and (boundp lang-headers)
|
||||
(equal (cdr pair)
|
||||
(alist-get (car pair)
|
||||
(eval lang-headers t))))))
|
||||
(format "%s %s" (car pair) (cdr pair))))
|
||||
(nth 2 info)))
|
||||
(header-arg-string
|
||||
(mapconcat #'identity (delq nil header-args) " ")))
|
||||
(unless (seq-empty-p header-arg-string) (concat " " header-arg-string))))
|
||||
,@(mapcar (lambda (pair)
|
||||
(cons (substring (symbol-name (car pair)) 1)
|
||||
(format "%S" (cdr pair))))
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
(require 'ob)
|
||||
(require 'org-macs)
|
||||
|
||||
(declare-function org-latex-preview-create-image "org-latex-preview" (string tofile options buffer &optional type))
|
||||
(declare-function org-create-formula-image "org" (string tofile options buffer &optional type))
|
||||
(declare-function org-latex-compile "ox-latex" (texfile &optional snippet))
|
||||
(declare-function org-latex-guess-inputenc "ox-latex" (header))
|
||||
(declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra))
|
||||
|
@ -48,10 +48,11 @@
|
|||
(defvar org-babel-tangle-lang-exts)
|
||||
(add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex"))
|
||||
|
||||
(defvar org-latex-preview-header) ; From org-latex-preview.el
|
||||
(defvar org-latex-preview-appearance-options) ; From org-latex-preview.el
|
||||
(defvar org-latex-default-packages-alist) ; From org-latex-preview.el
|
||||
(defvar org-latex-packages-alist) ; From org-latex-preview.el
|
||||
(defvar org-format-latex-header) ; From org.el
|
||||
(defvar org-format-latex-options) ; From org.el
|
||||
(defvar org-latex-default-packages-alist) ; From org.el
|
||||
(defvar org-latex-packages-alist) ; From org.el
|
||||
(defvar org-preview-latex-process-alist) ; From org.el
|
||||
|
||||
(defvar org-babel-default-header-args:latex
|
||||
'((:results . "latex") (:exports . "results"))
|
||||
|
@ -128,6 +129,18 @@ exporting the literal LaTeX source."
|
|||
:group 'org-babel
|
||||
:type '(repeat (string)))
|
||||
|
||||
(defcustom org-babel-latex-process-alist
|
||||
`(,(cons 'png (alist-get 'dvipng org-preview-latex-process-alist)))
|
||||
"Definitions of external processes for LaTeX result generation.
|
||||
See `org-preview-latex-process-alist' for more details.
|
||||
|
||||
The following process symbols are recognized:
|
||||
- `png' :: Process used to produce .png output."
|
||||
:group 'org-babel
|
||||
:package-version '(Org . "9.7")
|
||||
:type '(alist :tag "LaTeX to image backends"
|
||||
:value-type (plist)))
|
||||
|
||||
(defun org-babel-expand-body:latex (body params)
|
||||
"Expand BODY according to PARAMS, return the expanded body."
|
||||
(mapc (lambda (pair) ;; replace variables
|
||||
|
@ -167,11 +180,12 @@ This function is called by `org-babel-execute-src-block'."
|
|||
(append (cdr (assq :packages params)) org-latex-packages-alist)))
|
||||
(cond
|
||||
((and (string-suffix-p ".png" out-file) (not imagemagick))
|
||||
(let ((org-latex-preview-header
|
||||
(concat org-latex-preview-header "\n"
|
||||
(mapconcat #'identity headers "\n"))))
|
||||
(org-latex-preview-create-image
|
||||
body out-file org-latex-preview-appearance-options in-buffer)))
|
||||
(let ((org-format-latex-header
|
||||
(concat org-format-latex-header "\n"
|
||||
(mapconcat #'identity headers "\n")))
|
||||
(org-preview-latex-process-alist org-babel-latex-process-alist))
|
||||
(org-create-formula-image
|
||||
body out-file org-format-latex-options in-buffer 'png)))
|
||||
((string= "svg" extension)
|
||||
(with-temp-file tex-file
|
||||
(insert (concat (funcall org-babel-latex-preamble params)
|
||||
|
@ -238,7 +252,7 @@ This function is called by `org-babel-execute-src-block'."
|
|||
(insert
|
||||
(org-latex-guess-inputenc
|
||||
(org-splice-latex-header
|
||||
org-latex-preview-header
|
||||
org-format-latex-header
|
||||
(delq
|
||||
nil
|
||||
(mapcar
|
||||
|
|
|
@ -81,6 +81,12 @@ This will typically be `lua-mode'."
|
|||
:package-version '(Org . "8.3")
|
||||
:type 'symbol)
|
||||
|
||||
(defcustom org-babel-lua-multiple-values-separator ", "
|
||||
"Separate multiple values with this string."
|
||||
:group 'org-babel
|
||||
:package-version '(Org . "9.7")
|
||||
:type 'string)
|
||||
|
||||
(defun org-babel-execute:lua (body params)
|
||||
"Execute Lua BODY according to PARAMS.
|
||||
This function is called by `org-babel-execute-src-block'."
|
||||
|
@ -251,41 +257,47 @@ function main()
|
|||
%s
|
||||
end
|
||||
|
||||
fd=io.open(\"%s\", \"w\")
|
||||
fd:write( main() )
|
||||
fd:close()")
|
||||
(defvar org-babel-lua-pp-wrapper-method
|
||||
"
|
||||
-- table to string
|
||||
function t2s(t, indent)
|
||||
function dump(it, indent)
|
||||
if indent == nil then
|
||||
indent = \"\"
|
||||
indent = ''
|
||||
end
|
||||
if type(t) == \"table\" then
|
||||
ts = \"\"
|
||||
for k,v in pairs(t) do
|
||||
if type(v) == \"table\" then
|
||||
ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \\n\" ..
|
||||
t2s(v, indent .. \" \")
|
||||
else
|
||||
ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \" ..
|
||||
t2s(v, indent .. \" \") .. \"\\n\"
|
||||
if type(it) == 'table' and %s then
|
||||
local count = 0
|
||||
for _ in pairs(it) do
|
||||
count = count + 1
|
||||
end
|
||||
local result = ''
|
||||
if #indent ~= 0 then
|
||||
result = result .. '\\n'
|
||||
end
|
||||
for key, value in pairs(it) do
|
||||
result = result
|
||||
.. indent
|
||||
.. dump(key)
|
||||
.. ' = '
|
||||
.. dump(value, indent .. ' ')
|
||||
count = count - 1
|
||||
if count ~= 0 then
|
||||
result = result .. '\\n'
|
||||
end
|
||||
end
|
||||
return ts
|
||||
return result
|
||||
else
|
||||
return tostring(t)
|
||||
return tostring(it)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function main()
|
||||
%s
|
||||
function combine(...)
|
||||
local result = {}
|
||||
for index = 1, select('#', ...) do
|
||||
result[index] = dump(select(index, ...))
|
||||
end
|
||||
return table.concat(result, '%s')
|
||||
end
|
||||
|
||||
fd=io.open(\"%s\", \"w\")
|
||||
fd:write(t2s(main()))
|
||||
fd:close()")
|
||||
output = io.open('%s', 'w')
|
||||
output:write(combine(main()))
|
||||
output:close()")
|
||||
|
||||
(defun org-babel-lua-evaluate
|
||||
(session body &optional result-type result-params preamble)
|
||||
|
@ -319,15 +331,17 @@ PREAMBLE string is appended to BODY."
|
|||
(concat
|
||||
preamble (and preamble "\n")
|
||||
(format
|
||||
(if (member "pp" result-params)
|
||||
org-babel-lua-pp-wrapper-method
|
||||
org-babel-lua-wrapper-method)
|
||||
org-babel-lua-wrapper-method
|
||||
(mapconcat
|
||||
(lambda (line) (format "\t%s" line))
|
||||
(split-string
|
||||
(org-remove-indentation
|
||||
(org-trim body))
|
||||
"[\r\n]") "\n")
|
||||
"[\r\n]")
|
||||
"\n")
|
||||
(if (member "pp" result-params)
|
||||
"true" "false")
|
||||
org-babel-lua-multiple-values-separator
|
||||
(org-babel-process-file-name tmp-file 'noquote))))
|
||||
(org-babel-eval-read-file tmp-file))))))
|
||||
(org-babel-result-cond result-params
|
||||
|
|
|
@ -680,7 +680,7 @@ INFO is the export state as a property list."
|
|||
INFO is the export state, as a property list."
|
||||
(and field
|
||||
(lambda (a b)
|
||||
(string-collate-lessp
|
||||
(org-string<
|
||||
(org-cite-basic--get-field field a info 'raw)
|
||||
(org-cite-basic--get-field field b info 'raw)
|
||||
nil t))))
|
||||
|
|
|
@ -190,20 +190,23 @@ INITIAL is an initial style of comma-separated options, as a string or nil.
|
|||
STYLE is the style definition as a string or nil.
|
||||
|
||||
Return a string."
|
||||
(let ((options-no-style
|
||||
(and initial
|
||||
(let ((re (rx string-start (or "bibstyle" "citestyle" "style"))))
|
||||
(seq-filter
|
||||
(lambda (option) (not (string-match re option)))
|
||||
(split-string (org-unbracket-string "[" "]" initial)
|
||||
"," t " \t")))))
|
||||
(style-options
|
||||
(cond
|
||||
((null style) nil)
|
||||
((not (string-match "/" style)) (list (concat "style=" style)))
|
||||
(t
|
||||
(list (concat "bibstyle=" (substring style nil (match-beginning 0)))
|
||||
(concat "citestyle=" (substring style (match-end 0))))))))
|
||||
(let* ((options-no-style
|
||||
(and initial
|
||||
(let ((re (rx string-start (or "bibstyle" "citestyle" "style"))))
|
||||
(seq-filter
|
||||
(lambda (option) (not (string-match re option)))
|
||||
(split-string (org-unbracket-string "[" "]" initial)
|
||||
"," t " \t")))))
|
||||
;; Check whether the string is in key=val,...
|
||||
(biblatex-options-p (and (stringp style) (string-match-p "\\`[^,=]+=[^,]+\\(,[^=]+=[^,]+\\)\\'" style)))
|
||||
(style-options
|
||||
(cond
|
||||
((null style) nil)
|
||||
;; Assume it is a valid options string for biblatex if it is in key=val,... format
|
||||
((not (string-match "/" style)) (list (if biblatex-options-p style (concat "style=" style))))
|
||||
(t
|
||||
(list (concat "bibstyle=" (substring style nil (match-beginning 0)))
|
||||
(concat "citestyle=" (substring style (match-end 0))))))))
|
||||
(if (or options-no-style style-options)
|
||||
(format "[%s]"
|
||||
(mapconcat #'identity
|
||||
|
@ -376,47 +379,61 @@ INFO is the export state, as a property list."
|
|||
(other
|
||||
(user-error "Invalid entry %S in `org-cite-biblatex-styles'" other))))))
|
||||
|
||||
(defun org-cite-biblatex--generate-latex-usepackage (info)
|
||||
"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))))))
|
||||
(defun org-cite-biblatex-prepare-preamble (output _keys files style &rest _)
|
||||
"Prepare document preamble for \"biblatex\" usage.
|
||||
|
||||
(defun org-cite-biblatex--generate-latex-bibresources (info)
|
||||
"From INFO generate LaTeX that loads the relevant bibliography resource files."
|
||||
(let ((files (plist-get info :bibliography)))
|
||||
(mapconcat (lambda (f)
|
||||
(format "\\addbibresource%s{%s}"
|
||||
(if (org-url-p f) "[location=remote]" "")
|
||||
f))
|
||||
files
|
||||
"\n")))
|
||||
OUTPUT is the final output of the export process. FILES is the list of file
|
||||
names used as the bibliography.
|
||||
|
||||
This function ensures \"biblatex\" package is required. It also adds resources
|
||||
to the document, and set styles."
|
||||
(with-temp-buffer
|
||||
(save-excursion (insert output))
|
||||
(when (search-forward "\\begin{document}" nil t)
|
||||
;; Ensure there is a \usepackage{biblatex} somewhere or add one.
|
||||
;; 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
|
||||
(org-cite-register-processor 'biblatex
|
||||
:export-bibliography #'org-cite-biblatex-export-bibliography
|
||||
:export-citation #'org-cite-biblatex-export-citation
|
||||
:export-finalizer #'org-cite-biblatex-prepare-preamble
|
||||
:cite-styles #'org-cite-biblatex-list-styles)
|
||||
|
||||
(provide 'oc-biblatex)
|
||||
|
|
|
@ -134,6 +134,7 @@
|
|||
(declare-function citeproc-render-bib "ext:citeproc")
|
||||
(declare-function citeproc-hash-itemgetter-from-any "ext:citeproc")
|
||||
(declare-function citeproc-add-subbib-filters "ext:citeproc")
|
||||
(declare-function citeproc-style-cite-superscript-p "ext:citeproc")
|
||||
|
||||
(declare-function org-element-interpret-data "org-element" (data))
|
||||
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
|
||||
|
@ -435,6 +436,13 @@ INFO is the export state, as a property list."
|
|||
(citeproc-proc-style
|
||||
(org-cite-csl--processor info))))
|
||||
|
||||
(defun org-cite-csl--style-cite-superscript-p (info)
|
||||
"Non-nil when bibliography style produces citations in superscript.
|
||||
INFO is the export state, as a property list."
|
||||
(citeproc-style-cite-superscript-p
|
||||
(citeproc-proc-style
|
||||
(org-cite-csl--processor info))))
|
||||
|
||||
(defun org-cite-csl--nocite-p (citation info)
|
||||
"Non-nil when CITATION object's style is nocite.
|
||||
INFO is the export state, as a property list."
|
||||
|
@ -681,6 +689,9 @@ INFO is the export state, as a property list."
|
|||
(when (and (not footnote) (org-cite-csl--note-style-p info))
|
||||
(org-cite-adjust-note citation info)
|
||||
(setq footnote (org-cite-wrap-citation citation info)))
|
||||
;; Remove white space before CITATION when it is in superscript.
|
||||
(when (org-cite-csl--style-cite-superscript-p info)
|
||||
(org-cite--set-previous-post-blank citation 0 info))
|
||||
;; Return structure.
|
||||
(apply #'citeproc-citation-create
|
||||
`(:note-index
|
||||
|
@ -847,11 +858,27 @@ INFO is the export state, as a property list."
|
|||
;; process.
|
||||
(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
|
||||
(org-cite-register-processor 'csl
|
||||
:export-citation #'org-cite-csl-render-citation
|
||||
:export-bibliography #'org-cite-csl-render-bibliography
|
||||
:export-finalizer #'org-cite-csl-finalizer
|
||||
:cite-styles
|
||||
'((("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"))
|
||||
|
|
|
@ -168,25 +168,32 @@ state, as a property list."
|
|||
(org-cite-natbib--build-optional-arguments citation info)
|
||||
(org-cite-natbib--build-arguments citation)))
|
||||
|
||||
(defun org-cite-natbib--generate-latex-preamble (info)
|
||||
"Ensure that the \"natbib\" package is loaded.
|
||||
INFO is a plist used as a communication channel."
|
||||
(and (not (string-match
|
||||
(rx "\\usepackage" (opt "[" (*? nonl) "]") "{natbib}")
|
||||
(plist-get info :latex-full-header)))
|
||||
(format "\\usepackage%s{natbib}\n"
|
||||
(if (null org-cite-natbib-options)
|
||||
""
|
||||
(format "[%s]"
|
||||
(mapconcat #'symbol-name
|
||||
org-cite-natbib-options
|
||||
","))))))
|
||||
(defun org-cite-natbib-use-package (output &rest _)
|
||||
"Ensure output requires \"natbib\" package.
|
||||
OUTPUT is the final output of the export process."
|
||||
(with-temp-buffer
|
||||
(save-excursion (insert output))
|
||||
(when (search-forward "\\begin{document}" nil t)
|
||||
;; Ensure there is a \usepackage{natbib} somewhere or add one.
|
||||
(goto-char (match-beginning 0))
|
||||
(let ((re (rx "\\usepackage" (opt "[" (*? nonl) "]") "{natbib}")))
|
||||
(unless (re-search-backward re nil t)
|
||||
(insert
|
||||
(format "\\usepackage%s{natbib}\n"
|
||||
(if (null org-cite-natbib-options)
|
||||
""
|
||||
(format "[%s]"
|
||||
(mapconcat #'symbol-name
|
||||
org-cite-natbib-options
|
||||
","))))))))
|
||||
(buffer-string)))
|
||||
|
||||
|
||||
;;; Register `natbib' processor
|
||||
(org-cite-register-processor 'natbib
|
||||
:export-bibliography #'org-cite-natbib-export-bibliography
|
||||
:export-citation #'org-cite-natbib-export-citation
|
||||
:export-finalizer #'org-cite-natbib-use-package
|
||||
:cite-styles
|
||||
'((("author" "a") ("caps" "a") ("full" "f"))
|
||||
(("noauthor" "na") ("bare" "b"))
|
||||
|
|
101
lisp/ol.el
101
lisp/ol.el
|
@ -206,7 +206,7 @@ This function is intended to be used as a :set function."
|
|||
(set symbol value)
|
||||
(dolist (buf (org-buffer-list))
|
||||
(with-current-buffer buf
|
||||
(org-link-descriptive-ensure))))
|
||||
(org-restart-font-lock))))
|
||||
|
||||
(defcustom org-link-descriptive t
|
||||
"Non-nil means Org displays descriptive links.
|
||||
|
@ -590,9 +590,9 @@ taken to make the search successful, another function should be
|
|||
added to the companion hook `org-execute-file-search-functions',
|
||||
which see.
|
||||
|
||||
A function in this hook may also use `setq' to set the variable
|
||||
`description' to provide a suggestion for the descriptive text to
|
||||
be used for this link when it gets inserted into an Org buffer
|
||||
A function in this hook may also use `org-link-store-props' and set
|
||||
`:description' property to provide a suggestion for the descriptive
|
||||
text to be used for this link when it gets inserted into an Org buffer
|
||||
with \\[org-insert-link].")
|
||||
|
||||
(defvar org-execute-file-search-functions nil
|
||||
|
@ -649,22 +649,6 @@ exact and fuzzy text search.")
|
|||
(defvar org-link--search-failed nil
|
||||
"Non-nil when last link search failed.")
|
||||
|
||||
|
||||
(defvar-local org-link--link-folding-spec '(org-link
|
||||
(:global t)
|
||||
(:ellipsis . nil)
|
||||
(:isearch-open . t)
|
||||
(:fragile . org-link--reveal-maybe))
|
||||
"Folding spec used to hide invisible parts of links.")
|
||||
|
||||
(defvar-local org-link--description-folding-spec '(org-link-description
|
||||
(:global t)
|
||||
(:ellipsis . nil)
|
||||
(:visible . t)
|
||||
(:isearch-open . nil)
|
||||
(:fragile . org-link--reveal-maybe))
|
||||
"Folding spec used to reveal link description.")
|
||||
|
||||
|
||||
;;; Internal Functions
|
||||
|
||||
|
@ -1134,12 +1118,12 @@ The functions are defined in the `:store' property of
|
|||
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
|
||||
value. Each function should check if it is responsible for
|
||||
creating this link (for example by looking at the major mode).
|
||||
If not, it must exit and return nil. If yes, it should return
|
||||
a non-nil value after calling `org-link-store-props' with a list
|
||||
of properties and values. Special properties are:
|
||||
Each function will be called in turn with a single argument
|
||||
INTERACTIVE? - non-nil when user interaction is allowed. Each function
|
||||
should check if it is responsible for creating this link (for example
|
||||
by looking at the major mode). If not, it must return nil. If yes,
|
||||
it should return a non-nil value after calling `org-link-store-props'
|
||||
with a list of properties and values. Special properties are:
|
||||
|
||||
:type The link prefix, like \"http\". This must be given.
|
||||
:link The link, like \"http://www.astro.uva.nl/~dominik\".
|
||||
|
@ -1156,9 +1140,6 @@ and then used in capture templates."
|
|||
if store-func
|
||||
collect store-func))
|
||||
|
||||
(defvar org-link--abbrev-functions nil
|
||||
"Alist of abbrev link expressions and functions.")
|
||||
|
||||
(defun org-link-expand-abbrev (link)
|
||||
"Replace link abbreviations in LINK string.
|
||||
Abbreviations are defined in `org-link-abbrev-alist'."
|
||||
|
@ -1173,27 +1154,14 @@ Abbreviations are defined in `org-link-abbrev-alist'."
|
|||
(setq rpl (cdr as))
|
||||
(cond
|
||||
((symbolp rpl) (funcall rpl tag))
|
||||
((string-match "%(\\([^) ]+\\))" rpl) ; %(function)
|
||||
((string-match "%(\\([^)]+\\))" rpl)
|
||||
(replace-match
|
||||
(save-match-data
|
||||
(funcall (intern-soft (match-string 1 rpl)) tag))
|
||||
t t rpl))
|
||||
((string-match "%(\\(.+\\))" rpl) ; %(sexpr using tag)
|
||||
(replace-match
|
||||
(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 ""))))))
|
||||
((string-match "%s" rpl) (replace-match (or tag "") t t rpl))
|
||||
((string-match "%h" rpl)
|
||||
(replace-match (url-hexify-string (or tag "")) t t rpl))
|
||||
(t (concat rpl tag)))))))
|
||||
|
||||
(defun org-link-open (link &optional arg)
|
||||
|
@ -1695,18 +1663,12 @@ If the link is in hidden text, expose it."
|
|||
(interactive)
|
||||
(org-next-link t))
|
||||
|
||||
(defun org-link-descriptive-ensure ()
|
||||
"Toggle the literal or descriptive display of links in current buffer if needed."
|
||||
(org-fold-core-set-folding-spec-property
|
||||
(car org-link--link-folding-spec)
|
||||
:visible (not org-link-descriptive)))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-toggle-link-display ()
|
||||
"Toggle the literal or descriptive display of links in current buffer."
|
||||
(interactive)
|
||||
(setq org-link-descriptive (not org-link-descriptive))
|
||||
(org-link-descriptive-ensure))
|
||||
(org-restart-font-lock))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-store-link (arg &optional interactive?)
|
||||
|
@ -1754,7 +1716,7 @@ NAME."
|
|||
;; 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
|
||||
link desc search agenda-link) ;; description
|
||||
(cond
|
||||
;; Store a link using an external link type, if any function is
|
||||
;; available, unless external link types are skipped for this
|
||||
|
@ -1825,9 +1787,8 @@ NAME."
|
|||
|
||||
;; Image mode
|
||||
((eq major-mode 'image-mode)
|
||||
(setq cpltxt (concat "file:"
|
||||
(abbreviate-file-name buffer-file-name))
|
||||
link cpltxt)
|
||||
(setq link (concat "file:"
|
||||
(abbreviate-file-name buffer-file-name)))
|
||||
(org-link-store-props :type "image" :file buffer-file-name))
|
||||
|
||||
;; In dired, store a link to the file of the current line
|
||||
|
@ -1838,20 +1799,16 @@ NAME."
|
|||
(expand-file-name (dired-get-filename nil t)))
|
||||
;; Otherwise, no file so use current directory.
|
||||
default-directory))
|
||||
(setq cpltxt (concat "file:" file)
|
||||
link cpltxt)))
|
||||
(setq link (concat "file:" file))))
|
||||
|
||||
;; 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)).
|
||||
;; the provided search string.
|
||||
((setq search (run-hook-with-args-until-success
|
||||
'org-create-file-search-functions))
|
||||
(setq link (concat "file:" (abbreviate-file-name buffer-file-name)
|
||||
"::" search))
|
||||
(setq cpltxt (or link))) ;; description
|
||||
"::" search)
|
||||
desc (plist-get org-store-link-plist :description)))
|
||||
|
||||
;; Main logic for storing built-in link types in org-mode
|
||||
;; buffers
|
||||
|
@ -1869,22 +1826,19 @@ NAME."
|
|||
;; Avoid [[target][file:~/org/test.org::target]]
|
||||
;; links. Maybe the case of identical target and
|
||||
;; description should be handled by `org-insert-link'.
|
||||
cpltxt nil
|
||||
desc nil))
|
||||
(t
|
||||
;; Just link to current headline.
|
||||
(let ((here (org-link--file-link-to-here)))
|
||||
(setq cpltxt (car here))
|
||||
(setq desc (cdr here)))
|
||||
(setq link cpltxt)))))
|
||||
(setq link (car here))
|
||||
(setq desc (cdr here)))))))
|
||||
|
||||
;; Buffer linked to file, but not an org-mode buffer.
|
||||
((buffer-file-name (buffer-base-buffer))
|
||||
;; Just link to this file here.
|
||||
(let ((here (org-link--file-link-to-here)))
|
||||
(setq cpltxt (car here))
|
||||
(setq desc (cdr here)))
|
||||
(setq link cpltxt))
|
||||
(setq link (car here))
|
||||
(setq desc (cdr here))))
|
||||
|
||||
(interactive?
|
||||
(user-error "No method for storing a link from this buffer"))
|
||||
|
@ -1892,8 +1846,7 @@ NAME."
|
|||
(t (setq link nil)))
|
||||
|
||||
;; We're done setting link and desc, clean up
|
||||
(when (consp link) (setq cpltxt (car link) link (cdr link)))
|
||||
(setq link (or link cpltxt))
|
||||
(when (consp link) (setq link (or (cdr link) (car link))))
|
||||
(cond ((not desc))
|
||||
((equal desc "NONE") (setq desc nil))
|
||||
(t (setq desc (org-link-display-format desc))))
|
||||
|
|
|
@ -2385,8 +2385,7 @@ The following commands are available:
|
|||
org-agenda-clockreport-mode org-agenda-start-with-clockreport-mode
|
||||
org-agenda-archives-mode org-agenda-start-with-archives-mode))
|
||||
(add-to-invisibility-spec '(org-filtered))
|
||||
(org-fold-core-initialize `(,org-link--description-folding-spec
|
||||
,org-link--link-folding-spec))
|
||||
(add-to-invisibility-spec '(org-link))
|
||||
(easy-menu-change
|
||||
'("Agenda") "Agenda Files"
|
||||
(append
|
||||
|
@ -5833,7 +5832,7 @@ displayed in agenda view."
|
|||
(org-encode-time ; DATE bound by calendar
|
||||
0 0 0 (nth 1 date) (car date) (nth 2 date))))
|
||||
"\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)"
|
||||
"\\|\\(<%%\\(([^>\n]+)\\)>\\)"))
|
||||
"\\|\\(<%%\\(([^>\n]+)\\)\\([^\n>]*\\)>\\)"))
|
||||
timestamp-items)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward regexp nil t)
|
||||
|
@ -7491,8 +7490,8 @@ The optional argument TYPE tells the agenda type."
|
|||
"Compare the string values of categories of strings A and B."
|
||||
(let ((ca (or (get-text-property (1- (length a)) 'org-category a) ""))
|
||||
(cb (or (get-text-property (1- (length b)) 'org-category b) "")))
|
||||
(cond ((string-lessp ca cb) -1)
|
||||
((string-lessp cb ca) +1))))
|
||||
(cond ((org-string< ca cb) -1)
|
||||
((org-string< cb ca) +1))))
|
||||
|
||||
(defsubst org-cmp-todo-state (a b)
|
||||
"Compare the todo states of strings A and B."
|
||||
|
@ -7538,8 +7537,8 @@ The optional argument TYPE tells the agenda type."
|
|||
(cond ((not (or ta tb)) nil)
|
||||
((not ta) +1)
|
||||
((not tb) -1)
|
||||
((string-lessp ta tb) -1)
|
||||
((string-lessp tb ta) +1))))
|
||||
((org-string< ta tb) -1)
|
||||
((org-string< tb ta) +1))))
|
||||
|
||||
(defsubst org-cmp-tag (a b)
|
||||
"Compare the string values of the first tags of A and B."
|
||||
|
@ -7548,8 +7547,8 @@ The optional argument TYPE tells the agenda type."
|
|||
(cond ((not (or ta tb)) nil)
|
||||
((not ta) +1)
|
||||
((not tb) -1)
|
||||
((string-lessp ta tb) -1)
|
||||
((string-lessp tb ta) +1))))
|
||||
((funcall (or org-tags-sort-function #'org-string<) ta tb) -1)
|
||||
((funcall (or org-tags-sort-function #'org-string<) tb ta) +1))))
|
||||
|
||||
(defsubst org-cmp-time (a b)
|
||||
"Compare the time-of-day values of strings A and B."
|
||||
|
@ -8595,6 +8594,14 @@ Negative selection means regexp must not match for selection of an entry."
|
|||
(org-agenda-redo))
|
||||
(message "Display now includes inactive timestamps as well"))
|
||||
((eq org-agenda-type 'search)
|
||||
;; Previous calls to `org-agenda-manipulate-query' could already
|
||||
;; add trailing text to the query. Prevent duplicating it.
|
||||
;; Trim the trailing spaces and +/.
|
||||
(setq org-agenda-query-string
|
||||
(replace-regexp-in-string
|
||||
(rx (or (1+ " ") (seq (1+ " ") (any "+-") (opt "{}"))) eos)
|
||||
""
|
||||
org-agenda-query-string))
|
||||
(org-add-to-string
|
||||
'org-agenda-query-string
|
||||
(if org-agenda-last-search-view-search-was-boolean
|
||||
|
@ -9215,7 +9222,8 @@ When called with a prefix argument, include all archive files as well."
|
|||
(when (and (markerp m) (marker-buffer m))
|
||||
(and org-agenda-follow-mode
|
||||
(if org-agenda-follow-indirect
|
||||
(org-agenda-tree-to-indirect-buffer nil)
|
||||
(let ((org-indirect-buffer-display 'other-window))
|
||||
(org-agenda-tree-to-indirect-buffer nil))
|
||||
(org-agenda-show)))
|
||||
(and org-agenda-show-outline-path
|
||||
(org-with-point-at m (org-display-outline-path org-agenda-show-outline-path))))))
|
||||
|
@ -9307,20 +9315,17 @@ Pass ARG, FORCE-ARG, DELETE and BODY to `org-agenda-do-in-region'."
|
|||
(marker (or (org-get-at-bol 'org-marker)
|
||||
(org-agenda-error)))
|
||||
(buffer (marker-buffer marker))
|
||||
(pos (marker-position marker))
|
||||
(type (org-get-at-bol 'type))
|
||||
dbeg dend (n 0))
|
||||
(org-with-remote-undo buffer
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(if (and (derived-mode-p 'org-mode) (not (member type '("sexp"))))
|
||||
(setq dbeg (progn (org-back-to-heading t) (point))
|
||||
dend (org-end-of-subtree t t))
|
||||
(setq dbeg (line-beginning-position)
|
||||
dend (min (point-max) (1+ (line-end-position)))))
|
||||
(goto-char dbeg)
|
||||
(while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n)))))
|
||||
(org-with-point-at marker
|
||||
(if (and (derived-mode-p 'org-mode) (not (member type '("sexp"))))
|
||||
(setq dbeg (progn (org-back-to-heading t) (point))
|
||||
dend (org-end-of-subtree t t))
|
||||
(setq dbeg (line-beginning-position)
|
||||
dend (min (point-max) (1+ (line-end-position)))))
|
||||
(goto-char dbeg)
|
||||
(while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n))))
|
||||
(when (or (eq t org-agenda-confirm-kill)
|
||||
(and (numberp org-agenda-confirm-kill)
|
||||
(> n org-agenda-confirm-kill)))
|
||||
|
@ -9337,7 +9342,7 @@ Pass ARG, FORCE-ARG, DELETE and BODY to `org-agenda-do-in-region'."
|
|||
(set-window-configuration win-conf))))
|
||||
(let ((org-agenda-buffer-name bufname-orig))
|
||||
(org-remove-subtree-entries-from-agenda buffer dbeg dend))
|
||||
(with-current-buffer buffer (delete-region dbeg dend))
|
||||
(org-with-point-at marker (delete-region dbeg dend))
|
||||
(message "Agenda item and source killed")))))
|
||||
|
||||
(defvar org-archive-default-command) ; defined in org-archive.el
|
||||
|
@ -9400,26 +9405,26 @@ Pass ARG, FORCE-ARG, DELETE and BODY to `org-agenda-do-in-region'."
|
|||
The subtree is the one in buffer BUF, starting at BEG and ending at END.
|
||||
If this information is not given, the function uses the tree at point."
|
||||
(let ((buf (or buf (current-buffer))) m p)
|
||||
(save-excursion
|
||||
(unless (and beg end)
|
||||
(org-back-to-heading t)
|
||||
(setq beg (point))
|
||||
(org-end-of-subtree t)
|
||||
(setq end (point)))
|
||||
(set-buffer (get-buffer org-agenda-buffer-name))
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(forward-line 0)
|
||||
(while (not (bobp))
|
||||
(when (and (setq m (org-get-at-bol 'org-marker))
|
||||
(equal buf (marker-buffer m))
|
||||
(setq p (marker-position m))
|
||||
(>= p beg)
|
||||
(< p end))
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region (line-beginning-position)
|
||||
(1+ (line-end-position)))))
|
||||
(forward-line -1))))))
|
||||
(org-with-wide-buffer
|
||||
(unless (and beg end)
|
||||
(org-back-to-heading t)
|
||||
(setq beg (point))
|
||||
(org-end-of-subtree t)
|
||||
(setq end (point)))
|
||||
(set-buffer (get-buffer org-agenda-buffer-name))
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(forward-line 0)
|
||||
(while (not (bobp))
|
||||
(when (and (setq m (org-get-at-bol 'org-marker))
|
||||
(equal buf (marker-buffer m))
|
||||
(setq p (marker-position m))
|
||||
(>= p beg)
|
||||
(< p end))
|
||||
(let ((inhibit-read-only t))
|
||||
(delete-region (line-beginning-position)
|
||||
(1+ (line-end-position)))))
|
||||
(forward-line -1))))))
|
||||
|
||||
(defun org-agenda-refile (&optional goto rfloc no-update)
|
||||
"Refile the item at point.
|
||||
|
@ -9688,27 +9693,6 @@ With a `\\[universal-argument]' prefix, make a separate frame for this tree, \
|
|||
i.e. don't use
|
||||
the dedicated frame."
|
||||
(interactive "P")
|
||||
(if current-prefix-arg
|
||||
(org-agenda-do-tree-to-indirect-buffer arg)
|
||||
(let ((agenda-buffer (buffer-name))
|
||||
(agenda-window (selected-window))
|
||||
(indirect-window
|
||||
(and org-last-indirect-buffer
|
||||
(get-buffer-window org-last-indirect-buffer))))
|
||||
(save-window-excursion (org-agenda-do-tree-to-indirect-buffer arg))
|
||||
(unless (or (eq org-indirect-buffer-display 'new-frame)
|
||||
(eq org-indirect-buffer-display 'dedicated-frame))
|
||||
(unwind-protect
|
||||
(unless (and indirect-window (window-live-p indirect-window))
|
||||
(setq indirect-window (split-window agenda-window)))
|
||||
(and indirect-window (select-window indirect-window))
|
||||
(switch-to-buffer org-last-indirect-buffer :norecord)
|
||||
(fit-window-to-buffer indirect-window)))
|
||||
(select-window (get-buffer-window agenda-buffer))
|
||||
(setq org-agenda-last-indirect-buffer org-last-indirect-buffer))))
|
||||
|
||||
(defun org-agenda-do-tree-to-indirect-buffer (arg)
|
||||
"Same as `org-agenda-tree-to-indirect-buffer' without saving window."
|
||||
(org-agenda-check-no-diary)
|
||||
(let* ((marker (or (org-get-at-bol 'org-marker)
|
||||
(org-agenda-error)))
|
||||
|
@ -9717,7 +9701,8 @@ the dedicated frame."
|
|||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char pos)
|
||||
(org-tree-to-indirect-buffer arg)))))
|
||||
(org-tree-to-indirect-buffer arg))))
|
||||
(setq org-agenda-last-indirect-buffer org-last-indirect-buffer))
|
||||
|
||||
(defvar org-last-heading-marker (make-marker)
|
||||
"Marker pointing to the headline that last changed its TODO state
|
||||
|
|
|
@ -808,6 +808,7 @@ previous clocking intervals."
|
|||
60)))
|
||||
(+ currently-clocked-time (or org-clock-total-time 0))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-clock-modify-effort-estimate (&optional value)
|
||||
"Add to or set the effort estimate of the item currently being clocked.
|
||||
VALUE can be a number of minutes, or a string with format hh:mm or mm.
|
||||
|
@ -3111,8 +3112,9 @@ PROPERTIES: The list properties specified in the `:properties' parameter
|
|||
(let* ((todo (org-get-todo-state))
|
||||
(tags-list (org-get-tags))
|
||||
(org-scanner-tags tags-list)
|
||||
(org-trust-scanner-tags t))
|
||||
(funcall matcher todo tags-list nil)))))
|
||||
(org-trust-scanner-tags t)
|
||||
(level (org-current-level)))
|
||||
(funcall matcher todo tags-list level)))))
|
||||
(goto-char (point-min))
|
||||
(setq st t)
|
||||
(while (or (and (bobp) (prog1 st (setq st nil))
|
||||
|
|
|
@ -123,6 +123,12 @@ in `org-columns-summary-types-default', which see."
|
|||
(function :tag "Summarize")
|
||||
(function :tag "Collect")))))
|
||||
|
||||
(defcustom org-columns-dblock-formatter #'org-columns-dblock-write-default
|
||||
"Function to format data in column view dynamic blocks.
|
||||
For more information, see `org-columns-dblock-write-default'."
|
||||
:group 'org-properties
|
||||
:package-version '(Org . "9.7")
|
||||
:type 'function)
|
||||
|
||||
|
||||
;;; Column View
|
||||
|
@ -1442,9 +1448,13 @@ that will be excluded from the resulting view. FORMAT is a
|
|||
format string for columns, or nil. When LOCAL is non-nil, only
|
||||
capture headings in current subtree.
|
||||
|
||||
This function returns a list containing the title row and all
|
||||
other rows. Each row is a list of fields, as strings, or
|
||||
`hline'."
|
||||
This function returns a list containing the title row and all other
|
||||
rows. Each row is either a list, or the symbol `hline'. The first list
|
||||
is the heading row as a list of strings with the column titles according
|
||||
to FORMAT. All subsequent lists each represent a body row as a list
|
||||
whose first element is an integer indicating the outline level of the
|
||||
entry, and whose remaining elements are strings with the contents for
|
||||
the columns according to FORMAT."
|
||||
(org-columns (not local) format)
|
||||
(goto-char org-columns-top-level-marker)
|
||||
(let ((columns (length org-columns-current-fmt-compiled))
|
||||
|
@ -1457,11 +1467,10 @@ other rows. Each row is a list of fields, as strings, or
|
|||
(dotimes (i columns)
|
||||
(let* ((col (+ (line-beginning-position) i))
|
||||
(p (get-char-property col 'org-columns-key)))
|
||||
(push (org-quote-vert
|
||||
(get-char-property col
|
||||
(if (string= p "ITEM")
|
||||
'org-columns-value
|
||||
'org-columns-value-modified)))
|
||||
(push (get-char-property col
|
||||
(if (string= p "ITEM")
|
||||
'org-columns-value
|
||||
'org-columns-value-modified))
|
||||
row)))
|
||||
(unless (or
|
||||
(and skip-empty
|
||||
|
@ -1493,7 +1502,9 @@ an inline src-block."
|
|||
'(footnote-reference inline-babel-call inline-src-block target
|
||||
radio-target statistics-cookie)
|
||||
#'org-element-extract)
|
||||
(org-no-properties (org-element-interpret-data data))))
|
||||
(org-quote-vert
|
||||
(org-no-properties
|
||||
(org-element-interpret-data data)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun org-dblock-write:columnview (params)
|
||||
|
@ -1545,7 +1556,17 @@ PARAMS is a property list of parameters:
|
|||
`:vlines'
|
||||
|
||||
When non-nil, make each column a column group to enforce
|
||||
vertical lines."
|
||||
vertical lines.
|
||||
|
||||
`:link'
|
||||
|
||||
Link the item headlines in the table to their origins.
|
||||
|
||||
`:formatter'
|
||||
|
||||
A function to format the data and insert it into the
|
||||
buffer. Overrides the default formatting function set in
|
||||
`org-columns-dblock-formatter'."
|
||||
(let ((table
|
||||
(let ((id (plist-get params :id))
|
||||
view-file view-pos)
|
||||
|
@ -1573,9 +1594,20 @@ PARAMS is a property list of parameters:
|
|||
(plist-get params :exclude-tags)
|
||||
(plist-get params :format)
|
||||
view-pos)))))
|
||||
(width-specs
|
||||
(mapcar (lambda (spec) (nth 2 spec))
|
||||
org-columns-current-fmt-compiled)))
|
||||
(formatter (or (plist-get params :formatter)
|
||||
org-columns-dblock-formatter
|
||||
#'org-columns-dblock-write-default)))
|
||||
(funcall formatter (point) table params)))
|
||||
|
||||
(defun org-columns-dblock-write-default (ipos table params)
|
||||
"Write out a columnview table at position IPOS in the current buffer.
|
||||
TABLE is a table with data as produced by `org-columns--capture-view'.
|
||||
PARAMS is the parameter property list obtained from the dynamic block
|
||||
definition."
|
||||
(let ((link (plist-get params :link))
|
||||
(width-specs
|
||||
(mapcar (lambda (spec) (nth 2 spec))
|
||||
org-columns-current-fmt-compiled)))
|
||||
(when table
|
||||
;; Prune level information from the table. Also normalize
|
||||
;; headings: remove stars, add indentation entities, if
|
||||
|
@ -1599,7 +1631,14 @@ PARAMS is a property list of parameters:
|
|||
(and (numberp hlines) (<= level hlines))))
|
||||
(push 'hline new-table))
|
||||
(when item-index
|
||||
(let ((item (org-columns--clean-item (nth item-index (cdr row)))))
|
||||
(let* ((raw (nth item-index (cdr row)))
|
||||
(cleaned (org-columns--clean-item raw))
|
||||
(item (if (not link) cleaned
|
||||
(let ((search (org-link-heading-search-string raw)))
|
||||
(org-link-make-string
|
||||
(if (not (buffer-file-name)) search
|
||||
(format "file:%s::%s" (buffer-file-name) search))
|
||||
cleaned)))))
|
||||
(setf (nth item-index (cdr row))
|
||||
(if (and indent (> level 1))
|
||||
(concat "\\_" (make-string (* 2 (1- level)) ?\s) item)
|
||||
|
@ -1616,6 +1655,8 @@ PARAMS is a property list of parameters:
|
|||
;; to the resulting table, adding alignment field as the first
|
||||
;; row.
|
||||
(push (mapcar (lambda (width) (when width (format "<%d>" width))) width-specs) table))
|
||||
;; now insert the table into the buffer
|
||||
(goto-char ipos)
|
||||
(let ((content-lines (org-split-string (plist-get params :content) "\n"))
|
||||
recalc)
|
||||
;; Insert affiliated keywords before the table.
|
||||
|
|
|
@ -251,18 +251,6 @@ removed."
|
|||
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
|
||||
|
||||
|
@ -661,6 +649,29 @@ Counting starts at 1."
|
|||
(define-obsolete-variable-alias 'org-plantuml-executable-args 'org-plantuml-args
|
||||
"Org 9.6")
|
||||
|
||||
(defvar org-cached-props nil)
|
||||
(defvar org-use-property-inheritance)
|
||||
(declare-function org-entry-get "org" (epom property &optional inherit literal-nil))
|
||||
(declare-function org-entry-properties "org" (&optional epom which))
|
||||
(defun org-cached-entry-get (pom property)
|
||||
(if (or (eq t org-use-property-inheritance)
|
||||
(and (stringp org-use-property-inheritance)
|
||||
(let ((case-fold-search t))
|
||||
(string-match-p org-use-property-inheritance property)))
|
||||
(and (listp org-use-property-inheritance)
|
||||
(member-ignore-case property org-use-property-inheritance)))
|
||||
;; Caching is not possible, check it directly.
|
||||
(org-entry-get pom property 'inherit)
|
||||
;; Get all properties, so we can do complicated checks easily.
|
||||
(cdr (assoc-string property
|
||||
(or org-cached-props
|
||||
(setq org-cached-props (org-entry-properties pom)))
|
||||
t))))
|
||||
|
||||
(make-obsolete 'org-cached-entry-get
|
||||
"Performs badly. Instead use `org-entry-get' with the argument INHERIT set to `selective'"
|
||||
"9.7")
|
||||
|
||||
(defconst org-latex-line-break-safe "\\\\[0pt]"
|
||||
"Linebreak protecting the following [...].
|
||||
|
||||
|
@ -686,393 +697,6 @@ This constant, for example, makes the below code not err:
|
|||
"use `org-element' library"
|
||||
"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.
|
||||
(defun org-let (list &rest body) ;FIXME: So many kittens are suffering here.
|
||||
(declare (indent 1) (obsolete cl-progv "2021"))
|
||||
|
|
|
@ -57,6 +57,12 @@
|
|||
;; (add-hook 'org-mode-hook
|
||||
;; (lambda ()
|
||||
;; (define-key org-mode-map "\C-co" 'org-ctags-find-tag-interactive)))
|
||||
;; (with-eval-after-load "org-ctags"
|
||||
;; (org-ctags-enable))
|
||||
;;
|
||||
;; To activate the library, you need to call `org-ctags-enable' explicitly.
|
||||
;; It used to be invoked during library loading, but it was against Emacs
|
||||
;; policy and caused inconvenience of Org users who do not use `org-ctags'.
|
||||
;;
|
||||
;; By default, with org-ctags loaded, org will first try and visit the tag
|
||||
;; with the same name as the link; then, if unsuccessful, ask the user if
|
||||
|
@ -161,6 +167,20 @@ See the ctags documentation for more information.")
|
|||
:version "24.1"
|
||||
:type 'file)
|
||||
|
||||
(defconst org-ctags--open-link-functions-list
|
||||
(list
|
||||
#'org-ctags-find-tag
|
||||
#'org-ctags-ask-rebuild-tags-file-then-find-tag
|
||||
#'org-ctags-rebuild-tags-file-then-find-tag
|
||||
#'org-ctags-ask-append-topic
|
||||
#'org-ctags-append-topic
|
||||
#'org-ctags-ask-visit-buffer-or-file
|
||||
#'org-ctags-visit-buffer-or-file
|
||||
#'org-ctags-fail-silently)
|
||||
"Options for `org-open-link-functions'.
|
||||
Ensure that the user option and `unload-feature'
|
||||
use the same set of functions.")
|
||||
|
||||
(defcustom org-ctags-open-link-functions
|
||||
'(org-ctags-find-tag
|
||||
org-ctags-ask-rebuild-tags-file-then-find-tag
|
||||
|
@ -168,14 +188,7 @@ See the ctags documentation for more information.")
|
|||
"List of functions to be prepended to ORG-OPEN-LINK-FUNCTIONS by ORG-CTAGS."
|
||||
:version "24.1"
|
||||
:type 'hook
|
||||
:options '(org-ctags-find-tag
|
||||
org-ctags-ask-rebuild-tags-file-then-find-tag
|
||||
org-ctags-rebuild-tags-file-then-find-tag
|
||||
org-ctags-ask-append-topic
|
||||
org-ctags-append-topic
|
||||
org-ctags-ask-visit-buffer-or-file
|
||||
org-ctags-visit-buffer-or-file
|
||||
org-ctags-fail-silently))
|
||||
:options org-ctags--open-link-functions-list)
|
||||
|
||||
|
||||
(defvar org-ctags-tag-list nil
|
||||
|
@ -191,21 +204,21 @@ The following patterns are replaced in the string:
|
|||
:type 'string)
|
||||
|
||||
|
||||
(add-hook 'org-mode-hook
|
||||
(lambda ()
|
||||
(when (and org-ctags-enabled-p
|
||||
(buffer-file-name))
|
||||
;; Make sure this file's directory is added to default
|
||||
;; directories in which to search for tags.
|
||||
(let ((tags-filename
|
||||
(expand-file-name
|
||||
(concat (file-name-directory (buffer-file-name))
|
||||
"/TAGS"))))
|
||||
(when (file-exists-p tags-filename)
|
||||
(visit-tags-table tags-filename))))))
|
||||
(defun org-ctags--visit-tags-table ()
|
||||
"Load tags for current file.
|
||||
A function for `org-mode-hook."
|
||||
(when (and org-ctags-enabled-p
|
||||
(buffer-file-name))
|
||||
;; Make sure this file's directory is added to default
|
||||
;; directories in which to search for tags.
|
||||
(let ((tags-filename
|
||||
(expand-file-name
|
||||
(concat (file-name-directory (buffer-file-name))
|
||||
"/TAGS"))))
|
||||
(when (file-exists-p tags-filename)
|
||||
(visit-tags-table tags-filename)))))
|
||||
|
||||
|
||||
(advice-add 'visit-tags-table :after #'org--ctags-load-tag-list)
|
||||
(defun org--ctags-load-tag-list (&rest _)
|
||||
(when (and org-ctags-enabled-p tags-file-name)
|
||||
(setq-local org-ctags-tag-list
|
||||
|
@ -213,12 +226,28 @@ The following patterns are replaced in the string:
|
|||
|
||||
|
||||
(defun org-ctags-enable ()
|
||||
(add-hook 'org-mode-hook #'org-ctags--visit-tags-table)
|
||||
(advice-add 'visit-tags-table :after #'org--ctags-load-tag-list)
|
||||
(advice-add 'xref-find-definitions :before
|
||||
#'org--ctags-set-org-mark-before-finding-tag)
|
||||
|
||||
(put 'org-mode 'find-tag-default-function 'org-ctags-find-tag-at-point)
|
||||
(setq org-ctags-enabled-p t)
|
||||
(dolist (fn org-ctags-open-link-functions)
|
||||
(add-hook 'org-open-link-functions fn t)))
|
||||
|
||||
|
||||
(defun org-ctags-unload-function ()
|
||||
"Disable `org-ctags' library.
|
||||
Called by `unload-feature'."
|
||||
(put 'org-mode 'find-tag-default-function nil)
|
||||
(advice-remove 'visit-tags-table #'org--ctags-load-tag-list)
|
||||
(advice-remove 'xref-find-definitions
|
||||
#'org--ctags-set-org-mark-before-finding-tag)
|
||||
(dolist (fn org-ctags--open-link-functions-list)
|
||||
(remove-hook 'org-open-link-functions fn nil)))
|
||||
|
||||
|
||||
;;; General utility functions. ===============================================
|
||||
;; These work outside org-ctags mode.
|
||||
|
||||
|
@ -294,8 +323,6 @@ The new topic will be titled NAME (or TITLE if supplied)."
|
|||
;;;; Misc interoperability with etags system =================================
|
||||
|
||||
|
||||
(advice-add 'xref-find-definitions :before
|
||||
#'org--ctags-set-org-mark-before-finding-tag)
|
||||
(defun org--ctags-set-org-mark-before-finding-tag (&rest _)
|
||||
"Before trying to find a tag, save our current position on org mark ring."
|
||||
(save-excursion
|
||||
|
@ -477,18 +504,21 @@ function may take several seconds to finish if the directory or
|
|||
its subdirectories contain large numbers of taggable files."
|
||||
(interactive)
|
||||
(cl-assert (buffer-file-name))
|
||||
(let ((dir-name (or directory-name
|
||||
(file-name-directory (buffer-file-name))))
|
||||
(let ((dir-name (shell-quote-argument
|
||||
(expand-file-name
|
||||
(if directory-name
|
||||
(file-name-as-directory directory-name)
|
||||
(file-name-directory (buffer-file-name))))))
|
||||
(exitcode nil))
|
||||
(save-excursion
|
||||
(setq exitcode
|
||||
(shell-command
|
||||
(format (concat "%s --langdef=orgmode --langmap=orgmode:.org "
|
||||
"--regex-orgmode=\"%s\" -f \"%s\" -e -R %s")
|
||||
"--regex-orgmode=%s -f %sTAGS -e -R %s*")
|
||||
org-ctags-path-to-ctags
|
||||
org-ctags-tag-regexp
|
||||
(expand-file-name (concat dir-name "/TAGS"))
|
||||
(expand-file-name (concat (shell-quote-argument dir-name) "/*")))))
|
||||
(shell-quote-argument org-ctags-tag-regexp)
|
||||
dir-name
|
||||
dir-name)))
|
||||
(cond
|
||||
((eql 0 exitcode)
|
||||
(setq-local org-ctags-tag-list
|
||||
|
@ -523,8 +553,6 @@ a new topic."
|
|||
'org-open-link-functions tag))))))
|
||||
|
||||
|
||||
(org-ctags-enable)
|
||||
|
||||
(provide 'org-ctags)
|
||||
|
||||
;;; org-ctags.el ends here
|
||||
|
|
|
@ -4282,7 +4282,7 @@ Assume point is at the target."
|
|||
"\\|"
|
||||
"\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)"
|
||||
"\\|"
|
||||
"\\(?:<%%\\(?:([^>\n]+)\\)>\\)")
|
||||
"\\(?:<%%\\(?:([^>\n]+)\\)\\([^\n>]*\\)>\\)")
|
||||
"Regexp matching any timestamp type object.")
|
||||
|
||||
(defconst org-element--timestamp-raw-value-regexp
|
||||
|
@ -4300,8 +4300,8 @@ containing `:type', `:range-type', `:raw-value', `:year-start',
|
|||
`:year-end', `:month-end', `:day-end', `:hour-end', `:minute-end',
|
||||
`:repeater-type', `:repeater-value', `:repeater-unit',
|
||||
`:repeater-deadline-value', `:repeater-deadline-unit', `:warning-type',
|
||||
`:warning-value', `:warning-unit', `:begin', `:end' and `:post-blank'
|
||||
properties. Otherwise, return nil.
|
||||
`:warning-value', `:warning-unit', `:diary-sexp', `:begin', `:end' and
|
||||
`:post-blank' properties. Otherwise, return nil.
|
||||
|
||||
Assume point is at the beginning of the timestamp."
|
||||
(when (looking-at-p org-element--timestamp-regexp)
|
||||
|
@ -4312,19 +4312,29 @@ Assume point is at the beginning of the timestamp."
|
|||
(progn
|
||||
(looking-at org-element--timestamp-raw-value-regexp)
|
||||
(match-string-no-properties 0)))
|
||||
(date-start (match-string-no-properties 1))
|
||||
(date-end (match-string-no-properties 3))
|
||||
(diaryp (match-beginning 2))
|
||||
diary-sexp
|
||||
(date-start (if diaryp
|
||||
;; Only consider part after sexp for
|
||||
;; diary timestamps.
|
||||
(save-match-data
|
||||
(looking-at org-element--timestamp-regexp)
|
||||
(setq diary-sexp
|
||||
(buffer-substring-no-properties
|
||||
(+ 3 (match-beginning 0))
|
||||
(match-beginning 2)))
|
||||
(match-string 2))
|
||||
(match-string-no-properties 1)))
|
||||
(date-end (match-string-no-properties 3))
|
||||
(post-blank (progn (goto-char (match-end 0))
|
||||
(skip-chars-forward " \t")))
|
||||
(end (point))
|
||||
(time-range
|
||||
(and (not diaryp)
|
||||
(string-match
|
||||
"[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
|
||||
date-start)
|
||||
(cons (string-to-number (match-string 2 date-start))
|
||||
(string-to-number (match-string 3 date-start)))))
|
||||
(when (string-match
|
||||
"[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
|
||||
date-start)
|
||||
(cons (string-to-number (match-string 2 date-start))
|
||||
(string-to-number (match-string 3 date-start)))))
|
||||
(type (cond (diaryp 'diary)
|
||||
((and activep (or date-end time-range)) 'active-range)
|
||||
(activep 'active)
|
||||
|
@ -4395,6 +4405,17 @@ Assume point is at the beginning of the timestamp."
|
|||
day-end (or (nth 3 date) day-start)
|
||||
hour-end (or (nth 2 date) (car time-range) hour-start)
|
||||
minute-end (or (nth 1 date) (cdr time-range) minute-start))))
|
||||
;; Diary timestamp with time.
|
||||
(when (and diaryp
|
||||
(string-match "\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)?" date-start))
|
||||
(setq hour-start (match-string 1 date-start)
|
||||
minute-start (match-string 2 date-start)
|
||||
hour-end (match-string 4 date-start)
|
||||
minute-end (match-string 5 date-start))
|
||||
(when hour-start (setq hour-start (string-to-number hour-start)))
|
||||
(when minute-start (setq minute-start (string-to-number minute-start)))
|
||||
(when hour-end (setq hour-end (string-to-number hour-end)))
|
||||
(when minute-end (setq minute-end (string-to-number minute-end))))
|
||||
(org-element-create
|
||||
'timestamp
|
||||
(nconc (list :type type
|
||||
|
@ -4413,137 +4434,144 @@ Assume point is at the beginning of the timestamp."
|
|||
:begin begin
|
||||
:end end
|
||||
:post-blank post-blank)
|
||||
(and diary-sexp (list :diary-sexp diary-sexp))
|
||||
repeater-props
|
||||
warning-props))))))
|
||||
|
||||
(defun org-element-timestamp-interpreter (timestamp _)
|
||||
"Interpret TIMESTAMP object as Org syntax."
|
||||
(let((type (org-element-property :type timestamp)))
|
||||
(if (member type '(active inactive inactive-range active-range))
|
||||
(let ((day-start (org-element-property :day-start timestamp))
|
||||
(month-start (org-element-property :month-start timestamp))
|
||||
(year-start (org-element-property :year-start timestamp)))
|
||||
;; Return nil when start date is not available. Could also
|
||||
;; throw an error, but the current behavior is historical.
|
||||
(when (and day-start month-start year-start)
|
||||
(let* ((repeat-string
|
||||
(concat
|
||||
(pcase (org-element-property :repeater-type timestamp)
|
||||
(`cumulate "+") (`catch-up "++") (`restart ".+"))
|
||||
(let ((val (org-element-property :repeater-value timestamp)))
|
||||
(and val (number-to-string val)))
|
||||
(pcase (org-element-property :repeater-unit timestamp)
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))
|
||||
(when-let ((repeater-deadline-value
|
||||
(org-element-property :repeater-deadline-value timestamp))
|
||||
(repeater-deadline-unit
|
||||
(org-element-property :repeater-deadline-unit timestamp)))
|
||||
(concat
|
||||
"/"
|
||||
(number-to-string repeater-deadline-value)
|
||||
(pcase repeater-deadline-unit
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))))
|
||||
(range-type (org-element-property :range-type timestamp))
|
||||
(warning-string
|
||||
(concat
|
||||
(pcase (org-element-property :warning-type timestamp)
|
||||
(`first "--") (`all "-"))
|
||||
(let ((val (org-element-property :warning-value timestamp)))
|
||||
(and val (number-to-string val)))
|
||||
(pcase (org-element-property :warning-unit timestamp)
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))
|
||||
(hour-start (org-element-property :hour-start timestamp))
|
||||
(minute-start (org-element-property :minute-start timestamp))
|
||||
(brackets
|
||||
(if (member
|
||||
type
|
||||
'(inactive inactive-range))
|
||||
(cons "[" "]")
|
||||
(cons "<" ">")))
|
||||
(timestamp-end
|
||||
(concat
|
||||
(and (org-string-nw-p repeat-string) (concat " " repeat-string))
|
||||
(and (org-string-nw-p warning-string) (concat " " warning-string))
|
||||
(cdr brackets))))
|
||||
(concat
|
||||
;; Opening backet: [ or <
|
||||
(car brackets)
|
||||
;; Starting date/time: YYYY-MM-DD DAY[ HH:MM]
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-start hour-start)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
0 (or minute-start 0) (or hour-start 0)
|
||||
day-start month-start year-start))
|
||||
;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM]
|
||||
(let ((hour-end (org-element-property :hour-end timestamp))
|
||||
(minute-end (org-element-property :minute-end timestamp)))
|
||||
(pcase type
|
||||
((or `active `inactive)
|
||||
;; `org-element-timestamp-parser' uses this type
|
||||
;; when no time/date range is provided. So,
|
||||
;; should normally return nil in this clause.
|
||||
(pcase range-type
|
||||
(`nil
|
||||
;; `org-element-timestamp-parser' assigns end
|
||||
;; times for `active'/`inactive' TYPE if start
|
||||
;; time is not nil. But manually built
|
||||
;; timestamps may not contain end times, so
|
||||
;; check for end times anyway.
|
||||
(when (and hour-start hour-end minute-start minute-end
|
||||
(or (/= hour-start hour-end)
|
||||
(/= minute-start minute-end)))
|
||||
;; Could also throw an error. Return range
|
||||
;; timestamp nevertheless to preserve
|
||||
;; historical behavior.
|
||||
(format "-%02d:%02d" hour-end minute-end)))
|
||||
((or `timerange `daterange)
|
||||
(error "`:range-type' must be `nil' for `active'/`inactive' type"))))
|
||||
;; Range must be present.
|
||||
((or `active-range `inactive-range)
|
||||
(pcase range-type
|
||||
;; End time: -HH:MM.
|
||||
;; Fall back to start time if end time is not defined (arbitrary historical choice).
|
||||
;; Error will be thrown if both end and begin time is not defined.
|
||||
(`timerange (format "-%02d:%02d" (or hour-end hour-start) (or minute-end minute-start)))
|
||||
;; End date: TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM
|
||||
((or `daterange
|
||||
;; Should never happen in the output of `org-element-timestamp-parser'.
|
||||
;; Treat as an equivalent of `daterange' arbitrarily.
|
||||
`nil)
|
||||
(concat
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end
|
||||
"--" (car brackets)
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-end hour-end)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
;; Closing HH:MM missing is a valid scenario.
|
||||
0 (or minute-end 0) (or hour-end 0)
|
||||
;; YEAR/MONTH/DAY-END will always be present
|
||||
;; for `daterange' range-type, as parsed by
|
||||
;; `org-element-timestamp-parser'.
|
||||
;; For manually constructed timestamp
|
||||
;; object, arbitrarily fall back to starting
|
||||
;; date.
|
||||
(or (org-element-property :day-end timestamp) day-start)
|
||||
(or (org-element-property :month-end timestamp) month-start)
|
||||
(or (org-element-property :year-end timestamp) year-start)))))))))
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end))))
|
||||
;; diary type.
|
||||
(org-element-property :raw-value timestamp))))
|
||||
(let ((day-start (org-element-property :day-start timestamp))
|
||||
(month-start (org-element-property :month-start timestamp))
|
||||
(year-start (org-element-property :year-start timestamp)))
|
||||
;; Return nil when start date is not available. Could also
|
||||
;; throw an error, but the current behavior is historical.
|
||||
(when (or (and day-start month-start year-start)
|
||||
(eq type 'diary))
|
||||
(let* ((repeat-string
|
||||
(concat
|
||||
(pcase (org-element-property :repeater-type timestamp)
|
||||
(`cumulate "+") (`catch-up "++") (`restart ".+"))
|
||||
(let ((val (org-element-property :repeater-value timestamp)))
|
||||
(and val (number-to-string val)))
|
||||
(pcase (org-element-property :repeater-unit timestamp)
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))
|
||||
(when-let ((repeater-deadline-value
|
||||
(org-element-property :repeater-deadline-value timestamp))
|
||||
(repeater-deadline-unit
|
||||
(org-element-property :repeater-deadline-unit timestamp)))
|
||||
(concat
|
||||
"/"
|
||||
(number-to-string repeater-deadline-value)
|
||||
(pcase repeater-deadline-unit
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))))
|
||||
(range-type (org-element-property :range-type timestamp))
|
||||
(warning-string
|
||||
(concat
|
||||
(pcase (org-element-property :warning-type timestamp)
|
||||
(`first "--") (`all "-"))
|
||||
(let ((val (org-element-property :warning-value timestamp)))
|
||||
(and val (number-to-string val)))
|
||||
(pcase (org-element-property :warning-unit timestamp)
|
||||
(`hour "h") (`day "d") (`week "w") (`month "m") (`year "y"))))
|
||||
(hour-start (org-element-property :hour-start timestamp))
|
||||
(minute-start (org-element-property :minute-start timestamp))
|
||||
(brackets
|
||||
(if (member
|
||||
type
|
||||
'(inactive inactive-range))
|
||||
(cons "[" "]")
|
||||
;; diary as well
|
||||
(cons "<" ">")))
|
||||
(timestamp-end
|
||||
(concat
|
||||
(and (org-string-nw-p repeat-string) (concat " " repeat-string))
|
||||
(and (org-string-nw-p warning-string) (concat " " warning-string))
|
||||
(cdr brackets))))
|
||||
(concat
|
||||
;; Opening backet: [ or <
|
||||
(car brackets)
|
||||
;; Starting date/time: YYYY-MM-DD DAY[ HH:MM]
|
||||
(if (eq type 'diary)
|
||||
(concat
|
||||
"%%"
|
||||
(org-element-property :diary-sexp timestamp)
|
||||
(when (and minute-start hour-start)
|
||||
(format " %02d:%02d" hour-start minute-start)))
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-start hour-start)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
0 (or minute-start 0) (or hour-start 0)
|
||||
day-start month-start year-start)))
|
||||
;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM]
|
||||
(let ((hour-end (org-element-property :hour-end timestamp))
|
||||
(minute-end (org-element-property :minute-end timestamp)))
|
||||
(pcase type
|
||||
((or `active `inactive)
|
||||
;; `org-element-timestamp-parser' uses this type
|
||||
;; when no time/date range is provided. So,
|
||||
;; should normally return nil in this clause.
|
||||
(pcase range-type
|
||||
(`nil
|
||||
;; `org-element-timestamp-parser' assigns end
|
||||
;; times for `active'/`inactive' TYPE if start
|
||||
;; time is not nil. But manually built
|
||||
;; timestamps may not contain end times, so
|
||||
;; check for end times anyway.
|
||||
(when (and hour-start hour-end minute-start minute-end
|
||||
(or (/= hour-start hour-end)
|
||||
(/= minute-start minute-end)))
|
||||
;; Could also throw an error. Return range
|
||||
;; timestamp nevertheless to preserve
|
||||
;; historical behavior.
|
||||
(format "-%02d:%02d" hour-end minute-end)))
|
||||
((or `timerange `daterange)
|
||||
(error "`:range-type' must be `nil' for `active'/`inactive' type"))))
|
||||
;; Range must be present.
|
||||
((or `active-range `inactive-range
|
||||
(and `diary (guard (eq 'timerange range-type))))
|
||||
(pcase range-type
|
||||
;; End time: -HH:MM.
|
||||
;; Fall back to start time if end time is not defined (arbitrary historical choice).
|
||||
;; Error will be thrown if both end and begin time is not defined.
|
||||
(`timerange (format "-%02d:%02d" (or hour-end hour-start) (or minute-end minute-start)))
|
||||
;; End date: TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM
|
||||
((or `daterange
|
||||
;; Should never happen in the output of `org-element-timestamp-parser'.
|
||||
;; Treat as an equivalent of `daterange' arbitrarily.
|
||||
`nil)
|
||||
(concat
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end
|
||||
"--" (car brackets)
|
||||
(format-time-string
|
||||
;; `org-time-stamp-formats'.
|
||||
(org-time-stamp-format
|
||||
;; Ignore time unless both HH:MM are available.
|
||||
;; Ignore means (car org-timestamp-formats).
|
||||
(and minute-end hour-end)
|
||||
'no-brackets)
|
||||
(org-encode-time
|
||||
;; Closing HH:MM missing is a valid scenario.
|
||||
0 (or minute-end 0) (or hour-end 0)
|
||||
;; YEAR/MONTH/DAY-END will always be present
|
||||
;; for `daterange' range-type, as parsed by
|
||||
;; `org-element-timestamp-parser'.
|
||||
;; For manually constructed timestamp
|
||||
;; object, arbitrarily fall back to starting
|
||||
;; date.
|
||||
(or (org-element-property :day-end timestamp) day-start)
|
||||
(or (org-element-property :month-end timestamp) month-start)
|
||||
(or (org-element-property :year-end timestamp) year-start)))))))))
|
||||
;; repeater + warning + closing > or ]
|
||||
;; This info is duplicated in date ranges.
|
||||
timestamp-end))))))
|
||||
;;;; Underline
|
||||
|
||||
(defun org-element-underline-parser ()
|
||||
|
@ -8292,8 +8320,11 @@ the cache."
|
|||
;; Reached LIMIT-COUNT. Abort.
|
||||
(when (and limit-count
|
||||
(>= count-predicate-calls-match
|
||||
limit-count))
|
||||
(cache-walk-abort)))
|
||||
limit-count))
|
||||
(cache-walk-abort))
|
||||
;; Make sure that we have a cached
|
||||
;; element at the new STAR.
|
||||
(when start (element-match-at-point)))
|
||||
;; Check if the buffer or cache has been modified.
|
||||
(unless (org-with-base-buffer nil
|
||||
(and (eq modified-tic org-element--cache-change-tic)
|
||||
|
|
|
@ -705,7 +705,7 @@ The folding spec properties will be set to PROPERTIES (see
|
|||
(let* ((full-properties (mapcar (lambda (prop) (cons prop (cdr (assq prop properties))))
|
||||
'( :visible :ellipsis :isearch-ignore
|
||||
:global :isearch-open :front-sticky
|
||||
:rear-sticky :fragile :alias)))
|
||||
:rear-sticky :fragile :alias :font-lock)))
|
||||
(full-spec (cons spec full-properties)))
|
||||
(add-to-list 'org-fold-core--specs full-spec append)
|
||||
(mapc (lambda (prop-cons) (org-fold-core-set-folding-spec-property spec (car prop-cons) (cdr prop-cons) 'force)) full-properties)
|
||||
|
@ -1094,8 +1094,7 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
|
|||
(overlay-put o 'invisible spec)
|
||||
;; Preserve priority.
|
||||
(overlay-put o 'priority (length (member spec (org-fold-core-folding-spec-list))))
|
||||
(overlay-put o 'isearch-open-invisible #'org-fold-core--isearch-show)
|
||||
(overlay-put o 'isearch-open-invisible-temporary #'org-fold-core--isearch-show-temporary))
|
||||
(overlay-put o 'isearch-open-invisible #'org-fold-core--isearch-show))
|
||||
(put-text-property from to (org-fold-core--property-symbol-get-create spec) spec)
|
||||
(put-text-property from to 'isearch-open-invisible #'org-fold-core--isearch-show)
|
||||
(put-text-property from to 'isearch-open-invisible-temporary #'org-fold-core--isearch-show-temporary)
|
||||
|
@ -1246,7 +1245,9 @@ This function is intended to be used as `isearch-filter-predicate'."
|
|||
;; FIXME: Reveal the match (usually point, but may sometimes go beyond the region).
|
||||
(when (< beg (point) end)
|
||||
(funcall org-fold-core-isearch-open-function (point)))
|
||||
(org-fold-core-region beg end nil)))
|
||||
(if (overlayp overlay-or-region)
|
||||
(delete-overlay overlay-or-region)
|
||||
(org-fold-core-region beg end nil))))
|
||||
|
||||
(defun org-fold-core--isearch-show-temporary (region hide-p)
|
||||
"Temporarily reveal text in REGION.
|
||||
|
|
|
@ -49,8 +49,6 @@
|
|||
(require 'org-fold-core)
|
||||
|
||||
(defvar org-inlinetask-min-level)
|
||||
(defvar org-link--link-folding-spec)
|
||||
(defvar org-link--description-folding-spec)
|
||||
(defvar org-odd-levels-only)
|
||||
(defvar org-drawer-regexp)
|
||||
(defvar org-property-end-re)
|
||||
|
@ -280,9 +278,7 @@ Also, see `org-fold-catch-invisible-edits'."
|
|||
(:isearch-open . t)
|
||||
(:font-lock . t)
|
||||
(:front-sticky . t)
|
||||
(:alias . (drawer property-drawer)))
|
||||
,org-link--description-folding-spec
|
||||
,org-link--link-folding-spec)))
|
||||
(:alias . (drawer property-drawer))))))
|
||||
|
||||
;;;; Searching and examining folded text
|
||||
|
||||
|
@ -679,19 +675,12 @@ DETAIL is either nil, `minimal', `local', `ancestors',
|
|||
(org-with-point-at (car region)
|
||||
(forward-line 0)
|
||||
(let (font-lock-extend-region-functions)
|
||||
(font-lock-fontify-region (max (point-min) (1- (car region))) (cdr region))))))
|
||||
;; Unfold links.
|
||||
(let (region)
|
||||
(dolist (spec '(org-link org-link-description))
|
||||
(setq region (org-fold-get-region-at-point spec))
|
||||
(when region (org-fold-region (car region) (cdr region) nil spec)))))
|
||||
(font-lock-fontify-region (max (point-min) (1- (car region))) (cdr region)))))))
|
||||
(let (region)
|
||||
(dolist (spec (org-fold-core-folding-spec-list))
|
||||
;; Links are taken care by above.
|
||||
(unless (memq spec '(org-link org-link-description))
|
||||
(setq region (org-fold-get-region-at-point spec))
|
||||
(when region
|
||||
(org-fold-region (car region) (cdr region) nil spec))))))
|
||||
(setq region (org-fold-get-region-at-point spec))
|
||||
(when region
|
||||
(org-fold-region (car region) (cdr region) nil spec)))))
|
||||
(unless (org-before-first-heading-p)
|
||||
(org-with-limited-levels
|
||||
(cl-case detail
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2979,7 +2979,7 @@ function is being called interactively."
|
|||
(error "Missing key extractor"))))
|
||||
(sort-func
|
||||
(cond
|
||||
((= dcst ?a) #'string-collate-lessp)
|
||||
((= dcst ?a) #'org-string<)
|
||||
((= dcst ?f)
|
||||
(or compare-func
|
||||
(and interactive?
|
||||
|
|
353
lisp/org-macs.el
353
lisp/org-macs.el
|
@ -113,7 +113,6 @@ Version mismatch is commonly encountered in the following situations:
|
|||
(declare-function org-fold-next-visibility-change "org-fold" (&optional pos limit ignore-hidden-p previous-p))
|
||||
(declare-function org-fold-core-with-forced-fontification "org-fold" (&rest body))
|
||||
(declare-function org-fold-folded-p "org-fold" (&optional pos limit ignore-hidden-p previous-p))
|
||||
(declare-function string-collate-lessp "org-compat" (s1 s2 &optional locale ignore-case))
|
||||
(declare-function org-time-convert-to-list "org-compat" (time))
|
||||
(declare-function org-buffer-text-pixel-width "org-compat" ())
|
||||
|
||||
|
@ -368,278 +367,6 @@ If EXCLUDE-TMP is non-nil, ignore temporary buffers."
|
|||
nil))
|
||||
(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
|
||||
|
@ -1254,20 +981,67 @@ return nil."
|
|||
|
||||
;;; String manipulation
|
||||
|
||||
(defun org-string< (a b)
|
||||
(string-collate-lessp a b))
|
||||
(defcustom org-sort-function #'string-collate-lessp
|
||||
"Function used to compare strings when sorting.
|
||||
This function affects how Org mode sorts headlines, agenda items,
|
||||
table lines, etc.
|
||||
|
||||
(defun org-string<= (a b)
|
||||
(or (string= a b) (string-collate-lessp a b)))
|
||||
The function must accept either 2 or 4 arguments: strings to compare
|
||||
and, optionally, LOCALE and IGNORE-CASE - locale name and flag to make
|
||||
comparison case-insensitive.
|
||||
|
||||
(defun org-string>= (a b)
|
||||
(not (string-collate-lessp a b)))
|
||||
The default value uses sorting rules according to OS language. Users
|
||||
who want to make sorting language-independent, may customize the value
|
||||
to `org-sort-function-fallback'.
|
||||
|
||||
(defun org-string> (a b)
|
||||
Note that some string sorting rules are known to be not accurate on
|
||||
MacOS. See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=59275.
|
||||
MacOS users may customize the value to
|
||||
`org-sort-function-fallback'."
|
||||
:group 'org
|
||||
:package-version '(Org . "9.7")
|
||||
:type '(choice
|
||||
(const :tag "According to OS language" string-collate-lessp)
|
||||
(const :tag "Using string comparison" org-sort-function-fallback)
|
||||
(function :tag "Custom function")))
|
||||
|
||||
(defun org-sort-function-fallback (a b &optional _ ignore-case)
|
||||
"Return non-nil when downcased string A < string B.
|
||||
Use `compare-strings' for comparison. Honor IGNORE-CASE."
|
||||
(let ((ans (compare-strings a nil nil b nil nil ignore-case)))
|
||||
(cond
|
||||
((and (numberp ans) (< ans 0)) t)
|
||||
(t nil))))
|
||||
|
||||
(defun org-string< (a b &optional locale ignore-case)
|
||||
"Return non-nil when string A < string B.
|
||||
LOCALE is the locale name. IGNORE-CASE, when non-nil, makes comparison
|
||||
ignore case."
|
||||
(if (= 4 (cdr (func-arity org-sort-function)))
|
||||
(funcall org-sort-function a b locale ignore-case)
|
||||
(funcall org-sort-function a b)))
|
||||
|
||||
(defun org-string<= (a b &optional locale ignore-case)
|
||||
"Return non-nil when string A <= string B.
|
||||
LOCALE is the locale name. IGNORE-CASE, when non-nil, makes comparison
|
||||
ignore case."
|
||||
(or (string= a b) (org-string< a b locale ignore-case)))
|
||||
|
||||
(defun org-string>= (a b &optional locale ignore-case)
|
||||
"Return non-nil when string A >= string B.
|
||||
LOCALE is the locale name. IGNORE-CASE, when non-nil, makes comparison
|
||||
ignore case."
|
||||
(not (org-string< a b locale ignore-case)))
|
||||
|
||||
(defun org-string> (a b &optional locale ignore-case)
|
||||
"Return non-nil when string A > string B.
|
||||
LOCALE is the locale name. IGNORE-CASE, when non-nil, makes comparison
|
||||
ignore case."
|
||||
(and (not (string= a b))
|
||||
(not (string-collate-lessp a b))))
|
||||
(not (org-string< a b locale ignore-case))))
|
||||
|
||||
(defun org-string<> (a b)
|
||||
"Return non-nil when string A and string B are not equal."
|
||||
(not (string= a b)))
|
||||
|
||||
(defsubst org-trim (s &optional keep-lead)
|
||||
|
@ -2016,6 +1790,23 @@ indirectly called by the latter."
|
|||
(prog1 nil
|
||||
(set-window-configuration window-configuration)))))
|
||||
|
||||
(defun org-display-buffer-in-window (buffer alist)
|
||||
"Display BUFFER in specific window.
|
||||
The window is defined according to the `window' slot in the ALIST.
|
||||
Then `same-frame' slot in the ALIST is set, only display buffer when
|
||||
window is present in the current frame.
|
||||
|
||||
This is an action function for buffer display, see Info
|
||||
node `(elisp) Buffer Display Action Functions'. It should be
|
||||
called only by `display-buffer' or a function directly or
|
||||
indirectly called by the latter."
|
||||
(let ((window (alist-get 'window alist)))
|
||||
(when (and window
|
||||
(window-live-p window)
|
||||
(or (not (alist-get 'same-frame alist))
|
||||
(eq (window-frame) (window-frame window))))
|
||||
(window--display-buffer buffer window 'reuse alist))))
|
||||
|
||||
(provide 'org-macs)
|
||||
|
||||
;; Local variables:
|
||||
|
|
|
@ -1057,7 +1057,7 @@ be returned that indicates what went wrong."
|
|||
(goto-char (match-beginning 4))
|
||||
(insert new)
|
||||
(delete-region (point) (+ (point) (length current)))
|
||||
(org-align-tags))
|
||||
(when org-auto-align-tags (org-align-tags)))
|
||||
(t
|
||||
(error
|
||||
"Heading changed in the mobile device and on the computer")))))))
|
||||
|
|
|
@ -426,13 +426,14 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
|
|||
(append
|
||||
(let ((tags (org-get-tags nil t)))
|
||||
(org-mouse-keyword-menu
|
||||
(sort (mapcar #'car (org-get-buffer-tags)) #'string-lessp)
|
||||
(sort (mapcar #'car (org-get-buffer-tags))
|
||||
(or org-tags-sort-function #'org-string<))
|
||||
(lambda (tag)
|
||||
(org-mouse-set-tags
|
||||
(sort (if (member tag tags)
|
||||
(delete tag tags)
|
||||
(cons tag tags))
|
||||
#'string-lessp)))
|
||||
(or org-tags-sort-function #'org-string<))))
|
||||
(lambda (tag) (member tag tags))
|
||||
))
|
||||
'("--"
|
||||
|
@ -473,7 +474,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
|
|||
(sort (if (member ',name ',options)
|
||||
(delete ',name ',options)
|
||||
(cons ',name ',options))
|
||||
'string-lessp)
|
||||
#'org-string<)
|
||||
" ")
|
||||
nil nil nil 1)
|
||||
(when (functionp ',function) (funcall ',function)))
|
||||
|
@ -502,7 +503,8 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
|
|||
["Check TODOs" org-show-todo-tree t]
|
||||
("Check Tags"
|
||||
,@(org-mouse-keyword-menu
|
||||
(sort (mapcar #'car (org-get-buffer-tags)) #'string-lessp)
|
||||
(sort (mapcar #'car (org-get-buffer-tags))
|
||||
(or org-tags-sort-function #'org-string<))
|
||||
(lambda (tag) (org-tags-sparse-tree nil tag)))
|
||||
"--"
|
||||
["Custom Tag ..." org-tags-sparse-tree t])
|
||||
|
@ -512,7 +514,8 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
|
|||
["Display TODO List" org-todo-list t]
|
||||
("Display Tags"
|
||||
,@(org-mouse-keyword-menu
|
||||
(sort (mapcar #'car (org-get-buffer-tags)) #'string-lessp)
|
||||
(sort (mapcar #'car (org-get-buffer-tags))
|
||||
(or org-tags-sort-function #'org-string<))
|
||||
(lambda (tag) (org-tags-view nil tag)))
|
||||
"--"
|
||||
["Custom Tag ..." org-tags-view t])
|
||||
|
|
|
@ -602,27 +602,22 @@ MISC, if non-nil will be appended to the collection. It must be a plist."
|
|||
|
||||
;;;; 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)
|
||||
"Normalize CONTAINER representation into (type . settings).
|
||||
|
||||
When INNER is non-nil, do not try to match as list of containers."
|
||||
(if org-persist--inhibit-container-normalization
|
||||
container
|
||||
(pcase container
|
||||
((or `elisp `elisp-data `version `file `index `url)
|
||||
`(,container nil))
|
||||
((or (pred keywordp) (pred stringp) `(quote . ,_))
|
||||
`(elisp-data ,container))
|
||||
((pred symbolp)
|
||||
`(elisp ,container))
|
||||
(`(,(or `elisp `elisp-data `version `file `index `url) . ,_)
|
||||
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)))))
|
||||
(pcase container
|
||||
((or `elisp `elisp-data `version `file `index `url)
|
||||
`(,container nil))
|
||||
((or (pred keywordp) (pred stringp) `(quote . ,_))
|
||||
`(elisp-data ,container))
|
||||
((pred symbolp)
|
||||
`(elisp ,container))
|
||||
(`(,(or `elisp `elisp-data `version `file `index `url) . ,_)
|
||||
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)
|
||||
"Buffer hash cache.")
|
||||
|
@ -828,23 +823,20 @@ COLLECTION is the plist holding data collection."
|
|||
(defun org-persist-write:file (c collection)
|
||||
"Write file container C according to COLLECTION."
|
||||
(org-persist-collection-let collection
|
||||
(if (or (and path (file-exists-p path))
|
||||
(and (stringp (cadr c)) (file-exists-p (cadr c))))
|
||||
(progn
|
||||
(when (and (stringp (cadr c)) (file-exists-p (cadr c)))
|
||||
(setq path (cadr c)))
|
||||
(let* ((persist-file (plist-get collection :persist-file))
|
||||
(ext (file-name-extension path))
|
||||
(file-copy (org-file-name-concat
|
||||
org-persist-directory
|
||||
(format "%s-%s.%s" persist-file (md5 path) ext))))
|
||||
(unless (file-exists-p file-copy)
|
||||
(unless (file-exists-p (file-name-directory file-copy))
|
||||
(make-directory (file-name-directory file-copy) t))
|
||||
(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)))))
|
||||
(when (or (and path (file-exists-p path))
|
||||
(and (stringp (cadr c)) (file-exists-p (cadr c))))
|
||||
(when (and (stringp (cadr c)) (file-exists-p (cadr c)))
|
||||
(setq path (cadr c)))
|
||||
(let* ((persist-file (plist-get collection :persist-file))
|
||||
(ext (file-name-extension path))
|
||||
(file-copy (org-file-name-concat
|
||||
org-persist-directory
|
||||
(format "%s-%s.%s" persist-file (md5 path) ext))))
|
||||
(unless (file-exists-p file-copy)
|
||||
(unless (file-exists-p (file-name-directory file-copy))
|
||||
(make-directory (file-name-directory file-copy) t))
|
||||
(copy-file path file-copy 'overwrite))
|
||||
(format "%s-%s.%s" persist-file (md5 path) ext)))))
|
||||
|
||||
(defun org-persist-write:url (c collection)
|
||||
"Write url container C according to COLLECTION."
|
||||
|
@ -958,27 +950,26 @@ VALUE pairs.
|
|||
When WRITE-IMMEDIATELY is non-nil, the return value will be the same
|
||||
with `org-persist-write'."
|
||||
(unless org-persist--index (org-persist--load-index))
|
||||
(let ((container (org-persist--normalize-container container))
|
||||
(inherit (and inherit (org-persist--normalize-container inherit)))
|
||||
(org-persist--inhibit-container-normalization t))
|
||||
(when inherit
|
||||
(let ((inherited-collection (org-persist--get-collection inherit associated))
|
||||
new-collection)
|
||||
(unless (member container (plist-get inherited-collection :container))
|
||||
(setq new-collection
|
||||
(plist-put (copy-sequence inherited-collection) :container
|
||||
(cons container (plist-get inherited-collection :container))))
|
||||
(org-persist--remove-from-index inherited-collection)
|
||||
(org-persist--add-to-index new-collection))))
|
||||
(let ((collection (org-persist--get-collection container associated misc)))
|
||||
(when (and expiry (not inherit))
|
||||
(when expiry (plist-put collection :expiry expiry))))
|
||||
(when (or (bufferp associated) (bufferp (plist-get associated :buffer)))
|
||||
(with-current-buffer (if (bufferp associated)
|
||||
associated
|
||||
(plist-get associated :buffer))
|
||||
(add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local)))
|
||||
(when write-immediately (org-persist-write container associated))))
|
||||
(setq container (org-persist--normalize-container container))
|
||||
(when inherit
|
||||
(setq inherit (org-persist--normalize-container inherit))
|
||||
(let ((inherited-collection (org-persist--get-collection inherit associated))
|
||||
new-collection)
|
||||
(unless (member container (plist-get inherited-collection :container))
|
||||
(setq new-collection
|
||||
(plist-put (copy-sequence inherited-collection) :container
|
||||
(cons container (plist-get inherited-collection :container))))
|
||||
(org-persist--remove-from-index inherited-collection)
|
||||
(org-persist--add-to-index new-collection))))
|
||||
(let ((collection (org-persist--get-collection container associated misc)))
|
||||
(when (and expiry (not inherit))
|
||||
(when expiry (plist-put collection :expiry expiry))))
|
||||
(when (or (bufferp associated) (bufferp (plist-get associated :buffer)))
|
||||
(with-current-buffer (if (bufferp associated)
|
||||
associated
|
||||
(plist-get associated :buffer))
|
||||
(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)
|
||||
"Unregister CONTAINER in ASSOCIATED to be persistent.
|
||||
|
@ -1093,9 +1084,6 @@ have the same meaning as in `org-persist-read'."
|
|||
"Call `org-persist-load-all' in 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)
|
||||
"Save CONTAINER according to ASSOCIATED.
|
||||
ASSOCIATED can be a plist, a buffer, or a string.
|
||||
|
@ -1105,33 +1093,31 @@ The return value is nil when writing fails and the written value (as
|
|||
returned by `org-persist-read') on success.
|
||||
When IGNORE-RETURN is non-nil, just return t on success without calling
|
||||
`org-persist-read'."
|
||||
(unless org-persist--inhibit-write
|
||||
(setq associated (org-persist--normalize-associated associated))
|
||||
;; Update hash
|
||||
(when (and (plist-get associated :file)
|
||||
(plist-get associated :hash)
|
||||
(get-file-buffer (plist-get associated :file)))
|
||||
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
|
||||
(let ((collection (org-persist--get-collection container associated))
|
||||
(org-persist--inhibit-write t))
|
||||
(setf collection (plist-put collection :associated associated))
|
||||
(unless (or
|
||||
;; Prevent data leakage from encrypted files.
|
||||
;; We do it in somewhat paranoid manner and do not
|
||||
;; allow anything related to encrypted files to be
|
||||
;; written.
|
||||
(and (plist-get associated :file)
|
||||
(string-match-p epa-file-name-regexp (plist-get associated :file)))
|
||||
(cl-some (lambda (v)
|
||||
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
|
||||
(plist-get collection :container)))
|
||||
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
|
||||
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
|
||||
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
|
||||
(plist-get collection :container))))
|
||||
(puthash file data org-persist--write-cache)
|
||||
(org-persist--write-elisp-file file data)
|
||||
(or ignore-return (org-persist-read container associated))))))))
|
||||
(setq associated (org-persist--normalize-associated associated))
|
||||
;; Update hash
|
||||
(when (and (plist-get associated :file)
|
||||
(plist-get associated :hash)
|
||||
(get-file-buffer (plist-get associated :file)))
|
||||
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
|
||||
(let ((collection (org-persist--get-collection container associated)))
|
||||
(setf collection (plist-put collection :associated associated))
|
||||
(unless (or
|
||||
;; Prevent data leakage from encrypted files.
|
||||
;; We do it in somewhat paranoid manner and do not
|
||||
;; allow anything related to encrypted files to be
|
||||
;; written.
|
||||
(and (plist-get associated :file)
|
||||
(string-match-p epa-file-name-regexp (plist-get associated :file)))
|
||||
(seq-find (lambda (v)
|
||||
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
|
||||
(plist-get collection :container)))
|
||||
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
|
||||
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
|
||||
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
|
||||
(plist-get collection :container))))
|
||||
(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)
|
||||
"Save all the persistent data.
|
||||
|
|
|
@ -754,8 +754,6 @@ as `org-src-fontify-natively' is non-nil."
|
|||
(s (and b (make-string (* (- e b) native-tab-width) ? ))))
|
||||
(when (and b (< b e)) (add-text-properties b e `(display ,s)))
|
||||
(forward-char)))))
|
||||
;; Clear abbreviated link folding.
|
||||
(org-fold-region start end nil 'org-link)
|
||||
(add-text-properties
|
||||
start end
|
||||
'(font-lock-fontified t fontified t font-lock-multiline t))
|
||||
|
@ -805,42 +803,9 @@ as `org-src-fontify-natively' is non-nil."
|
|||
(font-lock-append-text-property
|
||||
(1+ pt) (1- (point)) 'face 'org-inline-src-block)))
|
||||
(add-face-text-property (1- (point)) (point) '(org-inline-src-block shadow))
|
||||
(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))))
|
||||
(setq pt (point)))))
|
||||
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
|
||||
|
||||
|
|
|
@ -892,7 +892,10 @@ nil When nil, the command tries to be smart and figure out the
|
|||
separator in the following way:
|
||||
- when each line contains a TAB, assume TAB-separated material
|
||||
- when each line contains a comma, assume CSV material
|
||||
- else, assume one or more SPACE characters as separator."
|
||||
- else, assume one or more SPACE characters as separator.
|
||||
`babel-auto'
|
||||
Use the same rules as nil, but do not try any separator when
|
||||
the region contains a single line and has no commas or tabs."
|
||||
(interactive "r\nP")
|
||||
(let* ((beg (min beg0 end0))
|
||||
(end (max beg0 end0))
|
||||
|
@ -909,12 +912,15 @@ nil When nil, the command tries to be smart and figure out the
|
|||
(if (bolp) (backward-char 1) (end-of-line 1))
|
||||
(setq end (point-marker))
|
||||
;; Get the right field separator
|
||||
(unless separator
|
||||
(when (or (not separator) (eq separator 'babel-auto))
|
||||
(goto-char beg)
|
||||
(setq separator
|
||||
(cond
|
||||
((not (re-search-forward "^[^\n\t]+$" end t)) '(16))
|
||||
((not (re-search-forward "^[^\n,]+$" end t)) '(4))
|
||||
((not (save-excursion (re-search-forward "^[^\n\t]+$" end t))) '(16))
|
||||
((not (save-excursion (re-search-forward "^[^\n,]+$" end t))) '(4))
|
||||
((and (eq separator 'babel-auto)
|
||||
(= 1 (count-lines beg end)))
|
||||
(rx unmatchable))
|
||||
(t 1))))
|
||||
(goto-char beg)
|
||||
(if (equal separator '(4))
|
||||
|
@ -2881,15 +2887,15 @@ list, `literal' is for the format specifier L."
|
|||
(if lispp
|
||||
(if (eq lispp 'literal)
|
||||
elements
|
||||
(if (and (eq elements "") (not keep-empty))
|
||||
;; FIXME: This branch of `if' is never used because
|
||||
;; strings are never `eq' here. But changing to
|
||||
;; `equal' breaks tests.
|
||||
;; See
|
||||
;; https://list.orgmode.org/orgmode/20230827214320.46754-1-salutis@me.com/
|
||||
""
|
||||
(prin1-to-string
|
||||
(if numbers (string-to-number elements) elements))))
|
||||
;; Ignore KEEP-EMPTY here.
|
||||
;; When ELEMENTS="" and NUMBERS=t, (string-to-number "")
|
||||
;; returns 0 - consistent with (0) for Calc branch.
|
||||
;; When ELEMENTS="" and NUMBERS=nil, `prin1-to-string' will
|
||||
;; return "\"\"" - historical behavior that also does not
|
||||
;; leave missing arguments in formulas like (string< $1 $2)
|
||||
;; when $2 cell is empty.
|
||||
(prin1-to-string
|
||||
(if numbers (string-to-number elements) elements)))
|
||||
(if (string-match "\\S-" elements)
|
||||
(progn
|
||||
(when numbers (setq elements (number-to-string
|
||||
|
@ -4631,8 +4637,8 @@ function is being called interactively."
|
|||
(predicate
|
||||
(cl-case sorting-type
|
||||
((?n ?N ?t ?T) #'<)
|
||||
((?a ?A) (if with-case #'string-collate-lessp
|
||||
(lambda (s1 s2) (string-collate-lessp s1 s2 nil t))))
|
||||
((?a ?A) (if with-case #'org-string<
|
||||
(lambda (s1 s2) (org-string< s1 s2 nil t))))
|
||||
((?f ?F)
|
||||
(or compare-func
|
||||
(and interactive?
|
||||
|
|
1156
lisp/org.el
1156
lisp/org.el
File diff suppressed because it is too large
Load Diff
|
@ -954,14 +954,15 @@ channel."
|
|||
;; Only links with a description need an entry. Other are
|
||||
;; already handled in `org-ascii-link'.
|
||||
(when description
|
||||
(let ((dest (if (equal type "fuzzy")
|
||||
(org-export-resolve-fuzzy-link link info)
|
||||
;; Ignore broken links. On broken link,
|
||||
;; `org-export-resolve-id-link' will throw an
|
||||
;; error and we will return nil.
|
||||
(condition-case nil
|
||||
(org-export-resolve-id-link link info)
|
||||
(org-link-broken nil)))))
|
||||
(let ((dest
|
||||
;; Ignore broken links. On broken link,
|
||||
;; `org-export-resolve-id-link' will throw an
|
||||
;; error and we will return nil.
|
||||
(condition-case nil
|
||||
(if (equal type "fuzzy")
|
||||
(org-export-resolve-fuzzy-link link info)
|
||||
(org-export-resolve-id-link link info))
|
||||
(org-link-broken nil))))
|
||||
(when dest
|
||||
(concat
|
||||
(org-ascii--fill-string
|
||||
|
|
|
@ -860,7 +860,9 @@ holding export options."
|
|||
;; Timestamp.
|
||||
(and (plist-get info :time-stamp-file)
|
||||
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
|
||||
;; Document class, packages, and some configuration.
|
||||
;; LaTeX compiler
|
||||
(org-latex--insert-compiler info)
|
||||
;; Document class and packages.
|
||||
(org-latex-make-preamble info)
|
||||
;; Define the alternative frame environment, if needed.
|
||||
(when (plist-get info :beamer-define-frame)
|
||||
|
@ -913,6 +915,12 @@ holding export options."
|
|||
(let ((template (plist-get info :latex-hyperref-template)))
|
||||
(and (stringp template)
|
||||
(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.
|
||||
"\\begin{document}\n\n"
|
||||
;; Title command.
|
||||
|
|
433
lisp/ox-html.el
433
lisp/ox-html.el
|
@ -39,8 +39,6 @@
|
|||
(require 'ox)
|
||||
(require 'ox-publish)
|
||||
(require 'table)
|
||||
(require 'org-latex-preview)
|
||||
(require 'ox-mathml)
|
||||
|
||||
|
||||
;;; Function Declarations
|
||||
|
@ -109,8 +107,7 @@
|
|||
(verbatim . org-html-verbatim)
|
||||
(verse-block . org-html-verse-block))
|
||||
:filters-alist '((:filter-options . org-html-infojs-install-script)
|
||||
(:filter-parse-tree org-html-image-link-filter
|
||||
org-html-prepare-latex-images)
|
||||
(:filter-parse-tree . org-html-image-link-filter)
|
||||
(:filter-final-output . org-html-final-function))
|
||||
:menu-entry
|
||||
'(?h "Export to HTML"
|
||||
|
@ -158,7 +155,6 @@
|
|||
(:html-infojs-template nil nil org-html-infojs-template)
|
||||
(: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-latex-image-options nil nil org-html-latex-image-options)
|
||||
(:html-mathjax-options nil nil org-html-mathjax-options)
|
||||
(:html-mathjax-template nil nil org-html-mathjax-template)
|
||||
(:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format)
|
||||
|
@ -323,7 +319,7 @@ This affects IDs that are determined from the ID property.")
|
|||
pre.src-awk:before { content: 'Awk'; }
|
||||
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-clojure:before { content: 'Clojure'; }
|
||||
pre.src-css:before { content: 'CSS'; }
|
||||
pre.src-D:before { content: 'D'; }
|
||||
|
@ -826,7 +822,7 @@ e.g. \"tex:mathjax\". Allowed values are:
|
|||
be loaded.
|
||||
`html' Use `org-latex-to-html-convert-command' to convert
|
||||
LaTeX fragments to HTML.
|
||||
SYMBOL Any symbol defined in `org-latex-preview-process-alist',
|
||||
SYMBOL Any symbol defined in `org-preview-latex-process-alist',
|
||||
e.g., `dvipng'."
|
||||
:group 'org-export-html
|
||||
:version "24.4"
|
||||
|
@ -1172,27 +1168,6 @@ See `format-time-string' for more information on its components."
|
|||
:package-version '(Org . "8.0")
|
||||
: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.
|
||||
This is only applied when used along with svg, as in
|
||||
(svg svg-embed)."
|
||||
:group 'org-export-html
|
||||
:package-version '(Org . "9.7")
|
||||
:type 'plist)
|
||||
|
||||
;;;; Template :: Mathjax
|
||||
|
||||
(defcustom org-html-mathjax-options
|
||||
|
@ -1678,33 +1653,6 @@ https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag"
|
|||
:package-version '(Org . "9.1")
|
||||
: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 (shell-escaped).
|
||||
It must not be used inside a quoted argument, the result of %i
|
||||
expansion inside a quoted argument is undefined.
|
||||
|
||||
For example, this could be used with LaTeXML as
|
||||
\"latexmlc literal:%i --profile=math --preload=siunitx.sty 2>/dev/null\"."
|
||||
: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
|
||||
|
||||
|
@ -1740,7 +1688,6 @@ INFO is the current state of the export process, as a plist."
|
|||
"Return close-tag for string TAG.
|
||||
ATTR specifies additional attributes. INFO is a property list
|
||||
containing current export state."
|
||||
(declare (indent 1))
|
||||
(concat "<" tag
|
||||
(org-string-nw-p (concat " " attr))
|
||||
(if (org-html-xhtml-p info) " />" ">")))
|
||||
|
@ -1830,7 +1777,11 @@ a communication channel."
|
|||
(org-html--make-attribute-string
|
||||
(org-combine-plists
|
||||
(list :src source
|
||||
:alt (file-name-nondirectory source))
|
||||
:alt (if (string-match-p
|
||||
(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))
|
||||
(org-combine-plists '(:class "org-svg") attributes '(:fallback nil))
|
||||
attributes)))
|
||||
|
@ -1922,36 +1873,43 @@ INFO is a plist used as a communication channel."
|
|||
(pcase (org-export-collect-footnote-definitions info)
|
||||
(`nil nil)
|
||||
(definitions
|
||||
(format
|
||||
(plist-get info :html-footnotes-section)
|
||||
(org-html--translate "Footnotes" info)
|
||||
(format
|
||||
(plist-get info :html-footnotes-section)
|
||||
(org-html--translate "Footnotes" info)
|
||||
(format
|
||||
"\n%s\n"
|
||||
(mapconcat
|
||||
(lambda (definition)
|
||||
(pcase definition
|
||||
(`(,n ,_ ,def)
|
||||
;; `org-export-collect-footnote-definitions' can return
|
||||
;; two kinds of footnote definitions: inline and blocks.
|
||||
;; Since this should not make any difference in the HTML
|
||||
;; output, we wrap the inline definitions within
|
||||
;; a "footpara" class paragraph.
|
||||
(let ((inline? (not (org-element-map def org-element-all-elements
|
||||
#'identity nil t)))
|
||||
(anchor (org-html--anchor
|
||||
(format "fn.%d" n)
|
||||
n
|
||||
(format " class=\"footnum\" href=\"#fnr.%d\" role=\"doc-backlink\"" n)
|
||||
info))
|
||||
(contents (org-trim (org-export-data def info))))
|
||||
(format "<div class=\"footdef\">%s %s</div>\n"
|
||||
(format (plist-get info :html-footnote-format) anchor)
|
||||
(format "<div class=\"footpara\" role=\"doc-footnote\">%s</div>"
|
||||
(if (not inline?) contents
|
||||
(format "<p class=\"footpara\">%s</p>"
|
||||
contents))))))))
|
||||
definitions
|
||||
"\n"))))))
|
||||
"\n%s\n"
|
||||
(mapconcat
|
||||
(lambda (definition)
|
||||
(pcase definition
|
||||
(`(,n ,label ,def)
|
||||
;; Do not assign number labels as they appear in Org mode
|
||||
;; - the footnotes are re-numbered by
|
||||
;; `org-export-get-footnote-number'. If the label is not
|
||||
;; a number, keep it.
|
||||
(when (and (stringp label)
|
||||
(equal label (number-to-string (string-to-number label))))
|
||||
(setq label nil))
|
||||
;; `org-export-collect-footnote-definitions' can return
|
||||
;; two kinds of footnote definitions: inline and blocks.
|
||||
;; Since this should not make any difference in the HTML
|
||||
;; output, we wrap the inline definitions within
|
||||
;; a "footpara" class paragraph.
|
||||
(let ((inline? (not (org-element-map def org-element-all-elements
|
||||
#'identity nil t)))
|
||||
(anchor (org-html--anchor
|
||||
(format "fn.%s" (or label n))
|
||||
n
|
||||
(format " class=\"footnum\" href=\"#fnr.%s\" role=\"doc-backlink\"" (or label n))
|
||||
info))
|
||||
(contents (org-trim (org-export-data def info))))
|
||||
(format "<div class=\"footdef\">%s %s</div>\n"
|
||||
(format (plist-get info :html-footnote-format) anchor)
|
||||
(format "<div class=\"footpara\" role=\"doc-footnote\">%s</div>"
|
||||
(if (not inline?) contents
|
||||
(format "<p class=\"footpara\">%s</p>"
|
||||
contents))))))))
|
||||
definitions
|
||||
"\n"))))))
|
||||
|
||||
|
||||
;;; Template
|
||||
|
@ -2790,16 +2748,33 @@ CONTENTS is nil. INFO is a plist holding contextual information."
|
|||
(when (org-element-type-p prev 'footnote-reference)
|
||||
(plist-get info :html-footnote-separator)))
|
||||
(let* ((n (org-export-get-footnote-number footnote-reference info))
|
||||
(id (format "fnr.%d%s"
|
||||
n
|
||||
(label (org-element-property :label footnote-reference))
|
||||
;; Do not assign number labels as they appear in Org mode -
|
||||
;; the footnotes are re-numbered by
|
||||
;; `org-export-get-footnote-number'. If the label is not a
|
||||
;; number, keep it.
|
||||
(label (if (and (stringp label)
|
||||
(equal label (number-to-string (string-to-number label))))
|
||||
nil
|
||||
label))
|
||||
(id (format "fnr.%s%s"
|
||||
(or label n)
|
||||
(if (org-export-footnote-first-reference-p
|
||||
footnote-reference info)
|
||||
""
|
||||
".100"))))
|
||||
(let ((label (org-element-property :label footnote-reference)))
|
||||
(format
|
||||
".%d"
|
||||
(org-export-get-ordinal
|
||||
footnote-reference info '(footnote-reference)
|
||||
`(lambda (ref _)
|
||||
(if ,label
|
||||
(equal (org-element-property :label ref) ,label)
|
||||
(not (org-element-property :label ref)))))))))))
|
||||
(format
|
||||
(plist-get info :html-footnote-format)
|
||||
(org-html--anchor
|
||||
id n (format " class=\"footref\" href=\"#fn.%d\" role=\"doc-backlink\"" n) info)))))
|
||||
id n (format " class=\"footref\" href=\"#fn.%s\" role=\"doc-backlink\"" (or label n)) info)))))
|
||||
|
||||
;;;; Headline
|
||||
|
||||
|
@ -2946,7 +2921,7 @@ INFO arguments."
|
|||
(org-html-format-headline-default-function
|
||||
todo todo-type priority text tags info)
|
||||
(org-html-close-tag "br" nil info)
|
||||
contents))
|
||||
(or contents "")))
|
||||
|
||||
;;;; Italic
|
||||
|
||||
|
@ -3052,72 +3027,58 @@ CONTENTS is nil. INFO is a plist holding contextual information."
|
|||
|
||||
;;;; LaTeX Environment
|
||||
|
||||
(defun org-html-prepare-latex-images (parse-tree _backend info)
|
||||
"Make sure that appropriate preview images exist for all LaTeX.
|
||||
(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))))
|
||||
|
||||
Create a hash table containing preview images for all LaTeX
|
||||
fragments in PARSE-TREE, and add it to INFO. Filter out
|
||||
fragments to be ignored according to INFO (see the INFO argument
|
||||
of `org-element-map').
|
||||
|
||||
The keys of the hash table are elements and the values are lists
|
||||
containing image paths and metadata used for display."
|
||||
(prog1 nil
|
||||
(let ((processing-type (plist-get info :with-latex)))
|
||||
(when (assq processing-type org-latex-preview-process-alist)
|
||||
(let* ((image-options (plist-get info :html-latex-image-options))
|
||||
(inline-condition (org-ensure-list
|
||||
(plist-get image-options :inline)))
|
||||
(image-type
|
||||
(thread-first processing-type
|
||||
(alist-get org-latex-preview-process-alist)
|
||||
(plist-get :image-output-type)
|
||||
(intern)))
|
||||
(element-preview-hash-table
|
||||
(apply #'org-latex-preview-cache-images parse-tree info
|
||||
;; Do not copy preview images to :image-dir if
|
||||
;; inlining of images in html is requested
|
||||
(org-combine-plists
|
||||
image-options
|
||||
(and (memq image-type inline-condition)
|
||||
(list :image-dir nil))))))
|
||||
(plist-put info :html-latex-preview-hash-table element-preview-hash-table))))))
|
||||
|
||||
(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)
|
||||
(defun org-html--wrap-latex-environment (contents _ &optional caption label)
|
||||
"Wrap CONTENTS string within appropriate environment for equations.
|
||||
When optional arguments CAPTION and LABEL are given, use them for
|
||||
caption and \"id\" attribute."
|
||||
(format "\n<div%s class=\"equation-container\">\n%s\n</div>"
|
||||
(format "\n<div%s class=\"equation-container\">\n%s%s\n</div>"
|
||||
;; ID.
|
||||
(if (org-string-nw-p label) (format " id=\"%s\"" label) "")
|
||||
;; 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 _)
|
||||
"Non-nil when ELEMENT is a LaTeX math environment.
|
||||
|
@ -3149,137 +3110,57 @@ For instance, change an `equation' environment to `equation*'."
|
|||
"Transcode a LATEX-ENVIRONMENT element from Org to HTML.
|
||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
(let ((processing-type (plist-get info :with-latex))
|
||||
(latex-frag (org-remove-indentation
|
||||
(org-element-property :value latex-environment)))
|
||||
(label (org-html--reference latex-environment info t)))
|
||||
(if (memq processing-type '(t mathjax))
|
||||
(org-html--as-latex
|
||||
latex-environment info
|
||||
(if (org-string-nw-p label)
|
||||
(replace-regexp-in-string "\\`.*"
|
||||
(format "\\&\n\\\\label{%s}" label)
|
||||
latex-frag)
|
||||
latex-frag))
|
||||
(org-html--wrap-latex-environment
|
||||
(org-html--as-latex latex-environment info latex-frag)
|
||||
label))))
|
||||
(latex-frag (org-remove-indentation
|
||||
(org-element-property :value latex-environment)))
|
||||
(attributes (org-export-read-attribute :attr_html latex-environment))
|
||||
(label (org-html--reference latex-environment info t))
|
||||
(caption (and (org-html--latex-environment-numbered-p latex-environment)
|
||||
(number-to-string
|
||||
(org-export-get-ordinal
|
||||
latex-environment info nil
|
||||
(lambda (l _)
|
||||
(and (org-html--math-environment-p l)
|
||||
(org-html--latex-environment-numbered-p l))))))))
|
||||
(cond
|
||||
((memq processing-type '(t mathjax))
|
||||
(org-html-format-latex
|
||||
(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
|
||||
|
||||
(defun org-html-latex-fragment (latex-fragment _contents info)
|
||||
"Transcode a LATEX-FRAGMENT object from Org to HTML.
|
||||
CONTENTS is nil. INFO is a plist holding contextual information."
|
||||
(org-html--as-latex latex-fragment info))
|
||||
|
||||
(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* ((path-info
|
||||
(or (gethash element (plist-get info :html-latex-preview-hash-table))
|
||||
(prog1 nil
|
||||
(org-display-warning
|
||||
(format "Expected LaTeX preview image to exist for element, but none found: %s"
|
||||
(string-replace "\n" " " (org-element-property :value element)))))))
|
||||
(image-options (org-ensure-list (plist-get info :html-latex-image-options)))
|
||||
(block-p (memq (aref (org-element-property :value element) 1) '(?$ ?\[)))
|
||||
(image-source (if path-info (org-html-latex-image--data path-info info block-p) "")))
|
||||
(if (and (eq (plist-get (cdr path-info) :image-type) 'svg)
|
||||
(memq 'svg-embed (plist-get image-options :inline)))
|
||||
image-source
|
||||
(let ((scaling
|
||||
(if (and (plist-get (cdr path-info) :height)
|
||||
(plist-get (cdr path-info) :depth))
|
||||
(org-html-latex-image--scaling path-info info)
|
||||
(prog1 nil
|
||||
(org-display-warning
|
||||
(format "Missing geometry information for LaTeX preview image for element: %s"
|
||||
(string-replace "\n" " " (org-element-property :value element))))))))
|
||||
(org-html-close-tag "img"
|
||||
(org-html--make-attribute-string
|
||||
(nconc
|
||||
(list :src image-source
|
||||
:alt (org-html-encode-plain-text (org-element-property :value element))
|
||||
:style
|
||||
(if path-info
|
||||
(if scaling
|
||||
(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)))
|
||||
(if block-p "display: block" "display: inline-block"))
|
||||
"color: red")
|
||||
:class (format "org-latex org-latex-%s" (if block-p "block" "inline")))
|
||||
(unless path-info (list :title "LaTeX preview image not generated."))))
|
||||
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 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 (org-ensure-list (plist-get image-options :inline)))
|
||||
(image-format (plist-get (cdr image-path-info) :image-type))
|
||||
(source-file (car image-path-info)))
|
||||
(if (memq image-format inline-condition)
|
||||
(let ((coding-system-for-read 'utf-8)
|
||||
(file-name-handler-alist nil))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally source-file)
|
||||
(cond
|
||||
((and (memq 'svg-embed inline-condition)
|
||||
(eq image-format 'svg))
|
||||
(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)))))
|
||||
source-file)))
|
||||
(let ((latex-frag (org-element-property :value latex-fragment))
|
||||
(processing-type (plist-get info :with-latex)))
|
||||
(cond
|
||||
((memq processing-type '(t mathjax))
|
||||
(org-html-format-latex latex-frag 'mathjax info))
|
||||
((memq processing-type '(t html))
|
||||
(org-html-format-latex latex-frag 'html info))
|
||||
((assq processing-type org-preview-latex-process-alist)
|
||||
(let ((formula-link
|
||||
(org-html-format-latex 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--format-image source nil info)))))
|
||||
(t latex-frag))))
|
||||
|
||||
;;;; Line Break
|
||||
|
||||
|
@ -4086,7 +3967,7 @@ contextual information."
|
|||
CONTENTS is the exported HTML code. INFO is the info plist."
|
||||
(with-temp-buffer
|
||||
(insert contents)
|
||||
(set-auto-mode t)
|
||||
(delay-mode-hooks (set-auto-mode t))
|
||||
(when (plist-get info :html-indent)
|
||||
(indent-region (point-min) (point-max)))
|
||||
(buffer-substring-no-properties (point-min) (point-max))))
|
||||
|
|
|
@ -1142,7 +1142,13 @@ external process."
|
|||
(catch 'nextfile
|
||||
(org-check-agenda-file file)
|
||||
(with-current-buffer (org-get-agenda-file-buffer file)
|
||||
(org-icalendar-export-to-ics))))
|
||||
(condition-case err
|
||||
(org-icalendar-export-to-ics)
|
||||
(error
|
||||
(warn "Exporting %s to icalendar failed: %s"
|
||||
file
|
||||
(error-message-string err))
|
||||
(signal (car err) (cdr err)))))))
|
||||
(org-release-buffers org-agenda-new-buffers)))))
|
||||
|
||||
;;;###autoload
|
||||
|
|
687
lisp/ox-latex.el
687
lisp/ox-latex.el
|
@ -48,7 +48,6 @@
|
|||
(defvar engrave-faces-latex-output-style)
|
||||
(defvar engrave-faces-current-preset-style)
|
||||
(defvar engrave-faces-latex-mathescape)
|
||||
(defvar engrave-faces-latex-colorbox-strut)
|
||||
|
||||
|
||||
;;; Define Backend
|
||||
|
@ -111,9 +110,9 @@
|
|||
(?l "As LaTeX file" org-latex-export-to-latex)
|
||||
(?p "As PDF file" org-latex-export-to-pdf)
|
||||
(?o "As PDF file and open"
|
||||
(lambda (async subtreep visable-only body-only)
|
||||
(if async (org-latex-export-to-pdf t subtreep visable-only body-only)
|
||||
(org-latex-export-to-pdf-and-open nil subtreep visable-only body-only))))))
|
||||
(lambda (a s v b)
|
||||
(if a (org-latex-export-to-pdf t s v b)
|
||||
(org-open-file (org-latex-export-to-pdf nil s v b)))))))
|
||||
:filters-alist '((:filter-options . org-latex-math-block-options-filter)
|
||||
(:filter-paragraph . org-latex-clean-invalid-line-breaks)
|
||||
(:filter-parse-tree org-latex-math-block-tree-filter
|
||||
|
@ -136,6 +135,7 @@
|
|||
(:latex-default-table-environment nil nil org-latex-default-table-environment)
|
||||
(:latex-default-quote-environment nil nil org-latex-default-quote-environment)
|
||||
(:latex-default-table-mode nil nil org-latex-default-table-mode)
|
||||
(:latex-default-footnote-command "LATEX_FOOTNOTE_COMMAND" nil org-latex-default-footnote-command)
|
||||
(:latex-diary-timestamp-format nil nil org-latex-diary-timestamp-format)
|
||||
(:latex-engraved-options nil nil org-latex-engraved-options)
|
||||
(:latex-engraved-preamble nil nil org-latex-engraved-preamble)
|
||||
|
@ -171,83 +171,7 @@
|
|||
(:latex-toc-command nil nil org-latex-toc-command)
|
||||
(:latex-compiler "LATEX_COMPILER" nil org-latex-compiler)
|
||||
;; Redefine regular options.
|
||||
(: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)))
|
||||
(:date "DATE" nil "\\today" parse)))
|
||||
|
||||
|
||||
|
||||
|
@ -744,6 +668,17 @@ The function result will be used in the section format string."
|
|||
|
||||
;;;; Footnotes
|
||||
|
||||
(defcustom org-latex-default-footnote-command "\\footnote{%s%s}"
|
||||
"Default command used to insert footnotes.
|
||||
Customize this command if the LaTeX class provides a different
|
||||
command like \"\\sidenote{%s%s}\" that you want to use.
|
||||
The value will be passed as an argument to `format' as the following
|
||||
(format org-latex-default-footnote-command
|
||||
footnote-description footnote-label)"
|
||||
:group 'org-export-latex
|
||||
:package-version '(Org . "9.7")
|
||||
:type 'string)
|
||||
|
||||
(defcustom org-latex-footnote-separator "\\textsuperscript{,}\\,"
|
||||
"Text used to separate footnotes."
|
||||
:group 'org-export-latex
|
||||
|
@ -857,7 +792,6 @@ default we use here encompasses both."
|
|||
:group 'org-export-latex
|
||||
:type 'string)
|
||||
|
||||
|
||||
;;;; Tables
|
||||
|
||||
(defcustom org-latex-default-table-environment "tabular"
|
||||
|
@ -1277,10 +1211,9 @@ will produce
|
|||
|
||||
% Define a Code environment to prettily wrap the fontified code.
|
||||
\\usepackage[breakable,xparse]{tcolorbox}
|
||||
\\providecommand{\\codefont}{\\footnotesize}
|
||||
\\DeclareTColorBox[]{Code}{o}%
|
||||
{colback=EfD!98!EFD, colframe=EfD!95!EFD,
|
||||
fontupper=\\setlength{\\fboxsep}{0pt}\\codefont,
|
||||
fontupper=\\footnotesize\\setlength{\\fboxsep}{0pt},
|
||||
colupper=EFD,
|
||||
IfNoValueTF={#1}%
|
||||
{boxsep=2pt, arc=2.5pt, outer arc=2.5pt,
|
||||
|
@ -1304,9 +1237,7 @@ as long as it:
|
|||
In the default value the colors \"EFD\" and \"EfD\" are provided
|
||||
as they are respectively the foreground and background colors,
|
||||
just in case they aren't provided by the generated preamble, so
|
||||
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.
|
||||
we can assume they are always set.
|
||||
|
||||
Within this preamble there are two recognized macro-like placeholders:
|
||||
|
||||
|
@ -1403,19 +1334,7 @@ default values of which are given by `org-latex-engraved-preamble' and
|
|||
(alist-get 'default
|
||||
(if theme
|
||||
(engrave-faces-get-theme (intern theme))
|
||||
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))))))))
|
||||
engrave-faces-current-preset-style)))))))
|
||||
(when (stringp engraved-theme)
|
||||
(setq engraved-theme (intern engraved-theme)))
|
||||
(when (string-match "^[ \t]*\\[FVEXTRA-SETUP\\][ \t]*\n?" engraved-preamble)
|
||||
|
@ -1445,57 +1364,37 @@ default values of which are given by `org-latex-engraved-preamble' and
|
|||
t t
|
||||
engraved-preamble)))
|
||||
(concat
|
||||
"% Setup for code blocks [1/2]\n\n"
|
||||
"\n% Setup for code blocks [1/2]\n\n"
|
||||
engraved-preamble
|
||||
"\n\n% Setup for code blocks [2/2]: syntax highlighting colors\n\n"
|
||||
(if (require 'engrave-faces-latex nil t)
|
||||
(if engraved-themes
|
||||
(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
|
||||
gen-theme-command
|
||||
(cl-remove-if (lambda (theme) (string= theme (symbol-name engraved-theme)))
|
||||
engraved-themes)
|
||||
(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" "renewcommand"
|
||||
(replace-regexp-in-string
|
||||
"#" "##"
|
||||
(funcall gen-theme-spec theme)))))
|
||||
engraved-themes
|
||||
"\n")
|
||||
;; Load the default theme
|
||||
"\n\n\\engravedtheme"
|
||||
(replace-regexp-in-string
|
||||
"[^A-Za-z]" "" (symbol-name engraved-theme))
|
||||
"\n")
|
||||
"\n\n"
|
||||
(cond
|
||||
((memq engraved-theme engraved-themes)
|
||||
(concat "\\engravedtheme"
|
||||
(replace-regexp-in-string
|
||||
"[^A-Za-z]" "" engraved-theme)
|
||||
"\n"))
|
||||
(t (funcall gen-theme-spec engraved-theme))))
|
||||
(funcall gen-theme-spec engraved-theme))
|
||||
(warn "Cannot engrave source blocks. Consider installing `engrave-faces'.")
|
||||
"% WARNING syntax highlighting unavailable as engrave-faces-latex was missing.\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
|
||||
|
||||
(defcustom org-latex-compiler-file-string "%% Intended LaTeX compiler: %s\n"
|
||||
|
@ -1744,29 +1643,29 @@ For non-floats, see `org-latex--wrap-label'."
|
|||
(org-trim label)
|
||||
(org-export-data main info))))))
|
||||
|
||||
(defun org-latex-guess-inputenc (info)
|
||||
(defun org-latex-guess-inputenc (header)
|
||||
"Set the coding system in inputenc to what the buffer is.
|
||||
|
||||
INFO is the plist used as a communication channel.
|
||||
This function only applies when specified inputenc option is \"AUTO\".
|
||||
HEADER is the LaTeX header string. This function only applies
|
||||
when specified inputenc option is \"AUTO\".
|
||||
|
||||
Return the new header, as a string."
|
||||
(let ((header (plist-get info :latex-full-header))
|
||||
(cs (or (ignore-errors
|
||||
(latexenc-coding-system-to-inputenc
|
||||
(or org-export-coding-system buffer-file-coding-system)))
|
||||
"utf8")))
|
||||
(when (and cs (string-match "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}" header))
|
||||
(let* ((cs (or (ignore-errors
|
||||
(latexenc-coding-system-to-inputenc
|
||||
(or org-export-coding-system buffer-file-coding-system)))
|
||||
"utf8")))
|
||||
(if (not cs) header
|
||||
;; First translate if that is requested.
|
||||
(setq cs (or (cdr (assoc cs org-latex-inputenc-alist)) cs))
|
||||
(plist-put info :latex-full-header
|
||||
(replace-match cs t t header 1))))
|
||||
nil)
|
||||
;; Then find the \usepackage statement and replace the option.
|
||||
(replace-regexp-in-string "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}"
|
||||
cs header t nil 1))))
|
||||
|
||||
(defun org-latex-guess-babel-language (info)
|
||||
(defun org-latex-guess-babel-language (header info)
|
||||
"Set Babel's language according to LANGUAGE keyword.
|
||||
|
||||
INFO is the plist used as a communication channel.
|
||||
HEADER is the LaTeX header string. INFO is the plist used as
|
||||
a communication channel.
|
||||
|
||||
Insertion of guessed language only happens when Babel package has
|
||||
explicitly been loaded. Then it is added to the rest of
|
||||
|
@ -1780,48 +1679,52 @@ already loaded.
|
|||
|
||||
Return the new header."
|
||||
(let* ((language-code (plist-get info :language))
|
||||
(plist (cdr (assoc language-code org-latex-language-alist)))
|
||||
(language (plist-get plist :babel))
|
||||
(header (plist-get info :latex-full-header))
|
||||
(language-ini-only (plist-get plist :babel-ini-only))
|
||||
(plist (cdr
|
||||
(assoc language-code org-latex-language-alist)))
|
||||
(language (plist-get plist :babel))
|
||||
(language-ini-only (plist-get plist :babel-ini-only))
|
||||
(language-ini-alt (plist-get plist :babel-ini-alt))
|
||||
(babel-header-options
|
||||
;; If no language is set, or Babel package is not loaded, or
|
||||
;; LANGUAGE keyword value is a language served by Babel
|
||||
;; exclusively through ini files, return HEADER as-is.
|
||||
(and (not language-ini-only)
|
||||
(stringp language-code)
|
||||
(string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header)
|
||||
(let ((options (save-match-data
|
||||
(org-split-string (match-string 1 header) ",[ \t]*"))))
|
||||
(cond ((member language options) (delete "AUTO" options))
|
||||
((member "AUTO" options) options)
|
||||
(t (append options (list language))))))))
|
||||
(when babel-header-options
|
||||
;; If AUTO is present in the header options, replace it with `language'.
|
||||
(setq header
|
||||
(replace-match
|
||||
(mapconcat (lambda (option) (if (equal "AUTO" option) language option))
|
||||
babel-header-options
|
||||
", ")
|
||||
t nil header 1)))
|
||||
;; If no language is set, or Babel package is not loaded, or
|
||||
;; LANGUAGE keyword value is a language served by Babel
|
||||
;; exclusively through ini files, return HEADER as-is.
|
||||
(header (if (or language-ini-only
|
||||
(not (stringp language-code))
|
||||
(not (string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header)))
|
||||
header
|
||||
(let ((options (save-match-data
|
||||
(org-split-string (match-string 1 header) ",[ \t]*"))))
|
||||
;; If LANGUAGE is already loaded, return header
|
||||
;; without AUTO. Otherwise, replace AUTO with language or
|
||||
;; append language if AUTO is not present. Languages that are
|
||||
;; served in Babel exclusively through ini files are not added
|
||||
;; to the babel argument, and must be loaded using
|
||||
;; `\babelprovide'.
|
||||
(replace-match
|
||||
(mapconcat (lambda (option) (if (equal "AUTO" option) language option))
|
||||
(cond ((member language options) (delete "AUTO" options))
|
||||
((member "AUTO" options) options)
|
||||
(t (append options (list language))))
|
||||
", ")
|
||||
t nil header 1)))))
|
||||
;; If `\babelprovide[args]{AUTO}' is present, AUTO is
|
||||
;; replaced by LANGUAGE.
|
||||
(when (string-match "\\\\babelprovide\\[.*\\]{AUTO}" header)
|
||||
(setq header
|
||||
(replace-regexp-in-string
|
||||
(format
|
||||
"\\(\\\\babelprovide\\[.*\\]\\)\\({\\)%s}" babel-header-options)
|
||||
(format "\\1\\2%s}" (if language-ini-alt language-ini-alt
|
||||
(or language language-ini-only)))
|
||||
header t)))
|
||||
(plist-put info :latex-full-header header))
|
||||
nil)
|
||||
(if (not (string-match "\\\\babelprovide\\[.*\\]{\\(.+\\)}" header))
|
||||
header
|
||||
(let ((prov (match-string 1 header)))
|
||||
(if (equal "AUTO" prov)
|
||||
(replace-regexp-in-string (format
|
||||
"\\(\\\\babelprovide\\[.*\\]\\)\\({\\)%s}" prov)
|
||||
(format "\\1\\2%s}"
|
||||
(if language-ini-alt language-ini-alt
|
||||
(or language language-ini-only)))
|
||||
header t)
|
||||
header)))))
|
||||
|
||||
(defun org-latex-guess-polyglossia-language (info)
|
||||
(defun org-latex-guess-polyglossia-language (header info)
|
||||
"Set the Polyglossia language according to the LANGUAGE keyword.
|
||||
|
||||
INFO is the plist used as a communication channel.
|
||||
HEADER is the LaTeX header string. INFO is the plist used as
|
||||
a communication channel.
|
||||
|
||||
Insertion of guessed language only happens when the Polyglossia
|
||||
package has been explicitly loaded.
|
||||
|
@ -1832,50 +1735,48 @@ replaced with the language of the document or
|
|||
using \setdefaultlanguage and not as an option to the package.
|
||||
|
||||
Return the new header."
|
||||
(let ((header (plist-get info :latex-full-header))
|
||||
(language (plist-get info :language)))
|
||||
(let* ((language (plist-get info :language)))
|
||||
;; If no language is set or Polyglossia is not loaded, return
|
||||
;; HEADER as-is.
|
||||
(when (and (stringp language)
|
||||
(string-match
|
||||
"\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n"
|
||||
header))
|
||||
(if (or (not (stringp language))
|
||||
(not (string-match
|
||||
"\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n"
|
||||
header)))
|
||||
header
|
||||
(let* ((options (org-string-nw-p (match-string 1 header)))
|
||||
(languages (and options
|
||||
;; Reverse as the last loaded language is
|
||||
;; the main language.
|
||||
(nreverse
|
||||
(delete-dups
|
||||
(save-match-data
|
||||
(org-split-string
|
||||
(replace-regexp-in-string
|
||||
"AUTO" language options t)
|
||||
",[ \t]*"))))))
|
||||
(main-language-set
|
||||
(string-match-p "\\\\setmainlanguage{.*?}" header))
|
||||
(polyglossia-modified-header
|
||||
(replace-match
|
||||
(concat "\\usepackage{polyglossia}\n"
|
||||
(mapconcat
|
||||
(lambda (l)
|
||||
(let* ((plist (cdr (assoc language org-latex-language-alist)))
|
||||
(polyglossia-variant (plist-get plist :polyglossia-variant))
|
||||
(polyglossia-lang (plist-get plist :polyglossia))
|
||||
(l (if (equal l language)
|
||||
polyglossia-lang
|
||||
l)))
|
||||
(format (if main-language-set (format "\\setotherlanguage{%s}\n" l)
|
||||
(setq main-language-set t)
|
||||
"\\setmainlanguage%s{%s}\n")
|
||||
(if polyglossia-variant
|
||||
(format "[variant=%s]" polyglossia-variant)
|
||||
"")
|
||||
l)))
|
||||
languages
|
||||
""))
|
||||
t t header 0)))
|
||||
(plist-put info :latex-full-header polyglossia-modified-header))))
|
||||
nil)
|
||||
(languages (and options
|
||||
;; Reverse as the last loaded language is
|
||||
;; the main language.
|
||||
(nreverse
|
||||
(delete-dups
|
||||
(save-match-data
|
||||
(org-split-string
|
||||
(replace-regexp-in-string
|
||||
"AUTO" language options t)
|
||||
",[ \t]*"))))))
|
||||
(main-language-set
|
||||
(string-match-p "\\\\setmainlanguage{.*?}" header)))
|
||||
(replace-match
|
||||
(concat "\\usepackage{polyglossia}\n"
|
||||
(mapconcat
|
||||
(lambda (l)
|
||||
(let* ((plist (cdr
|
||||
(assoc language org-latex-language-alist)))
|
||||
(polyglossia-variant (plist-get plist :polyglossia-variant))
|
||||
(polyglossia-lang (plist-get plist :polyglossia))
|
||||
(l (if (equal l language)
|
||||
polyglossia-lang
|
||||
l)))
|
||||
(format (if main-language-set (format "\\setotherlanguage{%s}\n" l)
|
||||
(setq main-language-set t)
|
||||
"\\setmainlanguage%s{%s}\n")
|
||||
(if polyglossia-variant
|
||||
(format "[variant=%s]" polyglossia-variant)
|
||||
"")
|
||||
l)))
|
||||
languages
|
||||
""))
|
||||
t t header 0)))))
|
||||
|
||||
(defun org-latex--remove-packages (pkg-alist info)
|
||||
"Remove packages based on the current LaTeX compiler.
|
||||
|
@ -2080,103 +1981,32 @@ non-nil, only includes packages relevant to image generation, as
|
|||
specified in `org-latex-default-packages-alist' or
|
||||
`org-latex-packages-alist'."
|
||||
(let* ((class (plist-get info :latex-class))
|
||||
(class-template
|
||||
(or template
|
||||
(let* ((class-options (plist-get info :latex-class-options))
|
||||
(header (nth 1 (assoc class (plist-get info :latex-classes)))))
|
||||
(and (stringp header)
|
||||
(if (not class-options) header
|
||||
(replace-regexp-in-string
|
||||
"^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
|
||||
class-options header t nil 1))))
|
||||
(user-error "Unknown LaTeX class `%s'" class)))
|
||||
(header-split (format "\n%%--org-latex-header-temp-split-%d--\n" (random 10000)))
|
||||
(header (concat (org-element-normalize-string (plist-get info :latex-header))
|
||||
header-split
|
||||
(and (not snippet?)
|
||||
(org-element-normalize-string
|
||||
(plist-get info :latex-header-extra)))))
|
||||
generated-preamble preamble-nonprecompilable)
|
||||
(plist-put info :latex-full-header
|
||||
(org-element-normalize-string
|
||||
(org-splice-latex-header
|
||||
class-template
|
||||
(org-latex--remove-packages org-latex-default-packages-alist info)
|
||||
(org-latex--remove-packages org-latex-packages-alist info)
|
||||
snippet?
|
||||
header)))
|
||||
(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))))
|
||||
(class-template
|
||||
(or template
|
||||
(let* ((class-options (plist-get info :latex-class-options))
|
||||
(header (nth 1 (assoc class (plist-get info :latex-classes)))))
|
||||
(and (stringp header)
|
||||
(if (not class-options) header
|
||||
(replace-regexp-in-string
|
||||
"^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
|
||||
class-options header t nil 1))))
|
||||
(user-error "Unknown LaTeX class `%s'" class))))
|
||||
(org-latex-guess-polyglossia-language
|
||||
(org-latex-guess-babel-language
|
||||
(org-latex-guess-inputenc
|
||||
(org-element-normalize-string
|
||||
(org-splice-latex-header
|
||||
class-template
|
||||
(org-latex--remove-packages org-latex-default-packages-alist info)
|
||||
(org-latex--remove-packages org-latex-packages-alist info)
|
||||
snippet?
|
||||
(mapconcat #'org-element-normalize-string
|
||||
(list (plist-get info :latex-header)
|
||||
(and (not snippet?)
|
||||
(plist-get info :latex-header-extra)))
|
||||
""))))
|
||||
info)
|
||||
info)))
|
||||
|
||||
(defun org-latex-template (contents info)
|
||||
"Return complete document string after LaTeX conversion.
|
||||
|
@ -2185,7 +2015,12 @@ holding export options."
|
|||
(let ((title (org-export-data (plist-get info :title) info))
|
||||
(spec (org-latex--format-spec info)))
|
||||
(concat
|
||||
;; Timestamp, compiler statement, document class and packages.
|
||||
;; Timestamp.
|
||||
(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)
|
||||
;; Possibly limit depth for headline numbering.
|
||||
(let ((sec-num (plist-get info :section-numbers)))
|
||||
|
@ -2222,6 +2057,12 @@ holding export options."
|
|||
(let ((template (plist-get info :latex-hyperref-template)))
|
||||
(and (stringp template)
|
||||
(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.
|
||||
"\\begin{document}\n\n"
|
||||
;; Title command.
|
||||
|
@ -2249,118 +2090,6 @@ holding export options."
|
|||
;; Document end.
|
||||
"\\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
|
||||
|
@ -2521,7 +2250,7 @@ CONTENTS is nil. INFO is a plist holding contextual information."
|
|||
(t
|
||||
(let ((def (org-export-get-footnote-definition footnote-reference info)))
|
||||
(concat
|
||||
(format "\\footnote{%s%s}" (org-trim (org-export-data def info))
|
||||
(format (plist-get info :latex-default-footnote-command) (org-trim (org-export-data def info))
|
||||
;; Only insert a \label if there exist another
|
||||
;; reference to def.
|
||||
(cond ((not label) "")
|
||||
|
@ -2765,12 +2494,8 @@ INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
|
|||
(defun org-latex-inline-src-block--engraved (info code lang)
|
||||
"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'."
|
||||
(let ((engraved-theme (plist-get info :latex-engraved-theme)))
|
||||
(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)))
|
||||
(org-latex-src--engrave-code
|
||||
code lang nil (plist-get info :latex-engraved-options) t))
|
||||
|
||||
(defun org-latex-inline-src-block--listings (info code lang)
|
||||
"Transcode an inline src block's content from Org to LaTeX, using lstlistings.
|
||||
|
@ -3821,7 +3546,7 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
|
|||
(when (eq mathescape 'yes)
|
||||
(or engrave-faces-latex-mathescape t)))))
|
||||
|
||||
(defun org-latex-src--engrave-code (content lang &optional theme explicit-theme-p options inline)
|
||||
(defun org-latex-src--engrave-code (content lang &optional theme options inline)
|
||||
"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.
|
||||
|
||||
|
@ -3862,12 +3587,12 @@ to the Verbatim environment or Verb command."
|
|||
(concat "\\begin{Code}\n\\begin{Verbatim}" engraved-options "\n"
|
||||
engraved-code "\n\\end{Verbatim}\n\\end{Code}"))))
|
||||
(kill-buffer engraved-buffer)
|
||||
(if (and theme explicit-theme-p)
|
||||
(concat "\\begingroup\\engravedtheme"
|
||||
(if theme
|
||||
(concat "{\\engravedtheme"
|
||||
(replace-regexp-in-string "[^A-Za-z]" ""
|
||||
(symbol-name theme))
|
||||
engraved-wrapped
|
||||
"\\endgroup")
|
||||
"}")
|
||||
engraved-wrapped))
|
||||
(user-error "Cannot engrave code as `engrave-faces-latex' is unavailable.")))
|
||||
|
||||
|
@ -3901,16 +3626,7 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
|
|||
`(("linenos")
|
||||
("firstnumber" ,(number-to-string (1+ num-start)))))
|
||||
(and local-options `((,local-options))))))
|
||||
(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))
|
||||
(engraved-theme (plist-get attributes :engraved-theme))
|
||||
(content
|
||||
(let* ((code-info (org-export-unravel-code src-block))
|
||||
(max-width
|
||||
|
@ -3936,8 +3652,7 @@ 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-code
|
||||
content lang
|
||||
engraved-theme
|
||||
(not (eq engraved-theme engraved-doc-theme))
|
||||
(when engraved-theme (intern engraved-theme))
|
||||
options))))
|
||||
(concat (car float-env) body (cdr float-env))))
|
||||
|
||||
|
@ -4651,21 +4366,13 @@ Return PDF file's name."
|
|||
async subtreep visible-only body-only ext-plist
|
||||
#'org-latex-compile)))
|
||||
|
||||
(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)
|
||||
(defun org-latex-compile (texfile &optional snippet)
|
||||
"Compile a TeX file.
|
||||
|
||||
TEXFILE is the name of the file being compiled. Processing is
|
||||
done through the command specified in `org-latex-pdf-process',
|
||||
which see. Output is redirected to \"*Org PDF LaTeX Output*\"
|
||||
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'.
|
||||
buffer.
|
||||
|
||||
When optional argument SNIPPET is non-nil, TEXFILE is a temporary
|
||||
file used to preview a LaTeX snippet. In this case, do not
|
||||
|
@ -4700,34 +4407,16 @@ produced."
|
|||
(?L . ,(shell-quote-argument compiler))))
|
||||
(log-buf-name "*Org PDF LaTeX Output*")
|
||||
(log-buf (and (not snippet) (get-buffer-create log-buf-name)))
|
||||
(outfile (expand-file-name (concat (file-name-base texfile) ".pdf")
|
||||
(file-name-directory texfile))))
|
||||
outfile)
|
||||
;; Erase compile buffer at the start.
|
||||
(with-current-buffer log-buf
|
||||
(erase-buffer))
|
||||
(if (or noninteractive (functionp process))
|
||||
(progn
|
||||
(org-compile-file texfile process "pdf"
|
||||
(format "See %S for details" log-buf-name)
|
||||
log-buf spec)
|
||||
(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)))
|
||||
(setq outfile
|
||||
(org-compile-file
|
||||
texfile process "pdf"
|
||||
(format "See %S for details" log-buf-name)
|
||||
log-buf spec))
|
||||
(org-latex-compile--postprocess outfile log-buf snippet)
|
||||
;; Return output file name.
|
||||
outfile))
|
||||
|
||||
|
|
|
@ -293,6 +293,13 @@ This function shouldn't be used for floats. See
|
|||
"Protect minus and backslash characters in string TEXT."
|
||||
(replace-regexp-in-string "-" "\\-" text nil t))
|
||||
|
||||
(defun org-man--protect-example (text)
|
||||
"Escape necessary characters for verbatim TEXT."
|
||||
;; See man groff_man_style; \e must be used to render backslash.
|
||||
;; Note that groff's .eo (disable backslash) and .ec (re-enable
|
||||
;; backslash) cannot be used as per the same man page.
|
||||
(replace-regexp-in-string "\\\\" "\\e" text nil t))
|
||||
|
||||
|
||||
|
||||
;;; Template
|
||||
|
@ -400,7 +407,7 @@ information."
|
|||
(org-man--wrap-label
|
||||
example-block
|
||||
(format ".RS\n.nf\n%s\n.fi\n.RE"
|
||||
(org-export-format-code-default example-block info))))
|
||||
(org-man--protect-example (org-export-format-code-default example-block info)))))
|
||||
|
||||
|
||||
;;; Export Block
|
||||
|
@ -529,11 +536,11 @@ contextual information."
|
|||
(delete-file out-file)
|
||||
code-block)
|
||||
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE\n"
|
||||
code))))
|
||||
(org-man--protect-example code)))))
|
||||
|
||||
;; Do not use a special package: transcode it verbatim.
|
||||
(t
|
||||
(concat ".RS\n.nf\n" "\\fC" "\n" code "\n"
|
||||
(concat ".RS\n.nf\n" "\\fC" "\n" (org-man--protect-example code) "\n"
|
||||
"\\fP\n.fi\n.RE\n")))))
|
||||
|
||||
|
||||
|
@ -749,7 +756,7 @@ CONTENTS holds the contents of the item. INFO is a plist holding
|
|||
contextual information."
|
||||
(if (not (plist-get info :man-source-highlight))
|
||||
(format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n\n"
|
||||
(org-export-format-code-default src-block info))
|
||||
(org-man--protect-example (org-export-format-code-default src-block info)))
|
||||
(let* ((tmpdir temporary-file-directory)
|
||||
(in-file (make-temp-name (expand-file-name "srchilite" tmpdir)))
|
||||
(out-file (make-temp-name (expand-file-name "reshilite" tmpdir)))
|
||||
|
@ -772,7 +779,7 @@ contextual information."
|
|||
(delete-file in-file)
|
||||
(delete-file out-file)
|
||||
code-block)
|
||||
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" code)))))
|
||||
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" (org-man--protect-example code))))))
|
||||
|
||||
|
||||
;;; Statistics Cookie
|
||||
|
@ -836,9 +843,10 @@ contextual information."
|
|||
|
||||
(format ".nf\n\\fC%s\\fP\n.fi"
|
||||
;; Re-create table, without affiliated keywords.
|
||||
(org-trim
|
||||
(org-element-interpret-data
|
||||
`(table nil ,@(org-element-contents table))))))
|
||||
(org-man--protect-example
|
||||
(org-trim
|
||||
(org-element-interpret-data
|
||||
`(table nil ,@(org-element-contents table)))))))
|
||||
;; Case 2: Standard table.
|
||||
(t (org-man-table--org-table table contents info))))
|
||||
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
;;; 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: Shell-escaped LaTeX fragment to be converted.
|
||||
It must not be used inside a quoted argument, the result of %i
|
||||
expansion inside a quoted argument is undefined.
|
||||
%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
|
|
@ -47,11 +47,15 @@
|
|||
|
||||
(defcustom org-md-headline-style 'atx
|
||||
"Style used to format headlines.
|
||||
This variable can be set to either `atx' or `setext'."
|
||||
This variable can be set to either `atx', `setext', or `mixed'.
|
||||
|
||||
Mixed style uses Setext style markup for the first two headline levels
|
||||
and uses ATX style markup for the remaining four levels."
|
||||
:group 'org-export-md
|
||||
:type '(choice
|
||||
(const :tag "Use \"atx\" style" atx)
|
||||
(const :tag "Use \"Setext\" style" setext)))
|
||||
(const :tag "Use \"Setext\" style" setext)
|
||||
(const :tag "Use \"mixed\" style" mixed)))
|
||||
|
||||
|
||||
;;;; Footnotes
|
||||
|
@ -232,7 +236,7 @@ anchor tag for the section as a string. TAGS are the tags set on
|
|||
the section."
|
||||
(let ((anchor-lines (and anchor (concat anchor "\n\n"))))
|
||||
;; Use "Setext" style
|
||||
(if (and (eq style 'setext) (< level 3))
|
||||
(if (and (memq style '(setext mixed)) (< level 3))
|
||||
(let* ((underline-char (if (= level 1) ?= ?-))
|
||||
(underline (concat (make-string (length title) underline-char)
|
||||
"\n")))
|
||||
|
@ -397,9 +401,10 @@ a communication channel."
|
|||
(cond
|
||||
;; Cannot create a headline. Fall-back to a list.
|
||||
((or (org-export-low-level-p headline info)
|
||||
(not (memq style '(atx setext)))
|
||||
(not (memq style '(atx mixed setext)))
|
||||
(and (eq style 'atx) (> level 6))
|
||||
(and (eq style 'setext) (> level 2)))
|
||||
(and (eq style 'setext) (> level 2))
|
||||
(and (eq style 'mixed) (> level 6)))
|
||||
(let ((bullet
|
||||
(if (not (org-export-numbered-headline-p headline info)) "-"
|
||||
(concat (number-to-string
|
||||
|
@ -452,7 +457,7 @@ a communication channel."
|
|||
(org-list-parents-alist struct)))))
|
||||
"."))))
|
||||
(concat bullet
|
||||
(make-string (- 4 (length bullet)) ? )
|
||||
(make-string (max 1 (- 4 (length bullet))) ? )
|
||||
(pcase (org-element-property :checkbox item)
|
||||
(`on "[X] ")
|
||||
(`trans "[-] ")
|
||||
|
|
293
lisp/ox-odt.el
293
lisp/ox-odt.el
|
@ -34,8 +34,6 @@
|
|||
(require 'org-macs)
|
||||
(require 'ox)
|
||||
(require 'table nil 'noerror)
|
||||
(require 'ox-mathml)
|
||||
(require 'org-latex-preview)
|
||||
|
||||
(declare-function org-at-heading-p "org" (&optional _))
|
||||
(declare-function org-back-to-heading "org" (&optional invisible-ok))
|
||||
|
@ -121,7 +119,6 @@
|
|||
(:odt-pixels-per-inch nil nil org-odt-pixels-per-inch)
|
||||
(:odt-table-styles nil nil org-odt-table-styles)
|
||||
(:odt-use-date-fields nil nil org-odt-use-date-fields)
|
||||
(:odt-latex-image-options nil nil org-odt-latex-image-options)
|
||||
;; Redefine regular option.
|
||||
(:with-latex nil "tex" org-odt-with-latex)
|
||||
;; Retrieve LaTeX header for fragments.
|
||||
|
@ -723,15 +720,6 @@ Any other symbol is a synonym for `mathjax'."
|
|||
(const :tag "Use imagemagick to make images" imagemagick)
|
||||
(other :tag "Use MathJax to display math" mathjax)))
|
||||
|
||||
(defcustom org-odt-latex-image-options
|
||||
'(:foreground "Black" :background "Transparent"
|
||||
:page-width 1.0 :scale 1.0 :inline nil)
|
||||
"LaTeX preview options that apply to generated images.
|
||||
This is a ODT-specific counterpart to
|
||||
`org-latex-preview-appearance-options', which see."
|
||||
:group 'org-export-odt
|
||||
:package-version '(Org . "9.7")
|
||||
:type 'plist)
|
||||
|
||||
;;;; Links
|
||||
|
||||
|
@ -2265,11 +2253,11 @@ SHORT-CAPTION are strings."
|
|||
LINK is the link pointing to the inline image. INFO is a plist
|
||||
used as a communication channel."
|
||||
(cl-assert (org-element-type-p element 'link))
|
||||
(let* ((src (let* ((type (org-element-property :type element))
|
||||
(raw-path (org-element-property :path element)))
|
||||
(cl-assert (equal "file" (org-element-property :type element)))
|
||||
(let* ((src (let ((raw-path (org-element-property :path element)))
|
||||
(cond ((file-name-absolute-p raw-path)
|
||||
(expand-file-name raw-path))
|
||||
(t (concat type ":" raw-path)))))
|
||||
(t raw-path))))
|
||||
(src-expanded (if (file-name-absolute-p src) src
|
||||
(expand-file-name src (file-name-directory
|
||||
(plist-get info :input-file)))))
|
||||
|
@ -2296,24 +2284,12 @@ used as a communication channel."
|
|||
;;
|
||||
;; Handle `:width', `:height' and `:scale' properties. Read
|
||||
;; them as numbers since we need them for computations.
|
||||
(--em-to-cm
|
||||
;; FIXME: Hardcoded default font-size to 12 according to the
|
||||
;; default value of styles.xml in org-odt-styles-dir. I
|
||||
;; don't know how how to determine this dynamically.
|
||||
(lambda (size) (and size (* 12 0.0352778 size))))
|
||||
(em-geometry
|
||||
(cdr-safe
|
||||
(gethash element (plist-get info :odt-latex-preview-hash-table))))
|
||||
(width (or (funcall --em-to-cm (plist-get em-geometry :width)) ;latex image size
|
||||
(and-let* ((w (plist-get attr-plist :width)) ;ATTR_ODT specified size
|
||||
((stringp w)))
|
||||
(read w))))
|
||||
(height (or (funcall --em-to-cm (plist-get em-geometry :height)) ;latex image size
|
||||
(and-let* ((l (plist-get attr-plist :length)) ;ATTR_ODT specified size
|
||||
((stringp l)))
|
||||
(read l))))
|
||||
(size (org-odt--image-size
|
||||
src-expanded info width height
|
||||
(size (org-odt--image-size
|
||||
src-expanded info
|
||||
(let ((width (plist-get attr-plist :width)))
|
||||
(and width (read width)))
|
||||
(let ((length (plist-get attr-plist :length)))
|
||||
(and length (read length)))
|
||||
(let ((scale (plist-get attr-plist :scale)))
|
||||
(and scale (read scale)))
|
||||
nil ; embed-as
|
||||
|
@ -2706,8 +2682,6 @@ INFO is a plist holding contextual information. See
|
|||
(imagep (org-export-inline-image-p
|
||||
link (plist-get info :odt-inline-image-rules)))
|
||||
(path (cond
|
||||
((member type '("http" "https" "ftp" "mailto"))
|
||||
(concat type ":" raw-path))
|
||||
((string= type "file")
|
||||
(let ((path-uri (org-export-file-uri raw-path)))
|
||||
(if (string-prefix-p "file://" path-uri)
|
||||
|
@ -2717,9 +2691,10 @@ INFO is a plist holding contextual information. See
|
|||
;; archive. The directory containing the odt file
|
||||
;; is "../".
|
||||
(concat "../" path-uri))))
|
||||
(t raw-path)))
|
||||
(t (concat type ":" raw-path))))
|
||||
;; Convert & to & for correct XML representation
|
||||
(path (replace-regexp-in-string "&" "&" path)))
|
||||
(path (replace-regexp-in-string "&" "&" path))
|
||||
(raw-path (replace-regexp-in-string "&" "&" raw-path)))
|
||||
(cond
|
||||
;; Link type is handled by a special function.
|
||||
((org-export-custom-protocol-maybe link desc 'odt info))
|
||||
|
@ -2798,10 +2773,10 @@ INFO is a plist holding contextual information. See
|
|||
;; Coderef: replace link with the reference name or the
|
||||
;; equivalent line number.
|
||||
((string= type "coderef")
|
||||
(let* ((line-no (format "%d" (org-export-resolve-coderef path info)))
|
||||
(href (concat "coderef-" path)))
|
||||
(let* ((line-no (format "%d" (org-export-resolve-coderef raw-path info)))
|
||||
(href (concat "coderef-" raw-path)))
|
||||
(format
|
||||
(org-export-get-coderef-format path desc)
|
||||
(org-export-get-coderef-format raw-path desc)
|
||||
(format
|
||||
"<text:bookmark-ref text:reference-format=\"number\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
|
||||
href line-no))))
|
||||
|
@ -3753,135 +3728,115 @@ contextual information."
|
|||
|
||||
(defun org-odt--translate-latex-fragments (tree _backend info)
|
||||
(let ((processing-type (plist-get info :with-latex))
|
||||
(count 0)
|
||||
(count 0)
|
||||
(warning nil))
|
||||
;; MathML will be handled seperately.
|
||||
(if (and (memq processing-type '(t mathml))
|
||||
(fboundp 'org-mathml-converter-available-p)
|
||||
(org-mathml-converter-available-p)
|
||||
(plist-put info :with-latex 'mathml))
|
||||
(org-element-map tree '(latex-fragment latex-environment)
|
||||
(lambda (latex)
|
||||
(cl-incf count)
|
||||
(if-let ((latex-frag (org-element-property :value latex))
|
||||
(path (org-mathml-convert-latex-cached latex-frag))
|
||||
(link (list 'link
|
||||
(list :type "file"
|
||||
:path path
|
||||
:format 'bracket
|
||||
:raw-link (format "file:%s" path))))
|
||||
(replacement
|
||||
(if (eq (org-element-type latex) 'latex-environment)
|
||||
;;LaTeX environment. Mimic a "standalone image
|
||||
;; or formula" by enclosing the `link' in
|
||||
;; a `paragraph'. Copy over original
|
||||
;; attributes, captions to the enclosing
|
||||
;; paragraph.
|
||||
(org-element-adopt-elements
|
||||
(list 'paragraph
|
||||
(list :style "OrgFormula"
|
||||
:name
|
||||
(org-element-property :name latex)
|
||||
:caption
|
||||
(org-element-property :caption latex)))
|
||||
link)
|
||||
link)))
|
||||
(progn
|
||||
;; 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))
|
||||
(setq warning "Conversion of LaTeX to MathML failed. Falling back to verbatim.")))
|
||||
info nil nil)
|
||||
;; Normalize processing-type to one of dvipng or verbatim.
|
||||
;; If the desired converter is not available, force verbatim
|
||||
;; processing.
|
||||
(cl-case processing-type
|
||||
((t mathml)
|
||||
(setq warning "LaTeX to MathML converter not available. Falling back to verbatim."
|
||||
processing-type 'verbatim))
|
||||
((dvipng imagemagick dvisvgm)
|
||||
(let ((programs
|
||||
(thread-first processing-type
|
||||
(alist-get org-latex-preview-process-alist)
|
||||
(plist-get :programs))))
|
||||
(unless (cl-every (lambda (p) (org-check-external-command p "" 'no-error)) programs)
|
||||
(setq warning "LaTeX or image converter not available. Falling back to verbatim."
|
||||
processing-type 'verbatim))))
|
||||
(otherwise
|
||||
(setq warning "Unknown LaTeX option. Forcing verbatim."
|
||||
processing-type 'verbatim)))
|
||||
;; Display warning if the selected PROCESSING-TYPE is not
|
||||
;; available, but there are fragments to be converted.
|
||||
(when warning
|
||||
(org-element-map tree '(latex-fragment latex-environment)
|
||||
(lambda (_) (org-display-warning warning))
|
||||
info 'first-match nil t))
|
||||
;; Store normalized value for later use.
|
||||
(when (plist-get info :with-latex)
|
||||
(plist-put info :with-latex processing-type))
|
||||
(message "Formatting LaTeX using %s" processing-type)
|
||||
;; Convert `latex-fragment's and `latex-environment's.
|
||||
(when (memq processing-type '(dvipng imagemagick dvisvgm))
|
||||
;; Prepare hash table with image file data
|
||||
(plist-put info :odt-latex-preview-hash-table
|
||||
(apply #'org-latex-preview-cache-images tree info
|
||||
org-odt-latex-image-options))
|
||||
;; Map over the parse tree again and replace LaTeX
|
||||
;; fragments with links. If an image doesn't exist for the
|
||||
;; fragment, leave it in verbatim.
|
||||
(org-element-map tree '(latex-fragment latex-environment)
|
||||
(lambda (latex-*)
|
||||
(when-let*
|
||||
((latex-preview-hash-table (plist-get info :odt-latex-preview-hash-table))
|
||||
(latex-frag (org-element-property :value latex-*))
|
||||
(path-info
|
||||
(or (gethash latex-* latex-preview-hash-table)
|
||||
(prog1 nil (org-display-warning
|
||||
(format "Failed to generate preview image for element: %s" latex-frag)))))
|
||||
(source-file (car path-info))
|
||||
(link (list 'link (list :type "file"
|
||||
:path source-file
|
||||
:format 'bracket
|
||||
:raw-link (format "file:%s" source-file)))))
|
||||
(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)
|
||||
;; Also replace in the latex preview table
|
||||
(puthash replacement (gethash latex-* latex-preview-hash-table)
|
||||
latex-preview-hash-table))))
|
||||
info))))
|
||||
;; Normalize processing-type to one of dvipng, mathml or verbatim.
|
||||
;; If the desired converter is not available, force verbatim
|
||||
;; processing.
|
||||
(cl-case processing-type
|
||||
((t mathml)
|
||||
(if (and (fboundp 'org-format-latex-mathml-available-p)
|
||||
(org-format-latex-mathml-available-p))
|
||||
(setq processing-type 'mathml)
|
||||
(setq warning "`org-odt-with-latex': LaTeX to MathML converter not available. Falling back to verbatim.")
|
||||
(setq processing-type 'verbatim)))
|
||||
((dvipng imagemagick)
|
||||
(unless (and (org-check-external-command "latex" "" t)
|
||||
(org-check-external-command
|
||||
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
|
||||
(setq warning "`org-odt-with-latex': LaTeX to PNG converter not available. Falling back to verbatim.")
|
||||
(setq processing-type 'verbatim)))
|
||||
(verbatim) ;; nothing to do
|
||||
(otherwise
|
||||
(setq warning "`org-odt-with-latex': Unknown LaTeX option. Forcing verbatim.")
|
||||
(setq processing-type 'verbatim)))
|
||||
|
||||
;; Display warning if the selected PROCESSING-TYPE is not
|
||||
;; available, but there are fragments to be converted.
|
||||
(when warning
|
||||
(org-element-map tree '(latex-fragment latex-environment)
|
||||
(lambda (_) (warn warning))
|
||||
info 'first-match nil t))
|
||||
|
||||
;; Store normalized value for later use.
|
||||
(when (plist-get info :with-latex)
|
||||
(plist-put info :with-latex processing-type))
|
||||
(message "Formatting LaTeX using %s" processing-type)
|
||||
|
||||
;; Convert `latex-fragment's and `latex-environment's.
|
||||
(when (memq processing-type '(mathml dvipng imagemagick))
|
||||
(org-element-map tree '(latex-fragment latex-environment)
|
||||
(lambda (latex-*)
|
||||
(cl-incf count)
|
||||
(let* ((latex-frag (org-element-property :value latex-*))
|
||||
(input-file (plist-get info :input-file))
|
||||
(cache-dir (file-name-directory input-file))
|
||||
(cache-subdir (concat
|
||||
(cl-case processing-type
|
||||
((dvipng imagemagick)
|
||||
org-preview-latex-image-directory)
|
||||
(mathml "ltxmathml/"))
|
||||
(file-name-sans-extension
|
||||
(file-name-nondirectory input-file))))
|
||||
(display-msg
|
||||
(cl-case processing-type
|
||||
((dvipng imagemagick)
|
||||
(format "Creating LaTeX Image %d..." count))
|
||||
(mathml (format "Creating MathML snippet %d..." count))))
|
||||
;; Get an Org-style link to PNG image or the MathML
|
||||
;; file.
|
||||
(link
|
||||
(with-temp-buffer
|
||||
(insert latex-frag)
|
||||
(delay-mode-hooks (let ((org-inhibit-startup t)) (org-mode)))
|
||||
;; When converting to a PNG image, make sure to
|
||||
;; copy all LaTeX header specifications from the
|
||||
;; Org source.
|
||||
(unless (eq processing-type 'mathml)
|
||||
(let ((h (plist-get info :latex-header)))
|
||||
(when h
|
||||
(insert "\n"
|
||||
(replace-regexp-in-string
|
||||
"^" "#+LATEX_HEADER: " h)))))
|
||||
(org-format-latex cache-subdir nil nil cache-dir
|
||||
nil display-msg nil
|
||||
processing-type)
|
||||
(goto-char (point-min))
|
||||
(skip-chars-forward " \t\n")
|
||||
(org-element-link-parser))))
|
||||
(if (not (org-element-type-p link '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
|
||||
(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 latex-* replacement)))))
|
||||
info nil nil t)))
|
||||
tree)
|
||||
|
||||
|
||||
|
@ -4208,7 +4163,7 @@ MathML source to kill ring depending on the value of
|
|||
(save-buffer-coding-system 'utf-8))
|
||||
(set-buffer buffer)
|
||||
(set-buffer-file-coding-system coding-system-for-write)
|
||||
(let ((mathml (org-mathml-convert-latex-cached latex-frag)))
|
||||
(let ((mathml (org-create-math-formula latex-frag)))
|
||||
(unless mathml (error "No Math formula created"))
|
||||
(insert mathml)
|
||||
;; Add MathML to kill ring, if needed.
|
||||
|
|
|
@ -56,6 +56,9 @@
|
|||
"This will cache timestamps and titles for files in publishing projects.
|
||||
Blocks could hash sha1 values here.")
|
||||
|
||||
(defvar org-publish-transient-cache nil
|
||||
"This will cache information during publishing process.")
|
||||
|
||||
(defvar org-publish-after-publishing-hook nil
|
||||
"Hook run each time a file is published.
|
||||
Every function in this hook will be called with two arguments:
|
||||
|
@ -791,17 +794,14 @@ Default for SITEMAP-FILENAME is `sitemap.org'."
|
|||
(concat (file-name-directory b)
|
||||
(org-publish-find-title b project))
|
||||
b)))
|
||||
(setq retval
|
||||
(if ignore-case
|
||||
(not (string-lessp (upcase B) (upcase A)))
|
||||
(not (string-lessp B A))))))
|
||||
(setq retval (org-string<= A B nil ignore-case))))
|
||||
((or `anti-chronologically `chronologically)
|
||||
(let* ((adate (org-publish-find-date a project))
|
||||
(bdate (org-publish-find-date b project)))
|
||||
(setq retval
|
||||
(not (if (eq sort-files 'chronologically)
|
||||
(time-less-p bdate adate)
|
||||
(time-less-p adate bdate))))))
|
||||
(time-less-p bdate adate)
|
||||
(time-less-p adate bdate))))))
|
||||
(`nil nil)
|
||||
(_ (user-error "Invalid sort value %s" sort-files)))
|
||||
;; Directory-wise wins:
|
||||
|
@ -867,7 +867,7 @@ PROPERTY, i.e. \"behavior\" parameter from `org-export-options-alist'."
|
|||
(org-no-properties
|
||||
(org-element-interpret-data parsed-title))
|
||||
(file-name-nondirectory (file-name-sans-extension file)))))
|
||||
(org-publish-cache-set-file-property file :title title)))))
|
||||
(org-publish-cache-set-file-property file :title title nil 'transient)))))
|
||||
|
||||
(defun org-publish-find-date (file project)
|
||||
"Find the date of FILE in PROJECT.
|
||||
|
@ -892,7 +892,8 @@ time in `current-time' format."
|
|||
(org-time-string-to-time value))))))
|
||||
((file-exists-p file)
|
||||
(file-attribute-modification-time (file-attributes file)))
|
||||
(t (error "No such file: \"%s\"" file)))))))))
|
||||
(t (error "No such file: \"%s\"" file)))))
|
||||
nil 'transient))))
|
||||
|
||||
(defun org-publish-sitemap-default-entry (entry style project)
|
||||
"Default format for site map ENTRY, as a string.
|
||||
|
@ -1048,7 +1049,8 @@ its CDR is a string."
|
|||
(replace-regexp-in-string
|
||||
"\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
|
||||
(org-element-property :raw-value parent)))))))))
|
||||
info))))
|
||||
info))
|
||||
nil 'transient))
|
||||
;; Return output unchanged.
|
||||
output)
|
||||
|
||||
|
@ -1251,6 +1253,9 @@ If FREE-CACHE, empty the cache."
|
|||
(error "Org publish timestamp: %s is not a directory"
|
||||
org-publish-timestamp-directory))
|
||||
|
||||
(unless org-publish-transient-cache
|
||||
(setq org-publish-transient-cache (make-hash-table :test #'equal)))
|
||||
|
||||
(unless (and org-publish-cache
|
||||
(string= (org-publish-cache-get ":project:") project-name))
|
||||
(let* ((cache-file
|
||||
|
@ -1274,6 +1279,8 @@ If FREE-CACHE, empty the cache."
|
|||
(message "%s" "Resetting org-publish-cache")
|
||||
(when (hash-table-p org-publish-cache)
|
||||
(clrhash org-publish-cache))
|
||||
(when (hash-table-p org-publish-transient-cache)
|
||||
(clrhash org-publish-transient-cache))
|
||||
(setq org-publish-cache nil))
|
||||
|
||||
(defun org-publish-cache-file-needs-publishing
|
||||
|
@ -1319,16 +1326,22 @@ the file including them will be republished as well."
|
|||
included-files-mtime))))))
|
||||
|
||||
(defun org-publish-cache-set-file-property
|
||||
(filename property value &optional project-name)
|
||||
(filename property value &optional project-name transient)
|
||||
"Set the VALUE for a PROPERTY of file FILENAME in publishing cache to VALUE.
|
||||
Use cache file of PROJECT-NAME. If the entry does not exist, it
|
||||
will be created. Return VALUE."
|
||||
will be created. Return VALUE.
|
||||
|
||||
When TRANSIENT is non-nil, store value in transient cache that is only
|
||||
maintained during the current publish process."
|
||||
;; Evtl. load the requested cache file:
|
||||
(when project-name (org-publish-initialize-cache project-name))
|
||||
(let ((pl (org-publish-cache-get filename)))
|
||||
(if pl (progn (plist-put pl property value) value)
|
||||
(org-publish-cache-get-file-property
|
||||
filename property value nil project-name))))
|
||||
(if transient
|
||||
(puthash (cons filename property) value
|
||||
org-publish-transient-cache)
|
||||
(let ((pl (org-publish-cache-get filename)))
|
||||
(if pl (progn (plist-put pl property value) value)
|
||||
(org-publish-cache-get-file-property
|
||||
filename property value nil project-name)))))
|
||||
|
||||
(defun org-publish-cache-get-file-property
|
||||
(filename property &optional default no-create project-name)
|
||||
|
@ -1337,13 +1350,14 @@ Use cache file of PROJECT-NAME. Return the value of that PROPERTY,
|
|||
or DEFAULT, if the value does not yet exist. Create the entry,
|
||||
if necessary, unless NO-CREATE is non-nil."
|
||||
(when project-name (org-publish-initialize-cache project-name))
|
||||
(let ((properties (org-publish-cache-get filename)))
|
||||
(cond ((null properties)
|
||||
(unless no-create
|
||||
(org-publish-cache-set filename (list property default)))
|
||||
default)
|
||||
((plist-member properties property) (plist-get properties property))
|
||||
(t default))))
|
||||
(or (gethash (cons filename property) org-publish-transient-cache)
|
||||
(let ((properties (org-publish-cache-get filename)))
|
||||
(cond ((null properties)
|
||||
(unless no-create
|
||||
(org-publish-cache-set filename (list property default)))
|
||||
default)
|
||||
((plist-member properties property) (plist-get properties property))
|
||||
(t default)))))
|
||||
|
||||
(defun org-publish-cache-get (key)
|
||||
"Return the value stored in `org-publish-cache' for key KEY.
|
||||
|
|
643
lisp/ox.el
643
lisp/ox.el
|
@ -684,31 +684,14 @@ e.g. \"stat:nil\""
|
|||
"Non-nil means interpret \"_\" and \"^\" for export.
|
||||
|
||||
If you want to control how Org displays those characters, see
|
||||
`org-use-sub-superscripts'. `org-export-with-sub-superscripts'
|
||||
used to be an alias for `org-use-sub-superscripts' in Org <8.0,
|
||||
it is not anymore.
|
||||
`org-use-sub-superscripts'.
|
||||
|
||||
When this option is turned on, you can use TeX-like syntax for
|
||||
sub- and superscripts and see them exported correctly.
|
||||
|
||||
You can also set the option with #+OPTIONS: ^:t
|
||||
|
||||
Several characters after \"_\" or \"^\" will be considered as a
|
||||
single item - so grouping with {} is normally not needed. For
|
||||
example, the following things will be parsed as single sub- or
|
||||
superscripts:
|
||||
|
||||
10^24 or 10^tau several digits will be considered 1 item.
|
||||
10^-12 or 10^-tau a leading sign with digits or a word
|
||||
x^2-y^3 will be read as x^2 - y^3, because items are
|
||||
terminated by almost any nonword/nondigit char.
|
||||
x_{i^2} or x^(2-i) braces or parenthesis do grouping.
|
||||
|
||||
Still, ambiguity is possible. So when in doubt, use {} to enclose
|
||||
the sub/superscript. If you set this variable to the symbol `{}',
|
||||
the braces are *required* in order to trigger interpretations as
|
||||
sub/superscript. This can be helpful in documents that need \"_\"
|
||||
frequently in plain text."
|
||||
See `org-use-sub-superscripts' docstring for more details."
|
||||
:group 'org-export-general
|
||||
:version "24.4"
|
||||
:package-version '(Org . "8.0")
|
||||
|
@ -1060,7 +1043,7 @@ mode."
|
|||
|
||||
(cl-defstruct (org-export-backend (:constructor org-export-create-backend)
|
||||
(:copier nil))
|
||||
name parent transcoders options filters blocks menu feature-conditions feature-implementations)
|
||||
name parent transcoders options filters blocks menu)
|
||||
|
||||
;;;###autoload
|
||||
(defun org-export-get-backend (name)
|
||||
|
@ -1166,62 +1149,6 @@ returns filters inherited from parent backends, if any."
|
|||
(setq filters (append filters (org-export-backend-filters backend))))
|
||||
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)
|
||||
"Define a new backend BACKEND.
|
||||
|
||||
|
@ -1333,24 +1260,20 @@ keywords are understood:
|
|||
`org-export-options-alist' for more information about
|
||||
structure of the values."
|
||||
(declare (indent 1))
|
||||
(let (filters menu-entry options feature-conditions feature-implementations)
|
||||
(let (filters menu-entry options)
|
||||
(while (keywordp (car body))
|
||||
(let ((keyword (pop body)))
|
||||
(pcase keyword
|
||||
(:filters-alist (setq filters (pop body)))
|
||||
(:menu-entry (setq menu-entry (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)))))
|
||||
(org-export-register-backend
|
||||
(org-export-create-backend :name backend
|
||||
:transcoders transcoders
|
||||
:options options
|
||||
:filters filters
|
||||
:menu menu-entry
|
||||
:feature-conditions feature-conditions
|
||||
:feature-implementations feature-implementations))))
|
||||
:menu menu-entry))))
|
||||
|
||||
(defun org-export-define-derived-backend (child parent &rest body)
|
||||
"Create a new backend as a variant of an existing one.
|
||||
|
@ -1397,7 +1320,7 @@ The backend could then be called with, for example:
|
|||
|
||||
(org-export-to-buffer \\='my-latex \"*Test my-latex*\")"
|
||||
(declare (indent 2))
|
||||
(let (filters menu-entry options transcoders feature-conditions feature-implementations)
|
||||
(let (filters menu-entry options transcoders)
|
||||
(while (keywordp (car body))
|
||||
(let ((keyword (pop body)))
|
||||
(pcase keyword
|
||||
|
@ -1405,8 +1328,6 @@ The backend could then be called with, for example:
|
|||
(:menu-entry (setq menu-entry (pop body)))
|
||||
(:options-alist (setq options (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)))))
|
||||
(org-export-register-backend
|
||||
(org-export-create-backend :name child
|
||||
|
@ -1414,9 +1335,7 @@ The backend could then be called with, for example:
|
|||
:transcoders transcoders
|
||||
:options options
|
||||
:filters filters
|
||||
:menu menu-entry
|
||||
:feature-conditions feature-conditions
|
||||
:feature-implementations feature-implementations))))
|
||||
:menu menu-entry))))
|
||||
|
||||
|
||||
|
||||
|
@ -1981,7 +1900,7 @@ Return a string."
|
|||
(progn ,@body)
|
||||
(org-link-broken
|
||||
(pcase (plist-get info :with-broken-links)
|
||||
(`nil (user-error "Unable to resolve link: %S" (nth 1 err)))
|
||||
(`nil (user-error "Org export aborted. Unable to resolve link: %S\nSee `org-export-with-broken-links'." (nth 1 err)))
|
||||
(`mark (org-export-data
|
||||
(format "[BROKEN LINK: %s]" (nth 1 err)) info))
|
||||
(_ nil))))))
|
||||
|
@ -2119,550 +2038,6 @@ keywords before output."
|
|||
(funcall (intern (format "org-element-%s-interpreter" type))
|
||||
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
|
||||
|
@ -3737,8 +3112,6 @@ still inferior to file-local settings."
|
|||
(when (plist-get info :with-cite-processors)
|
||||
(org-cite-process-citations info)
|
||||
(org-cite-process-bibliography info))
|
||||
;; Install all the feature conditions and implementations.
|
||||
(org-export-process-features info)
|
||||
info))
|
||||
|
||||
;;;###autoload
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
#+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] \).
|
|
@ -39,6 +39,14 @@
|
|||
#+END_SRC"
|
||||
(should (equal "27" (org-babel-execute-src-block)))))
|
||||
|
||||
(ert-deftest ob-calc/float-var ()
|
||||
"Test of floating variable."
|
||||
(org-test-with-temp-text "\
|
||||
#+BEGIN_SRC calc :results silent :var x=2.0
|
||||
1/x
|
||||
#+END_SRC"
|
||||
(should (equal "0.5" (org-babel-execute-src-block)))))
|
||||
|
||||
(ert-deftest ob-calc/simple-program-symbolic ()
|
||||
"Test of simple symbolic algebra."
|
||||
(org-test-with-temp-text "\
|
||||
|
@ -59,7 +67,14 @@
|
|||
inv(a)
|
||||
#+END_SRC "
|
||||
(should (equal "[[-1, 0.625, -0.125], [0.25, -0.5, 0.25], [0.5, 0.125, -0.125]]"
|
||||
(org-babel-execute-src-block)))))
|
||||
(let ((calc-float-format '(float 0)))
|
||||
;; ;; Make sure that older Calc buffers are not present.
|
||||
(save-current-buffer
|
||||
(when (ignore-errors (calc-select-buffer))
|
||||
(kill-buffer)))
|
||||
;; Now, let-bound `calc-float-format' will take
|
||||
;; effect.
|
||||
(org-babel-execute-src-block))))))
|
||||
|
||||
(ert-deftest ob-calc/matrix-algebra ()
|
||||
"Test of simple matrix algebra."
|
||||
|
|
|
@ -205,14 +205,14 @@ Here is one at the end of a line. {{{results(=2=)}}}
|
|||
|
||||
(ert-deftest ob-exp/exports-inline-code ()
|
||||
(should
|
||||
(equal "src_emacs-lisp[]{(+ 1 1)}"
|
||||
(equal "src_emacs-lisp[ :exports code]{(+ 1 1)}"
|
||||
(org-test-with-temp-text "src_emacs-lisp[:exports code]{(+ 1 1)}"
|
||||
(let ((org-babel-inline-result-wrap "=%s=")
|
||||
(org-export-use-babel t))
|
||||
(org-babel-exp-process-buffer))
|
||||
(buffer-string))))
|
||||
(should
|
||||
(equal "src_emacs-lisp[]{(+ 1 1)}"
|
||||
(equal "src_emacs-lisp[ :exports code]{(+ 1 1)}"
|
||||
(org-test-with-temp-text "src_emacs-lisp[ :exports code ]{(+ 1 1)}"
|
||||
(let ((org-babel-inline-result-wrap "=%s=")
|
||||
(org-export-use-babel t))
|
||||
|
@ -220,14 +220,14 @@ Here is one at the end of a line. {{{results(=2=)}}}
|
|||
(buffer-string))))
|
||||
;; Do not escape characters in inline source blocks.
|
||||
(should
|
||||
(equal "src_c[]{*a}"
|
||||
(equal "src_c[ :exports code]{*a}"
|
||||
(org-test-with-temp-text "src_c[ :exports code ]{*a}"
|
||||
(let ((org-babel-inline-result-wrap "=%s=")
|
||||
(org-export-use-babel t))
|
||||
(org-babel-exp-process-buffer))
|
||||
(buffer-string))))
|
||||
(should
|
||||
(equal "src_emacs-lisp[]{(+ 1 1)} {{{results(=2=)}}}"
|
||||
(equal "src_emacs-lisp[ :exports both]{(+ 1 1)} {{{results(=2=)}}}"
|
||||
(org-test-with-temp-text "src_emacs-lisp[:exports both]{(+ 1 1)}"
|
||||
(let ((org-babel-inline-result-wrap "=%s=")
|
||||
(org-export-use-babel t))
|
||||
|
@ -262,10 +262,10 @@ Here is one at the end of a line. {{{results(=2=)}}}
|
|||
(string-match
|
||||
(replace-regexp-in-string
|
||||
"\\\\\\[]{" "\\(?:\\[]\\)?{" ;accept both src_sh[]{...} or src_sh{...}
|
||||
(regexp-quote "Here is one in the middle src_sh[]{echo 1} of a line.
|
||||
Here is one at the end of a line. src_sh[]{echo 2}
|
||||
src_sh[]{echo 3} Here is one at the beginning of a line.
|
||||
Here is one that is also evaluated: src_sh[]{echo 4} {{{results(=4=)}}}")
|
||||
(regexp-quote "Here is one in the middle src_sh[ :exports code]{echo 1} of a line.
|
||||
Here is one at the end of a line. src_sh[ :exports code]{echo 2}
|
||||
src_sh[ :exports code]{echo 3} Here is one at the beginning of a line.
|
||||
Here is one that is also evaluated: src_sh[ :exports both]{echo 4} {{{results(=4=)}}}")
|
||||
nil t)
|
||||
(org-test-at-id "cd54fc88-1b6b-45b6-8511-4d8fa7fc8076"
|
||||
(org-narrow-to-subtree)
|
||||
|
@ -301,7 +301,7 @@ be evaluated."
|
|||
(ert-deftest ob-exp/exports-inline-code-double-eval-exports-both ()
|
||||
(let ((org-export-use-babel t))
|
||||
(should
|
||||
(string-match (concat "\\`src_emacs-lisp\\(?:\\[]\\)?{(\\+ 1 1)} "
|
||||
(string-match (concat "\\`src_emacs-lisp\\(?:\\[.+?]\\)?{(\\+ 1 1)} "
|
||||
"{{{results(src_emacs-lisp\\[ :exports code\\]{2})}}}$")
|
||||
(org-test-with-temp-text
|
||||
(concat "src_emacs-lisp[:exports both :results code "
|
||||
|
@ -403,7 +403,7 @@ be evaluated."
|
|||
: 2
|
||||
|
||||
#+NAME: src1
|
||||
#+begin_src emacs-lisp
|
||||
#+begin_src emacs-lisp :exports both
|
||||
\(+ 1 1)
|
||||
#+end_src"
|
||||
(org-test-with-temp-text
|
||||
|
|
|
@ -77,9 +77,9 @@ return x[1]
|
|||
|
||||
|
||||
(ert-deftest test-ob-lua/colnames-yes-header-argument-pp ()
|
||||
"Test table passing with `colnames' header and pp option."
|
||||
"Test table passing with `colnames' header and `pp' option."
|
||||
(should
|
||||
(equal "a = 12\nb = 13\n"
|
||||
(equal "a = 12\nb = 13"
|
||||
(org-test-with-temp-text
|
||||
"#+name: eg
|
||||
| col | val |
|
||||
|
@ -99,7 +99,7 @@ return x
|
|||
(ert-deftest test-ob-lua/colnames-nil-header-argument ()
|
||||
"Test table with `colnames' set to `nil'."
|
||||
(should
|
||||
(equal "1 = a\n2 = b\n"
|
||||
(equal "1 = a\n2 = b"
|
||||
(org-test-with-temp-text
|
||||
"#+name: eg
|
||||
| col |
|
||||
|
@ -119,7 +119,7 @@ return x
|
|||
(ert-deftest test-ob-lua/colnames-no-header-argument ()
|
||||
"Test table passing without `colnames'."
|
||||
(should
|
||||
(equal "1 = col\n2 = a\n3 = b\n"
|
||||
(equal "1 = col\n2 = a\n3 = b"
|
||||
(org-test-with-temp-text
|
||||
"#+name: eg
|
||||
| col |
|
||||
|
@ -136,6 +136,46 @@ return x
|
|||
(org-babel-next-src-block)
|
||||
(org-babel-execute-src-block)))))
|
||||
|
||||
(ert-deftest test-ob-lua/types ()
|
||||
"Test returning different types."
|
||||
(should
|
||||
(equal "nil"
|
||||
(org-test-with-temp-text "src_lua{return nil}"
|
||||
(org-babel-execute-src-block))))
|
||||
(should
|
||||
(equal "true"
|
||||
(org-test-with-temp-text "src_lua{return true}"
|
||||
(org-babel-execute-src-block))))
|
||||
(should
|
||||
(equal "false"
|
||||
(org-test-with-temp-text "src_lua{return false}"
|
||||
(org-babel-execute-src-block))))
|
||||
(should
|
||||
(equal 1
|
||||
(org-test-with-temp-text "src_lua{return 1}"
|
||||
(org-babel-execute-src-block))))
|
||||
(should
|
||||
(equal "hello world"
|
||||
(org-test-with-temp-text "src_lua{return 'hello world'}"
|
||||
(org-babel-execute-src-block))))
|
||||
(should
|
||||
(equal 0
|
||||
(string-match "table: 0x[0-9A-F]+"
|
||||
(org-test-with-temp-text "src_lua{return {}}"
|
||||
(org-babel-execute-src-block))))))
|
||||
|
||||
(ert-deftest test-ob-lua/multiple-values ()
|
||||
"Test returning multiple values."
|
||||
(should
|
||||
(equal "1, 2, 3"
|
||||
(org-test-with-temp-text "src_lua{return 1, 2, 3}"
|
||||
(org-babel-execute-src-block))))
|
||||
(should
|
||||
(equal "1|2|3"
|
||||
(let ((org-babel-lua-multiple-values-separator "|"))
|
||||
(org-test-with-temp-text "src_lua{return 1, 2, 3}"
|
||||
(org-babel-execute-src-block))))))
|
||||
|
||||
(provide 'test-ob-lua)
|
||||
|
||||
;;; test-ob-lua.el ends here
|
||||
|
|
|
@ -183,6 +183,7 @@ that will return all elements of the array as a single string."
|
|||
echo ${array}
|
||||
<point>
|
||||
#+end_src"
|
||||
(skip-unless (executable-find "bash"))
|
||||
(should (equal "one" (org-trim (org-babel-execute-src-block))))))
|
||||
|
||||
(ert-deftest test-ob-shell/generic-uses-no-assoc-arrays-simple-map ()
|
||||
|
@ -229,6 +230,10 @@ echo ${table}
|
|||
Bash will see a table that contains the first column as the
|
||||
'index' of the associative array, and the second column as the
|
||||
value. "
|
||||
(skip-unless
|
||||
;; Old GPLv2 BASH in macOSX does not support associative arrays.
|
||||
(if-let ((bash (executable-find "bash")))
|
||||
(eq 0 (process-file bash nil nil nil "-c" "declare -A assoc_array"))))
|
||||
(org-test-with-temp-text
|
||||
"#+NAME: sample_mapping_table
|
||||
| first | one |
|
||||
|
@ -248,6 +253,10 @@ echo ${table[second]}
|
|||
|
||||
Bash will see an associative array that contains each row as a single
|
||||
string. Bash cannot handle lists in associative arrays."
|
||||
(skip-unless
|
||||
;; Old GPLv2 BASH in macOSX does not support associative arrays.
|
||||
(if-let ((bash (executable-find "bash")))
|
||||
(eq 0 (process-file bash nil nil nil "-c" "declare -A assoc_array"))))
|
||||
(org-test-with-temp-text
|
||||
"#+NAME: sample_big_table
|
||||
| bread | 2 | kg |
|
||||
|
|
|
@ -191,20 +191,20 @@ echo 1
|
|||
* Main
|
||||
#+header: :tangle \"test-ob-tangle.el\" :comments noweb :noweb yes
|
||||
#+begin_src emacs-lisp
|
||||
1
|
||||
<<inner>>
|
||||
'(1
|
||||
<<inner>>)
|
||||
#+end_src"
|
||||
(unwind-protect
|
||||
(let ((org-babel-tangle-use-relative-file-links t))
|
||||
(org-babel-tangle)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents "test-ob-tangle.el")
|
||||
(buffer-string)
|
||||
(goto-char (point-min))
|
||||
(and
|
||||
(search-forward (concat ";; [[file:" (file-name-nondirectory file) "::inner") nil t)
|
||||
(search-forward ";; inner ends here" nil t))))
|
||||
(delete-file "test-ob-tangle.el")))))
|
||||
(unwind-protect
|
||||
(let ((org-babel-tangle-use-relative-file-links t))
|
||||
(org-babel-tangle)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents "test-ob-tangle.el")
|
||||
(buffer-string)
|
||||
(goto-char (point-min))
|
||||
(and
|
||||
(search-forward (concat ";; [[file:" (file-name-nondirectory file) "::inner") nil t)
|
||||
(search-forward ";; inner ends here\n" nil t))))
|
||||
(delete-file "test-ob-tangle.el")))))
|
||||
|
||||
(ert-deftest ob-tangle/comment-noweb-absolute ()
|
||||
"Test :comments noweb tangling with absolute file path."
|
||||
|
|
|
@ -2545,6 +2545,70 @@ abc
|
|||
(lambda (&rest _) (error "No warnings should occur"))))
|
||||
(org-babel-import-elisp-from-file (buffer-file-name))))))
|
||||
|
||||
(ert-deftest test-ob/org-babel-read ()
|
||||
"Test `org-babel-read' specifications."
|
||||
(dolist (inhibit '(t nil))
|
||||
;; A number
|
||||
(should (equal 1 (org-babel-read "1" inhibit)))
|
||||
(should (equal -1 (org-babel-read "-1" inhibit)))
|
||||
(should (equal 1.2 (org-babel-read "1.2" inhibit)))
|
||||
;; Allow whitespace
|
||||
(should (equal 1 (org-babel-read " 1 " inhibit)))
|
||||
(should (equal 1 (org-babel-read " 1\n" inhibit)))
|
||||
;; Not a number
|
||||
(should-not (equal 1 (org-babel-read "1foo" inhibit)))
|
||||
;; Empty string
|
||||
(should (equal "" (org-babel-read "" inhibit)))
|
||||
(should (equal " " (org-babel-read " " inhibit)))
|
||||
;; Elisp function call
|
||||
(should
|
||||
(equal (if inhibit
|
||||
;; Verbatim string, with spaces
|
||||
"(+ 1 2) "
|
||||
;; Result of evaluation
|
||||
3)
|
||||
(org-babel-read "(+ 1 2) " inhibit)))
|
||||
;; Elisp function call must start from (
|
||||
(should-not (equal 3 (org-babel-read " (+ 1 2)" nil)))
|
||||
(should
|
||||
(equal (if inhibit
|
||||
"'(1 2)"
|
||||
;; Result of evaluation
|
||||
'(1 2))
|
||||
(org-babel-read "'(1 2)" inhibit)))
|
||||
;; `(...)
|
||||
(should
|
||||
(equal (if inhibit
|
||||
"`(1 ,(+ 1 2))"
|
||||
;; Result of evaluation
|
||||
'(1 3))
|
||||
(org-babel-read "`(1 ,(+ 1 2))" inhibit)))
|
||||
;; [...]
|
||||
(should
|
||||
(equal (if inhibit
|
||||
"[1 2 (foo)]"
|
||||
;; Result of evaluation
|
||||
[1 2 (foo)])
|
||||
(org-babel-read "[1 2 (foo)]" inhibit)))
|
||||
;; Special case: *this* literal is evaluated
|
||||
(defvar *this* nil)
|
||||
(let ((*this* 100))
|
||||
(should
|
||||
(equal
|
||||
(if inhibit "*this*" 100)
|
||||
(org-babel-read "*this*" inhibit))))
|
||||
;; Special case: data inside quotes
|
||||
(should (equal "foo" (org-babel-read " \"foo\" " inhibit)))
|
||||
(should (equal "foo" (org-babel-read " \"foo\"\n" inhibit)))
|
||||
(should (equal "foo with\" inside" (org-babel-read " \"foo with\\\" inside\" " inhibit)))
|
||||
(should (equal "abc\nsdf" (org-babel-read "\"abc\nsdf\"" inhibit)))
|
||||
(should (equal "foo" (org-babel-read "\"foo\"" inhibit)))
|
||||
(should (equal "\"foo\"(\"bar\"" (org-babel-read "\"foo\"(\"bar\"" inhibit)))
|
||||
;; Unpaired quotes
|
||||
(should (equal "\"foo\"\"bar\"" (org-babel-read "\"foo\"\"bar\"" inhibit)))
|
||||
;; Recover from `read' parsing errors.
|
||||
(org-babel-read "\"Quoted closing quote:\\\"" inhibit)))
|
||||
|
||||
(ert-deftest test-ob/demarcate-block-split-duplication ()
|
||||
"Test duplication of language, body, switches, and headers in splitting."
|
||||
(let ((caption "#+caption: caption.")
|
||||
|
|
|
@ -61,6 +61,7 @@ See https://github.com/yantar92/org/issues/4."
|
|||
(org-test-with-temp-text "* Org link test
|
||||
[[https://example.com][A link to a site]]"
|
||||
(dotimes (_ 2)
|
||||
(font-lock-ensure)
|
||||
(goto-char 1)
|
||||
(re-search-forward "\\[")
|
||||
(should-not (org-xor org-link-descriptive (org-invisible-p)))
|
||||
|
|
|
@ -690,43 +690,65 @@ Sunday 7 January 2024
|
|||
(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>"
|
||||
"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))))))
|
||||
|
||||
(ert-deftest test-org-agenda/diary-timestamp ()
|
||||
"Test diary timestamp handling."
|
||||
(org-test-at-time
|
||||
"2024-01-15"
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 01 15 2024)>"
|
||||
(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>"
|
||||
(should (search-forward "foo" nil t)))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 02 15 2024)>"
|
||||
(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>"
|
||||
(should-not (search-forward "foo" nil t)))
|
||||
;; Test time and time ranges in diary timestamps.
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 01 15 2024) 12:00>"
|
||||
(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>"
|
||||
(should (search-forward "12:00" nil t)))
|
||||
(org-test-agenda-with-agenda
|
||||
"* TODO foo\n<%%(diary-date 01 15 2024) 12:00-14:00>"
|
||||
(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))))))
|
||||
(should (search-forward "12:00-14:00" nil t)))))
|
||||
|
||||
|
||||
;; agenda redo
|
||||
|
|
|
@ -1398,6 +1398,13 @@
|
|||
|
||||
;;; Dynamic block
|
||||
|
||||
(defun test-org-colview/dblock-formatter (ipos table params)
|
||||
"User-defined columnview dblock formatting function."
|
||||
(goto-char ipos)
|
||||
(insert-before-markers "Hello columnview!" "\n")
|
||||
(insert-before-markers (format "table has %d rows" (length table)) "\n")
|
||||
(insert-before-markers (format "there are %d parameters" (/ (length params) 2))))
|
||||
|
||||
(ert-deftest test-org-colview/dblock ()
|
||||
"Test the column view table."
|
||||
(should
|
||||
|
@ -1703,6 +1710,31 @@ SCHEDULED: <2020-05-11 Mon> DEADLINE: <2020-05-14 Thu>
|
|||
(let ((org-columns-default-format
|
||||
"%ITEM %DEADLINE(d) %SCHEDULED(s) %TIMESTAMP(t)"))
|
||||
(org-update-dblock))
|
||||
(buffer-substring-no-properties (point) (point-max)))))
|
||||
;; custom formatting function
|
||||
(should
|
||||
(equal
|
||||
"#+BEGIN: columnview :formatter test-org-colview/dblock-formatter
|
||||
Hello columnview!
|
||||
table has 3 rows
|
||||
there are 4 parameters
|
||||
#+END:"
|
||||
(org-test-with-temp-text
|
||||
"* H\n<point>#+BEGIN: columnview :formatter test-org-colview/dblock-formatter\n#+END:"
|
||||
(let ((org-columns-default-format "%ITEM"))
|
||||
(org-update-dblock))
|
||||
(buffer-substring-no-properties (point) (point-max)))))
|
||||
;; test headline linkification
|
||||
(should
|
||||
(equal
|
||||
"#+BEGIN: columnview :link t
|
||||
| ITEM |
|
||||
|------|
|
||||
| [[*H][H]] |
|
||||
#+END:"
|
||||
(org-test-with-temp-text
|
||||
"* H\n<point>#+BEGIN: columnview :link t\n#+END:"
|
||||
(let ((org-columns-default-format "%ITEM")) (org-update-dblock))
|
||||
(buffer-substring-no-properties (point) (point-max))))))
|
||||
|
||||
(provide 'test-org-colview)
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
;;; test-org-ctags.el --- tests for org-ctags.el -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2024 Max Nikulin
|
||||
;; Authors: Max Nikulin
|
||||
|
||||
;; 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:
|
||||
|
||||
;; Alternative implementation for `test-org-ctags/mock-command'
|
||||
;; is required for cmd.exe.
|
||||
(unless (string-equal "-c" shell-command-switch)
|
||||
(signal 'missing-test-dependency "POSIX shell"))
|
||||
|
||||
(require 'org-ctags)
|
||||
|
||||
;;;; Helpers:
|
||||
|
||||
(defun test-org-ctags/mock-command (temp-file command-name)
|
||||
"Define shell function COMMAND-NAME wrining arguments to TEMP-FILE."
|
||||
;; Failure exit code is used to prevent further `org-ctags' actions.
|
||||
(format "%s() { printf '%%s\\n' %s \"$@\" >%s 2>&1 ; false ; } ; %s"
|
||||
command-name command-name
|
||||
(shell-quote-argument temp-file)
|
||||
command-name))
|
||||
|
||||
(defun test-org-ctags/get-args (temp-file base magic)
|
||||
"Read list of strings from TEMP-FILE.
|
||||
|
||||
If TEMP-FILE does not start from MAGIC then return
|
||||
its content as a string. Otherwise strip first line
|
||||
and trailing newline, replace BASE with \"TMPDIR\" string,
|
||||
return list of lines."
|
||||
(let* ((case-fold-search nil)
|
||||
(content
|
||||
(and
|
||||
(file-exists-p temp-file)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents temp-file)
|
||||
(goto-char (point-min))
|
||||
(when (looking-at magic)
|
||||
(while (search-forward base nil 'noerror)
|
||||
(replace-match "TMPDIR" 'fixedcase 'literal)))
|
||||
(goto-char (point-max))
|
||||
(when (and (bolp) (> (point) 1))
|
||||
(delete-char -1))
|
||||
(buffer-string)))))
|
||||
(if (and content (string-prefix-p magic content))
|
||||
(cdr (split-string content "\n"))
|
||||
content)))
|
||||
|
||||
(defmacro test-org-ctags/with-fake-ctags
|
||||
(temp-dir subdir &rest body)
|
||||
"Run BODY with `org-ctags-path-to-ctags' set to a test function.
|
||||
|
||||
Create a buffer backed by a file in the TEMP-DIR/SUBDIR directory."
|
||||
(declare (indent 2))
|
||||
(let ((buffer (gensym "buffer"))
|
||||
(base (gensym "base"))
|
||||
(dir (gensym "dir"))
|
||||
(temp-file (gensym "temp-file")))
|
||||
`(let* ((,base ,temp-dir)
|
||||
(,dir (concat ,base "/" ,subdir))
|
||||
(,temp-file (concat ,dir "/ctags.txt"))
|
||||
(org-ctags-path-to-ctags
|
||||
(test-org-ctags/mock-command ,temp-file "ctags_mock"))
|
||||
,buffer)
|
||||
(make-directory ,dir)
|
||||
(unwind-protect
|
||||
;; `org-ctags' commands call `buffer-file-name'.
|
||||
(with-current-buffer
|
||||
(setq ,buffer (find-file-noselect ,temp-file))
|
||||
(insert "Should be overwritten by org-ctags mock script")
|
||||
(save-buffer)
|
||||
,@body
|
||||
(test-org-ctags/get-args ,temp-file ,base "ctags_mock\n"))
|
||||
(kill-buffer ,buffer)
|
||||
(delete-file ,temp-file)
|
||||
(delete-directory ,dir)))))
|
||||
|
||||
;;;; Comparator to have informative failures:
|
||||
|
||||
(defun test-org-ctags/list-elements (lst &optional indicies)
|
||||
"Select INDICIES elements from LST list.
|
||||
|
||||
INDICIES should be sorted in growing order."
|
||||
(if (not (and indicies (listp lst)))
|
||||
lst
|
||||
(let (selected
|
||||
(prev 0))
|
||||
(dolist (i indicies (nreverse selected))
|
||||
(setq lst (nthcdr (- i prev) lst))
|
||||
(setq prev i)
|
||||
(push (car lst) selected)))))
|
||||
|
||||
(defun test-org-ctags/list-elements-equal-p
|
||||
(expect actual indicies &rest _comments)
|
||||
"Call `equal' for lists EXPECT and INDICIES elements from ACTUAL.
|
||||
|
||||
_COMMENTS should appear in failure message."
|
||||
(equal expect
|
||||
(test-org-ctags/list-elements actual indicies)))
|
||||
|
||||
(defun test-org-ctags/list-elements-equal-explain
|
||||
(expect actual indicies &rest _comments)
|
||||
"`ert-eplainer' for `test-org-ctags/list-elements-equal-p'."
|
||||
(if (listp actual)
|
||||
(list
|
||||
'selected-elements
|
||||
(test-org-ctags/list-elements actual indicies))
|
||||
"Shell command failed"))
|
||||
|
||||
(put 'test-org-ctags/list-elements-equal-p
|
||||
'ert-explainer
|
||||
'test-org-ctags/list-elements-equal-explain)
|
||||
|
||||
;;;; Tests:
|
||||
|
||||
(ert-deftest test-org-ctags/create-tags-escape ()
|
||||
"Test that `org-ctags-create-tags' escapes shell arguments."
|
||||
(let ((temp-dir (make-temp-file "test-org-ctags-" 'dir)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(should
|
||||
(test-org-ctags/list-elements-equal-p
|
||||
(list (format "--regex-orgmode=%s" org-ctags-tag-regexp))
|
||||
(test-org-ctags/with-fake-ctags temp-dir "regexp"
|
||||
(org-ctags-create-tags))
|
||||
'(2)
|
||||
"Regexp should be escaped."))
|
||||
|
||||
(should
|
||||
(test-org-ctags/list-elements-equal-p
|
||||
'("TMPDIR/regular/ctags.txt")
|
||||
(test-org-ctags/with-fake-ctags temp-dir "regular"
|
||||
(org-ctags-create-tags (concat temp-dir "/regular")))
|
||||
'(7)
|
||||
"Wildcard should be expanded."
|
||||
"Directory passed as an argument."))
|
||||
|
||||
(should
|
||||
(test-org-ctags/list-elements-equal-p
|
||||
'("TMPDIR/space char/TAGS" "TMPDIR/space char/ctags.txt")
|
||||
(test-org-ctags/with-fake-ctags temp-dir "space char"
|
||||
(org-ctags-create-tags (concat temp-dir "/space char")))
|
||||
'(4 7)
|
||||
"Space characters should not split arguments."
|
||||
"Directory passed as an argument."))
|
||||
|
||||
(should
|
||||
(test-org-ctags/list-elements-equal-p
|
||||
'("TMPDIR/apostrophe' sep '/TAGS" "TMPDIR/apostrophe' sep '/ctags.txt")
|
||||
(test-org-ctags/with-fake-ctags temp-dir "apostrophe' sep '"
|
||||
(org-ctags-create-tags))
|
||||
'(4 7)
|
||||
"Apostrophes should be regular characters."
|
||||
"Path is derived from `default-directory'."))
|
||||
|
||||
(should
|
||||
(test-org-ctags/list-elements-equal-p
|
||||
'("TMPDIR/def-dir.$HOME/TAGS" "TMPDIR/def-dir.$HOME/ctags.txt")
|
||||
(test-org-ctags/with-fake-ctags temp-dir "def-dir.$HOME"
|
||||
(org-ctags-create-tags))
|
||||
'(4 7)
|
||||
"$VARIABLES should not be expanded in directory names."
|
||||
"Path is derived from `default-directory'."))
|
||||
|
||||
(should
|
||||
(test-org-ctags/list-elements-equal-p
|
||||
'("TMPDIR/arg.$HOME/TAGS" "TMPDIR/arg.$HOME/ctags.txt")
|
||||
(test-org-ctags/with-fake-ctags temp-dir "arg.$HOME"
|
||||
(org-ctags-create-tags (concat temp-dir "/arg.$HOME")))
|
||||
'(4 7)
|
||||
"$VARIABLES should not be expanded in directory names."
|
||||
"Directory passed as an argument")))
|
||||
(delete-directory temp-dir))))
|
||||
|
||||
(provide 'test-org-ctags)
|
||||
;;; test-org.el ends here
|
|
@ -3989,8 +3989,31 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
|
|||
(org-test-parse-and-interpret
|
||||
"<2012-03-29 thu. 16:40-16:41>")))
|
||||
;; Diary.
|
||||
(should (equal (org-test-parse-and-interpret "<%%diary-float t 4 2>")
|
||||
"<%%diary-float t 4 2>\n"))
|
||||
(should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2)>")
|
||||
"<%%(diary-float t 4 2)>\n"))
|
||||
;; Diary with time.
|
||||
(should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2) 12:00>")
|
||||
"<%%(diary-float t 4 2) 12:00>\n"))
|
||||
(should (equal (org-test-parse-and-interpret "<%%(diary-cyclic 1 1 1 2020) 12:00-14:00>")
|
||||
"<%%(diary-cyclic 1 1 1 2020) 12:00-14:00>\n"))
|
||||
(org-test-with-temp-text "<%%(diary-float t 4 2) 12:00>"
|
||||
(let ((ts (org-element-context)))
|
||||
(should (org-element-type-p ts 'timestamp))
|
||||
(should (eq 'diary (org-element-property :type ts)))
|
||||
(should (eq nil (org-element-property :range-type ts)))
|
||||
(should (equal 12 (org-element-property :hour-start ts)))
|
||||
(should (equal 0 (org-element-property :minute-start ts)))
|
||||
(should-not (org-element-property :hour-end ts))
|
||||
(should-not (org-element-property :minute-end ts))))
|
||||
(org-test-with-temp-text "<%%(diary-float t 4 2) 12:00-14:01>"
|
||||
(let ((ts (org-element-context)))
|
||||
(should (org-element-type-p ts 'timestamp))
|
||||
(should (eq 'diary (org-element-property :type ts)))
|
||||
(should (eq 'timerange (org-element-property :range-type ts)))
|
||||
(should (equal 12 (org-element-property :hour-start ts)))
|
||||
(should (equal 0 (org-element-property :minute-start ts)))
|
||||
(should (equal 14 (org-element-property :hour-end ts)))
|
||||
(should (equal 1 (org-element-property :minute-end ts)))))
|
||||
;; Timestamp with repeater interval, repeater deadline, with delay, with combinations.
|
||||
(should
|
||||
(string-match "<2012-03-29 .* \\+1y>"
|
||||
|
@ -4983,6 +5006,18 @@ Text
|
|||
|
||||
|
||||
;;; Test Cache.
|
||||
(ert-deftest test-org-element/cache-map ()
|
||||
"Test `org-element-cache-map'."
|
||||
(org-test-with-temp-text "* headline\n:DRAWER:\nparagraph\n:END:\n* headline 2"
|
||||
(should
|
||||
(equal
|
||||
'(org-data headline section drawer paragraph headline)
|
||||
(org-element-cache-map #'car :granularity 'element))))
|
||||
(should
|
||||
(equal
|
||||
'(org-data headline section drawer paragraph)
|
||||
(org-test-with-temp-text "* headline\n:DRAWER:\nparagraph\n:END:"
|
||||
(org-element-cache-map #'car :granularity 'element)))))
|
||||
|
||||
(ert-deftest test-org-element/cache ()
|
||||
"Test basic expectations and common pitfalls for cache."
|
||||
|
|
|
@ -1,344 +0,0 @@
|
|||
;;; 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
|
|
@ -22,6 +22,21 @@
|
|||
|
||||
;;; Macros
|
||||
|
||||
(ert-deftest test-org-macro/initialize-templates ()
|
||||
"Test `org-macro-initialize-templates'."
|
||||
;; No code is executed during loading of Org mode files."
|
||||
(should
|
||||
(org-test-with-temp-text
|
||||
"#+MACRO: title (eval (eval-and-compile (error \"CVE-2024-30202\")))"
|
||||
(progn
|
||||
(org-macro-initialize-templates)
|
||||
t)))
|
||||
(org-test-with-temp-text
|
||||
"#+MACRO: title (eval (eval-and-compile (error \"CVE-2024-30202\")))"
|
||||
(progn
|
||||
(org-mode)
|
||||
t)))
|
||||
|
||||
(ert-deftest test-org/macro-replace-all ()
|
||||
"Test `org-macro-replace-all' specifications."
|
||||
;; Standard test.
|
||||
|
|
|
@ -938,7 +938,7 @@ See also URL `https://orgmode.org/worg/org-tutorials/org-lookups.html'."
|
|||
;; For Lisp formula
|
||||
(should (equal "\"0\"" (org-table-make-reference "0" nil nil t)))
|
||||
(should (equal "\"z\"" (org-table-make-reference "z" nil nil t)))
|
||||
(should (equal "" (org-table-make-reference "" nil nil t)))
|
||||
(should (equal "\"\"" (org-table-make-reference "" nil nil t)))
|
||||
(should (equal "\"0\" \"1\"" (org-table-make-reference '("0" "1") nil nil t)))
|
||||
(should (equal "\"z\" \"1\"" (org-table-make-reference '("z" "1") nil nil t)))
|
||||
(should (equal "\"1\"" (org-table-make-reference '("" "1") nil nil t)))
|
||||
|
@ -965,7 +965,7 @@ See also URL `https://orgmode.org/worg/org-tutorials/org-lookups.html'."
|
|||
;; For Lisp formula
|
||||
(should (equal "0" (org-table-make-reference "0" nil t t)))
|
||||
(should (equal "0" (org-table-make-reference "z" nil t t)))
|
||||
(should (equal "" (org-table-make-reference "" nil t t)))
|
||||
(should (equal "0" (org-table-make-reference "" nil t t)))
|
||||
(should (equal "0 1" (org-table-make-reference '("0" "1") nil t t)))
|
||||
(should (equal "0 1" (org-table-make-reference '("z" "1") nil t t)))
|
||||
(should (equal "1" (org-table-make-reference '("" "1") nil t t)))
|
||||
|
|
|
@ -9356,6 +9356,18 @@ CLOSED: %s
|
|||
(org-test-with-temp-text "* H1\n<point>Paragraph\n* H2"
|
||||
(org-paste-subtree nil "* Text")
|
||||
(buffer-string))))
|
||||
;; With prefix argument, move to the end of subtree.
|
||||
(should
|
||||
(equal "* H1\nParagraph\n** H1.1\n* Text\n* H2"
|
||||
(org-test-with-temp-text "* H1\n<point>Paragraph\n** H1.1\n* H2"
|
||||
(org-paste-subtree '(4) "* Text")
|
||||
(buffer-string))))
|
||||
;; With double prefix argument, move to first sibling
|
||||
(should
|
||||
(equal "* H1\nParagraph\n** Text\n** H1.1\n* H2"
|
||||
(org-test-with-temp-text "* H1\n<point>Paragraph\n** H1.1\n* H2"
|
||||
(org-paste-subtree '(16) "* Text")
|
||||
(buffer-string))))
|
||||
;; If point is between two headings, use the deepest level.
|
||||
(should
|
||||
(equal "* H1\n\n* Text\n* H2"
|
||||
|
@ -9372,6 +9384,30 @@ CLOSED: %s
|
|||
(org-test-with-temp-text "* H1\n<point>\n** H2"
|
||||
(org-paste-subtree nil "* Text")
|
||||
(buffer-string))))
|
||||
;; When point is on heading at bol, insert before
|
||||
(should
|
||||
(equal "* Text\n* H1\n** H2"
|
||||
(org-test-with-temp-text "<point>* H1\n** H2"
|
||||
(org-paste-subtree nil "*** Text")
|
||||
(buffer-string))))
|
||||
;; With prefix argument, ignore that we are at bol
|
||||
(should
|
||||
(equal "* H1\n** H2\n* Text\n"
|
||||
(org-test-with-temp-text "<point>* H1\n** H2"
|
||||
(org-paste-subtree '(4) "*** Text")
|
||||
(buffer-string))))
|
||||
;; When point is on heading but not at bol, use smallest level among
|
||||
;; current heading and next, inserting before the next heading.
|
||||
(should
|
||||
(equal "* H1\ncontents\n** Text\n** H2"
|
||||
(org-test-with-temp-text "* H1<point>\ncontents\n** H2"
|
||||
(org-paste-subtree nil "*** Text")
|
||||
(buffer-string))))
|
||||
(should
|
||||
(equal "*** H1\ncontents\n*** Text\n* H2"
|
||||
(org-test-with-temp-text "*** H1<point>\ncontents\n* H2"
|
||||
(org-paste-subtree nil "* Text")
|
||||
(buffer-string))))
|
||||
;; When on an empty heading, after the stars, deduce the new level
|
||||
;; from the number of stars.
|
||||
(should
|
||||
|
|
|
@ -39,5 +39,67 @@
|
|||
(goto-char (point-min))
|
||||
(should (search-forward "#### Footnotes"))))))
|
||||
|
||||
(ert-deftest ox-md/headline-style ()
|
||||
"Test `org-md-headline-style' being honored."
|
||||
(dolist (org-md-headline-style '(atx setext mixed))
|
||||
(let ((export-buffer "*Test MD Export*")
|
||||
(org-export-show-temporary-export-buffer nil))
|
||||
(org-test-with-temp-text "#+options: toc:nil h:10
|
||||
* level 1
|
||||
** level 2
|
||||
*** level 3
|
||||
**** level 4
|
||||
***** level 5
|
||||
****** level 6
|
||||
******* level 7
|
||||
"
|
||||
(org-export-to-buffer 'md export-buffer)
|
||||
(with-current-buffer export-buffer
|
||||
(goto-char (point-min))
|
||||
(pcase org-md-headline-style
|
||||
(`atx
|
||||
(should (search-forward "# level 1"))
|
||||
(should (search-forward "## level 2"))
|
||||
(should (search-forward "### level 3"))
|
||||
(should (search-forward "#### level 4"))
|
||||
(should (search-forward "##### level 5"))
|
||||
(should (search-forward "###### level 6"))
|
||||
(should (search-forward "1. level 7")))
|
||||
(`setext
|
||||
(should (search-forward "level 1\n======="))
|
||||
(should (search-forward "level 2\n------"))
|
||||
(should (search-forward "1. level 3"))
|
||||
(should (search-forward "1. level 4"))
|
||||
(should (search-forward "1. level 5"))
|
||||
(should (search-forward "1. level 6"))
|
||||
(should (search-forward "1. level 7")))
|
||||
(`mixed
|
||||
(should (search-forward "level 1\n======="))
|
||||
(should (search-forward "level 2\n------"))
|
||||
(should (search-forward "### level 3"))
|
||||
(should (search-forward "#### level 4"))
|
||||
(should (search-forward "##### level 5"))
|
||||
(should (search-forward "###### level 6"))
|
||||
(should (search-forward "1. level 7")))))))))
|
||||
|
||||
(ert-deftest ox-md/item ()
|
||||
"Test `org-md-item'."
|
||||
;; Align items at column 4.
|
||||
;; Columns >=100 not aligned.
|
||||
(org-test-with-temp-text
|
||||
(mapconcat
|
||||
#'identity
|
||||
(cl-loop for n from 1 to 105
|
||||
collect (format "%d. item" n))
|
||||
"\n")
|
||||
(let ((export-buffer "*Test MD Export*")
|
||||
(org-export-show-temporary-export-buffer nil))
|
||||
(org-export-to-buffer 'md export-buffer)
|
||||
(with-current-buffer export-buffer
|
||||
(goto-char (point-min))
|
||||
(should (search-forward "1. item"))
|
||||
(should (search-forward "10. item"))
|
||||
(should (search-forward "101. item"))))))
|
||||
|
||||
(provide 'test-ox-md)
|
||||
;;; test-ox-md.el ends here
|
||||
|
|
|
@ -2140,120 +2140,6 @@ Footnotes[fn:2], foot[fn:test] and [fn:inline:inline footnote]
|
|||
(bold . (lambda (bold contents info) (concat contents "!")))))
|
||||
'(: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
|
||||
|
|
Loading…
Reference in New Issue