Compare commits

..

186 Commits
main ... dev

Author SHA1 Message Date
TEC 8f1d75ae1b
---END PERSONAL NOPUSH DIVIDER---
--
2.42.0
2024-04-16 20:07:28 +08:00
TEC 2a78c11a96
NOPUSH org: Don't fill displayed equations in text
* list/org.el (org-fill-element): If a displayed equation (\[ ... \])
starts on its own line, it should not be filled into the rest of the
text. I.e.,

some nice text
\[
  1+1=2
\]
more text.

should not become,

some nice text \[ 1+1=3 \] more text.

While the above example may not look bad, with non-trivial equations
this can become quite messy.
2024-04-16 20:07:28 +08:00
TEC 8d3909de66
NOPUSH org-src: Prettify inline results
* lisp/org.el (org-inline-src-prettify-results):

* lisp/org-src.el (org-fontify-inline-src-blocks-1):
2024-04-16 20:07:28 +08:00
TEC 9183d8e6ff
---NOPUSH PERSONAL DIVIDER---
--
2.42.0
2024-04-16 20:07:28 +08:00
Karthik Chikmagalur f8c1678317
org-latex-preview: Fix live previews org-src setup
* lisp/org-latex-preview.el (org-latex-preview-live--src-buffer-setup):
Ensure that live LaTeX previews of org-src buffer contents are created
in the src buffer.  Since this runs on a timer, the active buffer can be
different from the org-src LaTeX buffer when the preview run fires.
Ensure that `org-element-context' is not called from the org-src buffer
by setting the element type persistently when it is set up.
2024-04-16 20:07:08 +08:00
Karthik Chikmagalur b3ed738b26
ox-odt: Update to use org-latex-preview
* lisp/ox-odt.el (org-odt-latex-image-options,
org-odt-link--inline-image, org-odt--translate-latex-fragments):
Use the org-latex-preview library to generate LaTeX preview images
for ODT exports when exporting with an appropriate method (dvipng,
dvisvgm or imagemagick).  The new user option
`org-odt-latex-image-options' adds LaTeX preview image processing
options for ODT exports similar to
`org-latex-preview-appearance-options' for in-buffer previews.
Add support for svg images in ODT exports, as well as LaTeX images
scaled to match the surrounding text.  Baseline alignment for
images is not implemented.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 2143cf5f09
ox-html: Use org-latex-preview for LaTeX preview images
* lisp/ox-html.el (org-html-latex-image-options,
org-html-prepare-latex-images, org-html-latex-image,
org-html-latex-image--data, org-html-close-tag): Delegate LaTeX
preview image creation to `org-latex-preview-cache-images'.
Require both 'svg and 'svg-embed in the :inline condition of
`org-html-latex-image-options' to embed svg tags.  Handle LaTeX
preview image generation errors gracefully when exporting to html,
and highlight missing images in the html output.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur bfad8754d7
org-latex-preview: Add export-helper function
* lisp/org-latex-preview.el (org-latex-preview-cache-images,
org-latex-preview--colors-around): Add
`org-latex-preview-cache-images', which produces preview images
synchronously for all applicable LaTeX fragments in a parse-tree
and provides a hash table containing this information.  This is
intended for use with export backends (html, odt) that can display
LaTeX using svg/png LaTeX preview images.  The function can also
be used for general scripting purposes, to obtain a list of image
files and metadata for LaTeX fragments in an Org buffer.
2024-04-16 19:58:50 +08:00
TEC e7b49a4686
ox-latex: Fix interning of engraved theme keyword
* lisp/ox-latex.el (org-latex-src-block--engraved,
org-latex-inline-src-block--engraved): The engrave-faces backend expects
themes to be named using symbols, as does the default theme used by org
`org-latex-engraved-theme'. However, when set using #+attr_latex or the
use of #+latex_engraved_theme the value in the info plist is a string.
Thus, the code path needs to handle both string and symbol values,
interning string values to symbols.
2024-04-16 19:58:50 +08:00
TEC 68f255213c
ox-latex: Introduce \codefont command
* lisp/ox-latex.el (org-latex-engraved-preamble): Make it easier to
customise the font used in engraved Code environments with a \codefont
command (defaulting to \footnotesize).
2024-04-16 19:58:50 +08:00
TEC 7f60422b2f
ox-latex: Use more robust engraved theme grouping
* lisp/ox-latex.el (org-latex-src--engrave-code): Use of curly brackets
for grouping has a few edge-cases which \begingroup-\endgroup should not
suffer from.
2024-04-16 19:58:50 +08:00
TEC 5efff6ebd4
ox-latex: Better handle multiple engraved themes
* lisp/ox-latex.el (org-latex-generate-engraved-preamble): Improve the
generated LaTeX code in the multiple-themes case. Using "\long\def"
instead of "\newcommand" mixed with "\renewcommand" is needed without
without duplicating the default theme.  The "\efstrut" command also
needs to be extracted out of the theme-generated commands, which was
previously overlooked.
2024-04-16 19:58:50 +08:00
TEC a8b35e4fc1
ox-latex: Fix engraved-theme propagation
* lisp/ox-latex.el (org-latex-src-block--engraved,
org-latex-src--engrave-code, org-latex-inline-src-block--engraved):
Actually propagate the ":latex-engraved-theme" property through to the
`org-latex-src--engrave-code` call.  To account for whether the theme is
the default theme, or whether it will need to be explicitly switched to,
a new indicator argument ("explicit-theme-p") has been added to
`org-latex-src--engrave-code`.
2024-04-16 19:58:50 +08:00
TEC 159cb401bf
ol: more versatile #+link replacement forms (WIP)
* lisp/ol.el (org-link-expand-abbrev, org-link--abbrev-functions): Allow
link abbreviations to use %s and %h as format-specs, as well as allowing
an anonymous function form with %(sexpr using tag).
2024-04-16 19:58:50 +08:00
TEC 4f0592b21b
org: Introduce semantic seperator fontification
* lisp/org.el (org-fontify-semantic-seperator,
org-set-font-lock-defaults): TODO
2024-04-16 19:58:50 +08:00
TEC 2fde128b19
ox-html: Fix C++ label CSS
* lisp/ox-html.el (org-html-style-default): The "C++" pre-label works if
the "+" characters are backslash-escaped.
2024-04-16 19:58:50 +08:00
TEC 39975f281b
---PATCH SET DIVIDER---
--
2.42.0
2024-04-16 19:58:50 +08:00
TEC e03c702bc4
org-latex-preview: Add authors header
* lisp/org-latex-preview.el: Mention that Karthik and I authored this
file.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur f6c7309e4d
org-latex-preview: Fix bug when checking previews
*
lisp/org-latex-preview.el (org-latex-preview--check-all-fragments-produced):
When the first fragment in a preview run silently fails to render,
this function tries to cache a nil value as a fragment.  Fix by
checking that the fragment that fails to render is valid as a
fragment.
2024-04-16 19:58:50 +08:00
TEC 157784392c
org-latex-preview: Take advantage of dvisvgm3.2
* lisp/org-latex-preview.el (org-latex-preview--tex-styled,
org-latex-preview--dvisvgm-filter,
org-latex-preview--dvisvgm-version>=3.1,
org-latex-preview-process-alist,
org-latex-preview--dvisvgm3-minor-version): Take advantage of the new
--message flag of dvisvgm 3.2 to produce a more concise output.
2024-04-16 19:58:50 +08:00
TEC 3019d930d6
ox-html: Allow inlining svg tex as <svg> elements
* lisp/ox-html.el (org-html-latex-image--data, org-html-latex-image):
Add support for inlining dvisvgm-exported svgs as <svg> elements,
preserving the effect of the currentColor attribute.  While making this
change, we improve a few other aspects of the html LaTeX fragment export
system, namely refactoring/code style improvements and the recognition
of \[...\] and $$...$$ fragments as blocks not inline fragments.
2024-04-16 19:58:50 +08:00
TEC fdaeb38609
org-latex-preview: Skip blank fragments
* lisp/org-latex-preview.el (org-latex-preview--construct-entries,
org-latex-preview-auto--regenerate-overlay): Blank fragments produce
invisible previews, which is at best silly and at worst confusing.  It's
much better to simply not produce a preview image of blank fragments.
We also now avoid exessive re-numbering calculations when repeatadly
generating live previews.
2024-04-16 19:58:50 +08:00
TEC de4fb9de11
org-latex-preview: Accommodate a flaky cache
* lisp/org-latex-preview.el (org-latex-preview--get-cached): When using
persist, occasionally we need to be more careful about assuming that
cache entries exist.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 59f4ba2111
org-latex-preview: Handle html export preview fail
* lisp/org-latex-preview.el (org-latex-preview--check-all-fragments-produced):
This function assumes that previews are being generated for placement in
the buffer.  Handle the case of preview generation for HTML exports.
2024-04-16 19:58:50 +08:00
TEC d4c97fee06
org-latex-preview: Fix face selection Emacs 28 bug
* lisp/org-latex-preview.el (org-latex-preview--face-around): On Emacs
28 (and presumably below), should a face with :extend set be identified
as appropriate, the overlay may cast a pall (background colour) over the
rest of the line.  This can be addressed by replacing the use of the
default face to normalise display with an anonymous face that inherits
from default and has the :extend attribute set.

Reported-by: Roshan Shariff <roshan.shariff@gmail.com>
Link: https://list.orgmode.org/CAG8iPGxoAD7qmKzDXQ_SRFbmxD7jdM96piCa-dCSCpzae4rCsw@mail.gmail.com
2024-04-16 19:58:50 +08:00
TEC bc2ef6c91d
ox-html: Support for customised latex image dir
* lisp/ox-html.el (org-html-latex-image, org-html-prepare-latex-images,
org-html-latex-image-options): Allow for customising the LaTex image
directory in `org-html-latex-image-options'.
2024-04-16 19:58:50 +08:00
TEC a7b576a567
org-latex-preview: Rework general cache variable
* lisp/org-latex-preview.el (org-latex-preview--get-cached,
org-latex-preview--table, org-latex-preview-persist,
org-latex-preview-cache): Rename and tweak `org-latex-preview-persist'
to also allow for caching to a custom directory, and (in the near
future) unify behaviour with HTML export.

* testing/lisp/test-org-latex-preview.el: Accommodate for the cache variable
change.

* etc/ORG-NEWS: Accommodate for the cache variable change.
2024-04-16 19:58:50 +08:00
TEC 988e24b254
org-latex-preview: Add ignored auto environments
* lisp/org-latex-preview.el (org-latex-preview-live--src-buffer-setup,
org-latex-preview-auto--maybe-track-element-here,
org-latex-preview-auto-ignored-environments): Introduce a new
customisation variable `org-latex-preview-auto-ignored-environments',
for specifying environments that should not be automatically previewed.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 2ca8200554
org-latex-preview: Dynamic throttle for live previews
* lisp/org-latex-preview.el (org-latex-preview-live--throttle,
org-latex-preview-live-throttle,
org-latex-preview-live--preview-times,
org-latex-preview-live--update-times,
org-latex-preview-live--record-hook,
org-latex-preview-live--regenerate,
org-latex-preview-live--update-props,
org-latex-preview-live--src-buffer-setup,
org-latex-preview-live--setup, org-latex-preview-live--teardown,
org-latex-preview--create-image-async): Find the optimal throttle
time for live preview updates dynamically.  The variable
`org-latex-preview-live--preview-times' records the last three
process run times, and `org-latex-preview-live-throttle' is set to
the average of these.  This ensures that there are never more than
two concurrent processes live-previewing a single fragment.  Start
another preview run process the end of the throttle period if the
preview image is out of date to ensure that the state of the
preview is eventually consistent with the contents of the
fragment.
2024-04-16 19:58:50 +08:00
TEC 36178651d4
org-latex-preview: Adjust customisation scheme
* lisp/org-latex-preview.el (org-latex-preview-live--teardown,
org-latex-preview-live--setup, org-latex-preview-live--src-buffer-setup,
org-latex-preview-live--ensure-overlay, org-latex-preview-throttle,
org-latex-preview-debounce, org-latex-preview-live-preview-inline,
org-latex-preview-auto--open-this-overlay,
org-latex-preview-auto--detect-fragments-in-change,
org-latex-preview-auto--handle-post-cursor,
org-latex-preview--face-around, org-latex-preview-auto-generate,
org-latex-preview-auto-blacklist, org-latex-preview-clear-cache,
org-latex-preview--latex-preview-filter,
org-latex-preview--create-tex-file,
org-latex-preview--create-image-async, org-latex-preview--hash,
org-latex-preview--auto-aware-toggle, org-latex-preview--preview-region,
org-latex-preview-live--teardown, org-latex-preview-live--setup,
org-latex-preview-live--src-buffer-setup,
org-latex-preview-live--ensure-overlay,
org-latex-preview-auto--regenerate-overlay,
org-latex-preview-auto--close-previous-overlay,
org-latex-preview-auto--open-this-overlay,
org-latex-preview-auto--detect-fragments-in-change,
org-latex-preview--update-overlay,
org-latex-preview--indicate-processing, org-latex-preview-precompile,
org-latex-preview-open-functions, org-latex-preview-close-functions,
org-latex-preview-update-overlay-functions,
org-latex-preview-auto-command-blacklist,
org-latex-preview-processing-indicator,
org-latex-preview-default-process): Change the customisation naming
scheme to be more consistent.

* testing/lisp/test-org-latex-preview.el: Change the LaTeX preview
customisation naming scheme to be more consistent.

* lisp/org-compat.el: Change the LaTeX preview customisation naming
scheme to be more consistent.

* etc/ORG-NEWS: Change the LaTeX preview customisation naming scheme to
be more consistent.

* doc/org-manual.org (Footnotes, Previewing LaTeX fragments): Change the
LaTeX preview customisation naming scheme to be more consistent.
2024-04-16 19:58:50 +08:00
TEC 443239a12a
org-latex-preview: Formatting and spacing tweaks
* lisp/org-latex-preview.el (org-latex-preview--dvipng-filter,
org-latex-preview--latex-preview-filter,
org-latex-preview--create-tex-file,
org-latex-preview--create-image-async,
org-latex-preview--get-numbered-environments,
org-latex-preview-live--setup,
org-latex-preview-live--update-overlay-run,
org-latex-preview-auto--detect-fragments-in-change,
org-latex-preview--update-overlay): Adjust formatting/spacing.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 8f2a647bc2
org-latex-preview: Live previews in org-src bufs
* lisp/org-latex-preview.el (org-latex-preview-live--src-buffer-setup):
When live previews are enabled in an Org buffer and using org-src
buffers to edit LaTeX fragments (via `org-edit-special'), show
continuously updated previews in the source Org buffer if it is
visible.  If it is not visible, show continuously updated previews
in the org-src buffer.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur dcf2982e99
org-latex-preview: Eldoc support for live previews
* lisp/org-latex-preview.el (org-latex-preview-live-display-type,
org-latex-preview-live--display-in-eldoc,
org-latex-preview-live--update-eldoc,
org-latex-preview-live--setup, org-latex-preview-live--teardown):
Add Eldoc support for live previews.  The user option
`org-latex-preview-live-display-type' now accepts the symbols
buffer and eldoc as values.  When it is set to eldoc and live
previews are enabled, display of the continuously updated previews is
handled by Eldoc.  Note that previewing larger images with Eldoc may
require `eldoc-echo-area-use-multiline-p' and `max-mini-window-height'
to be set appropriately.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur e7e7ec0cb1
org-latex-preview: Add support for live previews
* lisp/org-latex-preview.el (org-latex-preview-auto-generate,
org-latex-preview-live-preview-inline, org-latex-preview-throttle,
org-latex-preview-debounce, org-latex-preview-live-display-type,
org-latex-preview-live--debounce,
org-latex-preview-live--throttle,
org-latex-preview-live--clearout,
org-latex-preview-live--regenerate,
org-latex-preview-live--update-props,
org-latex-preview-live--ensure-overlay,
org-latex-preview-live--update-overlay,
org-latex-preview-live--setup, org-latex-preview-live--teardown,
org-latex-preview-live--docstring,
org-latex-preview-live--element-type,
org-latex-preview-live--generator,
org-latex-preview--ensure-overlay,
org-latex-preview--failure-callback,
org-latex-preview--svg-make-fg-currentColor): Add support for
live-previewing LaTeX fragments.  When
`org-latex-preview-auto-generate' is set to the symbol live and
`org-latex-preview-auto-mode' is turned on, LaTeX previews are
continuously updated when editing a LaTeX fragment.  These
previews are shown to the right of or below the fragment.  The
preview generation timing can be controlled using the options
`org-latex-preview-debounce' and `org-latex-preview-throttle'.
2024-04-16 19:58:50 +08:00
TEC a93808588f
org-latex-preview: Update copyright years 2024-04-16 19:58:50 +08:00
TEC 4280eb3869
org-latex-preview: Put :page-width in opt plist
* lisp/org-latex-preview.el (org-latex-preview-options,
org-latex-preview-appearance-options): Rename
`org-latex-preview-options' to `org-latex-preview-appearance-options',
as it better reflects the purpose of the variable.

(org-latex-preview--tex-styled, org-latex-preview--image-extract-async,
org-latex-preview--create-tex-file,
org-latex-preview--create-image-async, org-latex-preview--hash,
org-latex-preview-place, org-latex-preview--update-overlay): Put the
page width specification in `org-latex-preview-appearance-options', and
adjust existing code to handle this new structuring.

* lisp/ox-html.el (org-html-prepare-latex-images,
org-html-latex-image-options): Adjust for the
`org-latex-preview-appearance-options' rename, and put :page-width in
the options plist, just with a raised value of 1.0 as the HTML default.

* lisp/org.el (org-do-latex-and-related, org-latex-preview-options),
org-compute-latex-and-related-regexp): Adjust for the
`org-latex-preview-appearance-options' rename.

* lisp/org-compat.el: Adjust for the
`org-latex-preview-appearance-options' rename.

* lisp/ob-latex.el (org-babel-execute:latex, org-latex-preview-header)):
Adjust for the `org-latex-preview-appearance-options' rename.

* etc/ORG-NEWS: Adjust for the `org-latex-preview-appearance-options'
rename.

* doc/org-manual.org (Previewing LaTeX fragments): Adjust for the
`org-latex-preview-appearance-options' rename.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur b2adb517f4
org-latex-preview: Fix preview help-echo property
* lisp/org-latex-preview.el (org-latex-preview--update-overlay):
Add LaTeX compilation errors to the help-echo property when the
cursor is inside the fragment that is being previewed.  This
primarily affects the use of `org-latex-preview-auto-mode`.
2024-04-16 19:58:50 +08:00
TEC 79336cef3d
org-latex-preview: Improved face heuristics
* lisp/org-latex-preview.el (org-latex-preview--face-around): To work
around issues where syntax highlighting of the underlying LaTeX fragment
interferers with the foreground colour of the preview, we append default
to the face list regardless.

(org-latex-preview--ignored-faces): While we're improving face
heuristics, it's also come to my attention that whitespace-mode can
interferer with face selection, and whitespace-mode should also be
ignored.  Perhaps this should become part of the API?
2024-04-16 19:58:50 +08:00
TEC 5dbe159407
org-latex-preview: Support symbol entrypoint args
* lisp/org-latex-preview.el (org-latex-preview): Refactor to support a
symbol-value "mode" argument as well as prefix arguments.  This stops
forcing programmatic invocations from having to use uninformative
values, and actually makes the implementation slightly nicer to read I
think.

* lisp/org.el: Change the argument in the preview startup invocation to 'buffer.
2024-04-16 19:58:50 +08:00
TEC 5e7af27352
org-latex-preview: Robustify DPI calculation
* lisp/org-latex-preview.el (org-latex-preview--get-display-dpi): It was
reported that on a WSLg system using pgtk that
`org-latex-preview--get-display-dpi' produced an arithmetic overflow
error.  Inspecting the calculation performed and docstrings of functions
involved, it seems we need to be more careful of the `display-mm-height'
output.  We now guard against it being zero or nil, which should make
the DPI calculation more robust.

Simply falling back to a guessed DPI of 140 is non-ideal, but probably
the best we can reasonably do here.
2024-04-16 19:58:50 +08:00
TEC c20bf88205
org-latex-preview: Guard against deleted buf/ov
* lisp/org-latex-preview.el (org-latex-preview--place-images): Since the
image generation and placement is asynchronous, it is possible that in
the meantime the target overlay or even buffer could be deleted.  To
avoid errors, we simple check that this is not the case before
attempting to place preview images.
2024-04-16 19:58:50 +08:00
TEC 557275af54
org-latex-preview: Still show errs with no tooltip
* lisp/org-latex-preview.el (org-latex-preview--update-overlay): When
`tooltip-mode' is not active, still show the error, just remove multiple
spaces to be economic with the horizontal room available.  This isn't
good as showing the tooltip, but it's better than not showing anything.
2024-04-16 19:58:50 +08:00
TEC 90e7813dd7
org-macs: Set some process variables in org-async
* lisp/org-macs.el (org-async-call): When eking out maximum performance
from external commands, there are a few potentially important process
variables. `org-async-call' is extended to support setting these, with
default values that differ from Emacs' defaults but should enable better
performance in general.
2024-04-16 19:58:50 +08:00
TEC f52b7b4c89
org-latex-preview: Mention latex buffer if no dvi
* lisp/org-latex-preview.el (org-latex-preview--failure-callback,
org-latex-preview--create-image-async): When the image conversion step
fails due to a missing input file, point the user to the LaTeX output
buffer instead of the image conversion output buffer.
2024-04-16 19:58:50 +08:00
TEC 7834e6a850
org-latex-preview: Include running command in bufs
* lisp/org-latex-preview.el (org-latex-preview--image-extract-async,
org-latex-preview--tex-compile-async): Add a comment at the start of the
process output buffers mentioning what the command being run is.
2024-04-16 19:58:50 +08:00
TEC 40d8b3acea
org-latex-preview: Simplify color formatting
* lisp/org-latex-preview.el (org-latex-preview--format-color,
org-latex-preview--normalize-color): Use three decimal places, and
remove the now-obsolete `org-latex-preview--normalize-color'.

* lisp/org-compat.el (org-normalize-color): With the removal of the
`org-latex-preview--normalize-color' alias, mark `org-normalize-color'
as obsolete.
2024-04-16 19:58:50 +08:00
TEC b37062d4a8
org-latex-preview: Use dvisvgm 3.1+ currentcolor
* lisp/org-latex-preview.el (org-latex-preview--tex-styled,
org-latex-preview--svg-make-fg-currentColor,
org-latex-preview--dvisvgm-filter,
org-latex-preview--dvisvgm-version>=3.1): With dvisvgm 3.1+, it can
replace the foreground color with "currentColor" itself, and so we can
skip `org-latex-preview--svg-make-fg-currentColor' when we are using a
new enough dvisvgm.

(org-latex-preview--await-fragment-existance): Just in case the
asyncronicity in file writing causes issues, we keep the file existence
check (for now).
2024-04-16 19:58:50 +08:00
TEC 0090e25460
org-latex-preview: Improve env numbering calc
* lisp/org-latex-preview.el (org-latex-preview--get-numbered-environments,
org-latex-preview--environment-numbering-table): Make numbering
calculations more accurate, at the expense of a ~10x increase in the
time to calculate numbering in a document, mitigated by the use of
caching — resulting in a ~2x improvement compared to the simpler method
in subsequent runs.

(org-latex-preview--numbered-environments,
org-latex-preview--numbered-environments-single,
org-latex-preview--numbered-environments-multi,
org-latex-preview--numbered-environments-all): Split
`org-latex-preview--numbered-environments' into
`org-latex-preview--numbered-environments-single' and
`org-latex-preview--numbered-environments-multi', then rename
`org-latex-preview--numbered-environments' to
`org-latex-preview--numbered-environments-all' for the sake of clarity.

Determining the count of numbered equations in a LaTeX environment is
non-trivial.  Previously, a simple heuristic was used that counted the
number of lines ending with "\\" in particular environments, then
subtracted the number of "\nonumber" and "\tag{...}" instances.

However, lines can end with "\\" without contributing to the number of
equations in the environment, consider the "cases" and "bmatrix"
environments.  To improve the heuristic, we (with some care) remove
/all/ inner environments from the LaTeX maths environment before
processing.  Since this takes more effort, we split the numbered
environments into those we know will contain exactly one numbered
equation where we can avoid the extra processing, and those that require
the more sophisticated heuristics.

The new org-element caching API is very helpful for recovering the
performance cost of this extra computation in the long run.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 2ff67428fa
org-latex-preview: Fix process filter timing errs
* lisp/org-latex-preview.el (org-latex-preview--create-image-async,
org-latex-preview--latex-preview-filter,
org-latex-preview--dvipng-filter, org-latex-preview--dvisvgm-filter,
org-latex-preview--place-images): The dvipng and latex-preview filters
run concurrently (interleaved), and both contribute image metadata.
So caching/placing images cannot be associated with the dvipng filter,
this leads to non-deterministic errors where preview images can have
incomplete metadata.  To fix this, check if we are using dvipng when
running the LaTeX filter, and place images from whichever of the two
filters processes each fragment second.

Refactor: Move the image placement/caching logic fully to
`org-latex-preview--place-images' instead of duplicating code in all
three (latex-filter, dvipng and dvisvgm) previews.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 5210d52d20
org-latex-preview: Fix LaTeX header on fails + reruns
*
lisp/org-latex-preview.el (org-latex-preview--check-all-fragments-produced):
When rerunning a preview run (because of catastrophic failures), pass
the LaTeX header used to the new process.  The current behavior passes
an empty header, which causes the process to fail again.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur f206164ef4
org-latex-preview: Fix xelatex preview image sizes
*
lisp/org-latex-preview.el (org-latex-preview--include-preview-string,
org-latex-preview--latex-preview-filter):  when xetex is used,
preview.sty assumes PDF output by default and does not write any
postscript data to the output XDV file.  This makes dvisvgm unable to
correctly determine the preview geometry.  Fix by specifying the
`dvips' option to preview, which adds (apparently as a side effect)
this geometry information to the XDV file.

Remove a conditional in `org-latex-preview--latex-preview-filter' that
was previously used to attempt to work around this issue.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 3b7c6faf9f
ox-latex: Disable precompile for xelatex/lualatex
* lisp/ox-latex.el (org-latex-make-preamble): If xelatex or
lualatex are used for LaTeX export, disable LaTeX header
precompilation (via the CTAN package mylatexformat) locally in the
buffer and issue a warning.  Xelatex does not support
precompilation, and while Lualatex supports it in some cases, it
is best left unsupported by the Org LaTeX export process until
precompilation support for them improves upstream.

* lisp/org-latex-preview.el (org-latex-preview--create-tex-file):
Make the same changes as above when precompiling LaTeX headers
for LaTeX preview purposes.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur a9fbf9d7cf
org-latex-preview: Rename precompilation vars
* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview--latex-preview-filter,
org-latex-preview--create-tex-file,
org-latex-preview-use-precompilation, org-latex-preview--precompile):
Rename (i) the user option that controls precompilation for LaTeX
previews, and (ii) the function that precompiles headres for LaTeX
previews.  This makes the names shorter and consistent with their
corresponding equivalents in `ox-latex'.

Rename
`org-latex-preview-use-precompilation' -> `org-latex-preview-precompile'
`org-latex-preview-precompile' -> `org-latex-preview--precompile'

The new names have equivalents `org-latex-precompile' and
`org-latex--precompile' in `ox-latex'.

* testing/lisp/test-org-latex-preview.el: Update tests that set this
user option.

* etc/ORG-NEWS: Update the NEWS file.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 99f8c7b7fd
org-latex-preview: Fix bug clearing precompile cache
* lisp/org-latex-preview.el (org-latex-preview-clear-cache,
org-latex-preview--create-tex-file,
org-latex-preview--include-preview-string): LaTeX headers that are
precompiled include additional strings that set the
textwidth (according to `org-latex-preview-width') and include
preview.sty.  These strings are not appended to the header when
trying to clear the precompile cache for the present buffer.  Fix
by (i) removing the textwidth declaration from the precompiled
text, and (ii) including the preview.sty inclusion string with the
header we look for when trying to remove the corresponding fmt
file in the org-persist cache.

org-latex-preview--include-preview-string: Introduce string that
holds the text to include preview.sty in the document header.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 8f64d73bbd
org-latex-preview: Reduce delay when re-rendering
*
lisp/org-latex-preview.el (org-latex-preview-auto--close-previous-overlay):
Reduce the delay when re-rendering fragments (with `org-latex-preview-auto-mode' turned
on) from 50 ms to 10 ms. This delay is a heuristic number for
perceived smoothness, and this change represents a tuning effort.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 513b4a1b12
org-latex-preview: Change preview hashing parameters
* lisp/org-latex-preview.el (org-latex-preview--hash): Use only the
`:scale' parameter from `org-latex-preview-options' when hashing
fragments.  Other parameters like `:zoom' do not affect the content of
the image, so this helps avoid building up garbage in the org-persist
cache.  `org-latex-preview' is also faster since re-rendering images
is required less often.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur b7a789dbd8
org-latex-preview: Change hooks to abnormal hooks
*
lisp/org-latex-preview.el (org-latex-preview-auto--close-previous-overlay,
org-latex-preview-auto--open-this-overlay,
org-latex-preview-close-hook, org-latex-preview-open-hook): Change
`org-latex-preview-open-hook' and `org-latex-preview-close-hook' to
abnormal hooks that accept an overlay as the argument.  This makes it
easier to apply overlay-specific changes -- which is what these hooks
are primarily for -- without having to find the overlay by probing the
buffer first.

Rename:
`org-latex-preview-open-hook' to `org-latex-preview-open-functions'
`org-latex-preview-close-hook' to `org-latex-preview-close-functions'
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 0549f0f55f
org-latex-preview: Fix clear-cache command
* lisp/org-latex-preview.el (org-latex-preview-clear-cache): When
clearing the preview cache, clear the cached preamble after clearing
the image cache, since the preamble is used to compute the image
hashes.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur b536e6506d
test-org-latex-preview: Add preliminary tests
* testing/lisp/test-org-latex-preview.el: Tests for
org-latex-preview.el.

* testing/examples/org-latex-preview-test.org: Short but
full-featured document with LaTeX fragments to run tests on.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur efb6371315
org-latex-preview: Don't fully reset overlay state
* lisp/org-latex-preview.el (org-latex-preview--ensure-overlay,
org-latex-preview--update-overlay): When ensuring a preview overlay
exists, do not fully reset the state of the overlay, such as the
stored image spec, the flag that checks if the preview is open, etc.
Persisting prior state is required for the "live" preview
feature (introduced in the following commits) to function.  Similarly,
if "live" previewing a LaTeX element that has an overlay attached, do
not set the overlay face or error display indicator(s).
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 6fc221b439
org-latex-preview: Add hooks for customization
* lisp/org-latex-preview.el (org-latex-preview-process-finish-functions,
org-latex-preview-update-overlay-hook, org-latex-preview-close-hook,
org-latex-preview-open-hook, org-latex-preview--create-image-async,
org-latex-preview-auto--handle-post-cursor,
org-latex-preview--run-finish-functions): Add four hooks for better
control of the LaTeX preview process.

`org-latex-preview-process-finish-functions': Runs after the image
conversion process. This is an abnormal hook: each hook function
accepts the same arguments as other `org-async' callbacks for this
process.

`org-latex-preview-close-hook' and `org-latex-preview-open-hook':
Run after a preview is closed (text hidden to reveal image) or
opened (image hidden to reveal text) respectively.

`org-latex-preview-update-overlay-functions': Run after a LaTeX
preview overlay is updated with a new image.  This is an abnormal
hook: each hook function accepts one argument, the overlay that was
updated.

The "live" preview system introduced in the following commits uses
these hooks, along with the facilities provided by
`org-latex-preview-auto-mode'.  In addition, these hooks will
prove useful when writing code that reuse the API afforded by
`org-latex-preview-place' to provide LaTeX previews for other
major modes.
2024-04-16 19:58:50 +08:00
TEC 6a40115563
org-latex-preview: Ignored cmds list for auto mode
* lisp/org-latex-preview.el (org-latex-preview-auto-blacklist): Add a
blacklist for commands that should not cause LaTeX preview overlays to
be "opened", i.e. for preview images to be hidden and text revealed when
using `org-latex-preview-auto-mode'.  A common use case would be cursor
navigation commands like `next-line' or `scroll-up-command' that are
often intended to move the view of the buffer, where opening preview
overlays can be distracting.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur 9914b251af
org-latex-preview: Fix overlay boundary deletion bug
*
lisp/org-latex-preview.el (org-latex-preview-auto--regenerate-overlay):
When `org-latex-preview-auto-mode' is active and a LaTeX fragment
or environment boundary is deleted,
`org-latex-preview-auto--regenerate-overlay' generates a new
preview for the new org-element at point, which is no longer a
LaTeX fragment or environment.  This results in paragraph text or
other syntax being captured into the preview.  Fix by adding a
check to ensure that the element post-change is still a LaTeX
fragment or environment.
2024-04-16 19:58:50 +08:00
Karthik Chikmagalur e509cf6adc
org-latex-preview: non-buffer-local auto-marker bug
* lisp/org-latex-preview.el (org-latex-preview-auto--marker,
org-latex-preview-auto-mode): `org-latex-preview-auto--marker' is
initialized to a marker, which can cause it to not be
buffer-local.  Instead, initialize it to nil and explicitly set it
to a marker when running `org-latex-preview-auto-mode'.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 2835c96dcf
org-latex-preview: Fix startup preamble generation
* lisp/org-latex-preview.el (org-latex-preview--get-preamble):
When `org-startup-with-latex-preview' (or the `#+startup:
latexpreview') option is set, preamble generation and
precompilation fail as latex previews are generated in the clone
of the current buffer created by `org-export-with-buffer-copy'.
This is because org-mode is turned on in the cloned buffer, which
requires creating LaTeX previews there.  Not only is this
unnecessary, it causes a flurry of errors since the cloned buffer
is killed before the previews can be placed.  Explicitly avoid
org-startup-* tasks in the cloned buffer when generating the LaTeX
preamble for our main Org buffer.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 33c79f8e0e
org-latex-preview: Ensure fmt file cache is cleared
* lisp/org-latex-preview.el (org-latex-preview-clear-cache): When
running `org-latex-preview-clear-cache', the precompiled format
file is only removed from the `org-persist' cache if the preamble
has been cached in the current buffer.  We require the format file
to always be unregistered from the cache, so generate the preamble
if necessary so it can be hashed and used as the key to find the
corresponding format file in the cache.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 7bd5973233
org-latex-preview: Optimize overlay-modify hook
*
lisp/org-latex-preview.el (org-latex-preview-auto--mark-overlay-modified):
Reduce slightly the number of checks needed to run each time text
inside a LaTeX fragment overlay is modified.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 3ab374790d
Update ORG-NEWS
* etc/ORG-NEWS: Add summary of `org-latex-preview' changes.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur c0e314cb52
org-manual: Update LaTeX preview section
* doc/org-manual.org (Previewing LaTeX fragments): Mention newly
added user options and `org-latex-preview-auto-mode'.
2024-04-16 19:58:49 +08:00
TEC ea44c61cf1
ox-html: Allow inlining of LaTeX images
* lisp/ox-html.el (org-html-latex-image, org-html-latex-image-options):
Add a new `org-html-latex-image-options' keyword, ":inline", which
controls image inlining in `org-html-latex-image'.
2024-04-16 19:58:49 +08:00
TEC 1b1373f3c9
org-latex-preview: Emit warning when hyperref seen
* lisp/org-latex-preview.el (org-latex-preview--latex-preview-filter):
If during LaTeX compilation hyperref seems to be present, emit a warning
mentioning the sizing issue it is known to cause.
2024-04-16 19:58:49 +08:00
TEC f90b633f9a
org-latex-preview: Add preview-order eqn numbering
* lisp/org-latex-preview.el (org-latex-preview--tex-styled,
org-latex-preview-numbered): Allow for preview-order based equation
numbering, by setting `org-latex-preview-numbered' to the symbol
"preview".
2024-04-16 19:58:49 +08:00
TEC 7340c4fae7
org-latex-preview: Use icon for non-numbered eqns
* lisp/org-latex-preview.el (org-latex-preview--get-preamble,
org-latex-preview--single-eqn-format): When numbering is turned off (via
`org-latex-preview-numbered'), instead of always showing "(1)" as the
equation number, use a diamond symbol, and in multiline environments
show +offset.  The LaTeX setup for this is stored in the new const
`org-latex-preview--single-eqn-format'.
2024-04-16 19:58:49 +08:00
TEC fed2ebc16a
org-latex-preview: Fix snippet preamble hash
* lisp/org-latex-preview.el (org-latex-preview--create-tex-file,
org-latex-preview--hash, org-latex-preview-place,
org-latex-preview-clear-cache): The only the default preamble was being
hashed, which led to incorrect results across documents.  To correctly
hash the preamble, it is now determined at the start of
`org-latex-preview-place', and used in `org-latex-preview--hash',
`org-latex-preview--create-tex-file', and
`org-latex-preview-clear-cache'.
2024-04-16 19:58:49 +08:00
TEC 4ec83eb21e
org-latex-preview: Set overlay priority to -80
* lisp/org-latex-preview.el (org-latex-preview--ensure-overlay): Setting
the overlay priority to -80 ensures that the background colour of
hl-line-mode's overlays takes priority, improving the visual consistency
of previews in the buffer.
2024-04-16 19:58:49 +08:00
TEC 39e0faebd1
ox-html: Update LaTeX export for images and mathml
* lisp/ox-html.el (org-html-latex-environment, org-html-format-latex,
org-latex-to-html-convert-command, org-format-latex-as-html,
org-html-latex-image-options): Introduce new functions and variables to
work directly with the new org-latex-preview and ox-mathml API.

* lisp/org-latex-preview.el (org-format-latex-as-html,
org-latex-preview--tex-styled, org-latex-preview-replace-fragments,
org-latex-to-html-convert-command, org-latex-preview-options,
org-latex-preview-place-image-link): Move all of the HTML-specific
processing out of org-latex-preview.el, and into ox-html.el, and remove
now-obsolete code.

* lisp/org-compat.el (org-place-formula-image, org-html-format-latex):
Relocate a copy of `org-place-formula-image' and `org-html-format-latex'
here, and mark them as obsolete.
2024-04-16 19:58:49 +08:00
TEC 7ce7e39650
ox-mathml: Refactor mathml export
* lisp/ox-mathml.el: Introduce a new library for managing mathml conversion.

* lisp/org-latex-preview.el (org-create-math-formula,
org-format-latex-mathml-available-p, org-latex-to-mathml-jar-file): Move
the various mathml functions into ox-mathml.

* lisp/org-compat.el: Add compat entries for the various mathml
conversion functions that have now been brought under ox-mathml.

* lisp/ox-odt.el (org-odt-export-as-odf,
org-odt--translate-latex-fragments): Use the new org-mathml functions.
2024-04-16 19:58:49 +08:00
TEC 0d28e6ef72
org: Update LaTeX overlay during fontification
* lisp/org.el (org-do-latex-and-related, org-latex-preview-options): To
ensure that LaTeX preview overlays update as the face changes (for
instance, when in a heading that changes level), the face used is
updated during fontification when applicable.  This also ensures that
the correct face is used when JIT fontification is active.
2024-04-16 19:58:49 +08:00
TEC febabadb99
org-latex-preview: Improved face adaptation
* lisp/org-latex-preview.el (org-latex-preview--face-around): Instead of
only examining the face immediately before the LaTeX snippet, also look
at the face immediately after before determining the appropriate face to
use.  This is relevant when the prior face is
uninformative (e.g. org-indent) but the latter face contains the
applicable face (e.g. org-list-dt).  Determination of the applicable
face is now performed in the new function
`org-latex-preview--face-around'.
(org-latex-preview--colors-at, org-latex-preview--update-overlay):
Rename `org-latex-preview--colors-at' to
`org-latex-preview--colors-around', and use
`org-latex-preview--face-around' for better face determination.
(org-latex-preview-place, org-latex-preview-clear-cache): Update to
provide an "end" value to `org-latex-preview--colors-around'.
2024-04-16 19:58:49 +08:00
TEC ec8662aa82
org-latex-preview: Better handle temp/remote files
* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview--create-tex-file): Before obtaining the (compiled)
preamble, check for the temp-ness and remote-ness of the file, and act
accordingly.  Non-temp remote files are not currently supported, and an
error will now be thrown in such cases.  The temporary file status is
now passed on to `org-latex-preview-precompile' and through to
`org-latex--precompile'.
2024-04-16 19:58:49 +08:00
TEC 96dc874f91
ox-latex: Handle preamble temp-ness explicitly
* lisp/ox-latex.el (org-latex--precompile, org-latex-make-preamble):
Instead of detecting relative file includes within
`org-latex--precompile', have an optional argument to signal a file can
be safely compile in the tempdir.  Move the check to
`org-latex-make-preamble' when calling `org-latex--precompile'.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 52fd5cabea
org-latex-preview: Clean up tex file on failure
*
lisp/org-latex-preview.el (org-latex-preview--create-image-async):
Ensure that the latex-preview cleanup callback runs when the
preview image extraction process fails.  This is to avoid
littering the default-directory with temporary tex files.
2024-04-16 19:58:49 +08:00
TEC ce260ed2c9
org-latex-preview: Work around XeTeX misbehaviour
* lisp/org-latex-preview.el (org-latex-preview--latex-preview-filter):
Since XeLaTeX seems to misbehave with tightpage, we special-case the
tightpage processing to account for it.
2024-04-16 19:58:49 +08:00
TEC 178fd49021
org-latex-preview: Improved error messages
* lisp/org-latex-preview.el (org-latex-preview--image-extract-async,
org-latex-preview--tex-compile-async,
org-latex-preview--create-image-async): Edit the error messages for
LaTeX compilation and image generation failure to be slightly more informative.
2024-04-16 19:58:49 +08:00
TEC 3d46db577d
org-latex-preview: Disable buf precompile on error
* lisp/org-latex-preview.el (org-latex-preview--create-tex-file): When
an error occurs during LaTeX precompilation, disable it using
`setq-local' and emit an informative warning message.
2024-04-16 19:58:49 +08:00
TEC 8ccc93eae1
org-latex-preview: Use stand-in color for SVG fg
* lisp/org-latex-preview.el (org-latex-preview--svg-make-fg-currentColor,
org-latex-preview-place): Instead of guessing the SVG foreground to be
the first color seen, set a static stand-in color that is unlikely to
occur organically.
2024-04-16 19:58:49 +08:00
TEC 13888aeeb3
org-latex-preview: Guard against caching nil-files
* lisp/org-latex-preview.el (org-latex-preview--cache-image): When
caching an image with `org-latex-preview--cache-image', check that the
image path is non-nil.  Should this not be true, a warning message is
emitted suggesting this is a bug that should be reported to the Org
mailing list.
2024-04-16 19:58:49 +08:00
TEC c769032876
org-latex-preview: Get a list of all started tasks
* lisp/org-latex-preview.el (org-latex-preview--create-image-async):
Instead of just returning the main tex-compile task, return a list of
all async tasks created (i.e. the dvipng task, when used).  This will
allow for `org-async-wait-for' to be used on the list to wait until all
processing has completed.
2024-04-16 19:58:49 +08:00
TEC f592d7b3cc
org-latex-preview: Split out entry construction
* lisp/org-latex-preview.el (org-latex-preview--place-from-elements,
org-latex-preview--construct-entries): Split the entry construction code
into its own function `org-latex-preview--construct-entries'.

This change will make it easier to generate previews during export.
2024-04-16 19:58:49 +08:00
TEC 5d319e4339
org-latex-preview: Construct numbering from tree
* lisp/org-latex-preview.el (org-latex-preview--get-numbered-environments,
org-latex-preview--environment-numbering-table): Extend
`org-latex-preview--get-numbered-environments' and
`org-latex-preview--environment-numbering-table' to accept a parse tree
to operate on as an optional argument.

This will be useful when trying to integrate numbering with exports.
2024-04-16 19:58:49 +08:00
TEC e1fd05fda5
org-latex-preview: Wait for SVGs more robustly
* lisp/org-latex-preview.el (org-latex-preview--svg-make-fg-currentColor,
org-latex-preview--dvisvgm-filter): Instead of waiting 0.01s and hoping
that an SVG exists by that point, we can wait and check for the
existance of an SVG within
`org-latex-preview--svg-make-fg-currentColor', and that should reduce
the chance of edge cases where the delay is insufficient.
(org-latex-preview--svg-make-fg-currentColor): Adjust the
`buffer-substring' call to not error when the buffer is empty.
2024-04-16 19:58:49 +08:00
TEC 73df97486f
org-latex-preview: Move org-format-latex to compat
* lisp/org-latex-preview.el (org-latex-preview-create-image,
org-format-latex): It has become apparent that it is not reasonably
possible to maintain the behaviour of `org-format-latex' and
`org-create-formula-image' using the new image, so instead put the
original implementations in org-compat.
(org-preview-latex-image-directory): Move and mark as obsolete in
org-compat as it has not be used.

* lisp/org-compat.el: Introduce the original `org-format-latex' and
`org-create-formula-image' definitions.  Also move
`org-preview-latex-image-directory' here and mark as obsolete, since it
has ended up being completely unused.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 5a20575b6f
org-latex-preview: Get geometry from preview.sty
* lisp/org-latex-preview.el (org-latex-preview--latex-preview-filter,
org-latex-preview--tex-scale-divisor,
org-latex-preview--dvipng-dpi-pt-factor): Fetch image geometry and
alignment information from the LaTeX compilation output instead of
dvisvgm or dvipng.  This calculation is simpler and should work for all
image converters (including imagemagick) and with all latex
processors (including xelatex and lualatex).

(org-latex-preview--shameful-magic-tex-scaling-factor): New
variable to match image geometry reported by preview.sty and that
computed by dvisvgm.

(org-latex-preview--dvipng-filter, org-latex-preview--dvisvgm-filter,
org-latex-preview--latex-preview-filter,
org-latex-preview--display-info): Adjust to no longer use the old sizing
information.

* lisp/org-latex-preview.el (org-latex-preview-process-alist,
org-latex-preview-create-image, org-latex-preview--display-info,
org-latex-preview--image-extract-async): Remove the :image-size-adjust
keyword, as with the new geometry detection system it is redundant.

We tried also switching dvisgm from "--bbox=preview" to "--exact-bbox",
as it should no longer be needed, but testing found "--exact-bbox" to
cause sizing issues in practice.
2024-04-16 19:58:49 +08:00
TEC e6a76e7ce5
org-latex-preview: Introduce a place-preview arg
* lisp/org-latex-preview.el (org-latex-preview--dvipng-filter,
org-latex-preview--dvisvgm-filter,
org-latex-preview--check-all-fragments-produced,
org-latex-preview--generic-callback,
org-latex-preview--create-image-async, org-latex-preview-place):
Refactor the preview pipeline to use a "place-preview" argument to
indicate that LaTeX previews should be placed after generation.

This change will pave the way for use with export, where the LaTeX
preview should be generated but not previewed.
2024-04-16 19:58:49 +08:00
TEC 3376423921
ox-latex: Precompile preamble for export
* lisp/ox-latex.el (org-latex-precompile, org-latex-make-preamble): Make
use of "mylatexformat" to precompile the LaTeX preamble, for faster
exports.
(org-latex--remove-cached-preamble): Add a function to clear a cached
preamble.

* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview--get-preamble): Relocate the precompilation
functionality to ox-latex.el, and use the new ox-latex preamble
precompilation API.
(org-latex-preview-clear-cache): Use the new preamble cache clearing
functionality in ox-latex.
2024-04-16 19:58:49 +08:00
TEC 688427138d
org-latex-preview: Use org-persist for .fmt cache
* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview--create-tex-file): Cache compiled ".fmt" format dump
files with org-persist, instead of using predictably named tempfiles.
`org-latex-preview--precompile-preamble' is introduced to seperate out
the compilation process from the org-persist interaction.  As a result
of the new changes, the ".fmt" extension is also returned now, and so it
must be stripped in `org-latex-preview--create-tex-file'.
2024-04-16 19:58:49 +08:00
TEC 4e623d4593
org-latex-preview: Rework auto toggle behaviour
* lisp/org-latex-preview.el (org-latex-preview--auto-aware-toggle):
Adjust the toggle behaviour to hopefully behave more intuitively.
2024-04-16 19:58:49 +08:00
TEC 58b2400e00
org-latex-preview: org-latex-conditional-features
* lisp/org-latex-preview.el (org-latex-preview--get-preamble):
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 9724e205e7
org-latex-preview: Reduce verbosity of messaging
* lisp/org-latex-preview.el (org-latex-preview--cleanup-callback,
org-latex-preview--create-image-async, org-latex-preview-place,
org-latex-preview--auto-aware-toggle, org-latex-preview,
org-latex-preview-auto--regenerate-overlay): Do not emit a message
when previewing is successful.  This reduces how "busy" the echo area
is, especially when `org-latex-preview-auto-mode' is on.  When
previewing fails, display a message as before (behaviour unchanged).
2024-04-16 19:58:49 +08:00
TEC ba4e542fc1
org-latex-preview: Lazy-load, with autoloads
* lisp/org.el: No longer require org-latex-preview.

* lisp/org-latex-preview.el: Require the relevant files, and add
autoload tokens to `org-latex-preview-options',
`org-latex-preview-process-alist' (for backwards compatibility),
`org-latex-preview-auto-mode', `org-latex-preview' (the major likely
entry points), and `org-latex-preview-place` (potential external entry
point).
2024-04-16 19:58:49 +08:00
TEC 444583f0e6
org-latex-preview: Use the conditional preamble
* lisp/org-latex-preview.el (org-latex-preview--create-tex-file,
org-latex-preview--clear-preamble-cache): Instead using a minimal info
export plist, populate it with buffer-specific values so that the
conditional preamble can be appropriately generated, and cache the
result.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur b9f937bb2d
org-latex-preview: Speed up texfile creation time
* lisp/org-latex-preview.el (org-latex-preview--create-tex-file):
Speed up texfile creation time by inhibiting fsync and setting the
`coding-system-for-write' explicitly.  Times are reduced by about
40-50%.
2024-04-16 19:58:49 +08:00
TEC 0f1d2f0ca0
org-latex-preview: Fix spacing before LaTeX envs
* lisp/org-latex-preview.el (org-latex-preview--create-tex-file): Set
the \abovedisplayskip length to zero to remove the default padding.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 716e32492b
org-latex-preview: Fix element start positions
* lisp/org-latex-preview.el (org-latex-preview--place-from-elements,
org-latex-preview-auto--maybe-track-element-here): The org-element
property `:begin' includes any Org keyword text associated with the
element, such as "#+RESULTS:" or "#+ATTR_LATEX:" and these are swallowed
up in the created preview overlays.  Fix by using the `:post-affiliated'
property over `:begin' when it is available.  Note that some
calculations, such as equation numbering, still use the `:begin'
keyword.  These calculations don't involve creating overlays and thus
this is not (expected to be) an issue.
2024-04-16 19:58:49 +08:00
TEC 36d1b5d3b2
org-macs: Support blocking on async tasks
* lisp/org-macs.el (org-async--blocking-tasks, org-async-wait-for,
org-async--cleanup-process, org-async--execute-callback): Add support
for waiting for a set of async tasks to complete via the new function
`org-async-wait-for'.
2024-04-16 19:58:49 +08:00
TEC 37c95a6026
org-latex-preview: Redesign preview creation API
* lisp/org-latex-preview.el (org-latex-preview--check-all-fragments-produced,
org-latex-preview--create, org-latex-preview-fragments,
org-latex-preview-auto--regenerate-overlay,
org-latex-preview-auto--detect-fragments-in-change): Split the
org-element processing and the preview creation components of
`org-latex-preview--create` into two new functions, `
org-latex-preview--place-from-elements' and `org-latex-preview-place'.
The latter of these can serve as a convenient public API for creating
fragments.
(org-latex-preview-place, org-latex-preview-create-image-async): To make
`org-latex-preview-place' this easier to use, we also allow the LaTeX
preamble to be provided as an optional argument, and adjust
`org-latex-preview-create-image-async' to add this as the :latex-header
to the processing-info plist.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 62dbb79288
org-latex-preview: Better imagemagick cleanup
* lisp/org-latex-preview.el (org-latex-preview--generic-callback,
org-latex-preview-create-image-async,
org-latex-preview-process-alist): Reuse
`org-latex-preview--cleanup-callback' for imagemagick-based previews.
Additionally, check produced fragments and regenerate them if
necessary.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 3028030013
org-latex-preview: Fix auto-gen at end of overlay
* lisp/org-latex-preview.el (org-latex-preview-auto--maybe-track-element-here):
`overlays-at' does not include overlays that end at the position it is
called on.  This caused auto-generation to not trigger immediately
when manually typing the closing delimiters of a LaTeX fragment. Fix
this by recording the position just before the end of a newly created overlay.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 1323115972
org-latex-preview: Respect auto-generate option
* lisp/org-latex-preview.el (org-latex-preview-auto--detect-fragments-in-change):
Respect the user option `org-latex-preview-auto-generate' when
`org-latex-preview-auto-mode' is active.  (This was lost when the
auto-generation code was rewritten.)
2024-04-16 19:58:49 +08:00
TEC 5ca232e8ea
org-latex-preview: Use appropriate TeX compiler
* lisp/org-latex-preview.el (org-latex-preview--tex-compile-async,
org-latex-preview-process-alist): Introduce %l and %L placeholders for
the LaTeX compiler.
(org-latex-preview-precompile): Record the LaTeX compiler in the
preamble hash, and add %l/%L to the `org-compile-file' format spec.
(org-latex-preview-compiler-command-map): Introduce a new variable to
map each LaTeX compiler name to the command and arguments needed.

Note that due to idiosyncrasies in how the various TeX compilers behave,
pdfLaTeX will likely continue to provide a smoother experience.  In the
future, some work can be done to improve this situation.
2024-04-16 19:58:49 +08:00
TEC c19eadcf20
org-latex-preview: Allow setting persist expiry
* lisp/org-latex-preview.el (org-latex-preview--cache-image,
org-latex-preview-persist-expiry): Introduce a new variable
`org-latex-preview-persist-expiry' which is used within
`org-latex-preview--cache-image' as the expiry value when calling
`org-persist-register' on preview data.
2024-04-16 19:58:49 +08:00
TEC 3a585e28a1
org-latex-preview: Customisable preview width
* lisp/org-latex-preview.el (org-latex-preview--create-tex-file,
org-latex-preview-width ): Introduce a new variable
`org-latex-preview-width' for convenient setting of the preview width,
and use it in the preamble generation.
(org-latex-preview-header): Remove now-redundant content (thanks to the
use of preview.sty).
2024-04-16 19:58:49 +08:00
TEC 35f97f07c9
org-latex-preview: Wait for complete SVG file
* lisp/org-latex-preview.el (org-latex-preview--svg-make-fg-currentColor):
Wait until the SVG file is complete before processing it.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 482bada781
org-latex-preview: Custom processing indicator
* lisp/org-latex-preview.el (org-latex-preview-create-image-async,
org-latex-preview--update-overlay, org-latex-preview--ensure-overlay):
These functions are modified to incorporate customizable visual
indication of preview processing.
(org-latex-preview-processing-indicator): User option to customize how
preview processing is indicated visually.  The current options are:
no indication, show a fringe marker, or apply the
face `org-latex-preview-processing-face'.  The fringe marker is
currently set as the default.
(org-latex-preview--indicate-processing): Add function to handle
applying the appropriate processing indicator.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 7b6f8f4562
org-latex-preview: Compile previews in default-dir
* lisp/org-latex-preview.el (org-latex-preview--generic-callback,
org-latex-preview--tex-compile-async,
org-latex-preview--create-tex-file): Compile latex fragments in
`default-directory', with output placed in `temporary-file-directory'.
This is to ensure that relative paths in fragments (such as paths to
figures) are parsed correctly.  Note that relative paths in the header
are handled during precompilation instead.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 2b6b8605f7
org-latex-preview: Rename filename parts uniformly
* lisp/org-latex-preview.el (org-latex-preview-create-image,
org-latex-preview--dvipng-filter, org-latex-preview--generic-callback,
org-latex-preview--do-cleanup,
org-latex-preview--image-extract-async): Adopt the following
convention for parts of file names used in the latex and image
processing: For file with path 'foo', foo-base refers to its base
name (no directory, no extension) and foo-no-ext refers to its full
path but without a file extension.  The third option (no directory
component but with file extension) is not used in org-latex-preview.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur ad2fe7b48e
org-latex-preview: Precompile with relative paths
* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview-process-alist): Change the precompilation parameters
so the created format file includes all files included/input in the
header with paths relative to the original Org buffer.  The
precompilation is carried out by calling latex from the
`default-directory', with the source tex file and destination format
dump file both placed in `temporary-file-directory'.  Additionally the
source tex file for precompilation is deleted if the precompilation is
successful.
2024-04-16 19:58:49 +08:00
TEC a7a464af21
org-persist: Add situational write inhibition
* lisp/org-persist.el (org-persist-write, org-persist--inhibit-write): TODO
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 21a71b7d5e
org-latex-preview: Compile latex in default-dir
* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview--do-cleanup, org-latex-preview--image-extract-async,
org-latex-preview--tex-compile-async,
org-latex-preview--create-tex-file): Format dump-files can be referred
to by their full paths in latex file preambles by adding a space after the "%&"
token.  Using this, we can compile the tex file containing the
previews in the same directory as the org file and handle relative
file references.  The above functions now do the following:
- format dump-file: Compiled and placed in
temporary-file-directory (no change)
- preview tex file: Refers to the format dump file in
temporary-file-directory. Compiled in default-directory, with output to
default-directory.
- image processors: Input (from tex output) in and output to
temporary-file-directory.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 9405d8f6b4
org-latex-preview: Indicate errors when using pngs
* lisp/org-latex-preview.el (org-latex-preview--update-overlay,
org-latex-preview--ensure-overlay): When using dvipng, latex errors
cannot be indicated using an error face, and if a preview is generated
it is indistinguishable from a correct one.  Fall back to a fringe
marker in this situation.
2024-04-16 19:58:49 +08:00
TEC 12f22bd572
org-latex-preview: Introduce auto-aware toggling
* lisp/org-latex-preview.el (org-latex-preview--auto-aware-toggle,
org-latex-preview): Toggling of fragments with auto-mode active needs a
bit of work to function as most people would likely expect.  To provide
this more nuanced behaviour a new function
`org-latex-preview--auto-aware-toggle' is created and replaces the prior
"toggle this element" implementation in `org-latex-preview'.
2024-04-16 19:58:49 +08:00
TEC a931da50cf
org-latex-preview: Document async image logic
* lisp/org-latex-preview.el (org-latex-preview-create-image-async):
Explain the async call structure/logic with a large code comment.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur c40c287d07
org-latex-preview: Add error reporting via tooltip
* lisp/org-latex-preview.el (org-latex-preview--update-overlay): Add a
rudimentary interface for viewing compile errors (via tooltip).
2024-04-16 19:58:49 +08:00
TEC 90cb3e9507
org-persist: Fallback to associated files in write
* lisp/org-persist.el (org-persist-write:file): If the path given in a
file container does not exist, instead of returning nothing return the
associated path if possible.  This fixes an issue where should the
original file be deleted and `org-persist-write-all' be run, then the
file container's association will be set to nil and so when
`org-persist-gc' is run the nil path will cause
org-persist-associated-files:file to produce an error.
2024-04-16 19:58:49 +08:00
TEC eb7e65bcaf
org-latex-preview: Fix face checking
* lisp/org-latex-preview.el (org-latex-preview--resolved-faces-attr,
org-latex-preview--update-overlay): Handle more of the complexity of the
forms of face specifications, and avoid inheriting information from the
`org-indent' face, which is never useful.
2024-04-16 19:58:49 +08:00
TEC 93799947c8
org-latex-preview: Wait for dvisvgm to write
* lisp/org-latex-preview.el (org-latex-preview--dvisvgm-filter): As
mentioned in the code comment, dvisvgm seems to sometimes lie about when
file content has been written, so we need to work around that by waiting
a little bit.
(org-latex-preview--cleanup-callback): Now that the svg processing is on
a timer, we must be careful not to run the callback to soon.  This isn't
desperately urgent, so we can use a relatively generous timer for this.
2024-04-16 19:58:49 +08:00
TEC 8aabc04b08
org-latex-preview: Adjust defaults
* lisp/org-latex-preview.el (org-latex-preview,
org-latex-to-html-convert-command, org-latex-preview-process-alist):
Adjust the default LaTeX preview parameters to better suit the reworked
preview system.
2024-04-16 19:58:49 +08:00
TEC 6bebfc89a6
org-latex-preview: Equation numbering
* lisp/org-latex-preview.el (org-latex-preview-numbered): Introduce a
new variable to signal whether equations numbering should be calculated
to be correct throughout the document, or not.
(org-latex-preview--numbered-environments): Create a list of known
numbered environments.
(org-latex-preview--environment-numbering-table,
org-latex-preview--get-numbered-environments): Introduce functions which
create a numbering hash table from the buffer.
(org-latex-preview--tex-styled): When an initial equation number is
provided, add LaTeX code to set the counter to apply the numbering.
(org-latex-preview--create,
org-latex-preview-auto--detect-fragments-in-change,
org-latex-preview--create): Make use of the new equation numbering
facilities.
(org-latex-preview--hash): Include first equation number (if applicable)
in the hash.
(org-latex-preview-clear-cache): Use numbering information.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 72d3f815b0
org-latex-preview: Simplify overlay regeneration
* lisp/org-latex-preview.el (org-latex-preview--close-previous-overlay,
org-latex-preview--regenerate-overlay): Overlays carry information
about the buffer they belong to, so it is unnecessary to pass this
information explicitly when passing an overlay argument to a function.
2024-04-16 19:58:49 +08:00
TEC 1eda17bfb8
org-latex-preview: Reuse overlays more
* lisp/org-latex-preview.el (org-latex-preview--create,
org-latex-preview--maybe-track-element-here,
org-latex-preview--make-overlay): Instead of always deleting and
re-creating overlays in a region when wanting to ensure a suitable
overlay exists, we can look at the scanned overlays, check if a suitable
overlay already exists, and re-use it.  To indicate the change in
behaviour, as well as documentation updates
`org-latex-preview--make-overlay' is renamed to
`org-latex-preview--ensure-overlay'.  While we're at it, the
"front-advance" argument is now set when calling `make-overlay', to make
sure that text typed at the beginning of the fragment is not absorbed
into the overlay.
(org-latex-preview--failure-callback): It's possible the overlay
provided could be nil when something goes wrong, in which case do
nothing.
(org-latex-preview--mark-overlay-modified): Move to a separate function
for neatness.
2024-04-16 19:58:49 +08:00
TEC 922e17d98b
org-latex-preview: Better fragment insertion check
* lisp/org-latex-preview.el (org-latex-preview--handle-insert,
org-latex-preview--detect-fragments-in-change, ): Replace the old
checker `org-latex-preview--handle-insert' that was called as a
`post-insert-hook' with a new method of checking for newly created
fragments based on buffer modifications as a `after-change-functions'
hook.  This has a number of benefits, including generally improved
robustness, and the removal of a reliance on font-lock information.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 14558a6df6
org-latex-preview: Previews stored in /tmp
* lisp/org-latex-preview.el (org-latex-preview--get-cached,
org-latex-preview--remove-cached, org-latex-preview--cache-image):
These functions are modified to search in, remove from or add to a
session cache instead of org-persist's cache.
(org-latex-preview-persist-p): User option to turn off or on
persistent caching.
(org-latex-preview--table): Hash table used to hold preview image
metadata when using a session cache instead of org-persist.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 7b0679a262
org-latex-preview: Fix precomp when backend switch
* lisp/org-latex-preview: (org-latex-preview-process-alist,
org-latex-preview-precompile): Update programs required for
imagemagick conversion to include pdflatex.  Precompilation now
generates a new format file when switching
`org-latex-preview-default-process' to or from imagemagick.
2024-04-16 19:58:49 +08:00
TEC cfed7bbc79
org-macs: Ensure that filter run before callbacks
* lisp/org-macs.el (org-async--cleanup-process): Run
`accept-process-output` until no further output is fetched on process
exit, so we can assume that the callbacks are run after all filter
invocations.
2024-04-16 19:58:49 +08:00
TEC 2b36e6f5d3
org-macs: Add :coding kwarg to org-async-call
* lisp/org-macs.el (org-async-call): It is recommended in
(info "(elisp) Decoding Output") that the process coding system be set.
To allow for this we add a new keyword argument, :coding, and set the
default value to utf-8.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 030ad6c9e3
org-latex-preview: Improve preview creation msg
* lisp/org-latex-preview.el: (org-latex-preview,
org-latex-preview-create-image-async): `org-latex-preview' starts an
async process, and so it can only inform the user of the start of the
process. Information about the completion (to success or failure) is
provided by the relevant process sentinels instead.
(org-latex-preview--latex-log, org-latex-preview--image-log,
org-latex-preview--precompile-log): Log buffer names for preview latex
processing.
(org-latex-preview--tex-compile-async,
org-latex-preview--image-extract-async, org-latex-preview-precompile,
org-latex-preview-create-image): Use the newly created log buffer name
variables.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 1194febb98
org-latex-preview: Clear failed preview overlays
* lisp/org-latex-preview.el: (org-latex-preview-create-image-async): If
preview images fail to generate for some or all LaTeX fragments, the
overlays placed on the fragments during processing need to be cleared.
This is done by adding a (failure) callback function to the image
generation process.
(org-latex-preview--failure-callback): Add callback that clears overlays
for previews that fail to generate.  Other behaviours are possible here
but not implemented yet.  This includes signalling failure by changing
how the overlays are displayed, such as by adding a face or fringe
marker.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur daaa034cd0
org-latex-preview: Make clear-cache user-friendly
* lisp/org-latex-preview.el (org-latex-preview-clear-cache): Ask for
confirmation before wiping the entire LaTeX preview cache, and confirm
cache clearing at the end when clearing the buffer cache.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur b8e2f65cee
org-latex-preview: Add cache clearing function
* lisp/org-latex-preview.el (org-latex-preview-clear-cache): Clears the
latex preview cache for LaTeX fragments in a region.  This can be called
as a command by itself, but is primarily required when previewing
fragments by force-regenerating their preview images.  This might be
necessary when the output image corresponding to a fragment is malformed
in some way, for instance.
2024-04-16 19:58:49 +08:00
TEC 330d5a49ab
org-latex-preview: More robust face resolving
* lisp/org-latex-preview.el (org-latex-preview--colors-at,
org-latex-preview--resolved-faces-attr): Just using `face-attribute' on
the first face spec given when the face text property is a list is too
naive.  To do this correctly, a new function
`org-latex-preview--resolved-faces-attr' is introduced which takes care
of the annoying semantics, and used in `org-latex-preview--colors-at' to
better resolve the face attribute.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 3e54ddd45c
org-latex-preview: Split org-latex-preview--create
* lisp/org-latex-preview.el (org-latex-preview--create): This function
does many tasks, including computing face colors and hashes for
fragments.  Split this function into three to reuse the parts for other
planned purposes, such as clearing cached images.
(org-latex-preview--colors-at): Finds appropriate foreground and
background colors at a position for the preview image of a LaTeX
fragment.  Also improve the code to handle multiple faces.
(org-latex-preview--hash): Computes the hash key for a LaTeX fragment
combined with relevant processing metadata.  This key is used to
identify the fragment when caching and placing images.
2024-04-16 19:58:49 +08:00
TEC ab107da60e
org-latex-preview: Graceful precompile failure
* lisp/org-latex-preview.el (org-latex-preview-precompile,
org-latex-preview--create-tex-file): Fall back to compiling without
using a preamble format file if the preamble could not be precompiled
without error.  In such an event, alert the user that preamble
precompilation failed, and in particular let them know if mylatexformat
could not be found.
2024-04-16 19:58:49 +08:00
TEC b9e0db41ce
org-latex-preview: Check all fragments produced
* lisp/org-latex-preview.el (org-latex-preview--check-all-fragments-produced):
Create a new callback that checks that each fragment has indeed been
produced not, and if not guesses the last produced fragment caused a
problem.  The last produced fragment is then marked as erroneous,
its cache entry updated (replaced and re-inserted), and the remaining
fragments are regenerated.
(org-latex-preview-create-image-async): Run
`org-latex-preview--check-all-fragments-produced' after successfully
extracting images.
(org-latex-preview--cache-name, org-latex-preview--cache-image,
org-latex-preview--get-cached): Use a defconst instead of hardcoding the
cache container string in `org-latex-preview--cache-image' and
`org-latex-preview--get-cached'.
(org-latex-preview--remove-cached): Easy removal of an item from the
org-persist cache, as a counterpart to `org-latex-preview--cache-image'.
2024-04-16 19:58:49 +08:00
TEC b77f8b6e12
org-latex-preview: Strip redundant color switching
* lisp/org-latex-preview.el (org-latex-preview--create,
org-latex-preview--create-tex-file): Keep track of the last fragment's
foreground/background color, and only include LaTeX color specifications
as needed.
(org-latex-preview-create-image-async): Provide the processing type so
`org-latex-preview--create-tex-file' can change the style of color
setting as appropriate.  This allows the dvipng route to use postscript
special statements instead of regular color declarations.
2024-04-16 19:58:49 +08:00
TEC a3087aba81
org-latex-preview: Do not show empty SVG fragments
* lisp/org-latex-preview.el (org-latex-preview--svg-make-fg-currentColor):
When an SVG is empty, delete it.  This will cause
`org-latex-preview--cache-image' to give nil as the path.  When the
provided path is nil, do nothing.
(org-latex-preview--update-overlay): If the image path is nil, do
attempt to place an image.  If an error is recorded and there is no
image this causes the text to be shown with the error face.
(org-latex-preview--cleanup-callback): If the image path is nil, do not
try to delete it.
2024-04-16 19:58:49 +08:00
TEC 1931fc2131
org-persist: Allow inhibiting of normalisation
* lisp/org-persist.el (org-persist-register,
org-persist--normalize-container,
org-persist--inhibit-container-normalization): Since container
normalisation is applied frequently (via
`org-persist--normalize-container'), when registering many new
containers the cost can quickly add up.  To avoid redundant overhead,
after normalising the container initially in `org-persist-register' we
bind a new variable `org-persist--inhibit-container-normalization' to t
and adjust `org-persist--normalize-container' to do nothing when this
new variable is non-nil.
2024-04-16 19:58:49 +08:00
TEC a27cc761ff
org-latex-preview: Cache formatted color strings
* lisp/org-latex-preview.el (org-latex-preview--format-color,
org-latex-preview--format-color-cache): Introduce a color cache alist, and use
it.
2024-04-16 19:58:49 +08:00
TEC 7a4925cac6
org-latex-preview: Reduce overhead in SVG editing
* lisp/org-latex-preview.el (org-latex-preview--svg-make-fg-currentColor):
By setting the coding system and avoiding triggering any file
handlers (such as the `image-file-handler'), memory usage is nearly
halved, reducing GC pressure.
2024-04-16 19:58:49 +08:00
TEC 9884886692
org-latex-preview: Tweak baseline alignment
* lisp/org-latex-preview.el (org-latex-preview--update-overlay): After
doing quite a bit of testing, tweak the ascent formula to introduce a
slight correction to visually improve the final result.
2024-04-16 19:58:49 +08:00
TEC 85b284d7f6
org-latex-preview: Introduce dvipng filter
* lisp/org-latex-preview.el (org-latex-preview--dvipng-filter,
org-latex-preview-create-image-async): Introduce a dvipng filter
function to capture information from the dvipng stdout, and place images
as they are generated.
(org-latex-preview-process-alist): Update default dvipng process to emit
depth and height information.
2024-04-16 19:58:49 +08:00
TEC a1ea4c705d
org-latex-preview: Eagerly make fragment overlays
* lisp/org-latex-preview.el (org-create-latex-export,
org-latex-preview--place-images, org-latex-preview--generic-callback,
org-latex-preview--create-tex-file,
org-latex-preview-create-image-async, org-latex-preview--create,
org-latex-preview--close-previous-overlay,
org-latex-preview--make-overlay, org-latex-preview--update-overlay,
org-latex-preview-processing-face): Instead of saving buffer locations
and creating an overlay after the fragment image has been generated,
create an overlay initially and then update it when the image has been
created.  This is done by splitting the image-related code of
`org-latex-preview--make-overlay' into a new function,
`org-latex-preview--update-overlay'.  This provides a number of
advantages, primarily robustness to buffer edits during image
generation.  It also allows us to create a face to indicate
in-progress (`org-latex-preview-processing-face'), which is a nice
visual indicator.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur faccbc04e9
org-latex-preview: Avoid duplicating cached images
* lisp/org-latex-preview.el (org-latex-preview--cache-image): When
caching an image, check if there is already an entry in the
`org-persist--index' with the same hash as this one.  This avoids
missing preview images for fragments with duplicates in a buffer, or
across org-persist's cache.  Without this measure, when we try to
register two fragments identified by the same hash,
`org-persist-register' returns a structure with the correct metadata
for both, but with the cached image path field missing for the second
fragment.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 18095e6578
org-latex-preview: Prioritise near latex previews
* lisp/org-latex-preview.el (org-latex-preview-fragments): Reorder
gathered latex fragments so that they are processed starting at the
window start position.  When previewing a large file, this makes a
significant difference to the perceived speed of the processing.
Remove references to `org-preview-latex-image-directory' from this
function, as it is no longer used to cache images.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur f02c40835b
org-latex-preview: Avoid duplicate latex fragments
* lisp/org-latex-preview.el (org-latex-preview-collect-fragments): This
function was collecting duplicate org-elements when handling nested
latex environments (For instance, a matrix env inside an equation env).
Fix by comparing the current element with the previously collected one.
2024-04-16 19:58:49 +08:00
TEC a1f2f6806d
org-latex-preview: Inhibit fsync when editing SVGs
* lisp/org-latex-preview.el (org-latex-preview--svg-make-fg-currentColor):
The value of fsync appears questionable at best on modern hardware, and
makes little sense for small files where the performance overhead is
significant.  Thus, it makes sense to inhibit fsync when editing the
generated SVGs.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur b014c81a3f
org-latex-preview: Add org-preview-latex-auto-mode
* lisp/org-latex-preview.el (org-latex-preview--make-overlay): Add
overlay hooks that correct for any edits made inside the overlay, and
store a copy of the image specification in the "preview-image" slot.
Also return the created overlay, so it can be used in other functions.
(org-latex-preview--from-overlay-p, org-latex-preview--marker,
org-latex-preview--inhibit): New variables to keep track of overlay
state when using auto-mode.
(org-latex-preview--handle-pre-cursor,
org-latex-preview--handle-post-cursor, org-latex-preview--move-into):
Detect when the cursor is entering or leaving a preview overlay, and
trigger the appropriate action.
(org-latex-preview--open-this-overlay,
org-latex-preview--close-previous-overlay): When cursor has entered/left
a preview overlay, show the text/image as appropriate.
(org-latex-preview--handle-insert): Create a dummy preview overlay when
a new LaTeX fragment is created.
(org-latex-preview--insert-front-handler,
org-preview--insert-behind-handler): Extend preview overlay boundaries
when their content changes.
(org-latex-preview-auto-mode): A new minor mode for automatic
opening/closing of preview overlays, and regeneration.
(org-latex-preview-auto-generate-p): Variable which affects the
regeneration behaviour of `org-latex-preview-auto-mode'.
2024-04-16 19:58:49 +08:00
TEC 239f3bab04
org-latex-preview: Use prefix more consistently
* lisp/ox-odt.el (org-odt--translate-latex-fragments): Use renamed
org-latex-preview-* functions/variables.

* lisp/ox-html.el (org-html-latex-fragment, org-html-latex-environment,
org-html-format-latex, org-html-with-latex): Use renamed
org-latex-preview-* functions/variables.

* lisp/org.el (org-compute-latex-and-related-regexp,
org-latex-packages-alist): Use renamed org-latex-preview-*
functions/variables.

* lisp/org-latex-preview.el (org-dvipng-color, org-create-formula-image,
org--get-display-dpi, org-create-latex-export,
org-preview-latex--tex-styled, org-preview-precompile,
org-latex-preview--place-images, org-latex-preview--generic-callback,
org-latex-preview--image-extract-async,
org-preview-latex--create-tex-file, org-create-formula-image-async,
org-create-latex-preview, org-format-latex, org-latex-preview-fragments,
org-latex-replace-fragments, org-latex-preview,
org--latex-preview-region, org-clear-latex-preview,
org-latex-preview--handle-insert,
org-latex-preview--close-previous-overlay,
org-latex-preview--handle-post-cursor, org--make-preview-overlay,
org-latex-preview-auto-generate-p, org-preview-use-precompilation,
org-format-latex-header, org-preview-latex-process-alist,
org-preview-latex-default-process, org-format-latex-signal-error,
org-format-latex-options): Use renamed org-latex-preview-*
functions/variables.  Use the org-latex-preview group instead of
org-latex for preview-related variables.

* lisp/org-compat.el: Add entries for renamed and obsoleted
org-latex-preview-* functions/variables.

* lisp/ob-latex.el (org-babel-execute:latex, org-format-latex-header)):
Use renamed org-latex-preview-* functions/variables.

* doc/org-manual.org (Footnotes, Previewing LaTeX fragments, LaTeX
fragments): Use renamed org-latex-preview-* functions/variables.

org-latex-preview: clear overlays name

* lisp/org-latex-preview.el (org-clear-latex-preview):
2024-04-16 19:58:49 +08:00
TEC 0b16419f21
org-latex-preview: Basic error indication
* lisp/org-latex-preview.el (org-latex-preview--tex-compile-async,
org-latex-preview--latex-preview-filter): Search the latex compilation
stdout for fragment error messages via a process filter, and record any
found.
(org-latex-preview--display-info): Save the :error field of fragments.
(org--make-preview-overlay): When showing an SVG that has an error, use
the error face.
(org-preview-latex--tex-styled): Make any generated styling directives
take up exactly one line in all situations, so the fragment-relative
line numbers can easily be worked out in
`org-latex-preview--latex-preview-filter'.
2024-04-16 19:58:49 +08:00
TEC d528301806
org-latex-preview: Filter dvisvgm process for info
* lisp/org-latex-preview.el (org-latex-preview--display-info,
org-latex-preview--cleanup-callback, org-create-formula-image-async,
org--make-preview-overlay): Apply a filter to the dvisvgm process in
order to place overlays as images are produced, and along the way
extract size and baseline information that is used to more accurately
position the overlay image.  Because `org-latex-preview--place-images'
is now run within a filter and hence the stdout buffer, we need to
record the org buffer and switch to it before placing overlays.
(org-latex-preview--image-extract-async): Record the DPI-based scaling
factor so it can be accounted for when calculating the pt-scale fragment
height/width/depth.
(org-latex-format-options): Introduce a new parameter :zoom for
affecting the display scaling of images with associated height
resolution, but unlike :scale not the resolution/size of the images
files themselves.  This is then used in `org--make-preview-overlay'.
2024-04-16 19:58:49 +08:00
TEC cb225ab2f2
org-latex-preview: Use a fragment info plists
* lisp/org-latex-preview.el (org-latex-preview--cleanup-callback,
org-create-formula-image-async, org-create-latex-preview): Instead of
using lists of the same length for fragment information, use a single
list of fragment information plists.  This allows for more information
to be stored/used without ending up with half a dozen disparate lists
that we rely on lining up.
2024-04-16 19:58:49 +08:00
TEC 9ecfa904a2
org-latex-preview: Use org-persist for caching
* lisp/org-latex-preview.el
(org-latex-preview--cache-image, org-latex-preview--get-cached):
Implement image data save/retrieve functions using org-persist.
(org-place-latex-image, org-create-latex-export,
org-latex-preview--cleanup-callback, org-create-formula-image-async,
org-create-latex-preview, org--make-preview-overlay): Use the new model
of image data caching relying on org-persist.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 8af5751ff2
org-latex-preview: Make image optional in overlay
* lisp/org-latex-preview.el (org-make--preview-overlay): Supplying an
image file when creating a latex preview overlay is now optional.
This change is in preparation for fully-automatic preview generation,
which requires "dummy" overlays to be placed on latex fragments that
have not yet been fully inserted or previewed.
2024-04-16 19:58:49 +08:00
Karthik Chikmagalur 69265adff0
org-latex-preview: Add imagemagick support
* lisp/org-latex-preview.el (org-preview-latex-process-alist,
org-preview-latex--create-tex-file, org-preview-precompile):
Add a new entry `:latex-precompiler' to
`org-preview-latex-process-alist', and tweak imagemagick-specific
`:image-converter' entries.  The function `org-preview-precompile' now
accepts two arguments.  Extra processing info is used to find the
right precompilation command.
2024-04-16 19:58:48 +08:00
TEC 7bc9de054a
org-macs: Rework async filter
* lisp/org-macs.el (org-async--filter, org-async-call): Expose :info to
the async filter function, and make the obvious modifications to the
process buffer content.
2024-04-16 19:58:48 +08:00
TEC fe5712eac8
org-macs: Accept nested org-async callbacks
* lisp/org-macs.el (org-async--execute-callback, org-async-call): Change
the org-async call arguments callback form to start with the symbol
org-async-task.  This allows for a list of callbacks to be
differentiated from a single callback.  `org-async-call' is also
extended to accept a a callback of this form as its sole argument.

* lisp/org-latex-preview.el (org-latex-preview--image-extract-async,
org-latex-preview--tex-compile-async, org-create-formula-image-async):
Use the new async callback form.
2024-04-16 19:58:48 +08:00
TEC 1223971314
org-latex-preview: use currentColor in SVGs
* lisp/org-latex-preview.el (org-place-latex-image,
org-create-formula-image-async, org--make-preview-overlay,
org-latex-preview--dvisvgm-callback): Edit the dvisvgm generated SVGs to
use "currentColor" instead of a hardcoded foreground color.  To then set
the color of the displayed images, set the overlay face as appropriate.
(org-create-latex-preview): Now that the foreground is generalised, when
producing the sha1 hash for an SVG image, the current foreground value
should not affect it.
2024-04-16 19:58:48 +08:00
TEC c230fef02b
org-latex-preview: Run dvipng concurrently
* lisp/org-latex-preview.el (org-create-formula-image-async): When using
dvipng with the "--follow" flag, image processing can occur
concurrently, so we may as well check for this case and start it
concurrently when this occurs.
2024-04-16 19:58:48 +08:00
TEC 82ac33c1d7
org-latex-preview: Refactor async formula function
* lisp/org-latex-preview.el (org-create-preview-string,
org-create-latex-preview, org-latex-replace-fragments,
org-create-formula-image-async): Refactor
`org-create-formula-image-async', specifically by splitting up the
texfile creation, texfile compilation, image extraction, and cleanup
phases into individual functions (`org-preview-latex--create-tex-file',
`org-latex-preview--tex-compile-async',
`org-latex-preview--image-extract-async',
`org-latex-preview--cleanup-callback') and use `org-async-call' for
managing async.  Along the way a few other minor tweaks have crept in.
2024-04-16 19:58:48 +08:00
Karthik Chikmagalur af24131046
org-latex-preview: Add preamble precompilation
* lisp/org-latex-preview.el (org-create-formula-image-async): Change
how the latex header is computed to account for precompiled headers.
(org-preview-use-precompilation): Boolean to control if
header/preamble precompilation is enabled when generating latex
previews.  If enabled, previewing will be much faster at the expense
of a slower first run.
(org-preview-precompile): This function creates precompiled dumps of
latex preambles/headers, and is intended for use with
`org-create-formula-image-async'.
2024-04-16 19:58:48 +08:00
Karthik Chikmagalur b47f2b23a8
org-latex-preview: Change default latex command
* lisp/org-latex-preview.el (org-preview-latex-process-alist,
org-create-formula-image-async): Change the default latex command to
include the input file name.  Previously the input file was introduced
via process input for compatibility with preview.sty directives.  But
it appears that it's possible to simply set \RequirePackage{preview}
in the latex header without issues.
2024-04-16 19:58:48 +08:00
TEC a8f433c31b
org-latex-preview: Get face from char before latex
* lisp/org-latex-preview.el (org-create-latex-preview): Instead of
getting the face using `face-at-point' use `get-text-property' on the
position one before the start of the LaTeX fragment.  This ensures that
the face is that of the parent element rather than the LaTeX fragment.
2024-04-16 19:58:48 +08:00
Karthik Chikmagalur 3f9cfd7849
org-latex-preview: Add async image previews
* lisp/org-latex-preview.el (org-latex-preview-fragments): Collect and
send all fragments to be processed in a single pass.

(org-create-latex-preview): Apply cached previews and send the
other fragments to be generated asynchronously.

(org-create-formula-image-async): Generate latex preview
images asynchronously and apply them to the buffer.

(org-create-preview-string): Helper function that accepts a math
fragment and returns a latex snippet suitable for use with
preview.sty. Respects color preferences.
2024-04-16 19:58:48 +08:00
TEC e827bb8a24
org-latex-preview: Collect math fragments in fun
* lisp/org-latex-preview.el (org-latex-preview-fragments,
org-latex-replace-fragments, org-latex-collect-fragments): Introduce a
new function to collect all LaTeX math fragments in a
region (`org-latex-collect-fragments') and use it in
`org-latex-preview-fragments' and `org-latex-replace-fragments'.
2024-04-16 19:58:48 +08:00
TEC 712f97aaaf
org-latex-preview: Split org-create-latex-preview
* lisp/org-latex-preview.el (org-create-latex-preview,
org-create-latex-export): Split the export and preview functionality of
`org-create-latex-preview' into two functions: a rewritten
`org-create-latex-preview' that now /only/ deals with previews, and
`org-create-latex-export' for exports.
(org-latex-preview-fragments): Use the new `org-create-latex-preview`.
(org-latex-replace-fragments): Use the new `org-create-latex-export`.
2024-04-16 19:58:48 +08:00
TEC 9edfc99fda
org-latex-preview: Split org-format-latex into two
* lisp/org-latex-preview.el (org-format-latex, org-create-latex-preview,
org-latex-replace-fragments): The rather large `org-format-latex'
function mixed the preview and export code paths together in rather
confusing way.  This has now been split into
`org-latex-replace-fragments' for export, and
`org-latex-preview-fragments' for in-buffer previews.
(org--latex-preview-region): Replace `org-format-latex' with the more
specific `org-latex-preview-fragments'.
(org-latex-tentative-math-re): Instead of let-binding the math regexp,
use a const and explain its usage.

* lisp/ox-odt.el (org-odt--translate-latex-fragments): Replace
`org-format-latex' with the more specific `org-latex-replace-fragments`.

* lisp/ox-html.el (org-html-format-latex): Replace `org-format-latex'
with the more specific `org-latex-replace-fragments`.
2024-04-16 19:58:48 +08:00
TEC 3b9b87548b
org-latex-preview: Split org-place-formula-image
* lisp/org-latex-preview.el (org-create-latex-preview,
org-place-formula-image, org-place-latex-image,
org-place-latex-image-link): Replace `org-place-formula-image' with two
functions, one for overlays (`org-place-latex-image') and another for
links (`org-place-latex-image-link').
2024-04-16 19:58:48 +08:00
TEC ae09ab8ebe
org-latex-preview: Split org-format-latex into two
* lisp/org-latex-preview.el (org-create-latex-preview,
org-format-latex): Split `org-format-latex' into two, creating a new
function for the `cond' branch that deals with
`org-preview-latex-process-alist'.
2024-04-16 19:58:48 +08:00
TEC c591dda62a
org.el: Relocate LaTeX preview code to new file
* lisp/org.el (org-format-latex-options, org-format-latex-signal-error,
org-latex-to-mathml-jar-file, org-latex-to-mathml-convert-command,
org-latex-to-html-convert-command, org-preview-latex-default-process,
org-preview-latex-process-alist, org-preview-latex-image-directory,
org-format-latex-header, org-format-latex-mathml-available-p,
org--make-preview-overlay, org-clear-latex-preview,
org--latex-preview-region, org-latex-preview, org-format-latex,
org-place-formula-image, org-create-math-formula,
org-format-latex-as-mathml, org-format-latex-as-html,
org--get-display-dpi, org-create-formula-image, org-dvipng-color,
org-dvipng-color-format, org-latex-color, org-latex-color-format,
org-normalize-color): Move to org-latex-preview.el.

* lisp/org-latex-preview.el: A new home for code related to the
generated of in-buffer previews of LaTeX fragments.
2024-04-16 19:58:48 +08:00
TEC 72ba4116eb
ox-latex: Call compilation commands asynchronously
* lisp/ox-latex.el (org-latex-compile): Make use of the new
`org-async-call' to call compilation commands asynchronously.

When running ox-export via the command line, the expectation is the
Emacs process will not exit until the export has completed.  This
assumption is violated with async export, so when the current Emacs
session is `noninteractive' we go back to the previous (synchronously)
method of compiling.
2024-04-16 19:58:48 +08:00
TEC 6b6aff2ef2
org-macs: Add an async command execution system
* lisp/org-macs.el (org-async--stack, org-async--wait-queue,
org-async-process-limit, org-async-timeout,
org-async-check-timeout-interval, org-async--counter, org-async-call,
org-async--sentinel, org-async--cleanup-process,
org-async--execute-callback, org-async--monitor-scheduled,
org-async--monitor): Introduce an asynchronous command execution system.
2024-04-16 19:58:48 +08:00
TEC 2917fe4e35
---PATCH SET DIVIDER--- 2024-04-16 19:58:48 +08:00
TEC b458757f0f
org-manual: Document export features
* doc/org-manual.org (+*** Export features): Initial manual entry on
export features.
2024-04-16 19:58:48 +08:00
TEC 3f0a84aced
test-ox: Add tests for export feature resolution
* testing/lisp/test-ox.el: Add a set of tests for
`org-export-resolve-feature-implementations'.
2024-04-16 19:58:48 +08:00
TEC 8bc2742d29
oc-*: Make use of conditional preamble for export
* lisp/oc-natbib.el (org-cite-natbib-use-package): Refactor to make use
of the conditional/generated preamble.

* lisp/oc-csl.el (org-cite-csl-finalizer): Refactor to make use of
the conditional/generated preamble.

* lisp/oc-biblatex.el (org-cite-biblatex-prepare-preamble): Refactor to
make use of the conditional/generated preamble.
2024-04-16 19:58:48 +08:00
TEC 64d0d115af
ox: Introduce conditional/generated preamble
* lisp/ox.el (org-export-detect-features, org-export-expand-features,
org-export-generate-features-preamble): New functions for detecting
features and generating content based on them.
(org-export-conditional-features): Customisation for feature detection.
(org-export-as): Add detected to features to info in the slot :features.
(org-export-update-features): Add a convenience function for users to
edit the feature condition/implementation lists.
(org-export--annotate-info, org-export-detect-features,
org-export-define-derived-backend, org-export-define-backend,
org-export-conditional-features): Refactor backend feature
conditions/implementations into a struct field.  This allows for parent
inheritance to be properly managed, and leads into future work making
features more widely used in the export process.
(org-export-expand-features, org-export-resolve-feature-implementations,
org-export-generate-features-preamble,
org-export-expand-feature-snippets): The main functions for working with
export features.
(org-export-process-features, org-export-update-features): Introduce
`org-export-process-features' to simplify the application of features to
INFO.
2024-04-16 19:58:48 +08:00
TEC c737d6ed77
org-compat: Add ensure-list as org-ensure-list
* lisp/org-compat.el (org-ensure-list): Add `ensure-list' from Emacs 28,
as `org-ensure-list'.
2024-04-16 19:58:48 +08:00
58 changed files with 7682 additions and 3305 deletions

File diff suppressed because it is too large Load Diff

View File

@ -13,49 +13,6 @@ Please send Org bug reports to mailto:emacs-orgmode@gnu.org.
* Version 9.7 (not released yet) * Version 9.7 (not released yet)
** Important announcements and breaking changes ** 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 *** Underline syntax now takes priority over subscript when both are applicable
Previously, Org mode interpreted =(_text_)= as subscript. Previously, Org mode interpreted =(_text_)= as subscript.
@ -126,11 +83,6 @@ transcoders.
Users can disable citations processors by customizing new Users can disable citations processors by customizing new
~org-export-process-citations~ option. ~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 *** =ox-org= disables citation processors by default
@ -362,43 +314,7 @@ 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 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~. and when inserting all the stored links with ~org-insert-all-links~.
*** Major changes and additions to Org element API *** Major changes and additions to Org 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" **** New term: "syntax node"
To reduce confusion with "element" referring to both "syntax element" To reduce confusion with "element" referring to both "syntax element"
@ -653,6 +569,157 @@ constant.
If you still want to use python-mode with ob-python, you might If you still want to use python-mode with ob-python, you might
consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where the code to support python-mode consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where the code to support python-mode
has been ported to. has been ported to.
*** The LaTeX preview system has been overhauled
Org's LaTeX preview system has been overhauled
Org mode's LaTeX preview system has been rewritten with several improvements and new features.
- LaTeX Previews are generated in bulk, and hundreds of LaTeX fragments can be processed per second.
- Images are placed in the buffer continuously as they are generated.
- Preview generation is asynchronous and will not block Emacs.
- Inline previews are aligned and scaled to match the font baseline and size.
- Previews scale along with text when the text scale is changed.
- Previews are coloured to match surrounding text and the active theme.
- SVG previews automatically change colors when the active theme changes.
- Error encountered when compiling LaTeX fragments can be accessed by mousing over preview images.
- Preview overlays can hide and show themselves dynamically based on cursor position.
- Org mode can auto-generate LaTeX previews as you type or edit the text of existing ones.
- Org mode can keep equation numbering consistent by regenerating previews as needed.
**** New features
***** New minor mode ~org-latex-preview-auto-mode~
When this mode is turned on, LaTeX preview overlays in the buffer can be temporarily disabled by moving the cursor into them. Moving the cursor out will display the LaTeX preview image overlay again. Additionally, editing LaTeX fragments that have previews will cause the preview images to be updated.
If the option ~org-latex-preview-auto-track-inserts~ is non-nil (which see), previews for LaTeX fragments will be auto-generated as you type.
**** New options
***** A new option ~org-latex-preview-compiler-command-map~ for setting precompile commands
This alist maps compilers in ~org-latex-compilers~ to command strings
used for LaTeX precompilation when creating previews or LaTeX exports.
***** New option ~org-latex-preview-cache~ to enable preview image caching
When set to =persist=, images produced using ~org-latex-preview~ will
be cached and persisted across Emacs sessions using
~org-persist~. Temporary or custom directories can also be used.
***** New option ~org-latex-preview-persist-expiry~ to set persistence period
This is the number of days for which LaTeX preview images will be
cached, assuming persistence is turned on with
~org-latex-preview-cache~.
***** New option ~org-latex-preview-process-active-indicator~ to indicate preview processing for LaTeX fragments
This option controls the style of visual indicator for LaTeX fragments
currently being processed.
***** New option ~org-latex-preview-numbered~ to automatically renumber previewed LaTeX environments
When non-nil, previewed LaTeX environments in the buffer are
renumbered automatically as required.
***** New option =:page-width= in ~org-latex-preview-appearance-options~ to control the text width for LaTeX fragment previews
This option controls the width of text in preview images. This is
relevant for displaymath-type LaTeX environments, and can be specified
in multiple ways.
***** New option ~org-latex-preview-process-precompiled~ to control LaTeX precompilation
This option controls whether LaTeX headers for buffers are precompiled
when generating the first preview for the buffer. Precompilation can
speed up preview generation by a moderate to large amount depending on
the complexity of the header.
***** New option ~org-latex-preview-auto-track-inserts~ to auto-generate LaTeX previews when typing
When using ~org-latex-preview-auto-mode~ (which see), setting this option to a non-nil value will cause LaTeX previews to be rendered as you type LaTeX fragments. This works however new text is added to the buffer, for example by yanking. When this option is nil and ~org-latex-preview-auto-mode~ is on, existing previews for LaTeX fragments will still be auto-regenerated when edited but no new ones will be auto-generated.
**** Removed or renamed functions and variables
***** Mark ~org-format-latex-options~ obsolete
Use ~org-latex-preview-appearance-options~ instead. The replacement acts in the
same way, except that
- the preview image render size and display size can be independently
controlled with the =:scale= and =:zoom= plist keys, and
- the HTML export-specific parameters =:html-scale=,
=:html-foreground= and =:html-background= have been moved to
~org-html-latex-image-options~, which see.
- the new option =:page-width= (mentioned earlier)
***** Mark ~org-format-latex-header~ obsolete
Use ~org-latex-preview-header~ instead. The replacement variable
behaves the exact same way and is renamed for consistency.
***** Mark ~org-preview-latex-default-process~ obsolete
Use ~org-latex-preview-process-default~ instead. The replacement
variable behaves the exact same way and is renamed for consistency.
***** Mark ~org-preview-latex-process-alist~ obsolete
Use ~org-latex-preview-process-alist~ instead. The replacement
variable acts in the same way, except that =:latex-compiler= must now
be a single command or a list containing a single command. The
placeholder ="%S"= in the format string for =:latex-compiler= is
ignored.
***** Mark ~org-clear-latex-preview~ obsolete
Use ~org-latex-preview-clear-overlays~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-place-formula-image~ obsolete
This functionality is replaced with ~org-latex-preview-place-image~
(for images) and ~org-latex-preview-place-image-link~ (for links).
***** Mark ~org-latex-color-format~ obsolete
Use ~org-latex-preview--format-color~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-latex-color~ obsolete
Use ~org-latex-preview--attr-color~ instead. The replacement function
acts in the exact same way and is renamed for consistency.
***** Mark ~org-normalize-color~ obsolete
Use ~org-latex-preview--normalize-color~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-latex-to-mathml-jar-file~ obsolete
Use ~org-mathml-converter-jar-file~ instead. The replacement variable
acts in the exact same way and is renamed for consistency.
***** Mark ~org-latex-to-mathml-convert-command~ obsolete
Use ~org-mathml-convert-command~ instead. The replacement variable
acts in the exact same way and is renamed for consistency.
***** Mark ~org-format-latex-mathml-available-p~ obsolete
Use ~org-mathml-converter-available-p~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-create-math-formula~ obsolete
Use ~org-mathml-convert-latex~ instead. The replacement function acts
in the exact same way and is renamed for consistency.
*** =ox-icalendar.el= line ending fix may affect downstream packages *** =ox-icalendar.el= line ending fix may affect downstream packages
@ -720,57 +787,7 @@ 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 link when storing any type of external link type in an Org file, not
just =id:= links. 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 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~ *** =.avif= images are now recognized in ~org-html-inline-image-rules~
In =ox-html=, =.avif= image links are now inlined by default. In =ox-html=, =.avif= image links are now inlined by default.
@ -1133,58 +1150,7 @@ 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 properties, links to headlines in the file can also be made more
robust by using the file id instead of the file path. 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 ** 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 *** =colview= dynamic block now writes column width specifications
When column format contains width specifications, =colview= dynamic When column format contains width specifications, =colview= dynamic
@ -1215,31 +1181,6 @@ Example:
: | PROYECTO EMACS | : | PROYECTO EMACS |
: #+END: : #+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 *** =ob-tangle.el=: New flag to remove tangle targets before writing
When ~org-babel-tangle-remove-file-before-write~ is set to ~t~ the When ~org-babel-tangle-remove-file-before-write~ is set to ~t~ the

View File

@ -66,21 +66,17 @@
(lambda (pair) (lambda (pair)
(let ((val (cdr pair))) (let ((val (cdr pair)))
(calc-push-list (calc-push-list
(list
(cond
;; For a vector, Calc follows the format (vec 1 2 3 ...) so ;; 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 ;; a matrix becomes (vec (vec 1 2 3) (vec 4 5 6) ...). See
;; the comments in "Arithmetic routines." section of ;; the comments in "Arithmetic routines." section of
;; calc.el. ;; calc.el.
((listp val) (list (if (listp val)
(cons 'vec (cons 'vec
(if (null (cdr val)) (if (null (cdr val))
(car val) (car val)
(mapcar (lambda (x) (if (listp x) (cons 'vec x) x)) (mapcar (lambda (x) (if (listp x) (cons 'vec x) x))
val)))
val)))) val))))
((numberp val)
(math-read-number (number-to-string val)))
(t val)))))
(calc-store-into (car pair))) (calc-store-into (car pair)))
vars) vars)
(mapc (mapc

View File

@ -3155,7 +3155,7 @@ block but are passed literally to the \"example-block\"."
(let ((cs (org-babel-tangle-comment-links ,i))) (let ((cs (org-babel-tangle-comment-links ,i)))
(concat (c-wrap (car cs)) "\n" (concat (c-wrap (car cs)) "\n"
b "\n" b "\n"
(c-wrap (cadr cs)) "\n"))))) (c-wrap (cadr cs)))))))
(expand-references (expand-references
(ref) (ref)
`(pcase (gethash ,ref org-babel-expand-noweb-references--cache) `(pcase (gethash ,ref org-babel-expand-noweb-references--cache)
@ -3360,22 +3360,10 @@ situations in which is it not appropriate."
(string= cell "*this*"))) (string= cell "*this*")))
;; FIXME: Arbitrary code evaluation. ;; FIXME: Arbitrary code evaluation.
(eval (read cell) t)) (eval (read cell) t))
((let (read-val) ((save-match-data
(and (string-match-p (and (string-match "^[[:space:]]*\"\\(.*\\)\"[[:space:]]*$" cell)
(rx bos (0+ (any space ?\n)) (not (string-match "[^\\]\"" (match-string 1 cell)))))
?\" (0+ anychar) ?\" (read cell))
(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)))) (t (org-no-properties cell))))
(defun org-babel--string-to-number (string) (defun org-babel--string-to-number (string)
@ -3401,9 +3389,7 @@ SEPARATOR is passed to `org-table-convert-region', which see."
;; If the file was empty, don't bother trying to ;; If the file was empty, don't bother trying to
;; convert the table. ;; convert the table.
(when (> pmax 1) (when (> pmax 1)
(org-table-convert-region (org-table-convert-region (point-min) pmax separator)
(point-min) pmax
(or separator 'babel-auto))
(delq nil (delq nil
(mapcar (lambda (row) (mapcar (lambda (row)
(and (not (eq row 'hline)) (and (not (eq row 'hline))

View File

@ -238,10 +238,7 @@ this template."
((not (string= replacement ((not (string= replacement
(buffer-substring begin end))) (buffer-substring begin end)))
(delete-region 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) ((or `babel-call `inline-babel-call)
(org-babel-exp-do-export (org-babel-exp-do-export
(or (org-babel-lob-get-info element) (or (org-babel-lob-get-info element)
@ -370,7 +367,7 @@ The function respects the value of the :exports header argument."
nil)))) nil))))
(defcustom org-babel-exp-code-template (defcustom org-babel-exp-code-template
"#+begin_src %lang%switches%flags%header-args\n%body\n#+end_src" "#+begin_src %lang%switches%flags\n%body\n#+end_src"
"Template used to export the body of code blocks. "Template used to export the body of code blocks.
This template may be customized to include additional information This template may be customized to include additional information
such as the code block name, or the values of particular header such as the code block name, or the values of particular header
@ -382,7 +379,6 @@ and the following %keys may be used.
body ------ the body of the code block body ------ the body of the code block
switches -- the switches associated to the code block switches -- the switches associated to the code block
flags ----- the flags passed 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 In addition to the keys mentioned above, every header argument
defined for the code block may be used as a key and will be defined for the code block may be used as a key and will be
@ -392,7 +388,7 @@ replaced with its value."
:package-version '(Org . "9.6")) :package-version '(Org . "9.6"))
(defcustom org-babel-exp-inline-code-template (defcustom org-babel-exp-inline-code-template
"src_%lang[%switches%flags%header-args]{%body}" "src_%lang[%switches%flags]{%body}"
"Template used to export the body of inline code blocks. "Template used to export the body of inline code blocks.
This template may be customized to include additional information This template may be customized to include additional information
such as the code block name, or the values of particular header such as the code block name, or the values of particular header
@ -404,7 +400,6 @@ and the following %keys may be used.
body ------ the body of the code block body ------ the body of the code block
switches -- the switches associated to the code block switches -- the switches associated to the code block
flags ----- the flags passed 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 In addition to the keys mentioned above, every header argument
defined for the code block may be used as a key and will be defined for the code block may be used as a key and will be
@ -437,41 +432,6 @@ replaced with its value."
(and (org-string-nw-p f) (concat " " f)))) (and (org-string-nw-p f) (concat " " f))))
("flags" . ,(let ((f (assq :flags (nth 2 info)))) ("flags" . ,(let ((f (assq :flags (nth 2 info))))
(and f (concat " " (cdr f))))) (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) ,@(mapcar (lambda (pair)
(cons (substring (symbol-name (car pair)) 1) (cons (substring (symbol-name (car pair)) 1)
(format "%S" (cdr pair)))) (format "%S" (cdr pair))))

View File

@ -37,7 +37,7 @@
(require 'ob) (require 'ob)
(require 'org-macs) (require 'org-macs)
(declare-function org-create-formula-image "org" (string tofile options buffer &optional type)) (declare-function org-latex-preview-create-image "org-latex-preview" (string tofile options buffer &optional type))
(declare-function org-latex-compile "ox-latex" (texfile &optional snippet)) (declare-function org-latex-compile "ox-latex" (texfile &optional snippet))
(declare-function org-latex-guess-inputenc "ox-latex" (header)) (declare-function org-latex-guess-inputenc "ox-latex" (header))
(declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra)) (declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra))
@ -48,11 +48,10 @@
(defvar org-babel-tangle-lang-exts) (defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex")) (add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex"))
(defvar org-format-latex-header) ; From org.el (defvar org-latex-preview-header) ; From org-latex-preview.el
(defvar org-format-latex-options) ; From org.el (defvar org-latex-preview-appearance-options) ; From org-latex-preview.el
(defvar org-latex-default-packages-alist) ; From org.el (defvar org-latex-default-packages-alist) ; From org-latex-preview.el
(defvar org-latex-packages-alist) ; From org.el (defvar org-latex-packages-alist) ; From org-latex-preview.el
(defvar org-preview-latex-process-alist) ; From org.el
(defvar org-babel-default-header-args:latex (defvar org-babel-default-header-args:latex
'((:results . "latex") (:exports . "results")) '((:results . "latex") (:exports . "results"))
@ -129,18 +128,6 @@ exporting the literal LaTeX source."
:group 'org-babel :group 'org-babel
:type '(repeat (string))) :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) (defun org-babel-expand-body:latex (body params)
"Expand BODY according to PARAMS, return the expanded body." "Expand BODY according to PARAMS, return the expanded body."
(mapc (lambda (pair) ;; replace variables (mapc (lambda (pair) ;; replace variables
@ -180,12 +167,11 @@ This function is called by `org-babel-execute-src-block'."
(append (cdr (assq :packages params)) org-latex-packages-alist))) (append (cdr (assq :packages params)) org-latex-packages-alist)))
(cond (cond
((and (string-suffix-p ".png" out-file) (not imagemagick)) ((and (string-suffix-p ".png" out-file) (not imagemagick))
(let ((org-format-latex-header (let ((org-latex-preview-header
(concat org-format-latex-header "\n" (concat org-latex-preview-header "\n"
(mapconcat #'identity headers "\n"))) (mapconcat #'identity headers "\n"))))
(org-preview-latex-process-alist org-babel-latex-process-alist)) (org-latex-preview-create-image
(org-create-formula-image body out-file org-latex-preview-appearance-options in-buffer)))
body out-file org-format-latex-options in-buffer 'png)))
((string= "svg" extension) ((string= "svg" extension)
(with-temp-file tex-file (with-temp-file tex-file
(insert (concat (funcall org-babel-latex-preamble params) (insert (concat (funcall org-babel-latex-preamble params)
@ -252,7 +238,7 @@ This function is called by `org-babel-execute-src-block'."
(insert (insert
(org-latex-guess-inputenc (org-latex-guess-inputenc
(org-splice-latex-header (org-splice-latex-header
org-format-latex-header org-latex-preview-header
(delq (delq
nil nil
(mapcar (mapcar

View File

@ -81,12 +81,6 @@ This will typically be `lua-mode'."
:package-version '(Org . "8.3") :package-version '(Org . "8.3")
:type 'symbol) :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) (defun org-babel-execute:lua (body params)
"Execute Lua BODY according to PARAMS. "Execute Lua BODY according to PARAMS.
This function is called by `org-babel-execute-src-block'." This function is called by `org-babel-execute-src-block'."
@ -257,47 +251,41 @@ function main()
%s %s
end end
function dump(it, indent) fd=io.open(\"%s\", \"w\")
fd:write( main() )
fd:close()")
(defvar org-babel-lua-pp-wrapper-method
"
-- table to string
function t2s(t, indent)
if indent == nil then if indent == nil then
indent = '' indent = \"\"
end end
if type(it) == 'table' and %s then if type(t) == \"table\" then
local count = 0 ts = \"\"
for _ in pairs(it) do for k,v in pairs(t) do
count = count + 1 if type(v) == \"table\" then
end ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \\n\" ..
local result = '' t2s(v, indent .. \" \")
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 result
else else
return tostring(it) ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \" ..
t2s(v, indent .. \" \") .. \"\\n\"
end
end
return ts
else
return tostring(t)
end end
end end
function combine(...)
local result = {} function main()
for index = 1, select('#', ...) do %s
result[index] = dump(select(index, ...))
end
return table.concat(result, '%s')
end end
output = io.open('%s', 'w') fd=io.open(\"%s\", \"w\")
output:write(combine(main())) fd:write(t2s(main()))
output:close()") fd:close()")
(defun org-babel-lua-evaluate (defun org-babel-lua-evaluate
(session body &optional result-type result-params preamble) (session body &optional result-type result-params preamble)
@ -331,17 +319,15 @@ PREAMBLE string is appended to BODY."
(concat (concat
preamble (and preamble "\n") preamble (and preamble "\n")
(format (format
org-babel-lua-wrapper-method (if (member "pp" result-params)
org-babel-lua-pp-wrapper-method
org-babel-lua-wrapper-method)
(mapconcat (mapconcat
(lambda (line) (format "\t%s" line)) (lambda (line) (format "\t%s" line))
(split-string (split-string
(org-remove-indentation (org-remove-indentation
(org-trim body)) (org-trim body))
"[\r\n]") "[\r\n]") "\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-process-file-name tmp-file 'noquote))))
(org-babel-eval-read-file tmp-file)))))) (org-babel-eval-read-file tmp-file))))))
(org-babel-result-cond result-params (org-babel-result-cond result-params

View File

@ -680,7 +680,7 @@ INFO is the export state as a property list."
INFO is the export state, as a property list." INFO is the export state, as a property list."
(and field (and field
(lambda (a b) (lambda (a b)
(org-string< (string-collate-lessp
(org-cite-basic--get-field field a info 'raw) (org-cite-basic--get-field field a info 'raw)
(org-cite-basic--get-field field b info 'raw) (org-cite-basic--get-field field b info 'raw)
nil t)))) nil t))))

View File

@ -190,20 +190,17 @@ INITIAL is an initial style of comma-separated options, as a string or nil.
STYLE is the style definition as a string or nil. STYLE is the style definition as a string or nil.
Return a string." Return a string."
(let* ((options-no-style (let ((options-no-style
(and initial (and initial
(let ((re (rx string-start (or "bibstyle" "citestyle" "style")))) (let ((re (rx string-start (or "bibstyle" "citestyle" "style"))))
(seq-filter (seq-filter
(lambda (option) (not (string-match re option))) (lambda (option) (not (string-match re option)))
(split-string (org-unbracket-string "[" "]" initial) (split-string (org-unbracket-string "[" "]" initial)
"," t " \t"))))) "," t " \t")))))
;; Check whether the string is in key=val,...
(biblatex-options-p (and (stringp style) (string-match-p "\\`[^,=]+=[^,]+\\(,[^=]+=[^,]+\\)\\'" style)))
(style-options (style-options
(cond (cond
((null style) nil) ((null style) nil)
;; Assume it is a valid options string for biblatex if it is in key=val,... format ((not (string-match "/" style)) (list (concat "style=" style)))
((not (string-match "/" style)) (list (if biblatex-options-p style (concat "style=" style))))
(t (t
(list (concat "bibstyle=" (substring style nil (match-beginning 0))) (list (concat "bibstyle=" (substring style nil (match-beginning 0)))
(concat "citestyle=" (substring style (match-end 0)))))))) (concat "citestyle=" (substring style (match-end 0))))))))
@ -379,61 +376,47 @@ INFO is the export state, as a property list."
(other (other
(user-error "Invalid entry %S in `org-cite-biblatex-styles'" other)))))) (user-error "Invalid entry %S in `org-cite-biblatex-styles'" other))))))
(defun org-cite-biblatex-prepare-preamble (output _keys files style &rest _) (defun org-cite-biblatex--generate-latex-usepackage (info)
"Prepare document preamble for \"biblatex\" usage. "Ensure that the biblatex package is loaded.
This is performed by extracting relevant information from the
OUTPUT is the final output of the export process. FILES is the list of file INFO export plist, and modifying any existing
names used as the bibliography. \\usepackage{biblatex} statement in the LaTeX header."
(let ((style (org-cite-bibliography-style info))
This function ensures \"biblatex\" package is required. It also adds resources (usepackage-rx (rx "\\usepackage"
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) "]")) (opt (group "[" (*? anything) "]"))
"{biblatex}"))) "{biblatex}")))
(cond (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 ;; No "biblatex" package loaded. Insert "usepackage" command
;; with appropriate options, including style. ;; with appropriate options, including style.
((not (re-search-backward re nil t))
(save-excursion
(insert
(format "\\usepackage%s{biblatex}\n" (format "\\usepackage%s{biblatex}\n"
(org-cite-biblatex--package-options (org-cite-biblatex--package-options
org-cite-biblatex-options style))))) org-cite-biblatex-options style))))))
;; "biblatex" package loaded, but without any option.
;; Include style only. (defun org-cite-biblatex--generate-latex-bibresources (info)
((not (match-beginning 1)) "From INFO generate LaTeX that loads the relevant bibliography resource files."
(search-forward "{" nil t) (let ((files (plist-get info :bibliography)))
(insert (org-cite-biblatex--package-options nil style))) (mapconcat (lambda (f)
;; "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}" (format "\\addbibresource%s{%s}"
(if (org-url-p f) "[location=remote]" "") (if (org-url-p f) "[location=remote]" "")
f)) f))
files files
"\n") "\n")))
"\n"))
(buffer-string)))
;;; Register `biblatex' processor ;;; Register `biblatex' processor
(org-cite-register-processor 'biblatex (org-cite-register-processor 'biblatex
:export-bibliography #'org-cite-biblatex-export-bibliography :export-bibliography #'org-cite-biblatex-export-bibliography
:export-citation #'org-cite-biblatex-export-citation :export-citation #'org-cite-biblatex-export-citation
:export-finalizer #'org-cite-biblatex-prepare-preamble
:cite-styles #'org-cite-biblatex-list-styles) :cite-styles #'org-cite-biblatex-list-styles)
(provide 'oc-biblatex) (provide 'oc-biblatex)

View File

@ -134,7 +134,6 @@
(declare-function citeproc-render-bib "ext:citeproc") (declare-function citeproc-render-bib "ext:citeproc")
(declare-function citeproc-hash-itemgetter-from-any "ext:citeproc") (declare-function citeproc-hash-itemgetter-from-any "ext:citeproc")
(declare-function citeproc-add-subbib-filters "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-interpret-data "org-element" (data))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated)) (declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
@ -436,13 +435,6 @@ INFO is the export state, as a property list."
(citeproc-proc-style (citeproc-proc-style
(org-cite-csl--processor info)))) (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) (defun org-cite-csl--nocite-p (citation info)
"Non-nil when CITATION object's style is nocite. "Non-nil when CITATION object's style is nocite.
INFO is the export state, as a property list." INFO is the export state, as a property list."
@ -689,9 +681,6 @@ INFO is the export state, as a property list."
(when (and (not footnote) (org-cite-csl--note-style-p info)) (when (and (not footnote) (org-cite-csl--note-style-p info))
(org-cite-adjust-note citation info) (org-cite-adjust-note citation info)
(setq footnote (org-cite-wrap-citation 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. ;; Return structure.
(apply #'citeproc-citation-create (apply #'citeproc-citation-create
`(:note-index `(:note-index
@ -858,27 +847,11 @@ INFO is the export state, as a property list."
;; process. ;; process.
(org-cite-parse-elements output))))) (org-cite-parse-elements output)))))
(defun org-cite-csl-finalizer (output _keys _files _style _backend info)
"Add \"hanging\" package if missing from LaTeX output.
OUTPUT is the export document, as a string. INFO is the export state, as a
property list."
(org-cite-csl--barf-without-citeproc)
(if (not (eq 'org-latex (org-cite-csl--output-format info)))
output
(with-temp-buffer
(save-excursion (insert output))
(when (search-forward "\\begin{document}" nil t)
(goto-char (match-beginning 0))
;; Insert the CSL-specific parts of the LaTeX preamble.
(insert (org-cite-csl--generate-latex-preamble info)))
(buffer-string))))
;;; Register `csl' processor ;;; Register `csl' processor
(org-cite-register-processor 'csl (org-cite-register-processor 'csl
:export-citation #'org-cite-csl-render-citation :export-citation #'org-cite-csl-render-citation
:export-bibliography #'org-cite-csl-render-bibliography :export-bibliography #'org-cite-csl-render-bibliography
:export-finalizer #'org-cite-csl-finalizer
:cite-styles :cite-styles
'((("author" "a") ("bare" "b") ("caps" "c") ("full" "f") ("bare-caps" "bc") ("caps-full" "cf") ("bare-caps-full" "bcf")) '((("author" "a") ("bare" "b") ("caps" "c") ("full" "f") ("bare-caps" "bc") ("caps-full" "cf") ("bare-caps-full" "bcf"))
(("noauthor" "na") ("bare" "b") ("caps" "c") ("bare-caps" "bc")) (("noauthor" "na") ("bare" "b") ("caps" "c") ("bare-caps" "bc"))

View File

@ -168,32 +168,25 @@ state, as a property list."
(org-cite-natbib--build-optional-arguments citation info) (org-cite-natbib--build-optional-arguments citation info)
(org-cite-natbib--build-arguments citation))) (org-cite-natbib--build-arguments citation)))
(defun org-cite-natbib-use-package (output &rest _) (defun org-cite-natbib--generate-latex-preamble (info)
"Ensure output requires \"natbib\" package. "Ensure that the \"natbib\" package is loaded.
OUTPUT is the final output of the export process." INFO is a plist used as a communication channel."
(with-temp-buffer (and (not (string-match
(save-excursion (insert output)) (rx "\\usepackage" (opt "[" (*? nonl) "]") "{natbib}")
(when (search-forward "\\begin{document}" nil t) (plist-get info :latex-full-header)))
;; Ensure there is a \usepackage{natbib} somewhere or add one.
(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" (format "\\usepackage%s{natbib}\n"
(if (null org-cite-natbib-options) (if (null org-cite-natbib-options)
"" ""
(format "[%s]" (format "[%s]"
(mapconcat #'symbol-name (mapconcat #'symbol-name
org-cite-natbib-options org-cite-natbib-options
",")))))))) ","))))))
(buffer-string)))
;;; Register `natbib' processor ;;; Register `natbib' processor
(org-cite-register-processor 'natbib (org-cite-register-processor 'natbib
:export-bibliography #'org-cite-natbib-export-bibliography :export-bibliography #'org-cite-natbib-export-bibliography
:export-citation #'org-cite-natbib-export-citation :export-citation #'org-cite-natbib-export-citation
:export-finalizer #'org-cite-natbib-use-package
:cite-styles :cite-styles
'((("author" "a") ("caps" "a") ("full" "f")) '((("author" "a") ("caps" "a") ("full" "f"))
(("noauthor" "na") ("bare" "b")) (("noauthor" "na") ("bare" "b"))

View File

@ -206,7 +206,7 @@ This function is intended to be used as a :set function."
(set symbol value) (set symbol value)
(dolist (buf (org-buffer-list)) (dolist (buf (org-buffer-list))
(with-current-buffer buf (with-current-buffer buf
(org-restart-font-lock)))) (org-link-descriptive-ensure))))
(defcustom org-link-descriptive t (defcustom org-link-descriptive t
"Non-nil means Org displays descriptive links. "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', added to the companion hook `org-execute-file-search-functions',
which see. which see.
A function in this hook may also use `org-link-store-props' and set A function in this hook may also use `setq' to set the variable
`:description' property to provide a suggestion for the descriptive `description' to provide a suggestion for the descriptive text to
text to be used for this link when it gets inserted into an Org buffer be used for this link when it gets inserted into an Org buffer
with \\[org-insert-link].") with \\[org-insert-link].")
(defvar org-execute-file-search-functions nil (defvar org-execute-file-search-functions nil
@ -649,6 +649,22 @@ exact and fuzzy text search.")
(defvar org-link--search-failed nil (defvar org-link--search-failed nil
"Non-nil when last link search failed.") "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 ;;; Internal Functions
@ -1118,12 +1134,12 @@ The functions are defined in the `:store' property of
INTERACTIVE? which indicates whether the user has initiated INTERACTIVE? which indicates whether the user has initiated
`org-store-link' interactively. `org-store-link' interactively.
Each function will be called in turn with a single argument Each function will be called in turn until one returns a non-nil
INTERACTIVE? - non-nil when user interaction is allowed. Each function value. Each function should check if it is responsible for
should check if it is responsible for creating this link (for example creating this link (for example by looking at the major mode).
by looking at the major mode). If not, it must return nil. If yes, If not, it must exit and return nil. If yes, it should return
it should return a non-nil value after calling `org-link-store-props' a non-nil value after calling `org-link-store-props' with a list
with a list of properties and values. Special properties are: of properties and values. Special properties are:
:type The link prefix, like \"http\". This must be given. :type The link prefix, like \"http\". This must be given.
:link The link, like \"http://www.astro.uva.nl/~dominik\". :link The link, like \"http://www.astro.uva.nl/~dominik\".
@ -1140,6 +1156,9 @@ and then used in capture templates."
if store-func if store-func
collect store-func)) collect store-func))
(defvar org-link--abbrev-functions nil
"Alist of abbrev link expressions and functions.")
(defun org-link-expand-abbrev (link) (defun org-link-expand-abbrev (link)
"Replace link abbreviations in LINK string. "Replace link abbreviations in LINK string.
Abbreviations are defined in `org-link-abbrev-alist'." Abbreviations are defined in `org-link-abbrev-alist'."
@ -1154,14 +1173,27 @@ Abbreviations are defined in `org-link-abbrev-alist'."
(setq rpl (cdr as)) (setq rpl (cdr as))
(cond (cond
((symbolp rpl) (funcall rpl tag)) ((symbolp rpl) (funcall rpl tag))
((string-match "%(\\([^)]+\\))" rpl) ((string-match "%(\\([^) ]+\\))" rpl) ; %(function)
(replace-match (replace-match
(save-match-data (save-match-data
(funcall (intern-soft (match-string 1 rpl)) tag)) (funcall (intern-soft (match-string 1 rpl)) tag))
t t rpl)) t t rpl))
((string-match "%s" rpl) (replace-match (or tag "") t t rpl)) ((string-match "%(\\(.+\\))" rpl) ; %(sexpr using tag)
((string-match "%h" rpl) (replace-match
(replace-match (url-hexify-string (or tag "")) t t rpl)) (save-match-data
(funcall (or (cdr (assoc (match-string 1 rpl)
org-link--abbrev-functions))
(cdar (push (cons (match-string 1 rpl)
(eval (read (format
"(lambda (tag) (%s))"
(match-string 1 rpl)))))
org-link--abbrev-functions)))
tag))
t t rpl))
((string-match-p "%[0-<>^_]?[0-9]*\\(?:\\.[0-9]+\\)?s" rpl)
(format-spec rpl `((?s . ,(or tag "")))))
((string-match-p "%[0-<>^_]?[0-9]*\\(?:\\.[0-9]+\\)?h" rpl)
(format-spec rpl `((?h . ,(url-hexify-string (or tag ""))))))
(t (concat rpl tag))))))) (t (concat rpl tag)))))))
(defun org-link-open (link &optional arg) (defun org-link-open (link &optional arg)
@ -1663,12 +1695,18 @@ If the link is in hidden text, expose it."
(interactive) (interactive)
(org-next-link t)) (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 ;;;###autoload
(defun org-toggle-link-display () (defun org-toggle-link-display ()
"Toggle the literal or descriptive display of links in current buffer." "Toggle the literal or descriptive display of links in current buffer."
(interactive) (interactive)
(setq org-link-descriptive (not org-link-descriptive)) (setq org-link-descriptive (not org-link-descriptive))
(org-restart-font-lock)) (org-link-descriptive-ensure))
;;;###autoload ;;;###autoload
(defun org-store-link (arg &optional interactive?) (defun org-store-link (arg &optional interactive?)
@ -1716,7 +1754,7 @@ NAME."
;; Negate `org-context-in-file-links' when given a single universal arg. ;; 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 (let ((org-link-context-for-files (org-xor org-link-context-for-files
(equal arg '(4)))) (equal arg '(4))))
link desc search agenda-link) ;; description link cpltxt desc search agenda-link) ;; description
(cond (cond
;; Store a link using an external link type, if any function is ;; Store a link using an external link type, if any function is
;; available, unless external link types are skipped for this ;; available, unless external link types are skipped for this
@ -1787,8 +1825,9 @@ NAME."
;; Image mode ;; Image mode
((eq major-mode 'image-mode) ((eq major-mode 'image-mode)
(setq link (concat "file:" (setq cpltxt (concat "file:"
(abbreviate-file-name buffer-file-name))) (abbreviate-file-name buffer-file-name))
link cpltxt)
(org-link-store-props :type "image" :file 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 ;; In dired, store a link to the file of the current line
@ -1799,16 +1838,20 @@ NAME."
(expand-file-name (dired-get-filename nil t))) (expand-file-name (dired-get-filename nil t)))
;; Otherwise, no file so use current directory. ;; Otherwise, no file so use current directory.
default-directory)) default-directory))
(setq link (concat "file:" file)))) (setq cpltxt (concat "file:" file)
link cpltxt)))
;; Try `org-create-file-search-functions`. If any are ;; Try `org-create-file-search-functions`. If any are
;; successful, create a file link to the current buffer with ;; successful, create a file link to the current buffer with
;; the provided search string. ;; the provided search string. (sets `link` and `cpltxt` to
;; the same thing; it looks like the intention originally was
;; that cpltxt was a description, which might have been set by
;; the search-function (removed in switch to lexical binding)).
((setq search (run-hook-with-args-until-success ((setq search (run-hook-with-args-until-success
'org-create-file-search-functions)) 'org-create-file-search-functions))
(setq link (concat "file:" (abbreviate-file-name buffer-file-name) (setq link (concat "file:" (abbreviate-file-name buffer-file-name)
"::" search) "::" search))
desc (plist-get org-store-link-plist :description))) (setq cpltxt (or link))) ;; description
;; Main logic for storing built-in link types in org-mode ;; Main logic for storing built-in link types in org-mode
;; buffers ;; buffers
@ -1826,19 +1869,22 @@ NAME."
;; Avoid [[target][file:~/org/test.org::target]] ;; Avoid [[target][file:~/org/test.org::target]]
;; links. Maybe the case of identical target and ;; links. Maybe the case of identical target and
;; description should be handled by `org-insert-link'. ;; description should be handled by `org-insert-link'.
cpltxt nil
desc nil)) desc nil))
(t (t
;; Just link to current headline. ;; Just link to current headline.
(let ((here (org-link--file-link-to-here))) (let ((here (org-link--file-link-to-here)))
(setq link (car here)) (setq cpltxt (car here))
(setq desc (cdr here))))))) (setq desc (cdr here)))
(setq link cpltxt)))))
;; Buffer linked to file, but not an org-mode buffer. ;; Buffer linked to file, but not an org-mode buffer.
((buffer-file-name (buffer-base-buffer)) ((buffer-file-name (buffer-base-buffer))
;; Just link to this file here. ;; Just link to this file here.
(let ((here (org-link--file-link-to-here))) (let ((here (org-link--file-link-to-here)))
(setq link (car here)) (setq cpltxt (car here))
(setq desc (cdr here)))) (setq desc (cdr here)))
(setq link cpltxt))
(interactive? (interactive?
(user-error "No method for storing a link from this buffer")) (user-error "No method for storing a link from this buffer"))
@ -1846,7 +1892,8 @@ NAME."
(t (setq link nil))) (t (setq link nil)))
;; We're done setting link and desc, clean up ;; We're done setting link and desc, clean up
(when (consp link) (setq link (or (cdr link) (car link)))) (when (consp link) (setq cpltxt (car link) link (cdr link)))
(setq link (or link cpltxt))
(cond ((not desc)) (cond ((not desc))
((equal desc "NONE") (setq desc nil)) ((equal desc "NONE") (setq desc nil))
(t (setq desc (org-link-display-format desc)))) (t (setq desc (org-link-display-format desc))))

View File

@ -2385,7 +2385,8 @@ The following commands are available:
org-agenda-clockreport-mode org-agenda-start-with-clockreport-mode org-agenda-clockreport-mode org-agenda-start-with-clockreport-mode
org-agenda-archives-mode org-agenda-start-with-archives-mode)) org-agenda-archives-mode org-agenda-start-with-archives-mode))
(add-to-invisibility-spec '(org-filtered)) (add-to-invisibility-spec '(org-filtered))
(add-to-invisibility-spec '(org-link)) (org-fold-core-initialize `(,org-link--description-folding-spec
,org-link--link-folding-spec))
(easy-menu-change (easy-menu-change
'("Agenda") "Agenda Files" '("Agenda") "Agenda Files"
(append (append
@ -5832,7 +5833,7 @@ displayed in agenda view."
(org-encode-time ; DATE bound by calendar (org-encode-time ; DATE bound by calendar
0 0 0 (nth 1 date) (car date) (nth 2 date)))) 0 0 0 (nth 1 date) (car date) (nth 2 date))))
"\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)" "\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[hdwmy]>\\)"
"\\|\\(<%%\\(([^>\n]+)\\)\\([^\n>]*\\)>\\)")) "\\|\\(<%%\\(([^>\n]+)\\)>\\)"))
timestamp-items) timestamp-items)
(goto-char (point-min)) (goto-char (point-min))
(while (re-search-forward regexp nil t) (while (re-search-forward regexp nil t)
@ -7490,8 +7491,8 @@ The optional argument TYPE tells the agenda type."
"Compare the string values of categories of strings A and B." "Compare the string values of categories of strings A and B."
(let ((ca (or (get-text-property (1- (length a)) 'org-category a) "")) (let ((ca (or (get-text-property (1- (length a)) 'org-category a) ""))
(cb (or (get-text-property (1- (length b)) 'org-category b) ""))) (cb (or (get-text-property (1- (length b)) 'org-category b) "")))
(cond ((org-string< ca cb) -1) (cond ((string-lessp ca cb) -1)
((org-string< cb ca) +1)))) ((string-lessp cb ca) +1))))
(defsubst org-cmp-todo-state (a b) (defsubst org-cmp-todo-state (a b)
"Compare the todo states of strings A and B." "Compare the todo states of strings A and B."
@ -7537,8 +7538,8 @@ The optional argument TYPE tells the agenda type."
(cond ((not (or ta tb)) nil) (cond ((not (or ta tb)) nil)
((not ta) +1) ((not ta) +1)
((not tb) -1) ((not tb) -1)
((org-string< ta tb) -1) ((string-lessp ta tb) -1)
((org-string< tb ta) +1)))) ((string-lessp tb ta) +1))))
(defsubst org-cmp-tag (a b) (defsubst org-cmp-tag (a b)
"Compare the string values of the first tags of A and B." "Compare the string values of the first tags of A and B."
@ -7547,8 +7548,8 @@ The optional argument TYPE tells the agenda type."
(cond ((not (or ta tb)) nil) (cond ((not (or ta tb)) nil)
((not ta) +1) ((not ta) +1)
((not tb) -1) ((not tb) -1)
((funcall (or org-tags-sort-function #'org-string<) ta tb) -1) ((string-lessp ta tb) -1)
((funcall (or org-tags-sort-function #'org-string<) tb ta) +1)))) ((string-lessp tb ta) +1))))
(defsubst org-cmp-time (a b) (defsubst org-cmp-time (a b)
"Compare the time-of-day values of strings A and B." "Compare the time-of-day values of strings A and B."
@ -8594,14 +8595,6 @@ Negative selection means regexp must not match for selection of an entry."
(org-agenda-redo)) (org-agenda-redo))
(message "Display now includes inactive timestamps as well")) (message "Display now includes inactive timestamps as well"))
((eq org-agenda-type 'search) ((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-add-to-string
'org-agenda-query-string 'org-agenda-query-string
(if org-agenda-last-search-view-search-was-boolean (if org-agenda-last-search-view-search-was-boolean
@ -9222,8 +9215,7 @@ When called with a prefix argument, include all archive files as well."
(when (and (markerp m) (marker-buffer m)) (when (and (markerp m) (marker-buffer m))
(and org-agenda-follow-mode (and org-agenda-follow-mode
(if org-agenda-follow-indirect (if org-agenda-follow-indirect
(let ((org-indirect-buffer-display 'other-window)) (org-agenda-tree-to-indirect-buffer nil)
(org-agenda-tree-to-indirect-buffer nil))
(org-agenda-show))) (org-agenda-show)))
(and org-agenda-show-outline-path (and org-agenda-show-outline-path
(org-with-point-at m (org-display-outline-path org-agenda-show-outline-path)))))) (org-with-point-at m (org-display-outline-path org-agenda-show-outline-path))))))
@ -9315,17 +9307,20 @@ Pass ARG, FORCE-ARG, DELETE and BODY to `org-agenda-do-in-region'."
(marker (or (org-get-at-bol 'org-marker) (marker (or (org-get-at-bol 'org-marker)
(org-agenda-error))) (org-agenda-error)))
(buffer (marker-buffer marker)) (buffer (marker-buffer marker))
(pos (marker-position marker))
(type (org-get-at-bol 'type)) (type (org-get-at-bol 'type))
dbeg dend (n 0)) dbeg dend (n 0))
(org-with-remote-undo buffer (org-with-remote-undo buffer
(org-with-point-at marker (with-current-buffer buffer
(save-excursion
(goto-char pos)
(if (and (derived-mode-p 'org-mode) (not (member type '("sexp")))) (if (and (derived-mode-p 'org-mode) (not (member type '("sexp"))))
(setq dbeg (progn (org-back-to-heading t) (point)) (setq dbeg (progn (org-back-to-heading t) (point))
dend (org-end-of-subtree t t)) dend (org-end-of-subtree t t))
(setq dbeg (line-beginning-position) (setq dbeg (line-beginning-position)
dend (min (point-max) (1+ (line-end-position))))) dend (min (point-max) (1+ (line-end-position)))))
(goto-char dbeg) (goto-char dbeg)
(while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n)))) (while (re-search-forward "^[ \t]*\\S-" dend t) (setq n (1+ n)))))
(when (or (eq t org-agenda-confirm-kill) (when (or (eq t org-agenda-confirm-kill)
(and (numberp org-agenda-confirm-kill) (and (numberp org-agenda-confirm-kill)
(> n org-agenda-confirm-kill))) (> n org-agenda-confirm-kill)))
@ -9342,7 +9337,7 @@ Pass ARG, FORCE-ARG, DELETE and BODY to `org-agenda-do-in-region'."
(set-window-configuration win-conf)))) (set-window-configuration win-conf))))
(let ((org-agenda-buffer-name bufname-orig)) (let ((org-agenda-buffer-name bufname-orig))
(org-remove-subtree-entries-from-agenda buffer dbeg dend)) (org-remove-subtree-entries-from-agenda buffer dbeg dend))
(org-with-point-at marker (delete-region dbeg dend)) (with-current-buffer buffer (delete-region dbeg dend))
(message "Agenda item and source killed"))))) (message "Agenda item and source killed")))))
(defvar org-archive-default-command) ; defined in org-archive.el (defvar org-archive-default-command) ; defined in org-archive.el
@ -9405,7 +9400,7 @@ 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. 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." If this information is not given, the function uses the tree at point."
(let ((buf (or buf (current-buffer))) m p) (let ((buf (or buf (current-buffer))) m p)
(org-with-wide-buffer (save-excursion
(unless (and beg end) (unless (and beg end)
(org-back-to-heading t) (org-back-to-heading t)
(setq beg (point)) (setq beg (point))
@ -9693,6 +9688,27 @@ With a `\\[universal-argument]' prefix, make a separate frame for this tree, \
i.e. don't use i.e. don't use
the dedicated frame." the dedicated frame."
(interactive "P") (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) (org-agenda-check-no-diary)
(let* ((marker (or (org-get-at-bol 'org-marker) (let* ((marker (or (org-get-at-bol 'org-marker)
(org-agenda-error))) (org-agenda-error)))
@ -9701,8 +9717,7 @@ the dedicated frame."
(with-current-buffer buffer (with-current-buffer buffer
(save-excursion (save-excursion
(goto-char pos) (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) (defvar org-last-heading-marker (make-marker)
"Marker pointing to the headline that last changed its TODO state "Marker pointing to the headline that last changed its TODO state

View File

@ -808,7 +808,6 @@ previous clocking intervals."
60))) 60)))
(+ currently-clocked-time (or org-clock-total-time 0)))) (+ currently-clocked-time (or org-clock-total-time 0))))
;;;###autoload
(defun org-clock-modify-effort-estimate (&optional value) (defun org-clock-modify-effort-estimate (&optional value)
"Add to or set the effort estimate of the item currently being clocked. "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. VALUE can be a number of minutes, or a string with format hh:mm or mm.
@ -3112,9 +3111,8 @@ PROPERTIES: The list properties specified in the `:properties' parameter
(let* ((todo (org-get-todo-state)) (let* ((todo (org-get-todo-state))
(tags-list (org-get-tags)) (tags-list (org-get-tags))
(org-scanner-tags tags-list) (org-scanner-tags tags-list)
(org-trust-scanner-tags t) (org-trust-scanner-tags t))
(level (org-current-level))) (funcall matcher todo tags-list nil)))))
(funcall matcher todo tags-list level)))))
(goto-char (point-min)) (goto-char (point-min))
(setq st t) (setq st t)
(while (or (and (bobp) (prog1 st (setq st nil)) (while (or (and (bobp) (prog1 st (setq st nil))

View File

@ -123,12 +123,6 @@ in `org-columns-summary-types-default', which see."
(function :tag "Summarize") (function :tag "Summarize")
(function :tag "Collect"))))) (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 ;;; Column View
@ -1448,13 +1442,9 @@ that will be excluded from the resulting view. FORMAT is a
format string for columns, or nil. When LOCAL is non-nil, only format string for columns, or nil. When LOCAL is non-nil, only
capture headings in current subtree. capture headings in current subtree.
This function returns a list containing the title row and all other This function returns a list containing the title row and all
rows. Each row is either a list, or the symbol `hline'. The first list other rows. Each row is a list of fields, as strings, or
is the heading row as a list of strings with the column titles according `hline'."
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) (org-columns (not local) format)
(goto-char org-columns-top-level-marker) (goto-char org-columns-top-level-marker)
(let ((columns (length org-columns-current-fmt-compiled)) (let ((columns (length org-columns-current-fmt-compiled))
@ -1467,10 +1457,11 @@ the columns according to FORMAT."
(dotimes (i columns) (dotimes (i columns)
(let* ((col (+ (line-beginning-position) i)) (let* ((col (+ (line-beginning-position) i))
(p (get-char-property col 'org-columns-key))) (p (get-char-property col 'org-columns-key)))
(push (get-char-property col (push (org-quote-vert
(get-char-property col
(if (string= p "ITEM") (if (string= p "ITEM")
'org-columns-value 'org-columns-value
'org-columns-value-modified)) 'org-columns-value-modified)))
row))) row)))
(unless (or (unless (or
(and skip-empty (and skip-empty
@ -1502,9 +1493,7 @@ an inline src-block."
'(footnote-reference inline-babel-call inline-src-block target '(footnote-reference inline-babel-call inline-src-block target
radio-target statistics-cookie) radio-target statistics-cookie)
#'org-element-extract) #'org-element-extract)
(org-quote-vert (org-no-properties (org-element-interpret-data data))))
(org-no-properties
(org-element-interpret-data data)))))
;;;###autoload ;;;###autoload
(defun org-dblock-write:columnview (params) (defun org-dblock-write:columnview (params)
@ -1556,17 +1545,7 @@ PARAMS is a property list of parameters:
`:vlines' `:vlines'
When non-nil, make each column a column group to enforce 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 ((table
(let ((id (plist-get params :id)) (let ((id (plist-get params :id))
view-file view-pos) view-file view-pos)
@ -1594,17 +1573,6 @@ PARAMS is a property list of parameters:
(plist-get params :exclude-tags) (plist-get params :exclude-tags)
(plist-get params :format) (plist-get params :format)
view-pos))))) view-pos)))))
(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 (width-specs
(mapcar (lambda (spec) (nth 2 spec)) (mapcar (lambda (spec) (nth 2 spec))
org-columns-current-fmt-compiled))) org-columns-current-fmt-compiled)))
@ -1631,14 +1599,7 @@ definition."
(and (numberp hlines) (<= level hlines)))) (and (numberp hlines) (<= level hlines))))
(push 'hline new-table)) (push 'hline new-table))
(when item-index (when item-index
(let* ((raw (nth item-index (cdr row))) (let ((item (org-columns--clean-item (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)) (setf (nth item-index (cdr row))
(if (and indent (> level 1)) (if (and indent (> level 1))
(concat "\\_" (make-string (* 2 (1- level)) ?\s) item) (concat "\\_" (make-string (* 2 (1- level)) ?\s) item)
@ -1655,8 +1616,6 @@ definition."
;; to the resulting table, adding alignment field as the first ;; to the resulting table, adding alignment field as the first
;; row. ;; row.
(push (mapcar (lambda (width) (when width (format "<%d>" width))) width-specs) table)) (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")) (let ((content-lines (org-split-string (plist-get params :content) "\n"))
recalc) recalc)
;; Insert affiliated keywords before the table. ;; Insert affiliated keywords before the table.

View File

@ -251,6 +251,18 @@ removed."
default))) default)))
": "))) ": ")))
(if (fboundp 'ensure-list)
(defalias 'org-ensure-list #'ensure-list)
(defun org-ensure-list (object)
"Return OBJECT as a list.
If OBJECT is already a list, return OBJECT itself. If it's
not a list, return a one-element list containing OBJECT.
Compatability substitute for `ensure-list' in Emacs 28."
(if (listp object)
object
(list object))))
;;; Emacs < 27.1 compatibility ;;; Emacs < 27.1 compatibility
@ -649,29 +661,6 @@ Counting starts at 1."
(define-obsolete-variable-alias 'org-plantuml-executable-args 'org-plantuml-args (define-obsolete-variable-alias 'org-plantuml-executable-args 'org-plantuml-args
"Org 9.6") "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]" (defconst org-latex-line-break-safe "\\\\[0pt]"
"Linebreak protecting the following [...]. "Linebreak protecting the following [...].
@ -697,6 +686,393 @@ This constant, for example, makes the below code not err:
"use `org-element' library" "use `org-element' library"
"9.0") "9.0")
(define-obsolete-variable-alias
'org-format-latex-options 'org-latex-preview-appearance-options "9.7")
(make-obsolete-variable
'org-format-latex-signal-error "no longer used" "9.7")
(define-obsolete-variable-alias
'org-format-latex-header 'org-latex-preview-preamble "9.7")
(define-obsolete-variable-alias
'org-preview-latex-default-process 'org-latex-preview-process-default "9.7")
(define-obsolete-variable-alias
'org-preview-latex-process-alist 'org-latex-preview-process-alist "9.7")
(define-obsolete-function-alias
'org-clear-latex-preview 'org-latex-preview-clear-overlays "9.7")
(make-obsolete
'org-place-formula-image "no longer used" "9.7")
(define-obsolete-function-alias
'org-latex-color-format 'org-latex-preview--format-color "9.7")
(define-obsolete-function-alias
'org-latex-color 'org-latex-preview--attr-color "9.7")
;; MathML related functions from org-latex-preview.el
(define-obsolete-variable-alias
'org-latex-to-mathml-jar-file 'org-mathml-converter-jar-file "9.7")
(define-obsolete-variable-alias
'org-latex-to-mathml-convert-command 'org-mathml-convert-command "9.7")
(define-obsolete-function-alias
'org-format-latex-mathml-available-p 'org-mathml-converter-available-p "9.7")
(define-obsolete-function-alias
'org-create-math-formula 'org-mathml-convert-latex "9.7")
;; FIXME: Unused; obsoleted; to be removed.
(defun org-format-latex-as-mathml (latex-frag latex-frag-type
prefix &optional dir)
(let* ((absprefix (expand-file-name prefix dir))
(print-length nil) (print-level nil)
(formula-id (concat
"formula-"
(sha1
(prin1-to-string
(list latex-frag
org-latex-to-mathml-convert-command)))))
(formula-cache (format "%s-%s.mathml" absprefix formula-id))
(formula-cache-dir (file-name-directory formula-cache)))
(unless (file-directory-p formula-cache-dir)
(make-directory formula-cache-dir t))
(unless (file-exists-p formula-cache)
(org-mathml-convert-latex latex-frag formula-cache))
(if (file-exists-p formula-cache)
;; Successful conversion. Return the link to MathML file.
(org-add-props
(format "[[file:%s]]" (file-relative-name formula-cache dir))
(list 'org-latex-src (replace-regexp-in-string "\"" "" latex-frag)
'org-latex-src-embed-type (if latex-frag-type
'paragraph 'character)))
;; Failed conversion. Return the LaTeX fragment verbatim
latex-frag)))
(make-obsolete #'org-format-latex-as-mathml "to be removed" "9.7")
;; FIXME: Unused; obsoleted; to be removed.
(defun org-dvipng-color (attr)
"Return a RGB color specification for dvipng."
(org-dvipng-color-format (face-attribute 'default attr nil)))
;; FIXME: Unused; obsoleted; to be removed.
(defun org-dvipng-color-format (color-name)
"Convert COLOR-NAME to a RGB color value for dvipng."
(apply #'format "rgb %s %s %s"
(mapcar 'org-latex-preview--normalize-color
(color-values color-name))))
(make-obsolete
'org-dvipng-color "to be removed" "9.7")
(make-obsolete
'org-dvipng-color-format "to be removed" "9.7")
;; FIXME: Unused; obsoleted; to be removed.
(defun org-normalize-color (value)
"Return string to be used as color value for an RGB component."
(format "%g" (/ value 65535.0)))
(make-obsolete 'org-normalize-color "to be removed" "9.7")
;; FIXME: Unused; obsoleted; to be removed.
(defcustom org-preview-latex-image-directory "ltximg/"
"Path to store latex preview images.
A relative path here creates many directories relative to the
processed Org files paths. An absolute path puts all preview
images at the same place."
:group 'org-latex
:version "26.1"
:package-version '(Org . "9.0")
:type 'string)
(make-obsolete-variable
'org-preview-latex-image-directory "replaced with org-persist" "9.7")
;; FIXME: Unused; obsoleted; to be removed.
(defun org-format-latex
(prefix &optional beg end dir overlays msg forbuffer processing-type)
"Replace LaTeX fragments with links to an image.
The function takes care of creating the replacement image.
Only consider fragments between BEG and END when those are
provided.
When optional argument OVERLAYS is non-nil, display the image on
top of the fragment instead of replacing it.
PROCESSING-TYPE is the conversion method to use, as a symbol.
Some of the options can be changed using the variable
`org-format-latex-options', which see."
(when (and overlays (fboundp 'clear-image-cache)) (clear-image-cache))
(unless (eq processing-type 'verbatim)
(let* ((math-regexp "\\$\\|\\\\[([]\\|^[ \t]*\\\\begin{[A-Za-z0-9*]+}")
(cnt 0)
checkdir-flag)
(goto-char (or beg (point-min)))
;; Optimize overlay creation: (info "(elisp) Managing Overlays").
(when (and overlays (memq processing-type '(dvipng imagemagick)))
(overlay-recenter (or end (point-max))))
(while (re-search-forward math-regexp end t)
(unless (and overlays
(eq (get-char-property (point) 'org-overlay-type)
'org-latex-overlay))
(let* ((context (org-element-context))
(type (org-element-type context)))
(when (memq type '(latex-environment latex-fragment))
(let ((block-type (eq type 'latex-environment))
(value (org-element-property :value context))
(beg (org-element-property :begin context))
(end (save-excursion
(goto-char (org-element-property :end context))
(skip-chars-backward " \r\t\n")
(point))))
(cond
((eq processing-type 'mathjax)
;; Prepare for MathJax processing.
(if (not (string-match "\\`\\$\\$?" value))
(goto-char end)
(delete-region beg end)
(if (string= (match-string 0 value) "$$")
(insert "\\[" (substring value 2 -2) "\\]")
(insert "\\(" (substring value 1 -1) "\\)"))))
((eq processing-type 'html)
(goto-char beg)
(delete-region beg end)
(insert (org-format-latex-as-html value)))
((assq processing-type org-preview-latex-process-alist)
;; Process to an image.
(cl-incf cnt)
(goto-char beg)
(let* ((processing-info
(cdr (assq processing-type org-preview-latex-process-alist)))
(face (face-at-point))
;; Get the colors from the face at point.
(fg
(let ((color (plist-get org-format-latex-options
:foreground)))
(if forbuffer
(cond
((eq color 'auto)
(face-attribute face :foreground nil 'default))
((eq color 'default)
(face-attribute 'default :foreground nil))
(t color))
color)))
(bg
(let ((color (plist-get org-format-latex-options
:background)))
(if forbuffer
(cond
((eq color 'auto)
(face-attribute face :background nil 'default))
((eq color 'default)
(face-attribute 'default :background nil))
(t color))
color)))
(hash (sha1 (prin1-to-string
(list org-format-latex-header
org-latex-default-packages-alist
org-latex-packages-alist
org-format-latex-options
forbuffer value fg bg))))
(imagetype (or (plist-get processing-info :image-output-type) "png"))
(absprefix (expand-file-name prefix dir))
(linkfile (format "%s_%s.%s" prefix hash imagetype))
(movefile (format "%s_%s.%s" absprefix hash imagetype))
(sep (and block-type "\n\n"))
(link (concat sep "[[file:" linkfile "]]" sep))
(options
(org-combine-plists
org-format-latex-options
`(:foreground ,fg :background ,bg))))
(when msg (message msg cnt))
(unless checkdir-flag ; Ensure the directory exists.
(setq checkdir-flag t)
(let ((todir (file-name-directory absprefix)))
(unless (file-directory-p todir)
(make-directory todir t))))
(unless (file-exists-p movefile)
(org-create-formula-image
value movefile options forbuffer processing-type))
(org-place-formula-image link block-type beg end value overlays movefile imagetype)))
((eq processing-type 'mathml)
;; Process to MathML.
(unless (org-format-latex-mathml-available-p)
(user-error "LaTeX to MathML converter not configured"))
(cl-incf cnt)
(when msg (message msg cnt))
(goto-char beg)
(delete-region beg end)
(insert (org-format-latex-as-mathml
value block-type prefix dir)))
(t
(error "Unknown conversion process %s for LaTeX fragments"
processing-type)))))))))))
;; FIXME: Unused; obsoleted; to be removed.
(defun org-place-formula-image (link block-type beg end value overlays movefile imagetype)
"Place an overlay from BEG to END showing MOVEFILE.
The overlay will be above BEG if OVERLAYS is non-nil."
(if overlays
(progn
(dolist (o (overlays-in beg end))
(when (eq (overlay-get o 'org-overlay-type)
'org-latex-overlay)
(delete-overlay o)))
(let ((ov (make-overlay beg end))
(imagetype (or (intern imagetype) 'png)))
(overlay-put ov 'org-overlay-type 'org-latex-overlay)
(overlay-put ov 'evaporate t)
(overlay-put ov
'modification-hooks
(list (lambda (o _flag _beg _end &optional _l)
(delete-overlay o))))
(overlay-put ov
'display
(list 'image :type imagetype :file movefile :ascent 'center)))
(goto-char end))
(delete-region beg end)
(insert
(org-add-props link
(list 'org-latex-src
(replace-regexp-in-string "\"" "" value)
'org-latex-src-embed-type
(if block-type 'paragraph 'character))))))
;; FIXME: Unused; obsoleted; to be removed.
(defun org-create-formula-image
(string tofile options buffer &optional processing-type)
"Create an image from LaTeX source using external processes.
The LaTeX STRING is saved to a temporary LaTeX file, then
converted to an image file by process PROCESSING-TYPE defined in
`org-preview-latex-process-alist'. A nil value defaults to
`org-preview-latex-default-process'.
The generated image file is eventually moved to TOFILE.
The OPTIONS argument controls the size, foreground color and
background color of the generated image.
When BUFFER non-nil, this function is used for LaTeX previewing.
Otherwise, it is used to deal with LaTeX snippets showed in
a HTML file."
(let* ((processing-type (or processing-type
org-preview-latex-default-process))
(processing-info
(cdr (assq processing-type org-preview-latex-process-alist)))
(programs (plist-get processing-info :programs))
(error-message (or (plist-get processing-info :message) ""))
(image-input-type (plist-get processing-info :image-input-type))
(image-output-type (plist-get processing-info :image-output-type))
(post-clean (or (plist-get processing-info :post-clean)
'(".dvi" ".xdv" ".pdf" ".tex" ".aux" ".log"
".svg" ".png" ".jpg" ".jpeg" ".out")))
(latex-header
(or (plist-get processing-info :latex-header)
(org-latex-make-preamble
(org-export-get-environment (org-export-get-backend 'latex))
org-format-latex-header
'snippet)))
(latex-compiler (plist-get processing-info :latex-compiler))
(tmpdir temporary-file-directory)
(texfilebase (make-temp-name
(expand-file-name "orgtex" tmpdir)))
(texfile (concat texfilebase ".tex"))
(image-size-adjust (or (plist-get processing-info :image-size-adjust)
'(1.0 . 1.0)))
(scale (* (if buffer (car image-size-adjust) (cdr image-size-adjust))
(or (plist-get options (if buffer :scale :html-scale)) 1.0)))
(dpi (* scale (if (and buffer (display-graphic-p)) (org--get-display-dpi) 140.0)))
(fg (or (plist-get options (if buffer :foreground :html-foreground))
"Black"))
(bg (or (plist-get options (if buffer :background :html-background))
"Transparent"))
(image-converter
(or (and (string= bg "Transparent")
(plist-get processing-info :transparent-image-converter))
(plist-get processing-info :image-converter)))
(log-buf (get-buffer-create "*Org Preview LaTeX Output*"))
(resize-mini-windows nil)) ;Fix Emacs flicker when creating image.
(dolist (program programs)
(org-check-external-command program error-message))
(if (eq fg 'default)
(setq fg (org-latex-color :foreground))
(setq fg (org-latex-color-format fg)))
(setq bg (cond
((eq bg 'default) (org-latex-color :background))
((string= bg "Transparent") nil)
(t (org-latex-color-format bg))))
;; Remove TeX \par at end of snippet to avoid trailing space.
(if (string-suffix-p string "\n")
(aset string (1- (length string)) ?%)
(setq string (concat string "%")))
(with-temp-file texfile
(insert latex-header)
(insert "\n\\begin{document}\n"
"\\definecolor{fg}{rgb}{" fg "}%\n"
(if bg
(concat "\\definecolor{bg}{rgb}{" bg "}%\n"
"\n\\pagecolor{bg}%\n")
"")
"\n{\\color{fg}\n"
string
"\n}\n"
"\n\\end{document}\n"))
(let* ((err-msg (format "Please adjust `%s' part of \
`org-preview-latex-process-alist'."
processing-type))
(image-input-file
(org-compile-file
texfile latex-compiler image-input-type err-msg log-buf))
(image-output-file
(org-compile-file
image-input-file image-converter image-output-type err-msg log-buf
`((?D . ,(shell-quote-argument (format "%s" dpi)))
(?S . ,(shell-quote-argument (format "%s" (/ dpi 140.0))))))))
(copy-file image-output-file tofile 'replace)
(dolist (e post-clean)
(when (file-exists-p (concat texfilebase e))
(delete-file (concat texfilebase e))))
image-output-file)))
;; FIXME: Unused; obsoleted; to be removed.
(defun org-html-format-latex (latex-frag processing-type info)
"Format a LaTeX fragment LATEX-FRAG into HTML.
PROCESSING-TYPE designates the tool used for conversion. It can
be `mathjax', `verbatim', `html', nil, t or symbols in
`org-preview-latex-process-alist', e.g., `dvipng', `dvisvgm' or
`imagemagick'. See `org-html-with-latex' for more information.
INFO is a plist containing export properties."
(let ((cache-relpath "") (cache-dir ""))
(unless (or (eq processing-type 'mathjax)
(eq processing-type 'html))
(let ((bfn (or (buffer-file-name)
(make-temp-name
(expand-file-name "latex" temporary-file-directory))))
(latex-header
(let ((header (plist-get info :latex-header)))
(and header
(concat (mapconcat
(lambda (line) (concat "#+LATEX_HEADER: " line))
(org-split-string header "\n")
"\n")
"\n")))))
(setq cache-relpath
(concat (file-name-as-directory org-preview-latex-image-directory)
(file-name-sans-extension
(file-name-nondirectory bfn)))
cache-dir (file-name-directory bfn))
;; Re-create LaTeX environment from original buffer in
;; temporary buffer so that dvipng/imagemagick can properly
;; turn the fragment into an image.
(setq latex-frag (concat latex-header latex-frag))))
(org-export-with-buffer-copy
:to-buffer (get-buffer-create " *Org HTML Export LaTeX*")
:drop-visibility t :drop-narrowing t :drop-contents t
(erase-buffer)
(insert latex-frag)
(org-format-latex cache-relpath nil nil cache-dir nil
"Creating LaTeX Image..." nil processing-type)
(buffer-string))))
(make-obsolete #'org-format-latex "to be removed" "9.7")
(make-obsolete #'org-create-formula-image "to be removed" "9.7")
(make-obsolete #'org-html-format-latex "to be removed" "9.7")
;; FIXME: Unused; obsoleted; to be removed. ;; FIXME: Unused; obsoleted; to be removed.
(defun org-let (list &rest body) ;FIXME: So many kittens are suffering here. (defun org-let (list &rest body) ;FIXME: So many kittens are suffering here.
(declare (indent 1) (obsolete cl-progv "2021")) (declare (indent 1) (obsolete cl-progv "2021"))

View File

@ -57,12 +57,6 @@
;; (add-hook 'org-mode-hook ;; (add-hook 'org-mode-hook
;; (lambda () ;; (lambda ()
;; (define-key org-mode-map "\C-co" 'org-ctags-find-tag-interactive))) ;; (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 ;; 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 ;; with the same name as the link; then, if unsuccessful, ask the user if
@ -167,20 +161,6 @@ See the ctags documentation for more information.")
:version "24.1" :version "24.1"
:type 'file) :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 (defcustom org-ctags-open-link-functions
'(org-ctags-find-tag '(org-ctags-find-tag
org-ctags-ask-rebuild-tags-file-then-find-tag org-ctags-ask-rebuild-tags-file-then-find-tag
@ -188,7 +168,14 @@ use the same set of functions.")
"List of functions to be prepended to ORG-OPEN-LINK-FUNCTIONS by ORG-CTAGS." "List of functions to be prepended to ORG-OPEN-LINK-FUNCTIONS by ORG-CTAGS."
:version "24.1" :version "24.1"
:type 'hook :type 'hook
:options org-ctags--open-link-functions-list) :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))
(defvar org-ctags-tag-list nil (defvar org-ctags-tag-list nil
@ -204,9 +191,8 @@ The following patterns are replaced in the string:
:type 'string) :type 'string)
(defun org-ctags--visit-tags-table () (add-hook 'org-mode-hook
"Load tags for current file. (lambda ()
A function for `org-mode-hook."
(when (and org-ctags-enabled-p (when (and org-ctags-enabled-p
(buffer-file-name)) (buffer-file-name))
;; Make sure this file's directory is added to default ;; Make sure this file's directory is added to default
@ -216,9 +202,10 @@ A function for `org-mode-hook."
(concat (file-name-directory (buffer-file-name)) (concat (file-name-directory (buffer-file-name))
"/TAGS")))) "/TAGS"))))
(when (file-exists-p tags-filename) (when (file-exists-p tags-filename)
(visit-tags-table 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 _) (defun org--ctags-load-tag-list (&rest _)
(when (and org-ctags-enabled-p tags-file-name) (when (and org-ctags-enabled-p tags-file-name)
(setq-local org-ctags-tag-list (setq-local org-ctags-tag-list
@ -226,28 +213,12 @@ A function for `org-mode-hook."
(defun org-ctags-enable () (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) (put 'org-mode 'find-tag-default-function 'org-ctags-find-tag-at-point)
(setq org-ctags-enabled-p t) (setq org-ctags-enabled-p t)
(dolist (fn org-ctags-open-link-functions) (dolist (fn org-ctags-open-link-functions)
(add-hook 'org-open-link-functions fn t))) (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. =============================================== ;;; General utility functions. ===============================================
;; These work outside org-ctags mode. ;; These work outside org-ctags mode.
@ -323,6 +294,8 @@ The new topic will be titled NAME (or TITLE if supplied)."
;;;; Misc interoperability with etags system ================================= ;;;; 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 _) (defun org--ctags-set-org-mark-before-finding-tag (&rest _)
"Before trying to find a tag, save our current position on org mark ring." "Before trying to find a tag, save our current position on org mark ring."
(save-excursion (save-excursion
@ -504,21 +477,18 @@ function may take several seconds to finish if the directory or
its subdirectories contain large numbers of taggable files." its subdirectories contain large numbers of taggable files."
(interactive) (interactive)
(cl-assert (buffer-file-name)) (cl-assert (buffer-file-name))
(let ((dir-name (shell-quote-argument (let ((dir-name (or directory-name
(expand-file-name (file-name-directory (buffer-file-name))))
(if directory-name
(file-name-as-directory directory-name)
(file-name-directory (buffer-file-name))))))
(exitcode nil)) (exitcode nil))
(save-excursion (save-excursion
(setq exitcode (setq exitcode
(shell-command (shell-command
(format (concat "%s --langdef=orgmode --langmap=orgmode:.org " (format (concat "%s --langdef=orgmode --langmap=orgmode:.org "
"--regex-orgmode=%s -f %sTAGS -e -R %s*") "--regex-orgmode=\"%s\" -f \"%s\" -e -R %s")
org-ctags-path-to-ctags org-ctags-path-to-ctags
(shell-quote-argument org-ctags-tag-regexp) org-ctags-tag-regexp
dir-name (expand-file-name (concat dir-name "/TAGS"))
dir-name))) (expand-file-name (concat (shell-quote-argument dir-name) "/*")))))
(cond (cond
((eql 0 exitcode) ((eql 0 exitcode)
(setq-local org-ctags-tag-list (setq-local org-ctags-tag-list
@ -553,6 +523,8 @@ a new topic."
'org-open-link-functions tag)))))) 'org-open-link-functions tag))))))
(org-ctags-enable)
(provide 'org-ctags) (provide 'org-ctags)
;;; org-ctags.el ends here ;;; org-ctags.el ends here

View File

@ -4282,7 +4282,7 @@ Assume point is at the target."
"\\|" "\\|"
"\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)" "\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)"
"\\|" "\\|"
"\\(?:<%%\\(?:([^>\n]+)\\)\\([^\n>]*\\)>\\)") "\\(?:<%%\\(?:([^>\n]+)\\)>\\)")
"Regexp matching any timestamp type object.") "Regexp matching any timestamp type object.")
(defconst org-element--timestamp-raw-value-regexp (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', `:year-end', `:month-end', `:day-end', `:hour-end', `:minute-end',
`:repeater-type', `:repeater-value', `:repeater-unit', `:repeater-type', `:repeater-value', `:repeater-unit',
`:repeater-deadline-value', `:repeater-deadline-unit', `:warning-type', `:repeater-deadline-value', `:repeater-deadline-unit', `:warning-type',
`:warning-value', `:warning-unit', `:diary-sexp', `:begin', `:end' and `:warning-value', `:warning-unit', `:begin', `:end' and `:post-blank'
`:post-blank' properties. Otherwise, return nil. properties. Otherwise, return nil.
Assume point is at the beginning of the timestamp." Assume point is at the beginning of the timestamp."
(when (looking-at-p org-element--timestamp-regexp) (when (looking-at-p org-element--timestamp-regexp)
@ -4312,25 +4312,15 @@ Assume point is at the beginning of the timestamp."
(progn (progn
(looking-at org-element--timestamp-raw-value-regexp) (looking-at org-element--timestamp-raw-value-regexp)
(match-string-no-properties 0))) (match-string-no-properties 0)))
(diaryp (match-beginning 2)) (date-start (match-string-no-properties 1))
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)) (date-end (match-string-no-properties 3))
(diaryp (match-beginning 2))
(post-blank (progn (goto-char (match-end 0)) (post-blank (progn (goto-char (match-end 0))
(skip-chars-forward " \t"))) (skip-chars-forward " \t")))
(end (point)) (end (point))
(time-range (time-range
(when (string-match (and (not diaryp)
(string-match
"[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)" "[012]?[0-9]:[0-5][0-9]\\(-\\([012]?[0-9]\\):\\([0-5][0-9]\\)\\)"
date-start) date-start)
(cons (string-to-number (match-string 2 date-start)) (cons (string-to-number (match-string 2 date-start))
@ -4405,17 +4395,6 @@ Assume point is at the beginning of the timestamp."
day-end (or (nth 3 date) day-start) day-end (or (nth 3 date) day-start)
hour-end (or (nth 2 date) (car time-range) hour-start) hour-end (or (nth 2 date) (car time-range) hour-start)
minute-end (or (nth 1 date) (cdr time-range) minute-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 (org-element-create
'timestamp 'timestamp
(nconc (list :type type (nconc (list :type type
@ -4434,20 +4413,19 @@ Assume point is at the beginning of the timestamp."
:begin begin :begin begin
:end end :end end
:post-blank post-blank) :post-blank post-blank)
(and diary-sexp (list :diary-sexp diary-sexp))
repeater-props repeater-props
warning-props)))))) warning-props))))))
(defun org-element-timestamp-interpreter (timestamp _) (defun org-element-timestamp-interpreter (timestamp _)
"Interpret TIMESTAMP object as Org syntax." "Interpret TIMESTAMP object as Org syntax."
(let((type (org-element-property :type timestamp))) (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)) (let ((day-start (org-element-property :day-start timestamp))
(month-start (org-element-property :month-start timestamp)) (month-start (org-element-property :month-start timestamp))
(year-start (org-element-property :year-start timestamp))) (year-start (org-element-property :year-start timestamp)))
;; Return nil when start date is not available. Could also ;; Return nil when start date is not available. Could also
;; throw an error, but the current behavior is historical. ;; throw an error, but the current behavior is historical.
(when (or (and day-start month-start year-start) (when (and day-start month-start year-start)
(eq type 'diary))
(let* ((repeat-string (let* ((repeat-string
(concat (concat
(pcase (org-element-property :repeater-type timestamp) (pcase (org-element-property :repeater-type timestamp)
@ -4481,7 +4459,6 @@ Assume point is at the beginning of the timestamp."
type type
'(inactive inactive-range)) '(inactive inactive-range))
(cons "[" "]") (cons "[" "]")
;; diary as well
(cons "<" ">"))) (cons "<" ">")))
(timestamp-end (timestamp-end
(concat (concat
@ -4492,12 +4469,6 @@ Assume point is at the beginning of the timestamp."
;; Opening backet: [ or < ;; Opening backet: [ or <
(car brackets) (car brackets)
;; Starting date/time: YYYY-MM-DD DAY[ HH:MM] ;; 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 (format-time-string
;; `org-time-stamp-formats'. ;; `org-time-stamp-formats'.
(org-time-stamp-format (org-time-stamp-format
@ -4507,7 +4478,7 @@ Assume point is at the beginning of the timestamp."
'no-brackets) 'no-brackets)
(org-encode-time (org-encode-time
0 (or minute-start 0) (or hour-start 0) 0 (or minute-start 0) (or hour-start 0)
day-start month-start year-start))) day-start month-start year-start))
;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM] ;; Range: -HH:MM or TIMESTAMP-END--[YYYY-MM-DD DAY HH:MM]
(let ((hour-end (org-element-property :hour-end timestamp)) (let ((hour-end (org-element-property :hour-end timestamp))
(minute-end (org-element-property :minute-end timestamp))) (minute-end (org-element-property :minute-end timestamp)))
@ -4533,8 +4504,7 @@ Assume point is at the beginning of the timestamp."
((or `timerange `daterange) ((or `timerange `daterange)
(error "`:range-type' must be `nil' for `active'/`inactive' type")))) (error "`:range-type' must be `nil' for `active'/`inactive' type"))))
;; Range must be present. ;; Range must be present.
((or `active-range `inactive-range ((or `active-range `inactive-range)
(and `diary (guard (eq 'timerange range-type))))
(pcase range-type (pcase range-type
;; End time: -HH:MM. ;; End time: -HH:MM.
;; Fall back to start time if end time is not defined (arbitrary historical choice). ;; Fall back to start time if end time is not defined (arbitrary historical choice).
@ -4571,7 +4541,9 @@ Assume point is at the beginning of the timestamp."
(or (org-element-property :year-end timestamp) year-start))))))))) (or (org-element-property :year-end timestamp) year-start)))))))))
;; repeater + warning + closing > or ] ;; repeater + warning + closing > or ]
;; This info is duplicated in date ranges. ;; This info is duplicated in date ranges.
timestamp-end)))))) timestamp-end))))
;; diary type.
(org-element-property :raw-value timestamp))))
;;;; Underline ;;;; Underline
(defun org-element-underline-parser () (defun org-element-underline-parser ()
@ -8321,10 +8293,7 @@ the cache."
(when (and limit-count (when (and limit-count
(>= count-predicate-calls-match (>= count-predicate-calls-match
limit-count)) limit-count))
(cache-walk-abort)) (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. ;; Check if the buffer or cache has been modified.
(unless (org-with-base-buffer nil (unless (org-with-base-buffer nil
(and (eq modified-tic org-element--cache-change-tic) (and (eq modified-tic org-element--cache-change-tic)

View File

@ -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)))) (let* ((full-properties (mapcar (lambda (prop) (cons prop (cdr (assq prop properties))))
'( :visible :ellipsis :isearch-ignore '( :visible :ellipsis :isearch-ignore
:global :isearch-open :front-sticky :global :isearch-open :front-sticky
:rear-sticky :fragile :alias :font-lock))) :rear-sticky :fragile :alias)))
(full-spec (cons spec full-properties))) (full-spec (cons spec full-properties)))
(add-to-list 'org-fold-core--specs full-spec append) (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) (mapc (lambda (prop-cons) (org-fold-core-set-folding-spec-property spec (car prop-cons) (cdr prop-cons) 'force)) full-properties)
@ -1094,7 +1094,8 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
(overlay-put o 'invisible spec) (overlay-put o 'invisible spec)
;; Preserve priority. ;; Preserve priority.
(overlay-put o 'priority (length (member spec (org-fold-core-folding-spec-list)))) (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 #'org-fold-core--isearch-show)
(overlay-put o 'isearch-open-invisible-temporary #'org-fold-core--isearch-show-temporary))
(put-text-property from to (org-fold-core--property-symbol-get-create spec) spec) (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 #'org-fold-core--isearch-show)
(put-text-property from to 'isearch-open-invisible-temporary #'org-fold-core--isearch-show-temporary) (put-text-property from to 'isearch-open-invisible-temporary #'org-fold-core--isearch-show-temporary)
@ -1245,9 +1246,7 @@ This function is intended to be used as `isearch-filter-predicate'."
;; FIXME: Reveal the match (usually point, but may sometimes go beyond the region). ;; FIXME: Reveal the match (usually point, but may sometimes go beyond the region).
(when (< beg (point) end) (when (< beg (point) end)
(funcall org-fold-core-isearch-open-function (point))) (funcall org-fold-core-isearch-open-function (point)))
(if (overlayp overlay-or-region) (org-fold-core-region beg end nil)))
(delete-overlay overlay-or-region)
(org-fold-core-region beg end nil))))
(defun org-fold-core--isearch-show-temporary (region hide-p) (defun org-fold-core--isearch-show-temporary (region hide-p)
"Temporarily reveal text in REGION. "Temporarily reveal text in REGION.

View File

@ -49,6 +49,8 @@
(require 'org-fold-core) (require 'org-fold-core)
(defvar org-inlinetask-min-level) (defvar org-inlinetask-min-level)
(defvar org-link--link-folding-spec)
(defvar org-link--description-folding-spec)
(defvar org-odd-levels-only) (defvar org-odd-levels-only)
(defvar org-drawer-regexp) (defvar org-drawer-regexp)
(defvar org-property-end-re) (defvar org-property-end-re)
@ -278,7 +280,9 @@ Also, see `org-fold-catch-invisible-edits'."
(:isearch-open . t) (:isearch-open . t)
(:font-lock . t) (:font-lock . t)
(:front-sticky . t) (:front-sticky . t)
(:alias . (drawer property-drawer)))))) (:alias . (drawer property-drawer)))
,org-link--description-folding-spec
,org-link--link-folding-spec)))
;;;; Searching and examining folded text ;;;; Searching and examining folded text
@ -675,12 +679,19 @@ DETAIL is either nil, `minimal', `local', `ancestors',
(org-with-point-at (car region) (org-with-point-at (car region)
(forward-line 0) (forward-line 0)
(let (font-lock-extend-region-functions) (let (font-lock-extend-region-functions)
(font-lock-fontify-region (max (point-min) (1- (car region))) (cdr region))))))) (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)))))
(let (region) (let (region)
(dolist (spec (org-fold-core-folding-spec-list)) (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)) (setq region (org-fold-get-region-at-point spec))
(when region (when region
(org-fold-region (car region) (cdr region) nil spec))))) (org-fold-region (car region) (cdr region) nil spec))))))
(unless (org-before-first-heading-p) (unless (org-before-first-heading-p)
(org-with-limited-levels (org-with-limited-levels
(cl-case detail (cl-case detail

3121
lisp/org-latex-preview.el Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2979,7 +2979,7 @@ function is being called interactively."
(error "Missing key extractor")))) (error "Missing key extractor"))))
(sort-func (sort-func
(cond (cond
((= dcst ?a) #'org-string<) ((= dcst ?a) #'string-collate-lessp)
((= dcst ?f) ((= dcst ?f)
(or compare-func (or compare-func
(and interactive? (and interactive?

View File

@ -113,6 +113,7 @@ 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-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-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 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-time-convert-to-list "org-compat" (time))
(declare-function org-buffer-text-pixel-width "org-compat" ()) (declare-function org-buffer-text-pixel-width "org-compat" ())
@ -367,6 +368,278 @@ If EXCLUDE-TMP is non-nil, ignore temporary buffers."
nil)) nil))
(buffer-list))))) (buffer-list)))))
;;; Async stack
(defvar org-async--stack nil
"List of async currently running task forms.
Each running task is represented by a list with the following structure:
(%PROCESS :success %FUN :failure %FUN
:filter %FUN :buffer %BUFFER
:timeout %FLOAT :start-time %FLOAT
:info %SEXP)")
(defvar org-async--wait-queue nil
"List of async queued task forms.
Each queued task is represented by a list with the following structure:
(%PROCESS :success %FUN :failure %FUN
:filter %FUN :buffer %BUFFER
:info %SEXP :dir %STRING
:timeout %FLOAT :coding %SYMBOL)")
(defvar org-async-process-limit 4
"Maximum number of processes to run at once.")
(defvar org-async-timeout 120
"Default timeout for a process started via `org-async-queue'.")
(defvar org-async-check-timeout-interval 1
"Check for processes which have exceeded their timeout every this many seconds.")
(defvar org-async--counter 0)
(cl-defun org-async-call (proc &key success failure filter buffer info timeout now process-variables
(dir default-directory) (coding 'utf-8))
"Start PROC and register it with callbacks SUCCESS and FAILURE.
PROC can be a process, string, or list. A string will be run as
a shell command, with `start-process-shell-command' and a list
run using `start-process' with the car as the command and the cdr
as the arguments. The process will be executed in DIR (if set)
or `default-directory'.
There is also a \"special form\" of PROC, namely a list where the
first item is the symbol org-async-task, and the rest constitutes
an argument list for `org-async-call'. This form allows for easy
specification of callbacks that are themselves async tasks, e.g.
(org-async-call \\='(\"sleep 1\")
:success \\='(org-async-task (\"notify-send\" \"done\")))
When using this form, all other arguments are ignored.
When BUFFER is provided, the output of PROC will be directed to it.
Shoud BUFFER be t, then a temp buffer will be created and removed
during `org-async--cleanup-process'.
SUCCESS and FAILURE can be any form accepted by `org-async--execute-callback',
namely:
- A string, which is used a `message' string with the exit-code,
process-buffer, and INFO as arguments.
- A function, which is called with exit-code, process-buffer,
and INFO as arguments.
- A list, which is used as an argument list for a new `org-async-call' call.
- nil, which does nothing.
When PROC succeeds by exiting with an exit code of zero, the SUCCESS
callback will be run. Should PROC fail, or be killed, or the process
runs for more than TIMEOUT seconds, the FAILURE callback will be run.
A function FILTER can be provided, in which case it will be
called in the same manner as a normal procecss filter, however
the function FILTER will be called with INFO as a third argument.
i.e. the call signature is (content new-content-string INFO)
When BUFFER is non-nil, there are two other major differences:
- The new content is silently inserted before FILTER is called
- Note that `point' is left alone and is not moved by this.
- The process buffer is the current buffer when FILTER is called.
When CODING is non-nil, both the process encode and decode system
will be set to CODING. If unset, UTF-8 is used.
When NOW is non-nil, the PROC is started immediately, regardless
of `org-async-process-limit'.
For improved performance, PROCESS-VARIABLES is a list of
let-style bindings that should be applied to the process.
Variables are supported on an individual basis (i.e. only certain
variables can be set), with the default value being equivalent to:
:process-variables ((process-adaptive-read-buffering nil)
(process-connection-type nil)
(read-process-output-max 65536))"
(cond
;; Called with a task (as can be used with callbacks), so re-call
;; with expanded arguments.
((and (consp proc)
(eq (car proc) 'org-async-task))
(apply #'org-async-call (cdr proc)))
;; Start the async process now.
((or now (< (length org-async--stack) org-async-process-limit))
(let ((proc
(let ((default-directory (or dir default-directory))
(process-adaptive-read-buffering ; No by default
(cadr (or (assoc 'process-adaptive-read-buffering process-variables) nil)))
(process-connection-type ; Use a pipe by default
(cadr (or (assoc 'process-connection-type process-variables) nil)))
(read-process-output-max ; Can be worth changing depending on the process
(or (assq 'read-process-output-max process-variables) read-process-output-max)))
(cond ((processp proc) proc)
((stringp proc)
(start-process-shell-command
(format "org-async-%d" (cl-incf org-async--counter))
buffer proc))
((consp proc)
(apply #'start-process
(format "org-async-%s-%d"
(car proc) (cl-incf org-async--counter))
buffer proc))
(t (error "Async process input %S not a recognised format"
proc)))))
(timeout (or timeout org-async-timeout)))
(set-process-sentinel proc #'org-async--sentinel)
(when filter
(set-process-filter proc #'org-async--filter))
(when coding
(set-process-coding-system proc coding coding))
(push (list proc
:success success
:failure failure
:filter filter
:buffer (if (eq buffer t)
(cons :temp (generate-new-buffer " *temp*" t))
buffer)
:info info
:timeout timeout
:start-time (float-time))
org-async--stack)
(org-async--monitor t)
(car org-async--stack)))
;; Queue the task to be run later.
(t
(setq org-async--wait-queue
(append org-async--wait-queue
(list (list proc
:success success
:failure failure
:filter filter
:buffer buffer
:info info
:dir dir
:timeout timeout
:coding coding))))
(last org-async--wait-queue))))
(defvar org-async--blocking-tasks nil
"List of async tasks currently being waited on.")
(defun org-async-wait-for (&rest tasks)
"Block until every task of TASKS has finished (including callback tasks)."
(setq org-async--blocking-tasks tasks)
(while org-async--blocking-tasks
(dolist (task org-async--blocking-tasks)
(accept-process-output (car task)))))
(defun org-async--filter (process string)
"After PROCESS recieves STRING, call the async filter.
This is implementated to satisfy the filter function documentation in
`org-async-call'."
(when-let ((proc-info (alist-get process org-async--stack)))
(let ((filter (plist-get proc-info :filter))
(buffer (plist-get proc-info :buffer)))
(if buffer
(with-current-buffer buffer
(save-excursion
(goto-char (point-max))
(insert string))
(funcall filter process string (plist-get proc-info :info)))
(funcall filter process string (plist-get proc-info :info))))))
(defun org-async--sentinel (process _signal)
"Watch PROCESS for death, and cleanup accordingly.
When a signal is recieved, the status of PROCESS is checked.
Should the it have an exit status, with status code 0,
`org-async--cleanup-process' is run with the \"failed\" argument
unset. Should the process have finished in any other manner,
`org-async--cleanup-process' is run with the \"failed\" argument."
(pcase (process-status process)
((and 'exit (guard (= 0 (process-exit-status process))))
(org-async--cleanup-process process))
((or 'exit 'signal 'failed)
(org-async--cleanup-process process 'failed))))
(defun org-async--cleanup-process (process &optional failed)
"Remove PROCESS from the async stack, and run its callback.
If the exit code of PROCESS is zero and FAILED is non-nil, then
the success callback is run (via `org-async--execute-callback').
Otherwise, the failure callback is run."
(when (assq process org-async--stack)
(let* ((proc-info (cdr (assq process org-async--stack)))
(buffer-val (plist-get proc-info :buffer))
(proc-buf (if (consp buffer-val) (cdr buffer-val) buffer-val))
(blocking-p (cl-member process org-async--blocking-tasks :key #'car)))
(setq org-async--stack
(delq (assq process org-async--stack) org-async--stack))
;; Ensure that any filter is called on the final output
;; prior to the callbacks.
(while (accept-process-output process))
(org-async--execute-callback
(plist-get
proc-info
(if (and (not failed)
(= 0 (process-exit-status process)))
:success :failure))
(process-exit-status process)
proc-buf
(plist-get proc-info :info)
blocking-p)
(when blocking-p
(setq org-async--blocking-tasks
(cl-delete process org-async--blocking-tasks :key #'car)))
(when (and (consp buffer-val) (eq :temp (car buffer-val)))
(kill-buffer proc-buf)))
(when (and org-async--wait-queue
(< org-async-process-limit (length org-async--stack)))
(apply #'org-async-call (pop org-async--wait-queue)))))
(defun org-async--execute-callback (callback exit-code process-buffer info &optional blocking)
"Run CALLBACK with EXIT-CODE, PROCESS-BUFFER, and INFO.
CALLBACK can take one of four forms:
- A string, which is used a `message' string with EXIT-CODE,
PROCESS-BUFFER, and INFO as arguments.
- A function, which is called with EXIT-CODE, PROCESS-BUFFER,
and INFO as arguments.
- A list, which is either:
- An (org-async-task ...) structure, which passed to an
`org-async-call' invocation.
- A list of callbacks, which are individually evaluated.
- nil, which does nothing.
When BLOCKING is set, all callback tasks are made blocking."
(cond
((stringp callback)
(message callback exit-code process-buffer info))
((functionp callback)
(funcall callback exit-code process-buffer info))
((consp callback)
(if (eq (car callback) 'org-async-task)
(if blocking
(push (org-async-call callback) org-async--blocking-tasks)
(org-async-call callback))
(dolist (clbk callback)
(org-async--execute-callback clbk exit-code process-buffer info blocking))))
((null callback)) ; Do nothing.
(t (message "Ignoring invalid `org-async-call' callback: %S" callback))))
(defvar org-async--monitor-scheduled nil)
(defun org-async--monitor (&optional force)
"Check each process against their timeouts, and kill any overdue.
The only runs when `org-async--monitor-scheduled' is nil, unless FORCE is set.
Should any processes still be alive after checking the stack, this will run
itself using a timer in `org-async-check-timeout-interval' seconds."
(when (or force (null org-async--monitor-scheduled))
(dolist (stack-proc org-async--stack)
(if (process-live-p (car stack-proc))
(let ((timeout (plist-get (cdr stack-proc) :timeout)))
(when (and (numberp timeout)
(< 0 timeout
(- (float-time)
(plist-get (cdr stack-proc) :start-time))))
(kill-process (car stack-proc))))
(org-async--cleanup-process (car stack-proc))))
(if org-async--stack
(setq org-async--monitor-scheduled
(run-at-time org-async-check-timeout-interval
nil #'org-async--monitor t))
(setq org-async--monitor-scheduled nil))))
;;; File ;;; File
@ -981,67 +1254,20 @@ return nil."
;;; String manipulation ;;; String manipulation
(defcustom org-sort-function #'string-collate-lessp (defun org-string< (a b)
"Function used to compare strings when sorting. (string-collate-lessp a b))
This function affects how Org mode sorts headlines, agenda items,
table lines, etc.
The function must accept either 2 or 4 arguments: strings to compare (defun org-string<= (a b)
and, optionally, LOCALE and IGNORE-CASE - locale name and flag to make (or (string= a b) (string-collate-lessp a b)))
comparison case-insensitive.
The default value uses sorting rules according to OS language. Users (defun org-string>= (a b)
who want to make sorting language-independent, may customize the value (not (string-collate-lessp a b)))
to `org-sort-function-fallback'.
Note that some string sorting rules are known to be not accurate on (defun org-string> (a b)
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)) (and (not (string= a b))
(not (org-string< a b locale ignore-case)))) (not (string-collate-lessp a b))))
(defun org-string<> (a b) (defun org-string<> (a b)
"Return non-nil when string A and string B are not equal."
(not (string= a b))) (not (string= a b)))
(defsubst org-trim (s &optional keep-lead) (defsubst org-trim (s &optional keep-lead)
@ -1790,23 +2016,6 @@ indirectly called by the latter."
(prog1 nil (prog1 nil
(set-window-configuration window-configuration))))) (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) (provide 'org-macs)
;; Local variables: ;; Local variables:

View File

@ -1057,7 +1057,7 @@ be returned that indicates what went wrong."
(goto-char (match-beginning 4)) (goto-char (match-beginning 4))
(insert new) (insert new)
(delete-region (point) (+ (point) (length current))) (delete-region (point) (+ (point) (length current)))
(when org-auto-align-tags (org-align-tags))) (org-align-tags))
(t (t
(error (error
"Heading changed in the mobile device and on the computer"))))))) "Heading changed in the mobile device and on the computer")))))))

View File

@ -426,14 +426,13 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
(append (append
(let ((tags (org-get-tags nil t))) (let ((tags (org-get-tags nil t)))
(org-mouse-keyword-menu (org-mouse-keyword-menu
(sort (mapcar #'car (org-get-buffer-tags)) (sort (mapcar #'car (org-get-buffer-tags)) #'string-lessp)
(or org-tags-sort-function #'org-string<))
(lambda (tag) (lambda (tag)
(org-mouse-set-tags (org-mouse-set-tags
(sort (if (member tag tags) (sort (if (member tag tags)
(delete tag tags) (delete tag tags)
(cons tag tags)) (cons tag tags))
(or org-tags-sort-function #'org-string<)))) #'string-lessp)))
(lambda (tag) (member tag tags)) (lambda (tag) (member tag tags))
)) ))
'("--" '("--"
@ -474,7 +473,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
(sort (if (member ',name ',options) (sort (if (member ',name ',options)
(delete ',name ',options) (delete ',name ',options)
(cons ',name ',options)) (cons ',name ',options))
#'org-string<) 'string-lessp)
" ") " ")
nil nil nil 1) nil nil nil 1)
(when (functionp ',function) (funcall ',function))) (when (functionp ',function) (funcall ',function)))
@ -503,8 +502,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
["Check TODOs" org-show-todo-tree t] ["Check TODOs" org-show-todo-tree t]
("Check Tags" ("Check Tags"
,@(org-mouse-keyword-menu ,@(org-mouse-keyword-menu
(sort (mapcar #'car (org-get-buffer-tags)) (sort (mapcar #'car (org-get-buffer-tags)) #'string-lessp)
(or org-tags-sort-function #'org-string<))
(lambda (tag) (org-tags-sparse-tree nil tag))) (lambda (tag) (org-tags-sparse-tree nil tag)))
"--" "--"
["Custom Tag ..." org-tags-sparse-tree t]) ["Custom Tag ..." org-tags-sparse-tree t])
@ -514,8 +512,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
["Display TODO List" org-todo-list t] ["Display TODO List" org-todo-list t]
("Display Tags" ("Display Tags"
,@(org-mouse-keyword-menu ,@(org-mouse-keyword-menu
(sort (mapcar #'car (org-get-buffer-tags)) (sort (mapcar #'car (org-get-buffer-tags)) #'string-lessp)
(or org-tags-sort-function #'org-string<))
(lambda (tag) (org-tags-view nil tag))) (lambda (tag) (org-tags-view nil tag)))
"--" "--"
["Custom Tag ..." org-tags-view t]) ["Custom Tag ..." org-tags-view t])

View File

@ -602,10 +602,15 @@ MISC, if non-nil will be appended to the collection. It must be a plist."
;;;; Reading container data. ;;;; Reading container data.
(defvar org-persist--inhibit-container-normalization nil
"Prevent `org-persist--normalize-container' from doing anything.")
(defun org-persist--normalize-container (container &optional inner) (defun org-persist--normalize-container (container &optional inner)
"Normalize CONTAINER representation into (type . settings). "Normalize CONTAINER representation into (type . settings).
When INNER is non-nil, do not try to match as list of containers." When INNER is non-nil, do not try to match as list of containers."
(if org-persist--inhibit-container-normalization
container
(pcase container (pcase container
((or `elisp `elisp-data `version `file `index `url) ((or `elisp `elisp-data `version `file `index `url)
`(,container nil)) `(,container nil))
@ -617,7 +622,7 @@ When INNER is non-nil, do not try to match as list of containers."
container) container)
((and (pred listp) (guard (not inner))) ((and (pred listp) (guard (not inner)))
(mapcar (lambda (c) (org-persist--normalize-container c 'inner)) container)) (mapcar (lambda (c) (org-persist--normalize-container c 'inner)) container))
(_ (error "org-persist: Unknown container type: %S" container)))) (_ (error "org-persist: Unknown container type: %S" container)))))
(defvar org-persist--associated-buffer-cache (make-hash-table :weakness 'key) (defvar org-persist--associated-buffer-cache (make-hash-table :weakness 'key)
"Buffer hash cache.") "Buffer hash cache.")
@ -823,8 +828,9 @@ COLLECTION is the plist holding data collection."
(defun org-persist-write:file (c collection) (defun org-persist-write:file (c collection)
"Write file container C according to COLLECTION." "Write file container C according to COLLECTION."
(org-persist-collection-let collection (org-persist-collection-let collection
(when (or (and path (file-exists-p path)) (if (or (and path (file-exists-p path))
(and (stringp (cadr c)) (file-exists-p (cadr c)))) (and (stringp (cadr c)) (file-exists-p (cadr c))))
(progn
(when (and (stringp (cadr c)) (file-exists-p (cadr c))) (when (and (stringp (cadr c)) (file-exists-p (cadr c)))
(setq path (cadr c))) (setq path (cadr c)))
(let* ((persist-file (plist-get collection :persist-file)) (let* ((persist-file (plist-get collection :persist-file))
@ -836,7 +842,9 @@ COLLECTION is the plist holding data collection."
(unless (file-exists-p (file-name-directory file-copy)) (unless (file-exists-p (file-name-directory file-copy))
(make-directory (file-name-directory file-copy) t)) (make-directory (file-name-directory file-copy) t))
(copy-file path file-copy 'overwrite)) (copy-file path file-copy 'overwrite))
(format "%s-%s.%s" persist-file (md5 path) ext))))) (format "%s-%s.%s" persist-file (md5 path) ext)))
(when-let ((file-copy (org-persist-read c associated)))
(file-relative-name file-copy org-persist-directory)))))
(defun org-persist-write:url (c collection) (defun org-persist-write:url (c collection)
"Write url container C according to COLLECTION." "Write url container C according to COLLECTION."
@ -950,9 +958,10 @@ VALUE pairs.
When WRITE-IMMEDIATELY is non-nil, the return value will be the same When WRITE-IMMEDIATELY is non-nil, the return value will be the same
with `org-persist-write'." with `org-persist-write'."
(unless org-persist--index (org-persist--load-index)) (unless org-persist--index (org-persist--load-index))
(setq container (org-persist--normalize-container container)) (let ((container (org-persist--normalize-container container))
(inherit (and inherit (org-persist--normalize-container inherit)))
(org-persist--inhibit-container-normalization t))
(when inherit (when inherit
(setq inherit (org-persist--normalize-container inherit))
(let ((inherited-collection (org-persist--get-collection inherit associated)) (let ((inherited-collection (org-persist--get-collection inherit associated))
new-collection) new-collection)
(unless (member container (plist-get inherited-collection :container)) (unless (member container (plist-get inherited-collection :container))
@ -969,7 +978,7 @@ with `org-persist-write'."
associated associated
(plist-get associated :buffer)) (plist-get associated :buffer))
(add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local))) (add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local)))
(when write-immediately (org-persist-write container associated))) (when write-immediately (org-persist-write container associated))))
(cl-defun org-persist-unregister (container &optional associated &key remove-related) (cl-defun org-persist-unregister (container &optional associated &key remove-related)
"Unregister CONTAINER in ASSOCIATED to be persistent. "Unregister CONTAINER in ASSOCIATED to be persistent.
@ -1084,6 +1093,9 @@ have the same meaning as in `org-persist-read'."
"Call `org-persist-load-all' in current buffer." "Call `org-persist-load-all' in current buffer."
(org-persist-load-all (current-buffer))) (org-persist-load-all (current-buffer)))
(defvar org-persist--inhibit-write nil
"Whether `org-persist-write' should be inhibited.")
(defun org-persist-write (container &optional associated ignore-return) (defun org-persist-write (container &optional associated ignore-return)
"Save CONTAINER according to ASSOCIATED. "Save CONTAINER according to ASSOCIATED.
ASSOCIATED can be a plist, a buffer, or a string. ASSOCIATED can be a plist, a buffer, or a string.
@ -1093,13 +1105,15 @@ The return value is nil when writing fails and the written value (as
returned by `org-persist-read') on success. returned by `org-persist-read') on success.
When IGNORE-RETURN is non-nil, just return t on success without calling When IGNORE-RETURN is non-nil, just return t on success without calling
`org-persist-read'." `org-persist-read'."
(unless org-persist--inhibit-write
(setq associated (org-persist--normalize-associated associated)) (setq associated (org-persist--normalize-associated associated))
;; Update hash ;; Update hash
(when (and (plist-get associated :file) (when (and (plist-get associated :file)
(plist-get associated :hash) (plist-get associated :hash)
(get-file-buffer (plist-get associated :file))) (get-file-buffer (plist-get associated :file)))
(setq associated (org-persist--normalize-associated (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))) (let ((collection (org-persist--get-collection container associated))
(org-persist--inhibit-write t))
(setf collection (plist-put collection :associated associated)) (setf collection (plist-put collection :associated associated))
(unless (or (unless (or
;; Prevent data leakage from encrypted files. ;; Prevent data leakage from encrypted files.
@ -1108,7 +1122,7 @@ When IGNORE-RETURN is non-nil, just return t on success without calling
;; written. ;; written.
(and (plist-get associated :file) (and (plist-get associated :file)
(string-match-p epa-file-name-regexp (plist-get associated :file))) (string-match-p epa-file-name-regexp (plist-get associated :file)))
(seq-find (lambda (v) (cl-some (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated)) (run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
(plist-get collection :container))) (plist-get collection :container)))
(when (or (file-exists-p org-persist-directory) (org-persist--save-index)) (when (or (file-exists-p org-persist-directory) (org-persist--save-index))
@ -1117,7 +1131,7 @@ When IGNORE-RETURN is non-nil, just return t on success without calling
(plist-get collection :container)))) (plist-get collection :container))))
(puthash file data org-persist--write-cache) (puthash file data org-persist--write-cache)
(org-persist--write-elisp-file file data) (org-persist--write-elisp-file file data)
(or ignore-return (org-persist-read container associated))))))) (or ignore-return (org-persist-read container associated))))))))
(defun org-persist-write-all (&optional associated) (defun org-persist-write-all (&optional associated)
"Save all the persistent data. "Save all the persistent data.

View File

@ -754,6 +754,8 @@ as `org-src-fontify-natively' is non-nil."
(s (and b (make-string (* (- e b) native-tab-width) ? )))) (s (and b (make-string (* (- e b) native-tab-width) ? ))))
(when (and b (< b e)) (add-text-properties b e `(display ,s))) (when (and b (< b e)) (add-text-properties b e `(display ,s)))
(forward-char))))) (forward-char)))))
;; Clear abbreviated link folding.
(org-fold-region start end nil 'org-link)
(add-text-properties (add-text-properties
start end start end
'(font-lock-fontified t fontified t font-lock-multiline t)) '(font-lock-fontified t fontified t font-lock-multiline t))
@ -803,9 +805,42 @@ as `org-src-fontify-natively' is non-nil."
(font-lock-append-text-property (font-lock-append-text-property
(1+ pt) (1- (point)) 'face 'org-inline-src-block))) (1+ pt) (1- (point)) 'face 'org-inline-src-block)))
(add-face-text-property (1- (point)) (point) '(org-inline-src-block shadow)) (add-face-text-property (1- (point)) (point) '(org-inline-src-block shadow))
(setq pt (point))))) (setq pt (point)))
(when (and org-inline-src-prettify-results
(re-search-forward "\\= {{{results(" limit t))
(font-lock-append-text-property pt (1+ pt) 'face 'org-inline-src-block)
(goto-char pt))))
t))) t)))
(defun org-fontify-inline-src-results (limit)
"Apply prettify-symbols modifications to inline results blocks.
Performed according to `org-inline-src-prettify-results'."
(when (and org-inline-src-prettify-results
(re-search-forward "{{{results(\\(.+?\\))}}}" limit t))
(remove-list-of-text-properties (match-beginning 0) (point)
'(composition
prettify-symbols-start
prettify-symbols-end))
(font-lock-append-text-property (match-beginning 0) (match-end 0)
'face 'org-block)
(let ((start (match-beginning 0)) (end (match-beginning 1)))
(with-silent-modifications
(compose-region start end (if (eq org-inline-src-prettify-results t)
"(" (car org-inline-src-prettify-results)))
(add-text-properties start end `(prettify-symbols-start ,start prettify-symbols-end ,end))))
(let ((start (match-end 1)) (end (point)))
(with-silent-modifications
(compose-region start end (if (eq org-inline-src-prettify-results t)
")" (cdr org-inline-src-prettify-results)))
(add-text-properties start end `(prettify-symbols-start ,start prettify-symbols-end ,end))))
t))
(defun org-toggle-inline-results-display ()
"Toggle the literal or contracted display of inline src blocks results."
(interactive)
(setq org-inline-src-prettify-results (not org-inline-src-prettify-results))
(org-restart-font-lock))
;;; Escape contents ;;; Escape contents

View File

@ -892,10 +892,7 @@ nil When nil, the command tries to be smart and figure out the
separator in the following way: separator in the following way:
- when each line contains a TAB, assume TAB-separated material - when each line contains a TAB, assume TAB-separated material
- when each line contains a comma, assume CSV 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") (interactive "r\nP")
(let* ((beg (min beg0 end0)) (let* ((beg (min beg0 end0))
(end (max beg0 end0)) (end (max beg0 end0))
@ -912,15 +909,12 @@ nil When nil, the command tries to be smart and figure out the
(if (bolp) (backward-char 1) (end-of-line 1)) (if (bolp) (backward-char 1) (end-of-line 1))
(setq end (point-marker)) (setq end (point-marker))
;; Get the right field separator ;; Get the right field separator
(when (or (not separator) (eq separator 'babel-auto)) (unless separator
(goto-char beg) (goto-char beg)
(setq separator (setq separator
(cond (cond
((not (save-excursion (re-search-forward "^[^\n\t]+$" end t))) '(16)) ((not (re-search-forward "^[^\n\t]+$" end t)) '(16))
((not (save-excursion (re-search-forward "^[^\n,]+$" end t))) '(4)) ((not (re-search-forward "^[^\n,]+$" end t)) '(4))
((and (eq separator 'babel-auto)
(= 1 (count-lines beg end)))
(rx unmatchable))
(t 1)))) (t 1))))
(goto-char beg) (goto-char beg)
(if (equal separator '(4)) (if (equal separator '(4))
@ -2887,15 +2881,15 @@ list, `literal' is for the format specifier L."
(if lispp (if lispp
(if (eq lispp 'literal) (if (eq lispp 'literal)
elements elements
;; Ignore KEEP-EMPTY here. (if (and (eq elements "") (not keep-empty))
;; When ELEMENTS="" and NUMBERS=t, (string-to-number "") ;; FIXME: This branch of `if' is never used because
;; returns 0 - consistent with (0) for Calc branch. ;; strings are never `eq' here. But changing to
;; When ELEMENTS="" and NUMBERS=nil, `prin1-to-string' will ;; `equal' breaks tests.
;; return "\"\"" - historical behavior that also does not ;; See
;; leave missing arguments in formulas like (string< $1 $2) ;; https://list.orgmode.org/orgmode/20230827214320.46754-1-salutis@me.com/
;; when $2 cell is empty. ""
(prin1-to-string (prin1-to-string
(if numbers (string-to-number elements) elements))) (if numbers (string-to-number elements) elements))))
(if (string-match "\\S-" elements) (if (string-match "\\S-" elements)
(progn (progn
(when numbers (setq elements (number-to-string (when numbers (setq elements (number-to-string
@ -4637,8 +4631,8 @@ function is being called interactively."
(predicate (predicate
(cl-case sorting-type (cl-case sorting-type
((?n ?N ?t ?T) #'<) ((?n ?N ?t ?T) #'<)
((?a ?A) (if with-case #'org-string< ((?a ?A) (if with-case #'string-collate-lessp
(lambda (s1 s2) (org-string< s1 s2 nil t)))) (lambda (s1 s2) (string-collate-lessp s1 s2 nil t))))
((?f ?F) ((?f ?F)
(or compare-func (or compare-func
(and interactive? (and interactive?

File diff suppressed because it is too large Load Diff

View File

@ -954,15 +954,14 @@ channel."
;; Only links with a description need an entry. Other are ;; Only links with a description need an entry. Other are
;; already handled in `org-ascii-link'. ;; already handled in `org-ascii-link'.
(when description (when description
(let ((dest (let ((dest (if (equal type "fuzzy")
(org-export-resolve-fuzzy-link link info)
;; Ignore broken links. On broken link, ;; Ignore broken links. On broken link,
;; `org-export-resolve-id-link' will throw an ;; `org-export-resolve-id-link' will throw an
;; error and we will return nil. ;; error and we will return nil.
(condition-case nil (condition-case nil
(if (equal type "fuzzy") (org-export-resolve-id-link link info)
(org-export-resolve-fuzzy-link link info) (org-link-broken nil)))))
(org-export-resolve-id-link link info))
(org-link-broken nil))))
(when dest (when dest
(concat (concat
(org-ascii--fill-string (org-ascii--fill-string

View File

@ -860,9 +860,7 @@ holding export options."
;; Timestamp. ;; Timestamp.
(and (plist-get info :time-stamp-file) (and (plist-get info :time-stamp-file)
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n")) (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
;; LaTeX compiler ;; Document class, packages, and some configuration.
(org-latex--insert-compiler info)
;; Document class and packages.
(org-latex-make-preamble info) (org-latex-make-preamble info)
;; Define the alternative frame environment, if needed. ;; Define the alternative frame environment, if needed.
(when (plist-get info :beamer-define-frame) (when (plist-get info :beamer-define-frame)
@ -915,12 +913,6 @@ holding export options."
(let ((template (plist-get info :latex-hyperref-template))) (let ((template (plist-get info :latex-hyperref-template)))
(and (stringp template) (and (stringp template)
(format-spec template (org-latex--format-spec info)))) (format-spec template (org-latex--format-spec info))))
;; engrave-faces-latex preamble
(when (and (eq (plist-get info :latex-src-block-backend) 'engraved)
(org-element-map (plist-get info :parse-tree)
'(src-block inline-src-block) #'identity
info t))
(org-latex-generate-engraved-preamble info))
;; Document start. ;; Document start.
"\\begin{document}\n\n" "\\begin{document}\n\n"
;; Title command. ;; Title command.

View File

@ -39,6 +39,8 @@
(require 'ox) (require 'ox)
(require 'ox-publish) (require 'ox-publish)
(require 'table) (require 'table)
(require 'org-latex-preview)
(require 'ox-mathml)
;;; Function Declarations ;;; Function Declarations
@ -107,7 +109,8 @@
(verbatim . org-html-verbatim) (verbatim . org-html-verbatim)
(verse-block . org-html-verse-block)) (verse-block . org-html-verse-block))
:filters-alist '((:filter-options . org-html-infojs-install-script) :filters-alist '((:filter-options . org-html-infojs-install-script)
(:filter-parse-tree . org-html-image-link-filter) (:filter-parse-tree org-html-image-link-filter
org-html-prepare-latex-images)
(:filter-final-output . org-html-final-function)) (:filter-final-output . org-html-final-function))
:menu-entry :menu-entry
'(?h "Export to HTML" '(?h "Export to HTML"
@ -155,6 +158,7 @@
(:html-infojs-template nil nil org-html-infojs-template) (:html-infojs-template nil nil org-html-infojs-template)
(:html-inline-image-rules nil nil org-html-inline-image-rules) (:html-inline-image-rules nil nil org-html-inline-image-rules)
(:html-link-org-files-as-html nil nil org-html-link-org-files-as-html) (:html-link-org-files-as-html nil nil org-html-link-org-files-as-html)
(:html-latex-image-options nil nil org-html-latex-image-options)
(:html-mathjax-options nil nil org-html-mathjax-options) (:html-mathjax-options nil nil org-html-mathjax-options)
(:html-mathjax-template nil nil org-html-mathjax-template) (:html-mathjax-template nil nil org-html-mathjax-template)
(:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format) (:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format)
@ -319,7 +323,7 @@ This affects IDs that are determined from the ID property.")
pre.src-awk:before { content: 'Awk'; } pre.src-awk:before { content: 'Awk'; }
pre.src-authinfo::before { content: 'Authinfo'; } pre.src-authinfo::before { content: 'Authinfo'; }
pre.src-C:before { content: 'C'; } pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */ pre.src-C\\+\\+:before { content: 'C++'; }
pre.src-clojure:before { content: 'Clojure'; } pre.src-clojure:before { content: 'Clojure'; }
pre.src-css:before { content: 'CSS'; } pre.src-css:before { content: 'CSS'; }
pre.src-D:before { content: 'D'; } pre.src-D:before { content: 'D'; }
@ -822,7 +826,7 @@ e.g. \"tex:mathjax\". Allowed values are:
be loaded. be loaded.
`html' Use `org-latex-to-html-convert-command' to convert `html' Use `org-latex-to-html-convert-command' to convert
LaTeX fragments to HTML. LaTeX fragments to HTML.
SYMBOL Any symbol defined in `org-preview-latex-process-alist', SYMBOL Any symbol defined in `org-latex-preview-process-alist',
e.g., `dvipng'." e.g., `dvipng'."
:group 'org-export-html :group 'org-export-html
:version "24.4" :version "24.4"
@ -1168,6 +1172,27 @@ See `format-time-string' for more information on its components."
:package-version '(Org . "8.0") :package-version '(Org . "8.0")
:type 'string) :type 'string)
(defcustom org-html-latex-image-options
'(:foreground "Black" :background "Transparent"
:page-width 1.0 :scale 1.0 :image-dir "ltximg" :inline nil)
"LaTeX preview options that apply to generated images.
This is a HTML-specific counterpart to
`org-latex-preview-appearance-options', which see.
This supports two extra properties,
:image-dir an html-export counterpart of `org-latex-preview-cache', and
:inline a list of image formats (or single format symbol) that
should not be saved according to :image-dir, but instead
inlined in the generated HTML. Valid format symbols are:
- png, to inline png images using <img> with a data URI
- svg, to inline svg images using <img> with a data URI
- svg-embed, to inline svg images using an <svg> element.
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 ;;;; Template :: Mathjax
(defcustom org-html-mathjax-options (defcustom org-html-mathjax-options
@ -1653,6 +1678,33 @@ https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag"
:package-version '(Org . "9.1") :package-version '(Org . "9.1")
:type 'string) :type 'string)
;;;; LaTeX Fragments
(defcustom org-latex-to-html-convert-command nil
"Shell command to convert LaTeX fragments to HTML.
This command is very open-ended: the output of the command will
directly replace the LaTeX fragment in the resulting HTML.
Replace format-specifiers in the command as noted below and use
`shell-command' to convert LaTeX to HTML.
%i: The LaTeX fragment to be converted (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 ;;;; Todos
@ -1688,6 +1740,7 @@ INFO is the current state of the export process, as a plist."
"Return close-tag for string TAG. "Return close-tag for string TAG.
ATTR specifies additional attributes. INFO is a property list ATTR specifies additional attributes. INFO is a property list
containing current export state." containing current export state."
(declare (indent 1))
(concat "<" tag (concat "<" tag
(org-string-nw-p (concat " " attr)) (org-string-nw-p (concat " " attr))
(if (org-html-xhtml-p info) " />" ">"))) (if (org-html-xhtml-p info) " />" ">")))
@ -1777,11 +1830,7 @@ a communication channel."
(org-html--make-attribute-string (org-html--make-attribute-string
(org-combine-plists (org-combine-plists
(list :src source (list :src source
:alt (if (string-match-p :alt (file-name-nondirectory source))
(concat "^" org-preview-latex-image-directory) source)
(org-html-encode-plain-text
(org-find-text-property-in-string 'org-latex-src source))
(file-name-nondirectory source)))
(if (string= "svg" (file-name-extension source)) (if (string= "svg" (file-name-extension source))
(org-combine-plists '(:class "org-svg") attributes '(:fallback nil)) (org-combine-plists '(:class "org-svg") attributes '(:fallback nil))
attributes))) attributes)))
@ -1881,14 +1930,7 @@ INFO is a plist used as a communication channel."
(mapconcat (mapconcat
(lambda (definition) (lambda (definition)
(pcase definition (pcase definition
(`(,n ,label ,def) (`(,n ,_ ,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 ;; `org-export-collect-footnote-definitions' can return
;; two kinds of footnote definitions: inline and blocks. ;; two kinds of footnote definitions: inline and blocks.
;; Since this should not make any difference in the HTML ;; Since this should not make any difference in the HTML
@ -1897,9 +1939,9 @@ INFO is a plist used as a communication channel."
(let ((inline? (not (org-element-map def org-element-all-elements (let ((inline? (not (org-element-map def org-element-all-elements
#'identity nil t))) #'identity nil t)))
(anchor (org-html--anchor (anchor (org-html--anchor
(format "fn.%s" (or label n)) (format "fn.%d" n)
n n
(format " class=\"footnum\" href=\"#fnr.%s\" role=\"doc-backlink\"" (or label n)) (format " class=\"footnum\" href=\"#fnr.%d\" role=\"doc-backlink\"" n)
info)) info))
(contents (org-trim (org-export-data def info)))) (contents (org-trim (org-export-data def info))))
(format "<div class=\"footdef\">%s %s</div>\n" (format "<div class=\"footdef\">%s %s</div>\n"
@ -2748,33 +2790,16 @@ CONTENTS is nil. INFO is a plist holding contextual information."
(when (org-element-type-p prev 'footnote-reference) (when (org-element-type-p prev 'footnote-reference)
(plist-get info :html-footnote-separator))) (plist-get info :html-footnote-separator)))
(let* ((n (org-export-get-footnote-number footnote-reference info)) (let* ((n (org-export-get-footnote-number footnote-reference info))
(label (org-element-property :label footnote-reference)) (id (format "fnr.%d%s"
;; Do not assign number labels as they appear in Org mode - n
;; 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 (if (org-export-footnote-first-reference-p
footnote-reference info) footnote-reference info)
"" ""
(let ((label (org-element-property :label footnote-reference))) ".100"))))
(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 (format
(plist-get info :html-footnote-format) (plist-get info :html-footnote-format)
(org-html--anchor (org-html--anchor
id n (format " class=\"footref\" href=\"#fn.%s\" role=\"doc-backlink\"" (or label n)) info))))) id n (format " class=\"footref\" href=\"#fn.%d\" role=\"doc-backlink\"" n) info)))))
;;;; Headline ;;;; Headline
@ -2921,7 +2946,7 @@ INFO arguments."
(org-html-format-headline-default-function (org-html-format-headline-default-function
todo todo-type priority text tags info) todo todo-type priority text tags info)
(org-html-close-tag "br" nil info) (org-html-close-tag "br" nil info)
(or contents ""))) contents))
;;;; Italic ;;;; Italic
@ -3027,58 +3052,72 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; LaTeX Environment ;;;; LaTeX Environment
(defun org-html-format-latex (latex-frag processing-type info) (defun org-html-prepare-latex-images (parse-tree _backend info)
"Format a LaTeX fragment LATEX-FRAG into HTML. "Make sure that appropriate preview images exist for all LaTeX.
PROCESSING-TYPE designates the tool used for conversion. It can
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))))
(defun org-html--wrap-latex-environment (contents _ &optional caption label) 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)
"Wrap CONTENTS string within appropriate environment for equations. "Wrap CONTENTS string within appropriate environment for equations.
When optional arguments CAPTION and LABEL are given, use them for When optional arguments CAPTION and LABEL are given, use them for
caption and \"id\" attribute." caption and \"id\" attribute."
(format "\n<div%s class=\"equation-container\">\n%s%s\n</div>" (format "\n<div%s class=\"equation-container\">\n%s\n</div>"
;; ID. ;; ID.
(if (org-string-nw-p label) (format " id=\"%s\"" label) "") (if (org-string-nw-p label) (format " id=\"%s\"" label) "")
;; Contents. ;; Contents.
(format "<span class=\"equation\">\n%s\n</span>" contents) (format "<span class=\"equation\">\n%s\n</span>" contents)))
;; Caption.
(if (not (org-string-nw-p caption)) ""
(format "\n<span class=\"equation-label\">\n%s\n</span>"
caption))))
(defun org-html--math-environment-p (element &optional _) (defun org-html--math-environment-p (element &optional _)
"Non-nil when ELEMENT is a LaTeX math environment. "Non-nil when ELEMENT is a LaTeX math environment.
@ -3112,55 +3151,135 @@ CONTENTS is nil. INFO is a plist holding contextual information."
(let ((processing-type (plist-get info :with-latex)) (let ((processing-type (plist-get info :with-latex))
(latex-frag (org-remove-indentation (latex-frag (org-remove-indentation
(org-element-property :value latex-environment))) (org-element-property :value latex-environment)))
(attributes (org-export-read-attribute :attr_html latex-environment)) (label (org-html--reference latex-environment info t)))
(label (org-html--reference latex-environment info t)) (if (memq processing-type '(t mathjax))
(caption (and (org-html--latex-environment-numbered-p latex-environment) (org-html--as-latex
(number-to-string latex-environment info
(org-export-get-ordinal
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) (if (org-string-nw-p label)
(replace-regexp-in-string "\\`.*" (replace-regexp-in-string "\\`.*"
(format "\\&\n\\\\label{%s}" label) (format "\\&\n\\\\label{%s}" label)
latex-frag) latex-frag)
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--wrap-latex-environment
(org-html--format-image source attributes info) (org-html--as-latex latex-environment info latex-frag)
info caption label))))) label))))
(t (org-html--wrap-latex-environment latex-frag info caption label)))))
;;;; LaTeX Fragment ;;;; LaTeX Fragment
(defun org-html-latex-fragment (latex-fragment _contents info) (defun org-html-latex-fragment (latex-fragment _contents info)
"Transcode a LATEX-FRAGMENT object from Org to HTML. "Transcode a LATEX-FRAGMENT object from Org to HTML.
CONTENTS is nil. INFO is a plist holding contextual information." CONTENTS is nil. INFO is a plist holding contextual information."
(let ((latex-frag (org-element-property :value latex-fragment)) (org-html--as-latex latex-fragment info))
(processing-type (plist-get info :with-latex)))
(defun org-html-latex-image (element info)
"Transcode the LaTeX fragment or environment ELEMENT from Org to HTML.
INFO is a plist holding contextual information, and it is assumed
that an image for ELEMENT already exists within it."
(let* ((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 (cond
((memq processing-type '(t mathjax)) ((and (memq 'svg-embed inline-condition)
(org-html-format-latex latex-frag 'mathjax info)) (eq image-format 'svg))
((memq processing-type '(t html)) (goto-char (point-min))
(org-html-format-latex latex-frag 'html info)) (let ((svg-closing-tag (and (search-forward "<svg" nil t)
((assq processing-type org-preview-latex-process-alist) (search-forward ">" nil t))))
(let ((formula-link
(org-html-format-latex latex-frag processing-type info))) (dolist (search '("<!-- This file was generated by dvisvgm [^\n]+ -->"
(when (and formula-link (string-match "file:\\([^]]*\\)" formula-link)) " height=['\"][^\"']+[\"']"
(let ((source (org-export-file-uri (match-string 1 formula-link)))) " width=['\"][^\"']+[\"']"))
(org-html--format-image source nil info))))) (goto-char (point-min))
(t latex-frag)))) (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)))
;;;; Line Break ;;;; Line Break
@ -3967,7 +4086,7 @@ contextual information."
CONTENTS is the exported HTML code. INFO is the info plist." CONTENTS is the exported HTML code. INFO is the info plist."
(with-temp-buffer (with-temp-buffer
(insert contents) (insert contents)
(delay-mode-hooks (set-auto-mode t)) (set-auto-mode t)
(when (plist-get info :html-indent) (when (plist-get info :html-indent)
(indent-region (point-min) (point-max))) (indent-region (point-min) (point-max)))
(buffer-substring-no-properties (point-min) (point-max)))) (buffer-substring-no-properties (point-min) (point-max))))

View File

@ -1142,13 +1142,7 @@ external process."
(catch 'nextfile (catch 'nextfile
(org-check-agenda-file file) (org-check-agenda-file file)
(with-current-buffer (org-get-agenda-file-buffer file) (with-current-buffer (org-get-agenda-file-buffer file)
(condition-case err (org-icalendar-export-to-ics))))
(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))))) (org-release-buffers org-agenda-new-buffers)))))
;;;###autoload ;;;###autoload

View File

@ -48,6 +48,7 @@
(defvar engrave-faces-latex-output-style) (defvar engrave-faces-latex-output-style)
(defvar engrave-faces-current-preset-style) (defvar engrave-faces-current-preset-style)
(defvar engrave-faces-latex-mathescape) (defvar engrave-faces-latex-mathescape)
(defvar engrave-faces-latex-colorbox-strut)
;;; Define Backend ;;; Define Backend
@ -110,9 +111,9 @@
(?l "As LaTeX file" org-latex-export-to-latex) (?l "As LaTeX file" org-latex-export-to-latex)
(?p "As PDF file" org-latex-export-to-pdf) (?p "As PDF file" org-latex-export-to-pdf)
(?o "As PDF file and open" (?o "As PDF file and open"
(lambda (a s v b) (lambda (async subtreep visable-only body-only)
(if a (org-latex-export-to-pdf t s v b) (if async (org-latex-export-to-pdf t subtreep visable-only body-only)
(org-open-file (org-latex-export-to-pdf nil s v b))))))) (org-latex-export-to-pdf-and-open nil subtreep visable-only body-only))))))
:filters-alist '((:filter-options . org-latex-math-block-options-filter) :filters-alist '((:filter-options . org-latex-math-block-options-filter)
(:filter-paragraph . org-latex-clean-invalid-line-breaks) (:filter-paragraph . org-latex-clean-invalid-line-breaks)
(:filter-parse-tree org-latex-math-block-tree-filter (:filter-parse-tree org-latex-math-block-tree-filter
@ -135,7 +136,6 @@
(:latex-default-table-environment nil nil org-latex-default-table-environment) (:latex-default-table-environment nil nil org-latex-default-table-environment)
(:latex-default-quote-environment nil nil org-latex-default-quote-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-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-diary-timestamp-format nil nil org-latex-diary-timestamp-format)
(:latex-engraved-options nil nil org-latex-engraved-options) (:latex-engraved-options nil nil org-latex-engraved-options)
(:latex-engraved-preamble nil nil org-latex-engraved-preamble) (:latex-engraved-preamble nil nil org-latex-engraved-preamble)
@ -171,7 +171,83 @@
(:latex-toc-command nil nil org-latex-toc-command) (:latex-toc-command nil nil org-latex-toc-command)
(:latex-compiler "LATEX_COMPILER" nil org-latex-compiler) (:latex-compiler "LATEX_COMPILER" nil org-latex-compiler)
;; Redefine regular options. ;; Redefine regular options.
(:date "DATE" nil "\\today" parse))) (:date "DATE" nil "\\today" parse))
:feature-conditions-alist
`((t !announce-start !announce-end
!guess-pollyglossia !guess-babel !guess-inputenc)
(,(lambda (info)
;; Since amsmath is added unconditionally when using
;; xelatex/lualatex (see `org-latex-default-packages-alist'),
;; and amssymb is not needed, we need not bother when using
;; thoese compilers.
(and (not (member (plist-get info :latex-compiler) '("xelatex" "lualatex")))
(org-element-map (plist-get info :parse-tree)
'(latex-fragment latex-environment) #'identity info t)))
maths)
(,(lambda (info)
(org-element-map (plist-get info :parse-tree)
'underline #'identity info t))
underline)
("\\\\uu?line\\|\\\\uwave\\|\\\\sout\\|\\\\xout\\|\\\\dashuline\\|\\dotuline\\|\\markoverwith"
underline)
(,(lambda (info)
(org-element-map (plist-get info :parse-tree)
'link
(lambda (link)
(and (member (org-element-property :type link)
'("http" "https" "ftp" "file"))
(file-name-extension (org-element-property :path link))
(equal (downcase (file-name-extension
(org-element-property :path link)))
"svg")))
info t))
svg)
(org-latex-tables-booktabs booktabs)
(,(lambda (info)
(equal (plist-get info :latex-default-table-environment)
"longtable"))
longtable)
("^[ \t]*\\+attr_latex: .*:environment +longtable"
longtable)
(,(lambda (info)
(eq (plist-get info :latex-src-block-backend) 'engraved))
engraved-code)
("^[ \t]*#\\+attr_latex: .*:float +wrap"
float-wrap)
("^[ \t]*#\\+attr_latex: .*:float +sideways"
rotate)
("^[ \t]*#\\+caption\\(?:\\[.*\\]\\)?:\\|\\\\caption{" caption))
:feature-implementations-alist
`((!announce-start
:snippet ,(lambda (info)
(with-temp-buffer
(setq-local left-margin 2)
(insert (string-join
(mapcar #'symbol-name
(plist-get info :features))
", ")
".")
(fill-region-as-paragraph (point-min) (point-max))
(goto-char (point-min))
(insert "%% ox-latex features:\n% ")
(while (search-forward "\n" nil t)
(insert "%"))
(buffer-string)))
:order -100)
(maths :snippet "\\usepackage{amsmath}\n\\usepackage{amssymb}" :order 0.2)
(underline :snippet "\\usepackage[normalem]{ulem}" :order 0.5)
(image :snippet "\\usepackage{graphicx}" :order 2)
(svg :snippet "\\usepackage[inkscapelatex=false]{svg}" :order 2 :when image)
(longtable :snippet "\\usepackage{longtable}" :when table :order 2)
(booktabs :snippet "\\usepackage{booktabs}" :when table :order 2)
(float-wrap :snippet "\\usepackage{wrapfig}" :order 2)
(rotate :snippet "\\usepackage{rotating}" :order 2)
(caption :snippet "\\usepackage{capt-of}")
(engraved-code :when code :snippet org-latex-generate-engraved-preamble)
(!guess-pollyglossia :snippet org-latex-guess-polyglossia-language)
(!guess-babel :snippet org-latex-guess-babel-language)
(!guess-inputenc :snippet org-latex-guess-inputenc)
(!announce-end :snippet "%% end ox-latex features\n" :order 100)))
@ -668,17 +744,6 @@ The function result will be used in the section format string."
;;;; Footnotes ;;;; 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{,}\\," (defcustom org-latex-footnote-separator "\\textsuperscript{,}\\,"
"Text used to separate footnotes." "Text used to separate footnotes."
:group 'org-export-latex :group 'org-export-latex
@ -792,6 +857,7 @@ default we use here encompasses both."
:group 'org-export-latex :group 'org-export-latex
:type 'string) :type 'string)
;;;; Tables ;;;; Tables
(defcustom org-latex-default-table-environment "tabular" (defcustom org-latex-default-table-environment "tabular"
@ -1211,9 +1277,10 @@ will produce
% Define a Code environment to prettily wrap the fontified code. % Define a Code environment to prettily wrap the fontified code.
\\usepackage[breakable,xparse]{tcolorbox} \\usepackage[breakable,xparse]{tcolorbox}
\\providecommand{\\codefont}{\\footnotesize}
\\DeclareTColorBox[]{Code}{o}% \\DeclareTColorBox[]{Code}{o}%
{colback=EfD!98!EFD, colframe=EfD!95!EFD, {colback=EfD!98!EFD, colframe=EfD!95!EFD,
fontupper=\\footnotesize\\setlength{\\fboxsep}{0pt}, fontupper=\\setlength{\\fboxsep}{0pt}\\codefont,
colupper=EFD, colupper=EFD,
IfNoValueTF={#1}% IfNoValueTF={#1}%
{boxsep=2pt, arc=2.5pt, outer arc=2.5pt, {boxsep=2pt, arc=2.5pt, outer arc=2.5pt,
@ -1237,7 +1304,9 @@ as long as it:
In the default value the colors \"EFD\" and \"EfD\" are provided In the default value the colors \"EFD\" and \"EfD\" are provided
as they are respectively the foreground and background colors, as they are respectively the foreground and background colors,
just in case they aren't provided by the generated preamble, so just in case they aren't provided by the generated preamble, so
we can assume they are always set. we can assume they are always set. The command \"\\codefont\" is
also provided (defaulting to \"\\footnotesize\"), to allow the
font used in \"Code\" environments to be easily tweaked.
Within this preamble there are two recognized macro-like placeholders: Within this preamble there are two recognized macro-like placeholders:
@ -1334,7 +1403,19 @@ default values of which are given by `org-latex-engraved-preamble' and
(alist-get 'default (alist-get 'default
(if theme (if theme
(engrave-faces-get-theme (intern theme)) (engrave-faces-get-theme (intern theme))
engrave-faces-current-preset-style))))))) engrave-faces-current-preset-style))))))
(gen-theme-command
(lambda (theme)
(format "\n\\newcommand{\\engravedtheme%s}{%%\n%s\n}"
(replace-regexp-in-string
"[^A-Za-z]" "" (symbol-name theme))
(replace-regexp-in-string
"\\\\newcommand\\\\efstrut[^\n]*\n" ""
(replace-regexp-in-string
"\\\\newcommand{\\(.+?\\)}\\[1\\]" "\\\\long\\\\def\\1##1"
(replace-regexp-in-string
"#1" "##1"
(funcall gen-theme-spec theme))))))))
(when (stringp engraved-theme) (when (stringp engraved-theme)
(setq engraved-theme (intern engraved-theme))) (setq engraved-theme (intern engraved-theme)))
(when (string-match "^[ \t]*\\[FVEXTRA-SETUP\\][ \t]*\n?" engraved-preamble) (when (string-match "^[ \t]*\\[FVEXTRA-SETUP\\][ \t]*\n?" engraved-preamble)
@ -1364,37 +1445,57 @@ default values of which are given by `org-latex-engraved-preamble' and
t t t t
engraved-preamble))) engraved-preamble)))
(concat (concat
"\n% Setup for code blocks [1/2]\n\n" "% Setup for code blocks [1/2]\n\n"
engraved-preamble engraved-preamble
"\n\n% Setup for code blocks [2/2]: syntax highlighting colors\n\n" "\n\n% Setup for code blocks [2/2]: syntax highlighting colors\n\n"
(if (require 'engrave-faces-latex nil t) (if (require 'engrave-faces-latex nil t)
(if engraved-themes (if engraved-themes
(concat (concat
;; We don't want to re-define the efstrut, so we now need to
;; define it seperately.
(format "\\newcommand\\efstrut{%s}\n\n"
engrave-faces-latex-colorbox-strut)
;; Define default theme
(funcall gen-theme-command engraved-theme)
"\n"
;; Define other themes
(mapconcat (mapconcat
(lambda (theme) gen-theme-command
(format (cl-remove-if (lambda (theme) (string= theme (symbol-name engraved-theme)))
"\n\\newcommand{\\engravedtheme%s}{%%\n%s\n}" engraved-themes)
(replace-regexp-in-string "[^A-Za-z]" "" (symbol-name theme))
(replace-regexp-in-string
"newcommand" "renewcommand"
(replace-regexp-in-string
"#" "##"
(funcall gen-theme-spec theme)))))
engraved-themes
"\n") "\n")
"\n\n" ;; Load the default theme
(cond "\n\n\\engravedtheme"
((memq engraved-theme engraved-themes)
(concat "\\engravedtheme"
(replace-regexp-in-string (replace-regexp-in-string
"[^A-Za-z]" "" engraved-theme) "[^A-Za-z]" "" (symbol-name engraved-theme))
"\n")) "\n")
(t (funcall gen-theme-spec engraved-theme))))
(funcall gen-theme-spec engraved-theme)) (funcall gen-theme-spec engraved-theme))
(warn "Cannot engrave source blocks. Consider installing `engrave-faces'.") (warn "Cannot engrave source blocks. Consider installing `engrave-faces'.")
"% WARNING syntax highlighting unavailable as engrave-faces-latex was missing.\n") "% WARNING syntax highlighting unavailable as engrave-faces-latex was missing.\n")
"\n"))) "\n")))
;; Citation features
(org-export-update-features 'latex
(bibliography-csl
:condition (eq (org-cite-processor info) 'csl)
:when bibliography
:snippet org-cite-csl--generate-latex-preamble)
(bibliography-biblatex
:condition (eq (org-cite-processor info) 'biblatex)
:when bibliography
:snippet org-cite-biblatex--generate-latex-usepackage)
(bibliography-biblatex-resources
:condition (eq (org-cite-processor info) 'biblatex)
:when bibliography
:snippet org-cite-biblatex--generate-latex-bibresources
:no-precompile t
:order 90)
(bibliography-natbib
:condition (eq (org-cite-processor info) 'natbib)
:when bibliography
:snippet org-cite-natbib--generate-latex-preamble))
;;;; Compilation ;;;; Compilation
(defcustom org-latex-compiler-file-string "%% Intended LaTeX compiler: %s\n" (defcustom org-latex-compiler-file-string "%% Intended LaTeX compiler: %s\n"
@ -1643,29 +1744,29 @@ For non-floats, see `org-latex--wrap-label'."
(org-trim label) (org-trim label)
(org-export-data main info)))))) (org-export-data main info))))))
(defun org-latex-guess-inputenc (header) (defun org-latex-guess-inputenc (info)
"Set the coding system in inputenc to what the buffer is. "Set the coding system in inputenc to what the buffer is.
HEADER is the LaTeX header string. This function only applies INFO is the plist used as a communication channel.
when specified inputenc option is \"AUTO\". This function only applies when specified inputenc option is \"AUTO\".
Return the new header, as a string." Return the new header, as a string."
(let* ((cs (or (ignore-errors (let ((header (plist-get info :latex-full-header))
(cs (or (ignore-errors
(latexenc-coding-system-to-inputenc (latexenc-coding-system-to-inputenc
(or org-export-coding-system buffer-file-coding-system))) (or org-export-coding-system buffer-file-coding-system)))
"utf8"))) "utf8")))
(if (not cs) header (when (and cs (string-match "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}" header))
;; First translate if that is requested. ;; First translate if that is requested.
(setq cs (or (cdr (assoc cs org-latex-inputenc-alist)) cs)) (setq cs (or (cdr (assoc cs org-latex-inputenc-alist)) cs))
;; Then find the \usepackage statement and replace the option. (plist-put info :latex-full-header
(replace-regexp-in-string "\\\\usepackage\\[\\(AUTO\\)\\]{inputenc}" (replace-match cs t t header 1))))
cs header t nil 1)))) nil)
(defun org-latex-guess-babel-language (header info) (defun org-latex-guess-babel-language (info)
"Set Babel's language according to LANGUAGE keyword. "Set Babel's language according to LANGUAGE keyword.
HEADER is the LaTeX header string. INFO is the plist used as INFO is the plist used as a communication channel.
a communication channel.
Insertion of guessed language only happens when Babel package has Insertion of guessed language only happens when Babel package has
explicitly been loaded. Then it is added to the rest of explicitly been loaded. Then it is added to the rest of
@ -1679,52 +1780,48 @@ already loaded.
Return the new header." Return the new header."
(let* ((language-code (plist-get info :language)) (let* ((language-code (plist-get info :language))
(plist (cdr (plist (cdr (assoc language-code org-latex-language-alist)))
(assoc language-code org-latex-language-alist)))
(language (plist-get plist :babel)) (language (plist-get plist :babel))
(header (plist-get info :latex-full-header))
(language-ini-only (plist-get plist :babel-ini-only)) (language-ini-only (plist-get plist :babel-ini-only))
(language-ini-alt (plist-get plist :babel-ini-alt)) (language-ini-alt (plist-get plist :babel-ini-alt))
(babel-header-options
;; If no language is set, or Babel package is not loaded, or ;; If no language is set, or Babel package is not loaded, or
;; LANGUAGE keyword value is a language served by Babel ;; LANGUAGE keyword value is a language served by Babel
;; exclusively through ini files, return HEADER as-is. ;; exclusively through ini files, return HEADER as-is.
(header (if (or language-ini-only (and (not language-ini-only)
(not (stringp language-code)) (stringp language-code)
(not (string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header))) (string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header)
header
(let ((options (save-match-data (let ((options (save-match-data
(org-split-string (match-string 1 header) ",[ \t]*")))) (org-split-string (match-string 1 header) ",[ \t]*"))))
;; If LANGUAGE is already loaded, return header
;; 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)) (cond ((member language options) (delete "AUTO" options))
((member "AUTO" options) options) ((member "AUTO" options) options)
(t (append options (list language)))) (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))))) t nil header 1)))
;; If `\babelprovide[args]{AUTO}' is present, AUTO is ;; If `\babelprovide[args]{AUTO}' is present, AUTO is
;; replaced by LANGUAGE. ;; replaced by LANGUAGE.
(if (not (string-match "\\\\babelprovide\\[.*\\]{\\(.+\\)}" header)) (when (string-match "\\\\babelprovide\\[.*\\]{AUTO}" header)
header (setq header
(let ((prov (match-string 1 header))) (replace-regexp-in-string
(if (equal "AUTO" prov) (format
(replace-regexp-in-string (format "\\(\\\\babelprovide\\[.*\\]\\)\\({\\)%s}" babel-header-options)
"\\(\\\\babelprovide\\[.*\\]\\)\\({\\)%s}" prov) (format "\\1\\2%s}" (if language-ini-alt language-ini-alt
(format "\\1\\2%s}"
(if language-ini-alt language-ini-alt
(or language language-ini-only))) (or language language-ini-only)))
header t) header t)))
header))))) (plist-put info :latex-full-header header))
nil)
(defun org-latex-guess-polyglossia-language (header info) (defun org-latex-guess-polyglossia-language (info)
"Set the Polyglossia language according to the LANGUAGE keyword. "Set the Polyglossia language according to the LANGUAGE keyword.
HEADER is the LaTeX header string. INFO is the plist used as INFO is the plist used as a communication channel.
a communication channel.
Insertion of guessed language only happens when the Polyglossia Insertion of guessed language only happens when the Polyglossia
package has been explicitly loaded. package has been explicitly loaded.
@ -1735,14 +1832,14 @@ replaced with the language of the document or
using \setdefaultlanguage and not as an option to the package. using \setdefaultlanguage and not as an option to the package.
Return the new header." Return the new header."
(let* ((language (plist-get info :language))) (let ((header (plist-get info :latex-full-header))
(language (plist-get info :language)))
;; If no language is set or Polyglossia is not loaded, return ;; If no language is set or Polyglossia is not loaded, return
;; HEADER as-is. ;; HEADER as-is.
(if (or (not (stringp language)) (when (and (stringp language)
(not (string-match (string-match
"\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n" "\\\\usepackage\\(?:\\[\\([^]]+?\\)\\]\\){polyglossia}\n"
header))) header))
header
(let* ((options (org-string-nw-p (match-string 1 header))) (let* ((options (org-string-nw-p (match-string 1 header)))
(languages (and options (languages (and options
;; Reverse as the last loaded language is ;; Reverse as the last loaded language is
@ -1755,13 +1852,13 @@ Return the new header."
"AUTO" language options t) "AUTO" language options t)
",[ \t]*")))))) ",[ \t]*"))))))
(main-language-set (main-language-set
(string-match-p "\\\\setmainlanguage{.*?}" header))) (string-match-p "\\\\setmainlanguage{.*?}" header))
(polyglossia-modified-header
(replace-match (replace-match
(concat "\\usepackage{polyglossia}\n" (concat "\\usepackage{polyglossia}\n"
(mapconcat (mapconcat
(lambda (l) (lambda (l)
(let* ((plist (cdr (let* ((plist (cdr (assoc language org-latex-language-alist)))
(assoc language org-latex-language-alist)))
(polyglossia-variant (plist-get plist :polyglossia-variant)) (polyglossia-variant (plist-get plist :polyglossia-variant))
(polyglossia-lang (plist-get plist :polyglossia)) (polyglossia-lang (plist-get plist :polyglossia))
(l (if (equal l language) (l (if (equal l language)
@ -1776,7 +1873,9 @@ Return the new header."
l))) l)))
languages languages
"")) ""))
t t header 0))))) t t header 0)))
(plist-put info :latex-full-header polyglossia-modified-header))))
nil)
(defun org-latex--remove-packages (pkg-alist info) (defun org-latex--remove-packages (pkg-alist info)
"Remove packages based on the current LaTeX compiler. "Remove packages based on the current LaTeX compiler.
@ -1990,23 +2089,94 @@ specified in `org-latex-default-packages-alist' or
(replace-regexp-in-string (replace-regexp-in-string
"^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)" "^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
class-options header t nil 1)))) class-options header t nil 1))))
(user-error "Unknown LaTeX class `%s'" class)))) (user-error "Unknown LaTeX class `%s'" class)))
(org-latex-guess-polyglossia-language (header-split (format "\n%%--org-latex-header-temp-split-%d--\n" (random 10000)))
(org-latex-guess-babel-language (header (concat (org-element-normalize-string (plist-get info :latex-header))
(org-latex-guess-inputenc header-split
(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-element-normalize-string
(org-splice-latex-header (org-splice-latex-header
class-template class-template
(org-latex--remove-packages org-latex-default-packages-alist info) (org-latex--remove-packages org-latex-default-packages-alist info)
(org-latex--remove-packages org-latex-packages-alist info) (org-latex--remove-packages org-latex-packages-alist info)
snippet? snippet?
(mapconcat #'org-element-normalize-string header)))
(list (plist-get info :latex-header) (if snippet?
(and (not snippet?) (setq generated-preamble
(plist-get info :latex-header-extra))) (progn
"")))) (org-latex-guess-inputenc info)
info) (org-latex-guess-babel-language info)
info))) (org-latex-guess-polyglossia-language info)
"\n% Generated preamble omitted for snippets."))
(let (impl-precomp impl-noprecomp)
(dolist (impl (plist-get info :feature-implementations))
(message "LaTeX feature: %s" (car impl))
(if (or impl-noprecomp (plist-get (cdr impl) :no-precompile))
(push impl impl-noprecomp)
(push impl impl-precomp)))
(message "Precomp feats: %S" (reverse (mapcar #'car impl-precomp)))
(message "No precomp feats: %S" (reverse (mapcar #'car impl-noprecomp)))
(setq generated-preamble
(string-join
(org-export-expand-feature-snippets
info (nreverse impl-precomp))
"\n\n")
preamble-nonprecompilable
(string-join
(org-export-expand-feature-snippets
info (nreverse impl-noprecomp))
"\n\n"))))
(let* ((header-parts
(split-string (plist-get info :latex-full-header)
(regexp-quote header-split)))
(header-main (car header-parts))
(header-extra (cadr header-parts))
(preamble
(concat
(org-latex--insert-compiler info)
header-main
"\n"
generated-preamble
"\n"))
(format-file
(and org-latex-precompile
;; Precompilation is disabled for xelatex/lualatex for now.
(if (member (plist-get info :latex-compiler)
'("xelatex" "lualatex"))
(progn
(display-warning
'(org latex-export disable-local-precompile)
(format "%s does not support precompilation, disabling LaTeX precompile in this buffer.\n To re-enable, run `(setq-local org-latex-precompile t)' or reopen this buffer."
(plist-get info :latex-compiler)))
(setf (buffer-local-value
'org-latex-precompile (get-buffer (plist-get info :input-buffer)))
nil))
(org-latex--precompile
info preamble
(string-match-p "\\(?:\\\\input{\\|\\\\include{\\)[^/]" preamble))))))
(when (and format-file (not snippet?))
(let ((preamble-parts (split-string preamble (regexp-quote header-split))))
(setq preamble (car preamble-parts)
preamble-nonprecompilable
(concat preamble-nonprecompilable
(cadr preamble-parts)))))
(concat (and format-file
(concat "%& " (file-name-sans-extension format-file) "\n"))
(and (plist-get info :time-stamp-file)
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
preamble
(and format-file
"\n% end precompiled preamble\n\\ifcsname endofdump\\endcsname\\endofdump\\fi\n")
"\n"
preamble-nonprecompilable
(and preamble-nonprecompilable
(not (string-empty-p preamble-nonprecompilable))
"\n")
header-extra))))
(defun org-latex-template (contents info) (defun org-latex-template (contents info)
"Return complete document string after LaTeX conversion. "Return complete document string after LaTeX conversion.
@ -2015,12 +2185,7 @@ holding export options."
(let ((title (org-export-data (plist-get info :title) info)) (let ((title (org-export-data (plist-get info :title) info))
(spec (org-latex--format-spec info))) (spec (org-latex--format-spec info)))
(concat (concat
;; Timestamp. ;; Timestamp, compiler statement, document class and packages.
(and (plist-get info :time-stamp-file)
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
;; LaTeX compiler.
(org-latex--insert-compiler info)
;; Document class and packages.
(org-latex-make-preamble info) (org-latex-make-preamble info)
;; Possibly limit depth for headline numbering. ;; Possibly limit depth for headline numbering.
(let ((sec-num (plist-get info :section-numbers))) (let ((sec-num (plist-get info :section-numbers)))
@ -2057,12 +2222,6 @@ holding export options."
(let ((template (plist-get info :latex-hyperref-template))) (let ((template (plist-get info :latex-hyperref-template)))
(and (stringp template) (and (stringp template)
(format-spec template spec))) (format-spec template spec)))
;; engrave-faces-latex preamble
(when (and (eq (plist-get info :latex-src-block-backend) 'engraved)
(org-element-map (plist-get info :parse-tree)
'(src-block inline-src-block) #'identity
info t))
(org-latex-generate-engraved-preamble info))
;; Document start. ;; Document start.
"\\begin{document}\n\n" "\\begin{document}\n\n"
;; Title command. ;; Title command.
@ -2090,6 +2249,118 @@ holding export options."
;; Document end. ;; Document end.
"\\end{document}"))) "\\end{document}")))
(defvar org-latex-precompile t
"Precompile the preamble during export.
This requires the LaTeX package \"mylatexformat\" to be installed.")
(defconst org-latex--precompile-log "*Org LaTeX Precompilation*")
(defvar org-latex-precompile-command
"%l -output-directory %o -ini -jobname=%b \"&%L\" mylatexformat.ltx %f")
(defvar org-latex-precompile-compiler-map
'(("pdflatex" . "latex")
("xelatex" . "xelatex -no-pdf")
("lualatex" . "dvilualatex")))
(defun org-latex--precompile (info preamble &optional tempfile-p)
"Precompile/dump LaTeX PREAMBLE text.
The path to the format file (.fmt) is returned. If the format
file could not be found in the persist cache, it is generated
according to PROCESSING-INFO and stored. INFO is a plist used as
a communication channel.
If TEMPFILE-P is non-nil, then it is assumed the preamble does
not contain any relative references to other files.
This is intended to speed up Org's LaTeX preview generation
process."
(let ((preamble-hash
(thread-first
preamble
(concat
(plist-get info :latex-compiler)
(if tempfile-p "-temp"
default-directory))
(sha1)))
(default-directory
(if tempfile-p temporary-file-directory default-directory)))
(or (cadr
(org-persist-read "LaTeX format file cache"
(list :key preamble-hash)
nil nil :read-related t))
(when-let ((dump-file
(org-latex--precompile-preamble
info preamble (expand-file-name preamble-hash temporary-file-directory))))
(cadr
(org-persist-register `(,"LaTeX format file cache"
(file ,dump-file))
(list :key preamble-hash)
:write-immediately t))))))
(defun org-latex--remove-cached-preamble (latex-compiler preamble &optional tempfile-p)
"Remove the cached preamble file for PREAMBLE compiled with LATEX-COMPILER.
TEMPFILE-P should be set to mirror the caching `org-latex--precompile' call
which is intended to be evicted from the cache."
(let ((preamble-hash
(thread-first
preamble
(concat
latex-compiler
(if tempfile-p "-temp"
default-directory))
(sha1))))
(org-persist-unregister "LaTeX format file cache"
(list :key preamble-hash)
:remove-related t)))
(defun org-latex--precompile-preamble (info preamble basepath)
"Precompile PREAMBLE with \"mylatexformat\".
The PREAMBLE string is placed in BASEPATH.tex and compiled
according to PROCESSING-INFO. If compilation and dumping
succeeded, BASEPATH.fmt will be returned.
Should any errors occur during compilation, nil will be returned,
and appropriate warnings may be emitted."
(let ((dump-file (concat basepath ".fmt"))
(preamble-file (concat basepath ".tex"))
(precompile-buffer
(with-current-buffer
(get-buffer-create org-latex--precompile-log)
(erase-buffer)
(current-buffer))))
(with-temp-file preamble-file
(insert preamble "\n\\endofdump\n"))
(message "Precompiling Org LaTeX preamble...")
(condition-case nil
(org-compile-file
preamble-file (list org-latex-precompile-command)
"fmt" nil precompile-buffer
(or (plist-get info :precompile-format-spec)
`((?l . ,(plist-get info :latex-compiler))
(?L . ,(plist-get info :latex-compiler)))))
(:success
(kill-buffer precompile-buffer)
(delete-file preamble-file)
dump-file)
(error
(unless (= 0 (call-process "kpsewhich" nil nil nil "mylatexformat.ltx"))
(display-warning
'(org latex-preview preamble-precompilation)
"The LaTeX package \"mylatexformat\" is required for precompilation, but could not be found")
:warning)
(unless (= 0 (call-process "kpsewhich" nil nil nil "preview.sty"))
(display-warning
'(org latex-preview preamble-precompilation)
"The LaTeX package \"preview\" is required for precompilation, but could not be found")
:warning)
(display-warning
'(org latex-preview preamble-precompilation)
(format "Failed to precompile preamble (%s), see the \"%s\" buffer."
preamble-file precompile-buffer)
:warning)
nil))))
;;; Transcode Functions ;;; Transcode Functions
@ -2250,7 +2521,7 @@ CONTENTS is nil. INFO is a plist holding contextual information."
(t (t
(let ((def (org-export-get-footnote-definition footnote-reference info))) (let ((def (org-export-get-footnote-definition footnote-reference info)))
(concat (concat
(format (plist-get info :latex-default-footnote-command) (org-trim (org-export-data def info)) (format "\\footnote{%s%s}" (org-trim (org-export-data def info))
;; Only insert a \label if there exist another ;; Only insert a \label if there exist another
;; reference to def. ;; reference to def.
(cond ((not label) "") (cond ((not label) "")
@ -2494,8 +2765,12 @@ INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
(defun org-latex-inline-src-block--engraved (info code lang) (defun org-latex-inline-src-block--engraved (info code lang)
"Transcode an inline src block's content from Org to LaTeX, using engrave-faces. "Transcode an inline src block's content from Org to LaTeX, using engrave-faces.
INFO, CODE, and LANG are provided by `org-latex-inline-src-block'." INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
(let ((engraved-theme (plist-get info :latex-engraved-theme)))
(when (stringp engraved-theme)
(setq engraved-theme (intern engraved-theme)))
(org-latex-src--engrave-code (org-latex-src--engrave-code
code lang nil (plist-get info :latex-engraved-options) t)) code lang engraved-theme nil
(plist-get info :latex-engraved-options) t)))
(defun org-latex-inline-src-block--listings (info code lang) (defun org-latex-inline-src-block--listings (info code lang)
"Transcode an inline src block's content from Org to LaTeX, using lstlistings. "Transcode an inline src block's content from Org to LaTeX, using lstlistings.
@ -3546,7 +3821,7 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
(when (eq mathescape 'yes) (when (eq mathescape 'yes)
(or engrave-faces-latex-mathescape t))))) (or engrave-faces-latex-mathescape t)))))
(defun org-latex-src--engrave-code (content lang &optional theme options inline) (defun org-latex-src--engrave-code (content lang &optional theme explicit-theme-p options inline)
"Engrave CONTENT to LaTeX in a LANG-mode buffer, and give the result. "Engrave CONTENT to LaTeX in a LANG-mode buffer, and give the result.
When the THEME symbol is non-nil, that theme will be used. When the THEME symbol is non-nil, that theme will be used.
@ -3587,12 +3862,12 @@ to the Verbatim environment or Verb command."
(concat "\\begin{Code}\n\\begin{Verbatim}" engraved-options "\n" (concat "\\begin{Code}\n\\begin{Verbatim}" engraved-options "\n"
engraved-code "\n\\end{Verbatim}\n\\end{Code}")))) engraved-code "\n\\end{Verbatim}\n\\end{Code}"))))
(kill-buffer engraved-buffer) (kill-buffer engraved-buffer)
(if theme (if (and theme explicit-theme-p)
(concat "{\\engravedtheme" (concat "\\begingroup\\engravedtheme"
(replace-regexp-in-string "[^A-Za-z]" "" (replace-regexp-in-string "[^A-Za-z]" ""
(symbol-name theme)) (symbol-name theme))
engraved-wrapped engraved-wrapped
"}") "\\endgroup")
engraved-wrapped)) engraved-wrapped))
(user-error "Cannot engrave code as `engrave-faces-latex' is unavailable."))) (user-error "Cannot engrave code as `engrave-faces-latex' is unavailable.")))
@ -3626,7 +3901,16 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
`(("linenos") `(("linenos")
("firstnumber" ,(number-to-string (1+ num-start))))) ("firstnumber" ,(number-to-string (1+ num-start)))))
(and local-options `((,local-options)))))) (and local-options `((,local-options))))))
(engraved-theme (plist-get attributes :engraved-theme)) (intern-safe
(lambda (name)
(if (stringp name)
(intern name)
name)))
(engraved-doc-theme
(funcall intern-safe (plist-get info :latex-engraved-theme)))
(engraved-theme
(or (funcall intern-safe (plist-get attributes :engraved-theme))
engraved-doc-theme))
(content (content
(let* ((code-info (org-export-unravel-code src-block)) (let* ((code-info (org-export-unravel-code src-block))
(max-width (max-width
@ -3652,7 +3936,8 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
(org-latex-src--engrave-mathescape-p info options))) (org-latex-src--engrave-mathescape-p info options)))
(org-latex-src--engrave-code (org-latex-src--engrave-code
content lang content lang
(when engraved-theme (intern engraved-theme)) engraved-theme
(not (eq engraved-theme engraved-doc-theme))
options)))) options))))
(concat (car float-env) body (cdr float-env)))) (concat (car float-env) body (cdr float-env))))
@ -4366,13 +4651,21 @@ Return PDF file's name."
async subtreep visible-only body-only ext-plist async subtreep visible-only body-only ext-plist
#'org-latex-compile))) #'org-latex-compile)))
(defun org-latex-compile (texfile &optional snippet) (defun org-latex-export-to-pdf-and-open (&optional async subtreep visible-only body-only ext-plist)
(let ((outfile (org-export-output-file-name ".tex" subtreep)))
(org-export-to-file 'latex outfile
async subtreep visible-only body-only ext-plist
(lambda (f) (org-latex-compile f nil t)))))
(defun org-latex-compile (texfile &optional snippet open-pdf)
"Compile a TeX file. "Compile a TeX file.
TEXFILE is the name of the file being compiled. Processing is TEXFILE is the name of the file being compiled. Processing is
done through the command specified in `org-latex-pdf-process', done through the command specified in `org-latex-pdf-process',
which see. Output is redirected to \"*Org PDF LaTeX Output*\" which see. Output is redirected to \"*Org PDF LaTeX Output*\"
buffer. buffer. When the process is a shell command (and not a
function), it will be run asyncronously via `org-async-call',
unless the current session is `noninteractive'.
When optional argument SNIPPET is non-nil, TEXFILE is a temporary When optional argument SNIPPET is non-nil, TEXFILE is a temporary
file used to preview a LaTeX snippet. In this case, do not file used to preview a LaTeX snippet. In this case, do not
@ -4407,16 +4700,34 @@ produced."
(?L . ,(shell-quote-argument compiler)))) (?L . ,(shell-quote-argument compiler))))
(log-buf-name "*Org PDF LaTeX Output*") (log-buf-name "*Org PDF LaTeX Output*")
(log-buf (and (not snippet) (get-buffer-create log-buf-name))) (log-buf (and (not snippet) (get-buffer-create log-buf-name)))
outfile) (outfile (expand-file-name (concat (file-name-base texfile) ".pdf")
;; Erase compile buffer at the start. (file-name-directory texfile))))
(with-current-buffer log-buf (with-current-buffer log-buf
(erase-buffer)) (erase-buffer))
(setq outfile (if (or noninteractive (functionp process))
(org-compile-file (progn
texfile process "pdf" (org-compile-file texfile process "pdf"
(format "See %S for details" log-buf-name) (format "See %S for details" log-buf-name)
log-buf spec)) log-buf spec)
(org-latex-compile--postprocess outfile log-buf snippet) (and open-pdf (org-open-file outfile))
(org-latex-compile--postprocess outfile log-buf snippet))
(let ((failure-msg (format "File %S wasn't produced (exit code %%d). See %%s for details."
outfile))
async-call-spec)
(dolist (cmd (reverse (org-compile-file-commands
texfile process "pdf" spec)))
(setq async-call-spec
(list cmd
:buffer log-buf
:success
(or async-call-spec
(list (and open-pdf (lambda (_ _ _) (org-open-file outfile)))
(lambda (_ buf _)
(org-latex-compile--postprocess outfile buf snippet))))
:failure
failure-msg)))
(message "Compiling %s..." texfile)
(apply #'org-async-call async-call-spec)))
;; Return output file name. ;; Return output file name.
outfile)) outfile))

View File

@ -293,13 +293,6 @@ This function shouldn't be used for floats. See
"Protect minus and backslash characters in string TEXT." "Protect minus and backslash characters in string TEXT."
(replace-regexp-in-string "-" "\\-" text nil t)) (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 ;;; Template
@ -407,7 +400,7 @@ information."
(org-man--wrap-label (org-man--wrap-label
example-block example-block
(format ".RS\n.nf\n%s\n.fi\n.RE" (format ".RS\n.nf\n%s\n.fi\n.RE"
(org-man--protect-example (org-export-format-code-default example-block info))))) (org-export-format-code-default example-block info))))
;;; Export Block ;;; Export Block
@ -536,11 +529,11 @@ contextual information."
(delete-file out-file) (delete-file out-file)
code-block) code-block)
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE\n" (format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE\n"
(org-man--protect-example code))))) code))))
;; Do not use a special package: transcode it verbatim. ;; Do not use a special package: transcode it verbatim.
(t (t
(concat ".RS\n.nf\n" "\\fC" "\n" (org-man--protect-example code) "\n" (concat ".RS\n.nf\n" "\\fC" "\n" code "\n"
"\\fP\n.fi\n.RE\n"))))) "\\fP\n.fi\n.RE\n")))))
@ -756,7 +749,7 @@ CONTENTS holds the contents of the item. INFO is a plist holding
contextual information." contextual information."
(if (not (plist-get info :man-source-highlight)) (if (not (plist-get info :man-source-highlight))
(format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n\n" (format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n\n"
(org-man--protect-example (org-export-format-code-default src-block info))) (org-export-format-code-default src-block info))
(let* ((tmpdir temporary-file-directory) (let* ((tmpdir temporary-file-directory)
(in-file (make-temp-name (expand-file-name "srchilite" tmpdir))) (in-file (make-temp-name (expand-file-name "srchilite" tmpdir)))
(out-file (make-temp-name (expand-file-name "reshilite" tmpdir))) (out-file (make-temp-name (expand-file-name "reshilite" tmpdir)))
@ -779,7 +772,7 @@ contextual information."
(delete-file in-file) (delete-file in-file)
(delete-file out-file) (delete-file out-file)
code-block) code-block)
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" (org-man--protect-example code)))))) (format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" code)))))
;;; Statistics Cookie ;;; Statistics Cookie
@ -843,10 +836,9 @@ contextual information."
(format ".nf\n\\fC%s\\fP\n.fi" (format ".nf\n\\fC%s\\fP\n.fi"
;; Re-create table, without affiliated keywords. ;; Re-create table, without affiliated keywords.
(org-man--protect-example
(org-trim (org-trim
(org-element-interpret-data (org-element-interpret-data
`(table nil ,@(org-element-contents table))))))) `(table nil ,@(org-element-contents table))))))
;; Case 2: Standard table. ;; Case 2: Standard table.
(t (org-man-table--org-table table contents info)))) (t (org-man-table--org-table table contents info))))

157
lisp/ox-mathml.el Normal file
View File

@ -0,0 +1,157 @@
;;; 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

View File

@ -47,15 +47,11 @@
(defcustom org-md-headline-style 'atx (defcustom org-md-headline-style 'atx
"Style used to format headlines. "Style used to format headlines.
This variable can be set to either `atx', `setext', or `mixed'. This variable can be set to either `atx' or `setext'."
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 :group 'org-export-md
:type '(choice :type '(choice
(const :tag "Use \"atx\" style" atx) (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 ;;;; Footnotes
@ -236,7 +232,7 @@ anchor tag for the section as a string. TAGS are the tags set on
the section." the section."
(let ((anchor-lines (and anchor (concat anchor "\n\n")))) (let ((anchor-lines (and anchor (concat anchor "\n\n"))))
;; Use "Setext" style ;; Use "Setext" style
(if (and (memq style '(setext mixed)) (< level 3)) (if (and (eq style 'setext) (< level 3))
(let* ((underline-char (if (= level 1) ?= ?-)) (let* ((underline-char (if (= level 1) ?= ?-))
(underline (concat (make-string (length title) underline-char) (underline (concat (make-string (length title) underline-char)
"\n"))) "\n")))
@ -401,10 +397,9 @@ a communication channel."
(cond (cond
;; Cannot create a headline. Fall-back to a list. ;; Cannot create a headline. Fall-back to a list.
((or (org-export-low-level-p headline info) ((or (org-export-low-level-p headline info)
(not (memq style '(atx mixed setext))) (not (memq style '(atx setext)))
(and (eq style 'atx) (> level 6)) (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 (let ((bullet
(if (not (org-export-numbered-headline-p headline info)) "-" (if (not (org-export-numbered-headline-p headline info)) "-"
(concat (number-to-string (concat (number-to-string
@ -457,7 +452,7 @@ a communication channel."
(org-list-parents-alist struct))))) (org-list-parents-alist struct)))))
".")))) "."))))
(concat bullet (concat bullet
(make-string (max 1 (- 4 (length bullet))) ? ) (make-string (- 4 (length bullet)) ? )
(pcase (org-element-property :checkbox item) (pcase (org-element-property :checkbox item)
(`on "[X] ") (`on "[X] ")
(`trans "[-] ") (`trans "[-] ")

View File

@ -34,6 +34,8 @@
(require 'org-macs) (require 'org-macs)
(require 'ox) (require 'ox)
(require 'table nil 'noerror) (require 'table nil 'noerror)
(require 'ox-mathml)
(require 'org-latex-preview)
(declare-function org-at-heading-p "org" (&optional _)) (declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok)) (declare-function org-back-to-heading "org" (&optional invisible-ok))
@ -119,6 +121,7 @@
(:odt-pixels-per-inch nil nil org-odt-pixels-per-inch) (:odt-pixels-per-inch nil nil org-odt-pixels-per-inch)
(:odt-table-styles nil nil org-odt-table-styles) (:odt-table-styles nil nil org-odt-table-styles)
(:odt-use-date-fields nil nil org-odt-use-date-fields) (: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. ;; Redefine regular option.
(:with-latex nil "tex" org-odt-with-latex) (:with-latex nil "tex" org-odt-with-latex)
;; Retrieve LaTeX header for fragments. ;; Retrieve LaTeX header for fragments.
@ -720,6 +723,15 @@ Any other symbol is a synonym for `mathjax'."
(const :tag "Use imagemagick to make images" imagemagick) (const :tag "Use imagemagick to make images" imagemagick)
(other :tag "Use MathJax to display math" mathjax))) (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 ;;;; Links
@ -2253,11 +2265,11 @@ SHORT-CAPTION are strings."
LINK is the link pointing to the inline image. INFO is a plist LINK is the link pointing to the inline image. INFO is a plist
used as a communication channel." used as a communication channel."
(cl-assert (org-element-type-p element 'link)) (cl-assert (org-element-type-p element 'link))
(cl-assert (equal "file" (org-element-property :type element))) (let* ((src (let* ((type (org-element-property :type element))
(let* ((src (let ((raw-path (org-element-property :path element))) (raw-path (org-element-property :path element)))
(cond ((file-name-absolute-p raw-path) (cond ((file-name-absolute-p raw-path)
(expand-file-name raw-path)) (expand-file-name raw-path))
(t raw-path)))) (t (concat type ":" raw-path)))))
(src-expanded (if (file-name-absolute-p src) src (src-expanded (if (file-name-absolute-p src) src
(expand-file-name src (file-name-directory (expand-file-name src (file-name-directory
(plist-get info :input-file))))) (plist-get info :input-file)))))
@ -2284,12 +2296,24 @@ used as a communication channel."
;; ;;
;; Handle `:width', `:height' and `:scale' properties. Read ;; Handle `:width', `:height' and `:scale' properties. Read
;; them as numbers since we need them for computations. ;; 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 (size (org-odt--image-size
src-expanded info src-expanded info width height
(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))) (let ((scale (plist-get attr-plist :scale)))
(and scale (read scale))) (and scale (read scale)))
nil ; embed-as nil ; embed-as
@ -2682,6 +2706,8 @@ INFO is a plist holding contextual information. See
(imagep (org-export-inline-image-p (imagep (org-export-inline-image-p
link (plist-get info :odt-inline-image-rules))) link (plist-get info :odt-inline-image-rules)))
(path (cond (path (cond
((member type '("http" "https" "ftp" "mailto"))
(concat type ":" raw-path))
((string= type "file") ((string= type "file")
(let ((path-uri (org-export-file-uri raw-path))) (let ((path-uri (org-export-file-uri raw-path)))
(if (string-prefix-p "file://" path-uri) (if (string-prefix-p "file://" path-uri)
@ -2691,10 +2717,9 @@ INFO is a plist holding contextual information. See
;; archive. The directory containing the odt file ;; archive. The directory containing the odt file
;; is "../". ;; is "../".
(concat "../" path-uri)))) (concat "../" path-uri))))
(t (concat type ":" raw-path)))) (t raw-path)))
;; Convert & to &amp; for correct XML representation ;; Convert & to &amp; for correct XML representation
(path (replace-regexp-in-string "&" "&amp;" path)) (path (replace-regexp-in-string "&" "&amp;" path)))
(raw-path (replace-regexp-in-string "&" "&amp;" raw-path)))
(cond (cond
;; Link type is handled by a special function. ;; Link type is handled by a special function.
((org-export-custom-protocol-maybe link desc 'odt info)) ((org-export-custom-protocol-maybe link desc 'odt info))
@ -2773,10 +2798,10 @@ INFO is a plist holding contextual information. See
;; Coderef: replace link with the reference name or the ;; Coderef: replace link with the reference name or the
;; equivalent line number. ;; equivalent line number.
((string= type "coderef") ((string= type "coderef")
(let* ((line-no (format "%d" (org-export-resolve-coderef raw-path info))) (let* ((line-no (format "%d" (org-export-resolve-coderef path info)))
(href (concat "coderef-" raw-path))) (href (concat "coderef-" path)))
(format (format
(org-export-get-coderef-format raw-path desc) (org-export-get-coderef-format path desc)
(format (format
"<text:bookmark-ref text:reference-format=\"number\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>" "<text:bookmark-ref text:reference-format=\"number\" text:ref-name=\"OrgXref.%s\">%s</text:bookmark-ref>"
href line-no)))) href line-no))))
@ -3730,84 +3755,101 @@ contextual information."
(let ((processing-type (plist-get info :with-latex)) (let ((processing-type (plist-get info :with-latex))
(count 0) (count 0)
(warning nil)) (warning nil))
;; Normalize processing-type to one of dvipng, mathml or verbatim. ;; MathML will be handled seperately.
(if (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 ;; If the desired converter is not available, force verbatim
;; processing. ;; processing.
(cl-case processing-type (cl-case processing-type
((t mathml) ((t mathml)
(if (and (fboundp 'org-format-latex-mathml-available-p) (setq warning "LaTeX to MathML converter not available. Falling back to verbatim."
(org-format-latex-mathml-available-p)) processing-type 'verbatim))
(setq processing-type 'mathml) ((dvipng imagemagick dvisvgm)
(setq warning "`org-odt-with-latex': LaTeX to MathML converter not available. Falling back to verbatim.") (let ((programs
(setq processing-type 'verbatim))) (thread-first processing-type
((dvipng imagemagick) (alist-get org-latex-preview-process-alist)
(unless (and (org-check-external-command "latex" "" t) (plist-get :programs))))
(org-check-external-command (unless (cl-every (lambda (p) (org-check-external-command p "" 'no-error)) programs)
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t)) (setq warning "LaTeX or image converter not available. Falling back to verbatim."
(setq warning "`org-odt-with-latex': LaTeX to PNG converter not available. Falling back to verbatim.") processing-type 'verbatim))))
(setq processing-type 'verbatim)))
(verbatim) ;; nothing to do
(otherwise (otherwise
(setq warning "`org-odt-with-latex': Unknown LaTeX option. Forcing verbatim.") (setq warning "Unknown LaTeX option. Forcing verbatim."
(setq processing-type 'verbatim))) processing-type 'verbatim)))
;; Display warning if the selected PROCESSING-TYPE is not ;; Display warning if the selected PROCESSING-TYPE is not
;; available, but there are fragments to be converted. ;; available, but there are fragments to be converted.
(when warning (when warning
(org-element-map tree '(latex-fragment latex-environment) (org-element-map tree '(latex-fragment latex-environment)
(lambda (_) (warn warning)) (lambda (_) (org-display-warning warning))
info 'first-match nil t)) info 'first-match nil t))
;; Store normalized value for later use. ;; Store normalized value for later use.
(when (plist-get info :with-latex) (when (plist-get info :with-latex)
(plist-put info :with-latex processing-type)) (plist-put info :with-latex processing-type))
(message "Formatting LaTeX using %s" processing-type) (message "Formatting LaTeX using %s" processing-type)
;; Convert `latex-fragment's and `latex-environment's. ;; Convert `latex-fragment's and `latex-environment's.
(when (memq processing-type '(mathml dvipng imagemagick)) (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) (org-element-map tree '(latex-fragment latex-environment)
(lambda (latex-*) (lambda (latex-*)
(cl-incf count) (when-let*
(let* ((latex-frag (org-element-property :value latex-*)) ((latex-preview-hash-table (plist-get info :odt-latex-preview-hash-table))
(input-file (plist-get info :input-file)) (latex-frag (org-element-property :value latex-*))
(cache-dir (file-name-directory input-file)) (path-info
(cache-subdir (concat (or (gethash latex-* latex-preview-hash-table)
(cl-case processing-type (prog1 nil (org-display-warning
((dvipng imagemagick) (format "Failed to generate preview image for element: %s" latex-frag)))))
org-preview-latex-image-directory) (source-file (car path-info))
(mathml "ltxmathml/")) (link (list 'link (list :type "file"
(file-name-sans-extension :path source-file
(file-name-nondirectory input-file)))) :format 'bracket
(display-msg :raw-link (format "file:%s" source-file)))))
(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 (let ((replacement
(cl-case (org-element-type latex-*) (cl-case (org-element-type latex-*)
;;LaTeX environment. Mimic a "standalone image ;;LaTeX environment. Mimic a "standalone image
@ -3816,7 +3858,7 @@ contextual information."
;; attributes, captions to the enclosing ;; attributes, captions to the enclosing
;; paragraph. ;; paragraph.
(latex-environment (latex-environment
(org-element-adopt (org-element-adopt-elements
(list 'paragraph (list 'paragraph
(list :style "OrgFormula" (list :style "OrgFormula"
:name :name
@ -3835,8 +3877,11 @@ contextual information."
replacement :post-blank replacement :post-blank
(org-element-property :post-blank latex-*)) (org-element-property :post-blank latex-*))
;; Replace now. ;; Replace now.
(org-element-set latex-* replacement))))) (org-element-set-element latex-* replacement)
info nil nil t))) ;; Also replace in the latex preview table
(puthash replacement (gethash latex-* latex-preview-hash-table)
latex-preview-hash-table))))
info))))
tree) tree)
@ -4163,7 +4208,7 @@ MathML source to kill ring depending on the value of
(save-buffer-coding-system 'utf-8)) (save-buffer-coding-system 'utf-8))
(set-buffer buffer) (set-buffer buffer)
(set-buffer-file-coding-system coding-system-for-write) (set-buffer-file-coding-system coding-system-for-write)
(let ((mathml (org-create-math-formula latex-frag))) (let ((mathml (org-mathml-convert-latex-cached latex-frag)))
(unless mathml (error "No Math formula created")) (unless mathml (error "No Math formula created"))
(insert mathml) (insert mathml)
;; Add MathML to kill ring, if needed. ;; Add MathML to kill ring, if needed.

View File

@ -56,9 +56,6 @@
"This will cache timestamps and titles for files in publishing projects. "This will cache timestamps and titles for files in publishing projects.
Blocks could hash sha1 values here.") 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 (defvar org-publish-after-publishing-hook nil
"Hook run each time a file is published. "Hook run each time a file is published.
Every function in this hook will be called with two arguments: Every function in this hook will be called with two arguments:
@ -794,7 +791,10 @@ Default for SITEMAP-FILENAME is `sitemap.org'."
(concat (file-name-directory b) (concat (file-name-directory b)
(org-publish-find-title b project)) (org-publish-find-title b project))
b))) b)))
(setq retval (org-string<= A B nil ignore-case)))) (setq retval
(if ignore-case
(not (string-lessp (upcase B) (upcase A)))
(not (string-lessp B A))))))
((or `anti-chronologically `chronologically) ((or `anti-chronologically `chronologically)
(let* ((adate (org-publish-find-date a project)) (let* ((adate (org-publish-find-date a project))
(bdate (org-publish-find-date b project))) (bdate (org-publish-find-date b project)))
@ -867,7 +867,7 @@ PROPERTY, i.e. \"behavior\" parameter from `org-export-options-alist'."
(org-no-properties (org-no-properties
(org-element-interpret-data parsed-title)) (org-element-interpret-data parsed-title))
(file-name-nondirectory (file-name-sans-extension file))))) (file-name-nondirectory (file-name-sans-extension file)))))
(org-publish-cache-set-file-property file :title title nil 'transient))))) (org-publish-cache-set-file-property file :title title)))))
(defun org-publish-find-date (file project) (defun org-publish-find-date (file project)
"Find the date of FILE in PROJECT. "Find the date of FILE in PROJECT.
@ -892,8 +892,7 @@ time in `current-time' format."
(org-time-string-to-time value)))))) (org-time-string-to-time value))))))
((file-exists-p file) ((file-exists-p file)
(file-attribute-modification-time (file-attributes 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) (defun org-publish-sitemap-default-entry (entry style project)
"Default format for site map ENTRY, as a string. "Default format for site map ENTRY, as a string.
@ -1049,8 +1048,7 @@ its CDR is a string."
(replace-regexp-in-string (replace-regexp-in-string
"\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
(org-element-property :raw-value parent))))))))) (org-element-property :raw-value parent)))))))))
info)) info))))
nil 'transient))
;; Return output unchanged. ;; Return output unchanged.
output) output)
@ -1253,9 +1251,6 @@ If FREE-CACHE, empty the cache."
(error "Org publish timestamp: %s is not a directory" (error "Org publish timestamp: %s is not a directory"
org-publish-timestamp-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 (unless (and org-publish-cache
(string= (org-publish-cache-get ":project:") project-name)) (string= (org-publish-cache-get ":project:") project-name))
(let* ((cache-file (let* ((cache-file
@ -1279,8 +1274,6 @@ If FREE-CACHE, empty the cache."
(message "%s" "Resetting org-publish-cache") (message "%s" "Resetting org-publish-cache")
(when (hash-table-p org-publish-cache) (when (hash-table-p org-publish-cache)
(clrhash 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)) (setq org-publish-cache nil))
(defun org-publish-cache-file-needs-publishing (defun org-publish-cache-file-needs-publishing
@ -1326,22 +1319,16 @@ the file including them will be republished as well."
included-files-mtime)))))) included-files-mtime))))))
(defun org-publish-cache-set-file-property (defun org-publish-cache-set-file-property
(filename property value &optional project-name transient) (filename property value &optional project-name)
"Set the VALUE for a PROPERTY of file FILENAME in publishing cache to VALUE. "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 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: ;; Evtl. load the requested cache file:
(when project-name (org-publish-initialize-cache project-name)) (when project-name (org-publish-initialize-cache project-name))
(if transient
(puthash (cons filename property) value
org-publish-transient-cache)
(let ((pl (org-publish-cache-get filename))) (let ((pl (org-publish-cache-get filename)))
(if pl (progn (plist-put pl property value) value) (if pl (progn (plist-put pl property value) value)
(org-publish-cache-get-file-property (org-publish-cache-get-file-property
filename property value nil project-name))))) filename property value nil project-name))))
(defun org-publish-cache-get-file-property (defun org-publish-cache-get-file-property
(filename property &optional default no-create project-name) (filename property &optional default no-create project-name)
@ -1350,14 +1337,13 @@ Use cache file of PROJECT-NAME. Return the value of that PROPERTY,
or DEFAULT, if the value does not yet exist. Create the entry, or DEFAULT, if the value does not yet exist. Create the entry,
if necessary, unless NO-CREATE is non-nil." if necessary, unless NO-CREATE is non-nil."
(when project-name (org-publish-initialize-cache project-name)) (when project-name (org-publish-initialize-cache project-name))
(or (gethash (cons filename property) org-publish-transient-cache)
(let ((properties (org-publish-cache-get filename))) (let ((properties (org-publish-cache-get filename)))
(cond ((null properties) (cond ((null properties)
(unless no-create (unless no-create
(org-publish-cache-set filename (list property default))) (org-publish-cache-set filename (list property default)))
default) default)
((plist-member properties property) (plist-get properties property)) ((plist-member properties property) (plist-get properties property))
(t default))))) (t default))))
(defun org-publish-cache-get (key) (defun org-publish-cache-get (key)
"Return the value stored in `org-publish-cache' for key KEY. "Return the value stored in `org-publish-cache' for key KEY.

View File

@ -684,14 +684,31 @@ e.g. \"stat:nil\""
"Non-nil means interpret \"_\" and \"^\" for export. "Non-nil means interpret \"_\" and \"^\" for export.
If you want to control how Org displays those characters, see If you want to control how Org displays those characters, see
`org-use-sub-superscripts'. `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.
When this option is turned on, you can use TeX-like syntax for When this option is turned on, you can use TeX-like syntax for
sub- and superscripts and see them exported correctly. sub- and superscripts and see them exported correctly.
You can also set the option with #+OPTIONS: ^:t You can also set the option with #+OPTIONS: ^:t
See `org-use-sub-superscripts' docstring for more details." 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."
:group 'org-export-general :group 'org-export-general
:version "24.4" :version "24.4"
:package-version '(Org . "8.0") :package-version '(Org . "8.0")
@ -1043,7 +1060,7 @@ mode."
(cl-defstruct (org-export-backend (:constructor org-export-create-backend) (cl-defstruct (org-export-backend (:constructor org-export-create-backend)
(:copier nil)) (:copier nil))
name parent transcoders options filters blocks menu) name parent transcoders options filters blocks menu feature-conditions feature-implementations)
;;;###autoload ;;;###autoload
(defun org-export-get-backend (name) (defun org-export-get-backend (name)
@ -1149,6 +1166,62 @@ returns filters inherited from parent backends, if any."
(setq filters (append filters (org-export-backend-filters backend)))) (setq filters (append filters (org-export-backend-filters backend))))
filters))) filters)))
(defvar org-export-conditional-features)
(defun org-export-get-all-feature-conditions (backend)
"Return full feature condition alist for BACKEND.
BACKEND is an export back-end, as return by, e.g,,
`org-export-create-backend'. Return value is an alist where keys
are feature conditions, and values are feature symbols.
Unlike `org-export-backend-feature-conditions', this function
also returns conditions inherited from parent back-ends, if any."
(when (symbolp backend) (setq backend (org-export-get-backend backend)))
(and backend
(let ((conditions (org-export-backend-feature-conditions backend))
parent)
(while (setq parent (org-export-backend-parent backend))
(setq backend (org-export-get-backend parent))
(dolist (condition (org-export-backend-feature-conditions backend))
(push condition conditions)))
(dolist (condition org-export-conditional-features)
(unless (assq (car condition) conditions)
(push condition conditions)))
conditions)))
(defun org-export-get-all-feature-implementations (backend)
"Return full feature implementation alist for BACKEND.
BACKEND is an export back-end, as return by, e.g,,
`org-export-create-backend'. Return value is an alist where keys
are feature symbols, and values are an implementation
specification plist.
Unlike `org-export-backend-feature-implementations', this function
also returns implementations inherited from parent back-ends, if any."
(when (symbolp backend) (setq backend (org-export-get-backend backend)))
(and backend
(let ((implementations (org-export-backend-feature-implementations backend))
parent)
(while (setq parent (org-export-backend-parent backend))
(setq backend (org-export-get-backend parent))
(dolist (implementation (org-export-backend-feature-implementations backend))
(unless (assq (car implementation) implementations)
(push implementation implementations))))
implementations)))
(defun org-export-install-features (info)
"Install feature conditions and implementations in the communication channel.
INFO is a plist containing the current communication channel.
Return the updated communication channel."
(plist-put info :feature-conditions
(org-export-get-all-feature-conditions
(plist-get info :back-end)))
(plist-put info :feature-implementations
(org-export-get-all-feature-implementations
(plist-get info :back-end))))
(defun org-export-define-backend (backend transcoders &rest body) (defun org-export-define-backend (backend transcoders &rest body)
"Define a new backend BACKEND. "Define a new backend BACKEND.
@ -1260,20 +1333,24 @@ keywords are understood:
`org-export-options-alist' for more information about `org-export-options-alist' for more information about
structure of the values." structure of the values."
(declare (indent 1)) (declare (indent 1))
(let (filters menu-entry options) (let (filters menu-entry options feature-conditions feature-implementations)
(while (keywordp (car body)) (while (keywordp (car body))
(let ((keyword (pop body))) (let ((keyword (pop body)))
(pcase keyword (pcase keyword
(:filters-alist (setq filters (pop body))) (:filters-alist (setq filters (pop body)))
(:menu-entry (setq menu-entry (pop body))) (:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body))) (:options-alist (setq options (pop body)))
(:feature-conditions-alist (setq feature-conditions (pop body)))
(:feature-implementations-alist (setq feature-implementations (pop body)))
(_ (error "Unknown keyword: %s" keyword))))) (_ (error "Unknown keyword: %s" keyword)))))
(org-export-register-backend (org-export-register-backend
(org-export-create-backend :name backend (org-export-create-backend :name backend
:transcoders transcoders :transcoders transcoders
:options options :options options
:filters filters :filters filters
:menu menu-entry)))) :menu menu-entry
:feature-conditions feature-conditions
:feature-implementations feature-implementations))))
(defun org-export-define-derived-backend (child parent &rest body) (defun org-export-define-derived-backend (child parent &rest body)
"Create a new backend as a variant of an existing one. "Create a new backend as a variant of an existing one.
@ -1320,7 +1397,7 @@ The backend could then be called with, for example:
(org-export-to-buffer \\='my-latex \"*Test my-latex*\")" (org-export-to-buffer \\='my-latex \"*Test my-latex*\")"
(declare (indent 2)) (declare (indent 2))
(let (filters menu-entry options transcoders) (let (filters menu-entry options transcoders feature-conditions feature-implementations)
(while (keywordp (car body)) (while (keywordp (car body))
(let ((keyword (pop body))) (let ((keyword (pop body)))
(pcase keyword (pcase keyword
@ -1328,6 +1405,8 @@ The backend could then be called with, for example:
(:menu-entry (setq menu-entry (pop body))) (:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body))) (:options-alist (setq options (pop body)))
(:translate-alist (setq transcoders (pop body))) (:translate-alist (setq transcoders (pop body)))
(:feature-conditions-alist (setq feature-conditions (pop body)))
(:feature-implementations-alist (setq feature-implementations (pop body)))
(_ (error "Unknown keyword: %s" keyword))))) (_ (error "Unknown keyword: %s" keyword)))))
(org-export-register-backend (org-export-register-backend
(org-export-create-backend :name child (org-export-create-backend :name child
@ -1335,7 +1414,9 @@ The backend could then be called with, for example:
:transcoders transcoders :transcoders transcoders
:options options :options options
:filters filters :filters filters
:menu menu-entry)))) :menu menu-entry
:feature-conditions feature-conditions
:feature-implementations feature-implementations))))
@ -1900,7 +1981,7 @@ Return a string."
(progn ,@body) (progn ,@body)
(org-link-broken (org-link-broken
(pcase (plist-get info :with-broken-links) (pcase (plist-get info :with-broken-links)
(`nil (user-error "Org export aborted. Unable to resolve link: %S\nSee `org-export-with-broken-links'." (nth 1 err))) (`nil (user-error "Unable to resolve link: %S" (nth 1 err)))
(`mark (org-export-data (`mark (org-export-data
(format "[BROKEN LINK: %s]" (nth 1 err)) info)) (format "[BROKEN LINK: %s]" (nth 1 err)) info))
(_ nil)))))) (_ nil))))))
@ -2038,6 +2119,550 @@ keywords before output."
(funcall (intern (format "org-element-%s-interpreter" type)) (funcall (intern (format "org-element-%s-interpreter" type))
blob contents)))) blob contents))))
;;; Conditional/Generated Features
;;
;; Many formats have some version of a preamble, whether it be HTML's
;; <head>...</head> or the content before LaTeX's \begin{document}.
;; Depending on the particular features in the Org document being
;; exported, different setup snippets will be needed. There's the
;; "everything and the kitchen sink" approach of adding absolutely
;; everything that might be needed, and the post-translation editing
;; with filters approach, but neither really solve this problem nicely.
;;
;; The conditional/generated preamble defines mechanisms of detecting
;; which "export features" are used in a document, handles
;; interactions between features, and provides/generates content to
;; support the features.
;;
;; Each export feature condition takes the form of a
;; (CONDITION . FEATURES) cons cell (see `org-export-detect-features'),
;; and each implementation takes the form of a (FEATURE . (:KEY VALUE ...))
;; associated plist (see `org-export-resolve-feature-implementations'
;; and `org-export-expand-feature-snippets').
;;
;; This functionality is applied during export as follows:
;; 1. The export feature conditions and implementations are installed
;; into the INFO plist with `org-export-install-features'.
;; This simply applies `org-export-get-all-feature-conditions' and
;; `org-export-get-all-feature-implementations', which merges the
;; backend's conditions/implementations with all of it's parents and
;; finally the global condition list
;; `org-export-conditional-features'.
;; 2. The "export features" used in a document are detected with
;; `org-export-detect-features'.
;; 3. The interaction between different feature implementations is
;; resolved with `org-export-resolve-feature-implementations',
;; producing an ordered list of implementations to be actually used
;; in an export.
;; 4. The feature implementation's snippets are transformed into strings
;; to be inserted with `org-export-expand-feature-snippets'.
(defcustom org-export-conditional-features
`(("^[ \t]*#\\+print_bibliography:" bibliography)
(,(lambda (info)
(org-element-map (plist-get info :parse-tree)
'link
(lambda (link)
(and (member (org-element-property :type link)
'("http" "https" "ftp" "file"))
(file-name-extension (org-element-property :path link))
(member (downcase (file-name-extension
(org-element-property :path link)))
image-file-name-extensions)))
info t))
image)
(,(lambda (info)
(org-element-map (plist-get info :parse-tree)
'table #'identity info t))
table)
(,(lambda (info)
(org-element-map (plist-get info :parse-tree)
'(src-block inline-src-block) #'identity info t))
code))
"Org feature tests and associated feature flags.
Alist where the car is a test for the presense of the feature,
and the CDR is either a single feature symbol or a list of
feature symbols.
See `org-export-detect-features' for how this is processed."
:group 'org-export-general
:type '(alist :key-type
(choice (regexp :tag "Feature test regexp")
(variable :tag "Feature variable")
(function :tag "Feature test function"))
:value-type
(repeat symbol :tag "Feature symbols")))
(defun org-export-detect-features (info)
"Detect features from `org-export-conditional-features' in INFO.
More specifically, for each (CONDITION . FEATURES) cons cell of
the :feature-conditions list in INFO, the CONDITION is evaluated
in two phases.
In phase one, CONDITION is transformed like so:
- If a variable symbol, the value is fetched
- If a function symbol, the function is called with INFO as the
sole argument
- If a string, passed on unmodified
In phase two, if the CONDITION result is a string, it is used as
a case-sensitive regexp search in the buffer. The regexp
matching is taken as confirmation of the existance of FEATURES.
Any other non-nil value indicates the existance of FEATURES.
A list of all detected feature symbols is returned.
This function should be run in the processed export Org buffer,
after includes have been expanded and commented trees removed."
(delete-dups
(cl-loop
for (condition . features) in (plist-get info :feature-conditions)
for matcher =
(cond
((stringp condition) condition)
((functionp condition) (funcall condition info))
((symbolp condition) (symbol-value condition))
(t (error "org-export: Feature condition %s (for %s) unable to be used"
condition features)))
for active-features =
(and (if (stringp matcher)
(save-excursion
(goto-char (point-min))
(re-search-forward matcher nil t))
matcher)
(copy-sequence features))
when active-features
nconc active-features)))
(define-error 'org-missing-feature-dependency
"A feature was asked for, but is not availible")
(define-error 'org-circular-feature-dependency
"There was a circular dependency between some features")
(defun org-export-resolve-feature-implementations (info &optional features implementations)
"Resolve the IMPLEMENTATIONS of FEATURES, of INFO.
FEATURES should be a list of all feature symbols to be resolved,
and defaults to (plist-get info :features). IMPLEMENTATIONS
should be an alist of feature symbols and specification plists,
and defaults to (plist-get info :feature-implementations).
The following keys of the each implementation plist are recognised:
- :snippet, which is either,
- A string, which should be included in the preamble verbatim.
- A variable, the value of which should be included in the preamble.
- A function, which is called with two arguments the export info,
and the list of feature flags. The returned value is included in
the preamble.
- :requires, a feature or list of features this feature will enable.
- :when, a feature or list of features which are required for this
feature to be active.
- :prevents, a feature or list of features that should be masked.
- :order, for when inclusion order matters. Feature implementations
with a lower order appear first. The default is 0.
- :after, a feature or list of features that must be preceding.
- :before, a feature or list of features that must be succeeding.
This function processes :requires, :when, and :prevents in turn,
sorting according by :order both before processing :requires and
after processing :prevents. The final implementation list is
returned."
(let* ((explicit-features (or features (plist-get info :features)))
(implementations (or implementations
(plist-get info :feature-implementations)))
(current-implementations
(sort (cl-loop for feat in explicit-features
collect (assq feat implementations))
(lambda (a b)
(< (or (plist-get (cdr a) :order) 0)
(or (plist-get (cdr b) :order) 0)))))
;; require-records serves to record /why/ a particular implementation
;; is used. It takes the form of an alist with feature symbols as the
;; keys, and a list of features that ask for that feature as values.
;; A t value is used to indicate the feature has been explicitly
;; required.
(require-records
(cl-loop for feat in explicit-features
collect (list feat t))))
;; * Process ~:requires~
;; Here we temporarily treat current-implementations as a queue of
;; unproceesed implementations, and for each implemention move
;; it to processed-implementations if not already present.
;; :requires are processed by being added to the current-implementations
;; stack as they are seen. Along the way require-records is built for
;; the sake of the subsequent :prevents processing.
(let ((impl-queue-last (last current-implementations))
processed-implementations impl)
(while current-implementations
(setq impl (pop current-implementations))
(unless (memq impl processed-implementations)
(push impl processed-implementations)
(dolist (req (org-ensure-list
(plist-get (cdar processed-implementations) :requires)))
(unless (assq req processed-implementations)
(let ((required-impl (assq req implementations)))
(unless required-impl
(signal 'org-missing-feature-dependency
(format "The feature `%s' was asked for but could not be found"
req)))
(setq impl-queue-last
(if current-implementations
(setcdr impl-queue-last (list required-impl))
(setq current-implementations (list required-impl))))
(push (car impl) (alist-get req require-records)))))))
(setq current-implementations
(nreverse (delq nil processed-implementations))))
;; * Process ~:when~
;; More specifically, remove features with unfulfilled :when conditions.
;; To correctly resolve all the various :when conditions,
;; do not make any assumptions about which features are active.
;; Initially only consider non-:when implementations to be
;; active, then run through the list of unconfirmed :when
;; implementations and check their conditions against the list
;; of confirmed features. Continue doing this until no more
;; features are confirmed.
(let ((processing t)
(confirmed-features
(cl-remove-if ; Count unimplemented features as present.
(lambda (feat) (assq feat current-implementations))
explicit-features))
conditional-implementations when)
;; Sort all features by the presense of :when.
(dolist (impl current-implementations)
(if (plist-get (cdr impl) :when)
(push impl conditional-implementations)
(push (car impl) confirmed-features)))
(while processing
(setq processing nil)
;; Check for implementations which have satisfied :when
;; contions.
(dolist (impl conditional-implementations)
(setq when (plist-get (cdr impl) :when))
(when (cond
((symbolp when)
(memq when confirmed-features))
((consp when)
(not (cl-set-difference when confirmed-features))))
(push (car impl) confirmed-features)
(setq conditional-implementations
(delq impl conditional-implementations)
processing t))))
;; Now all that remains is implementations with unsatisfiable
;; :when conditions.
(dolist (impl conditional-implementations)
(setq current-implementations
(delq impl current-implementations))))
;; * Process ~:prevents~
;; Go through every implementation and for prevented features
;; 1. Remove them from current-implementations
;; 2. Go through require-records and remove them from the cdrs.
;; By modifying require-records in this way, features that are
;; only present due to a now-prevented feature will have a
;; nil cdr. We can then (recursively) check for these features
;; with `rassq' and remove them.
;; Since we used a queue rather than a stack when processing
;; :requires, we know that second order requires (i.e. :requires
;; of :requires) will come after after first order requires.
;; This means that should a n-th order require be prevented by
;; (n-1)-th order require, it will be removed before being
;; processed, and hence handled correctly.
(let (feats-to-remove removed null-require)
(dolist (impl current-implementations)
(setq feats-to-remove (org-ensure-list (plist-get (cdr impl) :prevents)))
(while feats-to-remove
;; Remove each of feats-to-remove.
(dolist (feat feats-to-remove)
(unless (memq feat removed)
(push feat removed)
(setq current-implementations
(delq (assq feat current-implementations)
current-implementations))
(when (assq feat require-records)
(setq require-records
(delq (assq feat require-records) require-records)))))
(dolist (rec require-records)
(setcdr rec (cl-set-difference (cdr rec) feats-to-remove)))
;; The features have now been removed.
(setq feats-to-remove nil)
;; Look for orphan requires.
(when (setq null-require (rassq nil require-records))
(push (car null-require) feats-to-remove)))))
;; Re-sort by ~:order~, to position reqirued features correctly.
(setq current-implementations
(sort current-implementations
(lambda (a b)
(< (or (plist-get (cdr a) :order) 0)
(or (plist-get (cdr b) :order) 0)))))
;; * Processing ~:before~ and ~:after~
;; To resolve dependency order, we will now perform a stable topological
;; sort on any DAGs that exist within current-implementations.
(org-export--feature-implementation-toposort
current-implementations)))
(defun org-export--feature-implementation-toposort (implementations)
"Perform a stable topological sort of IMPLEMENTATIONS.
The sort is performed based on the :before and :after properties.
See <https://en.wikipedia.org/wiki/Topological_sorting> for more information
on what this entails."
(let ((feature-indicies
(cl-loop
for elt in implementations
and index from 0
collect (cons (car elt) index)))
resolved-implementations
adj-list node-stack)
;; Build an adjacency list from :before and :after.
(dolist (impl implementations)
(push (list (car impl)) adj-list))
(dolist (impl implementations)
(let ((before (org-ensure-list (plist-get (cdr impl) :before)))
(after (org-ensure-list (plist-get (cdr impl) :after))))
(dolist (child before)
(push (car impl) (cdr (assq child adj-list))))
(when after
(setcdr (assq (car impl) adj-list)
(nconc (cdr (assq (car impl) adj-list))
after)))))
;; Initialise the node stack with the first implementation.
(setq node-stack (list (car implementations))
;; Make the order of adj-list match implementations.
adj-list
(mapcar
(lambda (entry)
(cons (car entry)
;; Sort edges according to feature order, to do
;; the DFS in order and make the result stable.
(sort (cdr entry)
(lambda (a b)
(< (or (alist-get a feature-indicies)
most-positive-fixnum)
(or (alist-get b feature-indicies)
most-positive-fixnum))))))
(nreverse adj-list)))
(while adj-list
(let ((deps (alist-get (caar node-stack) adj-list))
new-dep-found)
;; Look for any unresolved dependencies.
(while (and deps (not new-dep-found))
(if (not (assq (car deps) adj-list))
(setq deps (cdr deps))
;; Check the unresolved dependency is not part of a cycle.
(when (assq (car deps) node-stack)
(signal 'org-circular-feature-dependency
(format "Found a cycle in the feature dependency graph: %S"
(cons (car deps)
(nreverse (memq (car deps)
(nreverse
(mapcar #'car node-stack))))))))
;; Push the unresolved dependency to the top of the stack.
(push (assq (car deps) implementations)
node-stack)
(setq new-dep-found t)))
(unless new-dep-found
;; The top item of the stack has no unresolved dependencies.
;; Move it to the resolved list, and remove its entry from
;; adj-list to both mark it as such and ensure that
;; node-stack will not be incremented to it when/if the
;; stack is emptied.
(push (car node-stack) resolved-implementations)
(setq adj-list
(delq (assq (caar node-stack) adj-list) adj-list)
node-stack
(or (cdr node-stack)
(list (assq (caar adj-list) implementations)))))))
(nreverse resolved-implementations)))
(defun org-export-expand-feature-snippets (info &rest feature-implementations)
"Expand each of the feature :snippet keys in FEATURE-IMPLEMENTATIONS.
FEATURE-IMPLEMENTATIONS is expected to be a list of implementation
plists, if not provided explicitly it is extracted from the
:feature-implementations key of INFO. Note that an explicitly
provided nil FEATURE-IMPLEMENTATIONS is interpreted as no features.
Each implementation plist's :snippet value is expanded in order, in
the following manner:
- nil values are ignored
- functions are called with INFO, and must produce a string or nil
- variable symbols use the value, which must be a string or nil
- strings are included verbatim
- all other values throw an `error'.
\(fn INFO &optional FEATURE-IMPLEMENTATIONS)"
(let ((feat-impls
(cond
((not feature-implementations)
(plist-get info :feature-implementations))
((= (length feature-implementations) 1)
(car feature-implementations))
(t (signal 'wrong-number-of-arguments
`(org-export-expand-feature-snippets
,(1+ (length feature-implementations)))))))
expanded-snippets snippet value)
(dolist (impl feat-impls)
(setq snippet (plist-get (cdr impl) :snippet)
value (cond
((null snippet) nil)
((functionp snippet) (funcall snippet info))
((symbolp snippet) (symbol-value snippet))
((stringp snippet) snippet)
(t (error "org-export: The %s feature snippet %S is invalid (must be either nil, a function/variable symbol, or a string)"
(car impl) snippet))))
(cond
((stringp value)
(push value expanded-snippets))
(value ; Non-string value, could come from function or variable.
(error "org-export: The %s feature snippet %s must give nil or a string, but instead gave %S"
(car impl)
(cond
((and (functionp snippet) (symbolp snippet))
(format "function (`%s')" snippet))
((functionp snippet) "anonymous function")
(t (format "variable (`%s')" snippet)))
value))))
(nreverse expanded-snippets)))
(defun org-export-process-features (info)
"Install feature conditions/implementations in INFO, and resolve them.
See `org-export-detect-features' and `org-export-resolve-feature-implementations' for
more information on what this entails."
(org-export-install-features info)
(let* ((exp-features (org-export-detect-features info))
(resolved-implementations
(org-export-resolve-feature-implementations info exp-features)))
(plist-put info :feature-implementations resolved-implementations)
(plist-put info :features (mapcar #'car resolved-implementations))))
;;;###autoload
(defmacro org-export-update-features (backend &rest feature-property-value-lists)
"For BACKEND's export spec, set each FEATURE's :PROPERTY to VALUE.
The behaviour of this macro is best behaved with an example.
For instance, to add some preamble content from the variable
\"my-org-beamer-metropolis-tweaks\" when using the metropolis theme
with beamer export:
(org-export-update-features \\='beamer
(beamer-metropolis
:condition (string-match-p \"metropolis$\" (plist-get info :beamer-theme))
:snippet my-org-beamer-metropolis-tweaks
:order 3))
The modifies the beamer backend, either creating or updating the
\"beamer-metropolis\" feature. The :condition property adds a
condition which detects the feature, and all other properties are
applied to the feature's implementation plist. Setting
:condition to t means the feature will always be enabled, and
conversely setting :condition to nil means the feature will never
be enabled.
When setting the :condition and :snippet properties, any sexp is
is implicitly converted to,
(lambda (info) SEXPR)
Each (FEATURE . (:PROPERTY VALUE)) form that is processed is
taken from the single &rest argument
FEATURE-PROPERTY-VALUE-LISTS.
\(fn BACKEND &rest (FEATURE . (:PROPERTY VALUE)...)...)"
(declare (indent 1))
(org-with-gensyms (backend-struct the-entry the-condition the-feat-impl cond-feat)
(let ((backend-expr
(if (and (eq (car-safe backend) 'quote)
(symbolp (cadr backend))
(not (cddr backend)))
`(org-export-get-backend ',(cadr backend))
`(if (symbolp ,backend)
(org-export-get-backend ,backend)
backend)))
(backend-impls
(list 'aref backend-struct
(cl-struct-slot-offset 'org-export-backend 'feature-implementations)))
(backend-conds
(list 'aref backend-struct
(cl-struct-slot-offset 'org-export-backend 'feature-conditions)))
body condition-set-p implementation-set-p)
(dolist (feature-property-value-set feature-property-value-lists)
(when (eq (car feature-property-value-set) 'quote)
(pop feature-property-value-set))
(let ((features (car feature-property-value-set))
(property-value-pairs (cdr feature-property-value-set))
let-body property value)
(while property-value-pairs
(setq property (pop property-value-pairs)
value (pop property-value-pairs))
(cond
((consp value)
(unless (memq (car value) '(function quote))
(if (and (memq property '(:condition :snippet))
(not (functionp value)))
(setq value `(lambda (info) ,value))
(setq value (list 'quote value)))))
((memq value '(nil t))) ; Leave unmodified.
((symbolp value)
(setq value (list 'quote value))))
(if (eq property :condition)
(progn
(unless condition-set-p
(setq condition-set-p t))
(push
(if value
(let ((the-features (org-ensure-list features)))
`(let* ((,the-condition ,value)
(,the-entry (assoc ,the-condition ,backend-conds)))
(if ,the-entry
(setcdr ,the-entry
(append ',the-features (cdr ,the-entry)))
(push (cons ,the-condition ',the-features)
,backend-conds))))
(let ((single-feature
(if (consp features)
(intern (string-join (mapcar #'symbol-name features)
"-and-"))
features)))
`(dolist (,cond-feat ,backend-conds)
(cond
((equal (cdr ,cond-feat) (list ',single-feature))
(setf ,backend-conds (delq ,cond-feat ,backend-conds)))
((memq ',single-feature (cdr ,cond-feat))
(setcdr ,cond-feat
(delq ',single-feature (cdr ,cond-feat))))))))
body))
(unless implementation-set-p
(setq implementation-set-p t))
(push
(if let-body
`(plist-put (cdr ,the-feat-impl) ,property ,value)
`(setcdr ,the-feat-impl
(plist-put (cdr ,the-feat-impl) ,property ,value)))
let-body)))
(when let-body
(let ((the-feature
(if (consp features)
(intern (string-join (mapcar #'symbol-name features)
"-and-"))
features)))
(when (consp features)
(push
`(plist-put (cdr ,the-feat-impl) :when ',features)
let-body))
(push
`(let ((,the-feat-impl
(or (assoc ',the-feature ,backend-impls)
(car (push (list ',the-feature ,property nil)
,backend-impls)))))
,@(nreverse let-body))
body)))))
`(let ((,backend-struct ,backend-expr))
,@(and (not (org-export-backend-p backend-expr))
`((unless (org-export-backend-p ,backend-struct)
(error "`%s' is not a loaded export backend" ,backend))))
,@(nreverse body)
nil))))
;;; The Filter System ;;; The Filter System
@ -3112,6 +3737,8 @@ still inferior to file-local settings."
(when (plist-get info :with-cite-processors) (when (plist-get info :with-cite-processors)
(org-cite-process-citations info) (org-cite-process-citations info)
(org-cite-process-bibliography info)) (org-cite-process-bibliography info))
;; Install all the feature conditions and implementations.
(org-export-process-features info)
info)) info))
;;;###autoload ;;;###autoload

View File

@ -0,0 +1,30 @@
#+latex_header: \usepackage{amsmath}
#+latex_header: \usepackage{amssymb}
* Inline fragments and LaTeX environments
:PROPERTIES:
:ID: 0b3807b3-69af-40cb-a27a-b380d54879cc
:END:
The LQR problem for a time-periodic system of the form
\begin{align}
\dot{x} = A(t) x + B(t) u, \quad t \in [0, \infty), \quad x(0) = x_i \label{eq:time-varying-system}\\
A(t+T) = A(t),\ B(t + T) = B(t) \nonumber
\end{align}
is as follows. With a quadratic form defined on \( (x,u) \) pairs
\begin{align}
\label{eq:quadratic-form}
\mathbf{q}(x, u) := \lim_{t_f \to \infty} \int_{0}^{t_f} \begin{bmatrix} x \\ u \end{bmatrix}^{\star} \begin{bmatrix}
Q & 0 \\
0 & r
\end{bmatrix} \begin{bmatrix} x \\ u \end{bmatrix} =: \lim_{t_f \to \infty} \int_{0}^{t_f} q(x,u) dt
\end{align}
with \( q \ge 0 \) and \( r \ge 0 \), find the infimum of the quadratic form \( \mathbf{q} \) subject to the dynamics:
\[
\inf_{x,u} \mathbf{q}(x,u).
\]
\begin{align}
\label{eq:lqr-inf-via-duality}
\inf_{x, u} \mathbf{q}(x, u) = x_i^{\star} \bar{\lambda}(0) x_i,
\end{align}
where \( \bar{\lambda} \) is the maximal solution of the differential linear matrix inequality over \( [0, t] \).

View File

@ -39,14 +39,6 @@
#+END_SRC" #+END_SRC"
(should (equal "27" (org-babel-execute-src-block))))) (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 () (ert-deftest ob-calc/simple-program-symbolic ()
"Test of simple symbolic algebra." "Test of simple symbolic algebra."
(org-test-with-temp-text "\ (org-test-with-temp-text "\
@ -67,14 +59,7 @@
inv(a) inv(a)
#+END_SRC " #+END_SRC "
(should (equal "[[-1, 0.625, -0.125], [0.25, -0.5, 0.25], [0.5, 0.125, -0.125]]" (should (equal "[[-1, 0.625, -0.125], [0.25, -0.5, 0.25], [0.5, 0.125, -0.125]]"
(let ((calc-float-format '(float 0))) (org-babel-execute-src-block)))))
;; ;; 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 () (ert-deftest ob-calc/matrix-algebra ()
"Test of simple matrix algebra." "Test of simple matrix algebra."

View File

@ -205,14 +205,14 @@ Here is one at the end of a line. {{{results(=2=)}}}
(ert-deftest ob-exp/exports-inline-code () (ert-deftest ob-exp/exports-inline-code ()
(should (should
(equal "src_emacs-lisp[ :exports code]{(+ 1 1)}" (equal "src_emacs-lisp[]{(+ 1 1)}"
(org-test-with-temp-text "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=") (let ((org-babel-inline-result-wrap "=%s=")
(org-export-use-babel t)) (org-export-use-babel t))
(org-babel-exp-process-buffer)) (org-babel-exp-process-buffer))
(buffer-string)))) (buffer-string))))
(should (should
(equal "src_emacs-lisp[ :exports code]{(+ 1 1)}" (equal "src_emacs-lisp[]{(+ 1 1)}"
(org-test-with-temp-text "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=") (let ((org-babel-inline-result-wrap "=%s=")
(org-export-use-babel t)) (org-export-use-babel t))
@ -220,14 +220,14 @@ Here is one at the end of a line. {{{results(=2=)}}}
(buffer-string)))) (buffer-string))))
;; Do not escape characters in inline source blocks. ;; Do not escape characters in inline source blocks.
(should (should
(equal "src_c[ :exports code]{*a}" (equal "src_c[]{*a}"
(org-test-with-temp-text "src_c[ :exports code ]{*a}" (org-test-with-temp-text "src_c[ :exports code ]{*a}"
(let ((org-babel-inline-result-wrap "=%s=") (let ((org-babel-inline-result-wrap "=%s=")
(org-export-use-babel t)) (org-export-use-babel t))
(org-babel-exp-process-buffer)) (org-babel-exp-process-buffer))
(buffer-string)))) (buffer-string))))
(should (should
(equal "src_emacs-lisp[ :exports both]{(+ 1 1)} {{{results(=2=)}}}" (equal "src_emacs-lisp[]{(+ 1 1)} {{{results(=2=)}}}"
(org-test-with-temp-text "src_emacs-lisp[:exports both]{(+ 1 1)}" (org-test-with-temp-text "src_emacs-lisp[:exports both]{(+ 1 1)}"
(let ((org-babel-inline-result-wrap "=%s=") (let ((org-babel-inline-result-wrap "=%s=")
(org-export-use-babel t)) (org-export-use-babel t))
@ -262,10 +262,10 @@ Here is one at the end of a line. {{{results(=2=)}}}
(string-match (string-match
(replace-regexp-in-string (replace-regexp-in-string
"\\\\\\[]{" "\\(?:\\[]\\)?{" ;accept both src_sh[]{...} or src_sh{...} "\\\\\\[]{" "\\(?:\\[]\\)?{" ;accept both src_sh[]{...} or src_sh{...}
(regexp-quote "Here is one in the middle src_sh[ :exports code]{echo 1} of a line. (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[ :exports code]{echo 2} Here is one at the end of a line. src_sh[]{echo 2}
src_sh[ :exports code]{echo 3} Here is one at the beginning of a line. src_sh[]{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=)}}}") Here is one that is also evaluated: src_sh[]{echo 4} {{{results(=4=)}}}")
nil t) nil t)
(org-test-at-id "cd54fc88-1b6b-45b6-8511-4d8fa7fc8076" (org-test-at-id "cd54fc88-1b6b-45b6-8511-4d8fa7fc8076"
(org-narrow-to-subtree) (org-narrow-to-subtree)
@ -301,7 +301,7 @@ be evaluated."
(ert-deftest ob-exp/exports-inline-code-double-eval-exports-both () (ert-deftest ob-exp/exports-inline-code-double-eval-exports-both ()
(let ((org-export-use-babel t)) (let ((org-export-use-babel t))
(should (should
(string-match (concat "\\`src_emacs-lisp\\(?:\\[.+?]\\)?{(\\+ 1 1)} " (string-match (concat "\\`src_emacs-lisp\\(?:\\[]\\)?{(\\+ 1 1)} "
"{{{results(src_emacs-lisp\\[ :exports code\\]{2})}}}$") "{{{results(src_emacs-lisp\\[ :exports code\\]{2})}}}$")
(org-test-with-temp-text (org-test-with-temp-text
(concat "src_emacs-lisp[:exports both :results code " (concat "src_emacs-lisp[:exports both :results code "
@ -403,7 +403,7 @@ be evaluated."
: 2 : 2
#+NAME: src1 #+NAME: src1
#+begin_src emacs-lisp :exports both #+begin_src emacs-lisp
\(+ 1 1) \(+ 1 1)
#+end_src" #+end_src"
(org-test-with-temp-text (org-test-with-temp-text

View File

@ -77,9 +77,9 @@ return x[1]
(ert-deftest test-ob-lua/colnames-yes-header-argument-pp () (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 (should
(equal "a = 12\nb = 13" (equal "a = 12\nb = 13\n"
(org-test-with-temp-text (org-test-with-temp-text
"#+name: eg "#+name: eg
| col | val | | col | val |
@ -99,7 +99,7 @@ return x
(ert-deftest test-ob-lua/colnames-nil-header-argument () (ert-deftest test-ob-lua/colnames-nil-header-argument ()
"Test table with `colnames' set to `nil'." "Test table with `colnames' set to `nil'."
(should (should
(equal "1 = a\n2 = b" (equal "1 = a\n2 = b\n"
(org-test-with-temp-text (org-test-with-temp-text
"#+name: eg "#+name: eg
| col | | col |
@ -119,7 +119,7 @@ return x
(ert-deftest test-ob-lua/colnames-no-header-argument () (ert-deftest test-ob-lua/colnames-no-header-argument ()
"Test table passing without `colnames'." "Test table passing without `colnames'."
(should (should
(equal "1 = col\n2 = a\n3 = b" (equal "1 = col\n2 = a\n3 = b\n"
(org-test-with-temp-text (org-test-with-temp-text
"#+name: eg "#+name: eg
| col | | col |
@ -136,46 +136,6 @@ return x
(org-babel-next-src-block) (org-babel-next-src-block)
(org-babel-execute-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) (provide 'test-ob-lua)
;;; test-ob-lua.el ends here ;;; test-ob-lua.el ends here

View File

@ -183,7 +183,6 @@ that will return all elements of the array as a single string."
echo ${array} echo ${array}
<point> <point>
#+end_src" #+end_src"
(skip-unless (executable-find "bash"))
(should (equal "one" (org-trim (org-babel-execute-src-block)))))) (should (equal "one" (org-trim (org-babel-execute-src-block))))))
(ert-deftest test-ob-shell/generic-uses-no-assoc-arrays-simple-map () (ert-deftest test-ob-shell/generic-uses-no-assoc-arrays-simple-map ()
@ -230,10 +229,6 @@ echo ${table}
Bash will see a table that contains the first column as the Bash will see a table that contains the first column as the
'index' of the associative array, and the second column as the 'index' of the associative array, and the second column as the
value. " 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 (org-test-with-temp-text
"#+NAME: sample_mapping_table "#+NAME: sample_mapping_table
| first | one | | first | one |
@ -253,10 +248,6 @@ echo ${table[second]}
Bash will see an associative array that contains each row as a single Bash will see an associative array that contains each row as a single
string. Bash cannot handle lists in associative arrays." 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 (org-test-with-temp-text
"#+NAME: sample_big_table "#+NAME: sample_big_table
| bread | 2 | kg | | bread | 2 | kg |

View File

@ -191,8 +191,8 @@ echo 1
* Main * Main
#+header: :tangle \"test-ob-tangle.el\" :comments noweb :noweb yes #+header: :tangle \"test-ob-tangle.el\" :comments noweb :noweb yes
#+begin_src emacs-lisp #+begin_src emacs-lisp
'(1 1
<<inner>>) <<inner>>
#+end_src" #+end_src"
(unwind-protect (unwind-protect
(let ((org-babel-tangle-use-relative-file-links t)) (let ((org-babel-tangle-use-relative-file-links t))
@ -203,7 +203,7 @@ echo 1
(goto-char (point-min)) (goto-char (point-min))
(and (and
(search-forward (concat ";; [[file:" (file-name-nondirectory file) "::inner") nil t) (search-forward (concat ";; [[file:" (file-name-nondirectory file) "::inner") nil t)
(search-forward ";; inner ends here\n" nil t)))) (search-forward ";; inner ends here" nil t))))
(delete-file "test-ob-tangle.el"))))) (delete-file "test-ob-tangle.el")))))
(ert-deftest ob-tangle/comment-noweb-absolute () (ert-deftest ob-tangle/comment-noweb-absolute ()

View File

@ -2545,70 +2545,6 @@ abc
(lambda (&rest _) (error "No warnings should occur")))) (lambda (&rest _) (error "No warnings should occur"))))
(org-babel-import-elisp-from-file (buffer-file-name)))))) (org-babel-import-elisp-from-file (buffer-file-name))))))
(ert-deftest test-ob/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 () (ert-deftest test-ob/demarcate-block-split-duplication ()
"Test duplication of language, body, switches, and headers in splitting." "Test duplication of language, body, switches, and headers in splitting."
(let ((caption "#+caption: caption.") (let ((caption "#+caption: caption.")

View File

@ -61,7 +61,6 @@ See https://github.com/yantar92/org/issues/4."
(org-test-with-temp-text "* Org link test (org-test-with-temp-text "* Org link test
[[https://example.com][A link to a site]]" [[https://example.com][A link to a site]]"
(dotimes (_ 2) (dotimes (_ 2)
(font-lock-ensure)
(goto-char 1) (goto-char 1)
(re-search-forward "\\[") (re-search-forward "\\[")
(should-not (org-xor org-link-descriptive (org-invisible-p))) (should-not (org-xor org-link-descriptive (org-invisible-p)))

View File

@ -728,28 +728,6 @@ Sunday 7 January 2024
(org-agenda-list nil nil 1) (org-agenda-list nil nil 1)
(should (search-forward "In " nil t)))))) (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 (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-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 (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 (search-forward "12:00-14:00" nil t)))))
;; agenda redo ;; agenda redo

View File

@ -1398,13 +1398,6 @@
;;; Dynamic block ;;; 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 () (ert-deftest test-org-colview/dblock ()
"Test the column view table." "Test the column view table."
(should (should
@ -1710,31 +1703,6 @@ SCHEDULED: <2020-05-11 Mon> DEADLINE: <2020-05-14 Thu>
(let ((org-columns-default-format (let ((org-columns-default-format
"%ITEM %DEADLINE(d) %SCHEDULED(s) %TIMESTAMP(t)")) "%ITEM %DEADLINE(d) %SCHEDULED(s) %TIMESTAMP(t)"))
(org-update-dblock)) (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)))))) (buffer-substring-no-properties (point) (point-max))))))
(provide 'test-org-colview) (provide 'test-org-colview)

View File

@ -1,192 +0,0 @@
;;; 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

View File

@ -3989,31 +3989,8 @@ DEADLINE: <2012-03-29 thu.> SCHEDULED: <2012-03-29 thu.> CLOSED: [2012-03-29 thu
(org-test-parse-and-interpret (org-test-parse-and-interpret
"<2012-03-29 thu. 16:40-16:41>"))) "<2012-03-29 thu. 16:40-16:41>")))
;; Diary. ;; Diary.
(should (equal (org-test-parse-and-interpret "<%%(diary-float t 4 2)>") (should (equal (org-test-parse-and-interpret "<%%diary-float t 4 2>")
"<%%(diary-float t 4 2)>\n")) "<%%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. ;; Timestamp with repeater interval, repeater deadline, with delay, with combinations.
(should (should
(string-match "<2012-03-29 .* \\+1y>" (string-match "<2012-03-29 .* \\+1y>"
@ -5006,18 +4983,6 @@ Text
;;; Test Cache. ;;; 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 () (ert-deftest test-org-element/cache ()
"Test basic expectations and common pitfalls for cache." "Test basic expectations and common pitfalls for cache."

View File

@ -0,0 +1,344 @@
;;; test-org-latex-preview.el --- tests for org-latex-preview.el -*- lexical-binding: t; -*-
;; Copyright (c) 2023 Karthik Chikmagalur
;; Authors: Karthik Chikmagalur
;; This file is not part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Code:
(unless (featurep 'org-latex-preview)
(signal 'missing-test-dependency "Support for LaTeX previews"))
(require 'org-latex-preview)
(require 'cl-lib)
(ert-deftest test-org-latex-preview/assert ()
(should t))
;; Test for executables required for preview generation
(org-test-for-executable "latex")
(org-test-for-executable "dvisvgm")
(org-test-for-executable "dvipng")
;; Should we test imagemagick?
;; (org-test-for-executable "convert")
;; fragment pre-processing tests
;;; Collect fragments
(ert-deftest test-org-latex-preview/collect-fragments-inline ()
"Test LaTeX fragment collection"
(let ((elements
'((latex-fragment
(:value "\\( q \\ge 0 \\)" :begin 770 :end 784 :post-blank 1 :parent
(paragraph
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-fragment
(:value "\\( r \\ge 0 \\)" :begin 788 :end 801 :post-blank 0 :parent
(paragraph
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments")))))))))))
(org-test-at-id
"0b3807b3-69af-40cb-a27a-b380d54879cc"
(should
(equal (org-latex-preview-collect-fragments 765 807) elements))
(should
(equal (org-latex-preview--construct-entries elements)
'(((770 783 "\\( q \\ge 0 \\)")
(788 801 "\\( r \\ge 0 \\)"))
nil)))
(should
(equal (org-latex-preview--construct-entries elements)
(org-latex-preview--construct-entries elements 'with-numbering))))))
(ert-deftest test-org-latex-preview/collect-fragments-environment ()
"Test LaTeX fragment collection"
(let ((elements
'((latex-environment
(:begin 241 :end 412 :value "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n" :post-blank 0 :post-affiliated 241 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments")))))))))
(org-test-at-id
"0b3807b3-69af-40cb-a27a-b380d54879cc"
(should
(equal (org-latex-preview-collect-fragments 186 426) elements))
(should
(equal (org-latex-preview--construct-entries elements)
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n"))
nil)))
(should
(equal (org-latex-preview--construct-entries elements 'with-numbering)
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n"))
(1)))))))
(ert-deftest test-org-latex-preview/collect-fragments-all ()
"Test LaTeX fragment collection"
(let ((elements
'((latex-environment
(:begin 241 :end 412 :value "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n" :post-blank 0 :post-affiliated 241 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))
(latex-fragment
(:value "\\( (x,u) \\)" :begin 460 :end 472 :post-blank 1 :parent
(paragraph
(:begin 412 :end 478 :contents-begin 412 :contents-end 478 :post-blank 0 :post-affiliated 412 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-environment
(:begin 478 :end 765 :value "\\begin{align}\n\\label{eq:quadratic-form}\n\\mathbf{q}(x, u) := \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} \\begin{bmatrix} x \\\\ u \\end{bmatrix}^{\\star} \\begin{bmatrix}\nQ & 0 \\\\\n0 & r\n \\end{bmatrix} \\begin{bmatrix} x \\\\ u \\end{bmatrix} =: \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} q(x,u) dt\n\\end{align}\n" :post-blank 0 :post-affiliated 478 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))
(latex-fragment
(:value "\\( q \\ge 0 \\)" :begin 770 :end 784 :post-blank 1 :parent
(paragraph
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-fragment
(:value "\\( r \\ge 0 \\)" :begin 788 :end 801 :post-blank 0 :parent
(paragraph
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-fragment
(:value "\\( \\mathbf{q} \\)" :begin 842 :end 859 :post-blank 1 :parent
(paragraph
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-fragment
(:value "\\[\n\\inf_{x,u} \\mathbf{q}(x,u).\n\\]" :begin 884 :end 917 :post-blank 0 :parent
(paragraph
(:begin 765 :end 918 :contents-begin 765 :contents-end 918 :post-blank 0 :post-affiliated 765 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-environment
(:begin 918 :end 1040 :value "\\begin{align}\n\\label{eq:lqr-inf-via-duality}\n\\inf_{x, u} \\mathbf{q}(x, u) = x_i^{\\star} \\bar{\\lambda}(0) x_i,\n\\end{align}\n" :post-blank 0 :post-affiliated 918 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))
(latex-fragment
(:value "\\( \\bar{\\lambda} \\)" :begin 1046 :end 1066 :post-blank 1 :parent
(paragraph
(:begin 1040 :end 1154 :contents-begin 1040 :contents-end 1154 :post-blank 0 :post-affiliated 1040 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments"))))))))
(latex-fragment
(:value "\\( [0, t] \\)" :begin 1140 :end 1152 :post-blank 0 :parent
(paragraph
(:begin 1040 :end 1154 :contents-begin 1040 :contents-end 1154 :post-blank 0 :post-affiliated 1040 :mode nil :granularity element :parent
(section
(:begin 118 :end 1154 :contents-begin 118 :contents-end 1154 :robust-begin 118 :robust-end 1152 :post-blank 0 :post-affiliated 118 :mode section :granularity element :parent
(headline
(:raw-value "Inline fragments and LaTeX environments" :begin 76 :end 1154 :pre-blank 0 :contents-begin 118 :contents-end 1154 :robust-begin 184 :robust-end 1152 :level 1 :priority nil :tags nil :todo-keyword nil :todo-type nil :post-blank 0 :footnote-section-p nil :archivedp nil :commentedp nil :post-affiliated 76 :ID "0b3807b3-69af-40cb-a27a-b380d54879cc" :title "Inline fragments and LaTeX environments")))))))))))
(org-test-at-id
"0b3807b3-69af-40cb-a27a-b380d54879cc"
(should
(equal (org-latex-preview-collect-fragments) elements))
(should (equal (org-latex-preview--construct-entries elements)
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n")
(460 471 "\\( (x,u) \\)")
(478 764 "\\begin{align}\n\\label{eq:quadratic-form}\n\\mathbf{q}(x, u) := \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} \\begin{bmatrix} x \\\\ u \\end{bmatrix}^{\\star} \\begin{bmatrix}\nQ & 0 \\\\\n0 & r\n \\end{bmatrix} \\begin{bmatrix} x \\\\ u \\end{bmatrix} =: \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} q(x,u) dt\n\\end{align}\n")
(770 783 "\\( q \\ge 0 \\)")
(788 801 "\\( r \\ge 0 \\)")
(842 858 "\\( \\mathbf{q} \\)")
(884 917 "\\[\n\\inf_{x,u} \\mathbf{q}(x,u).\n\\]")
(918 1039 "\\begin{align}\n\\label{eq:lqr-inf-via-duality}\n\\inf_{x, u} \\mathbf{q}(x, u) = x_i^{\\star} \\bar{\\lambda}(0) x_i,\n\\end{align}\n")
(1046 1065 "\\( \\bar{\\lambda} \\)")
(1140 1152 "\\( [0, t] \\)"))
nil)))
(should (equal (org-latex-preview--construct-entries elements 'with-numbering)
'(((241 411 "\\begin{align}\n\\dot{x} = A(t) x + B(t) u, \\quad t \\in [0, \\infty), \\quad x(0) = x_i \\label{eq:time-varying-system}\\\\\nA(t+T) = A(t),\\ B(t + T) = B(t) \\nonumber\n\\end{align}\n")
(460 471 "\\( (x,u) \\)")
(478 764 "\\begin{align}\n\\label{eq:quadratic-form}\n\\mathbf{q}(x, u) := \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} \\begin{bmatrix} x \\\\ u \\end{bmatrix}^{\\star} \\begin{bmatrix}\nQ & 0 \\\\\n0 & r\n \\end{bmatrix} \\begin{bmatrix} x \\\\ u \\end{bmatrix} =: \\lim_{t_f \\to \\infty} \\int_{0}^{t_f} q(x,u) dt\n\\end{align}\n")
(770 783 "\\( q \\ge 0 \\)")
(788 801 "\\( r \\ge 0 \\)")
(842 858 "\\( \\mathbf{q} \\)")
(884 917 "\\[\n\\inf_{x,u} \\mathbf{q}(x,u).\n\\]")
(918 1039 "\\begin{align}\n\\label{eq:lqr-inf-via-duality}\n\\inf_{x, u} \\mathbf{q}(x, u) = x_i^{\\star} \\bar{\\lambda}(0) x_i,\n\\end{align}\n")
(1046 1065 "\\( \\bar{\\lambda} \\)")
(1140 1152 "\\( [0, t] \\)"))
(1 nil 2 nil nil nil nil 4 nil nil)))))))
;;; Set up overlays
(ert-deftest test-org-latex-preview/ensure-overlay ()
(org-test-at-id "0b3807b3-69af-40cb-a27a-b380d54879cc"
(org-latex-preview-clear-overlays (point-min) (point-max))
(let* ((elements (org-latex-preview-collect-fragments))
(entries (car (org-latex-preview--construct-entries elements)))
(ov) (all-ovs))
(pcase-dolist(`(,beg ,end ,value) entries)
(setq ov (org-latex-preview--ensure-overlay beg end))
(push ov all-ovs)
(should (eq (get-char-property beg 'org-overlay-type) 'org-latex-overlay))
(should (eq (overlay-get ov 'org-overlay-type) 'org-latex-overlay))
(should (eq (overlay-get ov 'evaporate) t))
(should (eq (overlay-get ov 'priority) org-latex-preview--overlay-priority))
(should (equal (overlay-get ov 'modification-hooks)
(list #'org-latex-preview-auto--mark-overlay-modified)))
(should (equal (overlay-get ov 'insert-in-front-hooks)
(list #'org-latex-preview-auto--insert-front-handler)))
(should (equal (overlay-get ov 'insert-behind-hooks)
(list #'org-latex-preview-auto--insert-behind-handler))))
(should (eq (length all-ovs) (length elements))))
(org-latex-preview-clear-overlays (point-min) (point-max))))
;; Precompilation test
;; TODO
;; Process fragments
;;; No precompilation, no caching
(ert-deftest test-org-latex-preview/place-previews-1 ()
(org-test-at-id "0b3807b3-69af-40cb-a27a-b380d54879cc"
(let ((org-latex-preview-process-precompiled nil)
(org-latex-preview-cache 'temp)
(org-latex-preview-process-default 'dvisvgm))
(org-latex-preview-auto-mode -1)
(goto-char 255)
(let ((element (org-element-context)))
(should (eq (org-element-type element) 'latex-environment))
(org-latex-preview-clear-cache (org-element-property :begin element)
(org-element-property :end element))
(apply #'org-async-wait-for
(org-latex-preview--place-from-elements
org-latex-preview-process-default
(list element)))
(let ((ov (cl-some (lambda (o) (and (eq (overlay-get o 'org-overlay-type)
'org-latex-overlay)
o))
(overlays-at (point)))))
(should (overlayp ov))
(let ((display (overlay-get ov 'display))
(face (overlay-get ov 'face))
(hidden-face (overlay-get ov 'hidden-face))
(preview-image (overlay-get ov 'preview-image)))
;; Image properties
(should (consp display))
(should (eq (car display) 'image))
(should (eq (plist-get (cdr display) :type) 'svg))
(should (stringp (plist-get (cdr display) :file)))
(should (string-suffix-p ".svg" (plist-get (cdr display) :file)))
(should (eq preview-image display))
;; Face properties
(should (eq face 'default))
(should (eq hidden-face face))))))))
(ert-deftest test-org-latex-preview/place-previews-all ()
(org-test-at-id "0b3807b3-69af-40cb-a27a-b380d54879cc"
(let ((org-latex-preview-process-precompiled nil)
(org-latex-preview-cache 'temp)
(org-latex-preview-process-default 'dvisvgm))
(org-latex-preview-auto-mode -1)
(org-latex-preview-clear-cache (point-min) (point-max))
(let ((elements (org-latex-preview-collect-fragments)))
(apply #'org-async-wait-for
(org-latex-preview--place-from-elements
org-latex-preview-process-default
elements))
(dolist (element elements)
(let ((ov (cl-some (lambda (o) (and (eq (overlay-get o 'org-overlay-type)
'org-latex-overlay)
o))
(overlays-at (org-element-property :begin element)))))
(should (overlayp ov))
(let ((display (overlay-get ov 'display))
(face (overlay-get ov 'face))
(hidden-face (overlay-get ov 'hidden-face))
(preview-image (overlay-get ov 'preview-image)))
;; Image properties
(should (consp display))
(should (eq (car display) 'image))
(should (eq (plist-get (cdr display) :type) 'svg))
(should (stringp (plist-get (cdr display) :file)))
(should (string-suffix-p ".svg" (plist-get (cdr display) :file)))
(should (eq preview-image display))
;; Face properties
(should (eq face 'default))
(should (eq hidden-face face)))))
(org-latex-preview-clear-cache (point-min) (point-max))))))
;; TODO: Test with precompilation
;; TODO: Test with caching
;; dvisvgm filter+callback test
;; dvipng filter+callback test
;; imagemagick filter+callback test
;; TODO
;; Color extraction
;; TODO
;; Scaling
;; TODO
;; Caching
;; TODO
;;; clear-cache test
;; Numbering
;; TODO
;; org-latex-preview-auto-mode
;; TODO
;; live previews
;; TODO
;; lualatex-specific tests
;; xelatex-specific tests
(provide 'test-org-latex-preview)
;;; test-org-latex-preview.el ends here

View File

@ -22,21 +22,6 @@
;;; Macros ;;; 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 () (ert-deftest test-org/macro-replace-all ()
"Test `org-macro-replace-all' specifications." "Test `org-macro-replace-all' specifications."
;; Standard test. ;; Standard test.

View File

@ -938,7 +938,7 @@ See also URL `https://orgmode.org/worg/org-tutorials/org-lookups.html'."
;; For Lisp formula ;; For Lisp formula
(should (equal "\"0\"" (org-table-make-reference "0" nil nil t))) (should (equal "\"0\"" (org-table-make-reference "0" nil nil t)))
(should (equal "\"z\"" (org-table-make-reference "z" 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 "\"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 "\"z\" \"1\"" (org-table-make-reference '("z" "1") nil nil t)))
(should (equal "\"1\"" (org-table-make-reference '("" "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 ;; For Lisp formula
(should (equal "0" (org-table-make-reference "0" nil t t))) (should (equal "0" (org-table-make-reference "0" nil t t)))
(should (equal "0" (org-table-make-reference "z" nil t t))) (should (equal "0" (org-table-make-reference "z" nil t t)))
(should (equal "0" (org-table-make-reference "" nil t t))) (should (equal "" (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 '("0" "1") nil t t)))
(should (equal "0 1" (org-table-make-reference '("z" "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))) (should (equal "1" (org-table-make-reference '("" "1") nil t t)))

View File

@ -9356,18 +9356,6 @@ CLOSED: %s
(org-test-with-temp-text "* H1\n<point>Paragraph\n* H2" (org-test-with-temp-text "* H1\n<point>Paragraph\n* H2"
(org-paste-subtree nil "* Text") (org-paste-subtree nil "* Text")
(buffer-string)))) (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. ;; If point is between two headings, use the deepest level.
(should (should
(equal "* H1\n\n* Text\n* H2" (equal "* H1\n\n* Text\n* H2"
@ -9384,30 +9372,6 @@ CLOSED: %s
(org-test-with-temp-text "* H1\n<point>\n** H2" (org-test-with-temp-text "* H1\n<point>\n** H2"
(org-paste-subtree nil "* Text") (org-paste-subtree nil "* Text")
(buffer-string)))) (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 ;; When on an empty heading, after the stars, deduce the new level
;; from the number of stars. ;; from the number of stars.
(should (should

View File

@ -39,67 +39,5 @@
(goto-char (point-min)) (goto-char (point-min))
(should (search-forward "#### Footnotes")))))) (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) (provide 'test-ox-md)
;;; test-ox-md.el ends here ;;; test-ox-md.el ends here

View File

@ -2140,6 +2140,120 @@ Footnotes[fn:2], foot[fn:test] and [fn:inline:inline footnote]
(bold . (lambda (bold contents info) (concat contents "!"))))) (bold . (lambda (bold contents info) (concat contents "!")))))
'(:with-emphasize t))))) '(:with-emphasize t)))))
;;; Export features
(ert-deftest test-org-export/feature-resolution ()
"Test the behaviour of `org-export-resolve-feature-implementations'"
;; Check implementations for listed features are given.
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a) (b) (c)))
'((a) (b) (c))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a)))
'((a))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a) '((a) (b) (c)))
'((a))))
;; Check depencency resolution.
(should
(equal (org-export-resolve-feature-implementations
nil '(a) '((a :requires b) (b) (c)))
'((a :requires b) (b))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a) '((a :requires (b c)) (b) (c)))
'((a :requires (b c)) (b) (c))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a) '((a :requires b) (b :requires c) (c)))
'((a :requires b) (b :requires c) (c))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :prevents b) (b) (c)))
'((a :prevents b) (c))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :prevents (b c)) (b) (c)))
'((a :prevents (b c)))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c d) '((a :requires (b c)) (b) (c) (d :prevents b)))
'((a :requires (b c)) (c) (d :prevents b))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c d) '((a :requires (b c)) (b) (c :prevents b)))
'((a :requires (b c)) (c :prevents b))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a d) '((a :requires b) (b :requires c) (c) (d :prevents a)))
'((d :prevents a))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c d) '((a :requires b) (b :requires c) (c) (d :prevents a)))
'((b :requires c) (c) (d :prevents a))))
(should-error
(org-export-resolve-feature-implementations
nil '(a) '((a :requires b)))
:type 'org-missing-feature-dependency)
;; Check application of the :when condition.
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :when b) (b)))
'((a :when b) (b))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :when (b c)) (b) (c)))
'((a :when (b c)) (b) (c))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a) (b) (c :when (a b))))
'((a) (b) (c :when (a b)))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :when b) (b :when c) (c)))
'((a :when b) (b :when c) (c))))
;; Check simple ordering.
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :order 3) (b :order 1) (c :order 2)))
'((b :order 1) (c :order 2) (a :order 3))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :order 1) (b) (c :order -1)))
'((c :order -1) (b) (a :order 1))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a) '((a :order 1 :requires b) (b :requires c) (c :order -1)))
'((c :order -1) (b :requires c) (a :order 1 :requires b))))
;; Check before/after ordering.
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :after (b c)) (b) (c)))
'((b) (c) (a :after (b c)))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a :after b) (b :after c) (c)))
'((c) (b :after c) (a :after b))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a) (b) (c :before (a b))))
'((c :before (a b)) (a) (b))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a) (b :before a) (c :before b)))
'((c :before b) (b :before a) (a))))
(should
(equal (org-export-resolve-feature-implementations
nil '(a b c) '((a) (b :after c :before a) (c)))
'((c) (b :after c :before a) (a))))
(should-error ; Circular dependency
(org-export-resolve-feature-implementations
nil '(a b) '((a :after b) (b :after a)))
:type 'org-circular-feature-dependency))
;;; Comments ;;; Comments