Compare commits

...

240 Commits

Author SHA1 Message Date
TEC 12941a9df6
---END PERSONAL NOPUSH DIVIDER---
--
2.42.0
2024-03-08 14:44:44 +08:00
TEC 2eae918538
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-03-08 14:44:44 +08:00
TEC 145aec1036
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-03-08 14:44:44 +08:00
TEC ec78868507
---NOPUSH PERSONAL DIVIDER---
--
2.42.0
2024-03-08 14:44:44 +08:00
TEC 6665f5274c
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-03-08 14:44:44 +08:00
TEC 16240adb94
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-03-08 14:44:44 +08:00
TEC d42ccf4390
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-03-08 14:44:44 +08:00
TEC ec088a2def
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-03-08 14:44:43 +08:00
TEC 72b6555d87
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-03-08 14:44:43 +08:00
TEC f303074e67
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-03-08 14:44:43 +08:00
TEC 1a0f6ba424
org: Introduce semantic seperator fontification
* lisp/org.el (org-fontify-semantic-seperator,
org-set-font-lock-defaults): TODO
2024-03-08 14:44:43 +08:00
TEC 8a04f21e90
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-03-08 14:44:43 +08:00
TEC 30295b8ef9
---PATCH SET DIVIDER---
--
2.42.0
2024-03-08 14:44:43 +08:00
TEC 583f171439
org-latex-preview: Add authors header
* lisp/org-latex-preview.el: Mention that Karthik and I authored this
file.
2024-03-08 14:44:43 +08:00
Karthik Chikmagalur 094f4b1487
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-03-08 14:44:43 +08:00
TEC ccebe00fc8
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-03-08 14:44:43 +08:00
TEC 3b461cfa19
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-03-08 14:44:43 +08:00
TEC d530fb5e6d
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-03-08 14:44:43 +08:00
TEC 1f5237f230
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-03-08 14:44:43 +08:00
Karthik Chikmagalur f2af9023c9
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-03-08 14:44:43 +08:00
TEC b1b7e9b468
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-03-08 14:44:43 +08:00
TEC bb95960920
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-03-08 14:44:43 +08:00
TEC e54cc82172
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-03-08 14:44:43 +08:00
TEC b415f5ef2c
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-03-08 14:44:43 +08:00
Karthik Chikmagalur 74e62b6abf
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-03-08 14:44:43 +08:00
TEC d62e49f45f
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-03-08 14:44:43 +08:00
TEC 5b7254440f
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-03-08 14:43:11 +08:00
Karthik Chikmagalur 2aacef36b7
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-03-08 14:43:11 +08:00
Karthik Chikmagalur a5830e2494
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-03-08 14:43:11 +08:00
Karthik Chikmagalur 3bc1194184
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-03-08 14:43:11 +08:00
TEC ab395fdcdb
org-latex-preview: Update copyright years 2024-03-08 14:38:54 +08:00
TEC 8cdd2c8273
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 1d9baf095a
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-03-08 14:38:54 +08:00
TEC 8ecfa10539
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-03-08 14:38:54 +08:00
TEC 662eddd4d5
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-03-08 14:38:54 +08:00
TEC e6e0ed98a1
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-03-08 14:38:54 +08:00
TEC 2a3c5d662a
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-03-08 14:38:54 +08:00
TEC 277928c988
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-03-08 14:38:54 +08:00
TEC fac65c8d64
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-03-08 14:38:54 +08:00
TEC 95ef1d70f0
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-03-08 14:38:54 +08:00
TEC 3c1f1b0601
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-03-08 14:38:54 +08:00
TEC 282ac1e6ec
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-03-08 14:38:54 +08:00
TEC d1cace50f5
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-03-08 14:38:54 +08:00
TEC 281f5efb2d
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-03-08 14:38:54 +08:00
Karthik Chikmagalur c0d324940a
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-03-08 14:38:54 +08:00
Karthik Chikmagalur d0e409998c
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 3e265d0e40
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 546f8fb2dd
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 4b15ba9f0d
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-03-08 14:38:54 +08:00
Karthik Chikmagalur f44b741e7a
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-03-08 14:38:54 +08:00
Karthik Chikmagalur a3c6ab8a4f
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-03-08 14:38:54 +08:00
Karthik Chikmagalur e0867ab72b
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-03-08 14:38:54 +08:00
Karthik Chikmagalur d989a6ee47
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 3bdc048b73
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 44b53a36d0
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-03-08 14:38:54 +08:00
Karthik Chikmagalur b188f5ba7f
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 4ee09d64ed
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-03-08 14:38:54 +08:00
TEC afb431cfe0
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-03-08 14:38:54 +08:00
Karthik Chikmagalur e1348b280b
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-03-08 14:38:54 +08:00
Karthik Chikmagalur d4739c64d6
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 970b2fd96d
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 4dc877cdc3
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-03-08 14:38:54 +08:00
Karthik Chikmagalur 144ae10da7
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-03-08 14:38:54 +08:00
Karthik Chikmagalur cbd5d1ca59
Update ORG-NEWS
* etc/ORG-NEWS: Add summary of `org-latex-preview' changes.
2024-03-08 14:38:54 +08:00
Karthik Chikmagalur db91a3b9a1
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-03-08 14:38:54 +08:00
TEC 4424da9b8b
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-03-08 14:38:54 +08:00
TEC 8566cd774c
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-03-08 14:38:54 +08:00
TEC 734253d299
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-03-08 14:38:54 +08:00
TEC 7efa19fc0c
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-03-08 14:38:54 +08:00
TEC e3addbe5f0
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-03-08 14:38:54 +08:00
TEC 645d71f949
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-03-08 14:38:54 +08:00
TEC 36349dd5a3
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-03-08 14:38:54 +08:00
TEC 9593c81d1c
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-03-08 14:38:54 +08:00
TEC 13f37014ee
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-03-08 14:38:54 +08:00
TEC bf1ec9ab85
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-03-08 14:38:54 +08:00
TEC b60f8d810b
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-03-08 14:38:54 +08:00
TEC c3cce64d9e
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-03-08 14:38:54 +08:00
Karthik Chikmagalur d4761a5575
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-03-08 14:38:54 +08:00
TEC 856b15ef50
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-03-08 14:38:54 +08:00
TEC 3ed98c1eb8
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-03-08 14:38:54 +08:00
TEC 52cc553bc3
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-03-08 14:38:54 +08:00
TEC 9d81812629
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-03-08 14:38:54 +08:00
TEC 83489cb0d0
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-03-08 14:38:54 +08:00
TEC ec5bb4a797
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-03-08 14:38:54 +08:00
TEC 5b0f954b32
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-03-08 14:38:53 +08:00
TEC c7e0616ce2
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-03-08 14:38:53 +08:00
TEC 1fa471a2eb
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-03-08 14:38:53 +08:00
TEC 2f382fa52d
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-03-08 14:38:53 +08:00
Karthik Chikmagalur b7a2b22b4e
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-03-08 14:38:53 +08:00
TEC db430a240d
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-03-08 14:38:53 +08:00
TEC 749e775600
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-03-08 14:38:53 +08:00
TEC 39c070bcbb
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-03-08 14:38:53 +08:00
TEC 8e8b73a97e
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-03-08 14:38:53 +08:00
TEC 83090ae8bf
org-latex-preview: org-latex-conditional-features
* lisp/org-latex-preview.el (org-latex-preview--get-preamble):
2024-03-08 14:38:53 +08:00
Karthik Chikmagalur 58654796e6
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-03-08 14:38:53 +08:00
TEC 0b624d785e
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-03-08 14:38:53 +08:00
TEC 932373e15f
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-03-08 14:38:53 +08:00
Karthik Chikmagalur c9c27e108b
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-03-08 14:38:53 +08:00
TEC 9dd6cda864
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-03-08 14:38:53 +08:00
Karthik Chikmagalur bc00ffab1e
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-03-08 14:38:53 +08:00
TEC d8d2f289c6
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-03-08 14:38:53 +08:00
TEC 6fff40a0f2
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-03-08 14:38:53 +08:00
Karthik Chikmagalur f141c04eeb
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 75ba3e2d44
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 0d2d122de3
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-03-08 14:38:53 +08:00
TEC ada057a2e9
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-03-08 14:38:53 +08:00
TEC 98d57fcc16
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-03-08 14:38:53 +08:00
TEC 61b0995070
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-03-08 14:38:53 +08:00
TEC 126522aa38
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-03-08 14:38:53 +08:00
Karthik Chikmagalur fa2b4f979e
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 400fd0ffc9
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 8e839dff62
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 58d358fcba
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-03-08 14:38:53 +08:00
TEC cd9abc224b
org-persist: Add situational write inhibition
* lisp/org-persist.el (org-persist-write, org-persist--inhibit-write): TODO
2024-03-08 14:38:53 +08:00
Karthik Chikmagalur 872d322dea
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 0aec8a1103
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-03-08 14:38:53 +08:00
TEC 375ee16ab8
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-03-08 14:38:53 +08:00
TEC 734947dc1f
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-03-08 14:38:53 +08:00
Karthik Chikmagalur e5d2f78984
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-03-08 14:38:53 +08:00
TEC f19a69a909
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-03-08 14:38:53 +08:00
TEC 78654afc57
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-03-08 14:38:53 +08:00
TEC 5f1d5a79dd
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-03-08 14:38:53 +08:00
TEC 27cb324afc
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-03-08 14:38:53 +08:00
TEC 64b227ec6a
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 3fecc1f68c
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-03-08 14:38:53 +08:00
TEC c7783d10c7
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-03-08 14:38:53 +08:00
TEC 08490489fd
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 6fcbdb29a8
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 91030fd289
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-03-08 14:38:53 +08:00
TEC 5d98460919
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-03-08 14:38:53 +08:00
TEC b392768f24
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 2baabb2ab3
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 41e13c581e
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-03-08 14:38:53 +08:00
Karthik Chikmagalur ae81640d20
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 3f92449b8e
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-03-08 14:38:53 +08:00
TEC 41ddd5fa5b
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 15268d324d
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-03-08 14:38:53 +08:00
TEC 1f61558011
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-03-08 14:38:53 +08:00
TEC 81dd8dca93
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-03-08 14:38:53 +08:00
TEC 01b221c3ac
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-03-08 14:38:53 +08:00
TEC efcfb0af71
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-03-08 14:38:53 +08:00
TEC f1594e6c86
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-03-08 14:38:53 +08:00
TEC 946ca89939
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-03-08 14:38:53 +08:00
TEC 1413ca45b9
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-03-08 14:38:53 +08:00
TEC 4723e2cb64
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-03-08 14:38:53 +08:00
TEC bd3534de77
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-03-08 14:38:53 +08:00
TEC 52416fbf93
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-03-08 14:38:53 +08:00
Karthik Chikmagalur c2bb4808af
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-03-08 14:38:53 +08:00
Karthik Chikmagalur d4090d2d6a
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 38c86de1c6
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-03-08 14:38:53 +08:00
TEC f6932aa60d
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 5e1ad9fa24
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-03-08 14:38:53 +08:00
TEC c81cd36090
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-03-08 14:38:53 +08:00
TEC b3072ead33
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-03-08 14:38:53 +08:00
TEC 603b05a040
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-03-08 14:38:53 +08:00
TEC b2ed12b1ca
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-03-08 14:38:53 +08:00
TEC 58cc279623
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 581b85c898
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 0fd3e46e32
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-03-08 14:38:53 +08:00
TEC 9d3391f8d1
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-03-08 14:38:53 +08:00
TEC 139c270496
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-03-08 14:38:53 +08:00
TEC 1e8e73c841
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-03-08 14:38:53 +08:00
TEC 62eda0455f
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-03-08 14:38:53 +08:00
TEC f352cfa174
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 3a490c4ef3
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-03-08 14:38:53 +08:00
Karthik Chikmagalur fd6a9baf2f
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-03-08 14:38:53 +08:00
TEC 333f19c6f8
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-03-08 14:38:53 +08:00
Karthik Chikmagalur 0a9236e251
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-03-08 14:38:53 +08:00
TEC c88f221f08
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-03-08 14:38:53 +08:00
TEC f1620233c8
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-03-08 14:38:53 +08:00
TEC 9b808659b3
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-03-08 14:38:53 +08:00
TEC c500886d40
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-03-08 14:38:53 +08:00
TEC cff3668878
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-03-08 14:38:53 +08:00
TEC 88d308aa63
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-03-08 14:38:53 +08:00
TEC e962fb52a9
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-03-08 14:38:53 +08:00
TEC c07c20961d
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-03-08 14:38:53 +08:00
TEC a044b79eed
---PATCH SET DIVIDER--- 2024-03-08 14:38:53 +08:00
TEC 27c373d53d
org-manual: Document export features
* doc/org-manual.org (+*** Export features): Initial manual entry on
export features.
2024-03-08 14:38:53 +08:00
TEC f297ef8f4b
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-03-08 14:38:52 +08:00
TEC 180972266c
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-03-08 14:38:52 +08:00
TEC 6be558e9cd
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-03-08 14:38:52 +08:00
TEC 62f45e2bab
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-03-08 14:38:52 +08:00
Bastien Guerry 57ec015ab3 Merge branch 'bugfix' 2024-03-08 07:14:20 +01:00
Bastien Guerry 4ee6e88b2f doc/org-manual.org: Small fix
* doc/org-manual.org (Include Files): Add missing quotes.
2024-03-08 07:14:07 +01:00
Bastien Guerry 044dc80a77 doc/org-manual.org: Small fix
* doc/org-manual.org (Include Files): Add missing quotes.
2024-03-08 07:12:32 +01:00
Ihor Radchenko cab81f2428
org-babel: Improve parsing of colnames in tables with horizontal lines
* lisp/ob-core.el (org-babel-get-colnames): When table starts with
horizontal lines, consider column names to be the first non-hline row.
(org-babel-disassemble-tables): When detecting automatic column names,
do not assign colnames when the first row is an hline.
* doc/org-manual.org (Passing arguments): Update the manual, detailing
that leading hline rows are skipped for :colnames yes.

Link: https://orgmode.org/list/87wmqexjoj.fsf@localhost
2024-03-07 22:02:21 +03:00
Ihor Radchenko 0a6c881174
org-babel: Fix smart `replace-regexp-in-string' behavior during variable assignment
* lisp/ob-latex.el (org-babel-expand-body:latex):
* lisp/ob-lilypond.el (org-babel-expand-body:lilypond):
* lisp/ob-org.el (org-babel-expand-body:org):
* lisp/ob-sql.el (org-babel-sql-expand-vars): Pass FIXEDCASE and
LITERAL arguments to `replace-regexp-in-string'.  This avoids changing
case and special handling of \N constructs in the replacement string.
2024-03-07 15:49:07 +03:00
Ihor Radchenko 0608ae1062
Merge branch 'bugfix' 2024-03-07 15:43:53 +03:00
Ihor Radchenko 01a0f15b6b
org-babel-expand-body:gnuplot: Fix altering variable data case
* lisp/ob-gnuplot.el (org-babel-expand-body:gnuplot): Pass FIXEDCASE
and LITERAL arguments to `replace-regexp-in-string'.  This prevents it
from "smartly" altering the replacement text case when the variable
name is upcased like $C or $FOO; and from interpreting \N constructs.

Reported-by: Paul Stansell <paulstansell@gmail.com>
Link: https://orgmode.org/list/CAMJKaZyH0ZwaitHxCTdd2ZGMKOZtVCvwVf_OEuRe9crPbqG9jA@mail.gmail.com
2024-03-07 15:43:00 +03:00
Ihor Radchenko 8eb78048f0
org-mobile-create-index-file: Fix typo
* lisp/org-mobile.el (org-mobile-create-index-file): Fix argument
order in the call to `expand-file-name'.

Reported-by: Fabian Brosda <f.brosda@gmx.de>
Link: https://orgmode.org/list/87a5ngxzcr.fsf@gmx.de
2024-03-06 13:36:56 +03:00
Ihor Radchenko 1151c614b3
org-fold-core-region: Fix another edge case with newline fontification
* lisp/org-fold-core.el (org-fold-core-region): Do not just update
folds before/after.  Refresh newlines created by splicing a fold by
unfolding in the middle of an existing fold.  Then, fold list between
from/to will not cover the folds right before from and right after to.

Reported-by: StrawberryTea <look@strawberrytea.xyz>
Link: https://orgmode.org/list/878r2xfk60.fsf@strawberrytea.xyz
2024-03-05 11:02:21 +03:00
Gerard Vermeulen c2ea553be8
org-babel-demarcate-block: split using element API
* lisp/ob-babel.el (org-babel-demarcate-block): Modify a copy
of (org-element-at-point) to replace the old source block with 2 or 3
new modified copies by means of `org-element-interpret-data'.  The 1st
source block contains the text from the body of the old block before
point or region, the 2nd block contains the body text after point or
body text within region, and in case of region, the 3rd block contains
the text after region.  The caption and the name are deleted from the
1 or 2 blocks below the upper source block.  Indent all blocks
immediately after insertion.  Use :post-blank to control white lines
between inserted blocks.  Leave point at the last inserted block.
Trying to split when point or region is not within the body of the old
source block raises an user-error.
* lisp/ob-babel (org-get-src-block-info): add the "within blank lines
after a source block" condition to the doc-string to match it with the
doc-string of and a comment in `org-babel-demarcate-block'.
* testing/lisp/test-ob.el (test-ob/demarcate-block-split-duplication)
(test-ob/demarcate-block-split-prefix-point)
(test-ob/demarcate-block-split-prefix-region)
(test-ob/demarcate-block-split-user-errors)
(test-ob/demarcate-block-wrap-point)
(test-ob/demarcate-block-wrap-region): New tests to check test cases
that broke earlier versions of this patch.

Link: https://list.orgmode.org/7e41f9b6e9026a404e256f33371e974c@posteo.net/
2024-03-04 14:48:59 +03:00
Ihor Radchenko 5f5db3d353
org-babel-do-in-edit-buffer: Fix when opening src edit buffer fails
* lisp/ob-core.el (org-babel-do-in-edit-buffer): When opening src edit
buffer fails, do not leave point inside that buffer.

Reported-by: gerard.vermeulen@posteo.net
Link: https://orgmode.org/list/5bf6832d2485f0aa6be2e5ea2eba37d2@posteo.net
2024-03-04 13:11:21 +03:00
Anand Deopurkar 8ac99c33f0
org-agenda.el: Fix org-agenda-write to avoid duplicates
* lisp/org-agenda.el (org-agenda-write): Make sure headlines are not
duplicated when writing to an `org' file, even if they are repeated in
the agenda view.

For example, a headline may appear multiple times in the agenda
view (for example, if it has multiple time stamps).  But
org-agenda-write should write it only once to the output `org' file.

TINYCHANGE
2024-03-03 16:44:54 +03:00
Ihor Radchenko 2da622cad7
org-fold-core-region: Fix re-fontification when unfolding everything
* lisp/org-fold-core.el (org-fold-core-region): Make sure that we
refresh fontification of newlines after folds when unfolding is
requested across multiple non-intersecting folds.

Reported-by: StrawberryTea <look@strawberrytea.xyz>
Link: https://orgmode.org/list/87y1b0m0ij.fsf@strawberrytea.xyz
2024-03-03 16:42:03 +03:00
Anand Deopurkar 902dacb9ca
org-archive.el: Fix org-add-archive-files to correctly de-duplicate
* org-archive.el (org-add-archive-files): Use `seq-uniq' with TESTFN
`file-equal-p' to de-duplicate the list of gathered files.

Previously, `org-uniquify' was used.  This de-duplicates the
file-names, but not necessarily the files.  The problem occurs if the
list of file-names includes distinct file-names that reference the
same file (symbolic links, for example).

TINYCHANGE
2024-03-02 15:26:16 +03:00
Ihor Radchenko ca061cfacb
org-beamer-template: Fix when `org-beamer-frame-environement' is "frame"
* lisp/ox-beamer.el (org-beamer-template): Do not try to define
`org-beamer-frame-environement' when it is set to "frame" - the same
as the existing frame environment.

Reported-by: Pedro Andres Aranda Gutierrez <paaguti@gmail.com>
Link: https://orgmode.org/list/87a5nh1r9h.fsf@t14.reltub.ca
2024-03-02 15:19:31 +03:00
Leo Butler 60ddec4827
lisp/ox-beamer.el: fix orgframe environment definition
* (org-beamer-template):  pass the frame overlay specification to the
frame in the definition of the orgframe environment.

Bug reported by Pedro Andres Aranda Gutierrez.

Ref: https://list.orgmode.org/orgmode/CF58E7A4-233D-4F9D-BBE2-FC1A67E315BD@gmail.com/T/
2024-03-02 15:16:40 +03:00
Ihor Radchenko b127d7257e
lisp/ol.el (org--re-list-looking-at): Fix Emacs <29 compatibility 2024-02-29 16:00:06 +03:00
Ihor Radchenko c88da815ed
Revert "lisp/ob-sql.el: Prevent from getting stuck with an error"
This reverts commit 45ca597f96.

Another commit handles the problem more universally: a3ebd8c66.
2024-02-29 15:05:19 +03:00
Ihor Radchenko a3ebd8c669
org-babel-do-in-edit-buffer: When editing fails, do not throw an error
* lisp/ob-core.el (org-babel-do-in-edit-buffer): Avoid throwing an
error when something is not right with the code block.  We already
ignore errors when major mode fails to load and we generally don't
want this macro to fail as it is used during indentation.

Link: https://list.orgmode.org/orgmode/87wmqo3it5.fsf@localhost/
2024-02-29 15:01:00 +03:00
Ihor Radchenko ac49228040
Merge branch 'bugfix' 2024-02-29 14:49:10 +03:00
Ihor Radchenko 8648576573
org-up-heading-safe: Return true level, ignoring org-odd-levels-only
* lisp/org.el (org-up-heading-safe): Fix return value, making sure
that the returned level is not reduced.

Reported-by: E.L.K. <some.any.key@gmail.com>
Link: https://orgmode.org/list/CAF+cOoPuh8rzVRoV9-pGSP3SVRm-M8ZQrM=xGB+o4TqJat_O-A@mail.gmail.com
2024-02-29 14:47:31 +03:00
Ihor Radchenko 6ef0154576
org-up-heading-safe: Return true level, ignoring org-odd-levels-only
* lisp/org.el (org-up-heading-safe): Fix return value, making sure
that the returned level is not reduced.  This is what other code
expects and what used to be the case before refactoring, in previous
version of the function.
* testing/lisp/test-org.el (test-org/up-heading-safe): Add test.

Reported-by: E.L.K. <some.any.key@gmail.com>
Link: https://orgmode.org/list/CAF+cOoPuh8rzVRoV9-pGSP3SVRm-M8ZQrM=xGB+o4TqJat_O-A@mail.gmail.com
2024-02-29 14:36:26 +03:00
Ihor Radchenko 731d16f9e9
org-table-to-lisp: Preserve text properties
* lisp/org-table.el (org-table-to-lisp): When parsing table, keep text
properties.  These text properties are required to calculate table
alignment with invisible text.
* testing/lisp/test-org-table.el (test-org-table/align): Add new test.

Reported-by: Kostadin Ninev <dinkonin@gmail.com>
Link: https://orgmode.org/list/1709041784097.2987395179.620232376@gmail.com
2024-02-29 14:23:37 +03:00
Ihor Radchenko c841be9656
lisp/ol.el (org-target-link-regexp-limit): Bump value 2024-02-29 14:07:15 +03:00
Ihor Radchenko 341a01a07d
Work around regexp size limitation for large number of link targets
* lisp/ol.el (org-target-link-regexp-limit): New constant defining
maximum regexp limit where `org-target-link-regexp' is still safe to
use without triggering "Regexp too long" error.
(org-target-link-regexps): New variable holding a series of shorter
regexps to be used instead of too long single
`org-target-link-regexp'.
(org--re-list-search-forward): New function like `re-search-forward',
but accepting a list of regexps.
(org--re-list-looking-at): New function like `looking-at', but
accepting a list of regexps.
(org-update-radio-target-regexp): When `org-target-link-regexp' is too
long, set `org-target-link-regexps', partitioning the link target list
into smaller regexps.
* lisp/org-element.el (org-element-link-parser):
(org-element--object-lex):
* lisp/org.el (org-activate-target-links): Use
`org--re-list-search-forward' and `org--re-list-looking-at' when
`org-target-link-regexps' is non-nil.
* testing/lisp/test-org-element.el (test-org-element/link-parser): Add
tests.

Reported-by: Rudolf Adamkovič <salutis@me.com>
Link: https://list.orgmode.org/orgmode/m2lenax5m6.fsf@me.com/
2024-02-29 13:54:41 +03:00
Olivier Lischer dd4d05a159
ob-tangle: Add flag to optionally remove files before writing
* lisp/ob-tangle.el: Add new custom option
`org-babel-tangle-remove-file-before-write'.
(org-babel-tangle): Remove file before writing according to the value
of `org-babel-tangle-remove-file-before-write'.

The variable `org-babel-tangle-remove-file-before-write' adds support
for the current and old behaviour of `org-babel-tangle'.

Link: https://list.orgmode.org/orgmode/877cjzhjtg.fsf@liolin.ch/
Co-authored-by: Ihor Radchenko <yantar92@posteo.net>

TINYCHANGE
2024-02-29 13:41:34 +03:00
Ihor Radchenko 9029470eb9
fixup! oc-basic.el: Drop {...} when rendering Bibtex entries 2024-02-29 13:02:48 +03:00
Ihor Radchenko ac1ed2bf85
lisp/org-colview.el: Fix computing summary with low-level first child
* lisp/org-colview.el (org-columns--compute-spec): Do not assume that
all the children of an entry have the same LAST-LEVEL.  Handle
situation when the first child has lower level:

* Heading
****** Child 1
** Child 2
** Child 3

Reported-by: Uwe Brauer <oub@mat.ucm.es>
Link: https://orgmode.org/list/87wn8yj2i5.fsf@localhost
2024-02-29 12:57:24 +03:00
Ihor Radchenko d2df9624ce
oc-basic.el: Drop {...} when rendering Bibtex entries
* lisp/oc-basic.el (org-cite-basic--print-bibtex-string): New function
approximately parsing Bibtex strings and dropping {...}.
(org-cite-basic--print-entry): Use the new function.
* testing/examples/Basic.bib:
*
testing/lisp/test-oc-basic.el (test-org-cite-basic/export-bibliography):
Add test.

Reported-by: Dominik Schrempf <dominik.schrempf@gmail.com>
Link: https://orgmode.org/list/CAF+0kSg8O3RQBG1wXoHjMEHwnGFz0gaDkTTSGv+ZaOt4d4myCA@mail.gmail.com
2024-02-29 12:21:08 +03:00
Ihor Radchenko 286a8fb798
org-element-insert-before: Fix return value when setting plain-text
* lisp/org-element-ast.el (org-element-set-element): When setting
plain-text node, return the new node instead of immutable old one.
* testing/lisp/test-org-element.el (test-org-element/set): Add test.
2024-02-29 11:43:52 +03:00
Ihor Radchenko 755fef38f5
Merge branch 'bugfix' 2024-02-28 10:54:50 +03:00
Ihor Radchenko fe9bc9efd1
Update version number for the 9.6.20 release 2024-02-28 10:53:28 +03:00
Ihor Radchenko 80dff08ed1
Fix unescaped \ in docstrings
* lisp/org-macs.el (org-split-string):
* lisp/ox-latex.el (org-latex-guess-babel-language): Fix docstrings,
as revealed by compiler warnings.
2024-02-28 10:52:58 +03:00
Tim Ruffing 356072c1d6
org-agenda: Make sure skipping warning/delay days never increases their number
* lisp/org-agenda.el (org-agenda-get-deadlines, org-agenda-get-scheduled):
Use minimum of warning/delay days specified in timestamp cookie and the
limit specified by `org-agenda-skip-deadline-prewarning-if-scheduled' or
`org-agenda-skip-scheduled-delay-if-deadline`, respectively.
* testing/lisp/test-org-agenda.el (test-org-agenda/skip-deadline-prewarning-if-scheduled):
New test.

Link: https://orgmode.org/list/59e48dfe744dc9409ff47183255bc64e92d26d88.camel@timruffing.de

TINYCHANGE
2024-02-27 15:40:18 +03:00
Ihor Radchenko 8651c83991
Address Emacs 27 warnings
* lisp/org-entities.el (seq): Require seq.el - it is not preloaded in
Emacs <28.
* lisp/org.el (org--image-yank-media-handler): Suppress warning and
throw an error about `file-name-with-extension'.  This function is not
supposed to be called in older Emacs.
2024-02-27 13:34:07 +03:00
Ihor Radchenko 25a51bad2c
Address Emacs 28 warnings
* lisp/oc-basic.el (org-cite-basic--shorten-names): Provide third
argument for `mapconcat'.  It is mandatory in Emacs <29.
* lisp/org.el (org--image-yank-media-handler): Suppress warning and
throw and error in Emacs <29.
(org--dnd-rmc): Do not try to call `use-dialog-box-p' in Emacs <29.
2024-02-27 13:29:00 +03:00
Ihor Radchenko 87c9f9b5db
org-export: Fix exporting visible when org-fold-core-style is 'text-properties
* lisp/org-fold-core.el (org-fold-core--update-buffer-folds): New
function updating buffer folds in copied text for current buffer.
(org-fold-core-decouple-indirect-buffer-folds): Use the new function.
* lisp/org-element.el (org-element--generate-copy-script): Synchronize
folds.
2024-02-27 13:21:36 +03:00
Ihor Radchenko fef873b1cf
org-agenda-get-restriction-and-command: Do not leave window around
* lisp/org-agenda.el (org-agenda-get-restriction-and-command): When
agenda selection is aborted or completed by any means, quit agenda
command selection window.

Reported-by: Björn Bidar <bjorn.bidar@thaodan.de>
Link: https://list.orgmode.org/orgmode/87il2ai916.fsf@/
2024-02-27 12:18:34 +03:00
Ihor Radchenko 9f6894c106
org-activate-folds: Do not fontify newline after abbreviated links
* lisp/org.el (org-activate-folds): When a fold is created by link
abbreviation, and a link ends at eol, do not force the newline after
the ]] to have the same face as the link.

Reported-by: Rohit Patnaik <quanticle@quanticle.net>
Link: https://orgmode.org/list/6d440976-6747-4ce6-8913-b63931dd017f@app.fastmail.com
2024-02-26 15:52:25 +03:00
Ihor Radchenko 8bac4d386a
org-fold-core-get-folding-spec: Fix edge case
* lisp/org-fold-core.el (org-fold-core-get-folding-spec): When SPEC is
nil, do not rely upon 'invisible property value to determine the fold.
Examine unique property set by `org-fold-region' instead.
2024-02-26 15:51:31 +03:00
Ihor Radchenko 655e97208c
org-element--generate-copy-script: Copy all the overlay properties
* lisp/org-element.el (org-element--generate-copy-script): In buffer
copy, copy over all the overlay properties, not just 'invisible.  This
is needed to copy folding overlays correctly - they use more than just
'invisible property.
2024-02-26 15:51:04 +03:00
Thierry Banel 407a55c1c0
org-table.el: Enhanced table parsing
* lisp/org-table.el (org-table-to-lisp): Refactored.
* etc/ORG-NEWS: Document changes.

`org-table-to-lisp' is significantly faster.
It no longer uses regexps, nor clobbers the global regexp state.
2024-02-25 14:30:38 +03:00
Ihor Radchenko af9100382c
Never pass error message verbatim as the first argument for `message'/`error'
* lisp/org-capture.el (org-capture-store-last-position):
* lisp/org-crypt.el (org-encrypt-entry):
* lisp/org.el (org-open-file): Avoid (error string) code pattern.
Instead use (error "%s" string).  (error string) is erroneous when
STRING contains %-specs.

Reported-by: Corwin Kerr <corwin@corwinkerr.com>
Link: https://orgmode.org/list/beceaf05-ae15-487b-864b-2e23a33a3884@app.fastmail.com
2024-02-25 13:15:04 +03:00
Ihor Radchenko 9daad41ccf
Re-introduce undocumented fontification of example blocks
* lisp/org.el (org-fontify-meta-lines-and-blocks-1): Do not assert
that `org-src-fontify-natively' only applies to code blocks.  Allow
other blocks with language specifier to be fontified, but keep this
behavior undocumented.

Reported-by: Rudi C <rudiwillalwaysloveyou@gmail.com>
Link: https://orgmode.org/list/CAE9z9A3ko27NeN0oYFs_aBWV_cUbNm5YQTLrRux16dHsixsMBA@mail.gmail.com
2024-02-25 13:00:12 +03:00
Martin Kampas 37e468cf16
org-bibtex-yank: Allow to populate existing item
Align with `org-bibtex-create'.

* lisp/ol-bibtex.el (org-bibtex-write):
* lisp/ol-bibtex.el (org-bibtex-yank): New optional argument
UPDATE-HEADING to update heading at point instead of creating a new
heading.
(org-bibtex-create): Rename NONEW argument to UPDATE-HEADING.
* etc/ORG-NEWS (~org-bibtex-yank~ accepts a prefix argument):
(New optional argument =UPDATE-HEADING= for ~org-bibtex-yank~):
Announce the change.

Link: https://list.orgmode.org/orgmode/4868155.GXAFRqVoOG@gt1/
2024-02-25 11:50:38 +03:00
Ihor Radchenko 5d186b499d
org-fold: Refactor fontifying newlines after folds
* lisp/org.el (org-activate-folds): New fontification function to
arrange faces at newlines after folds to be the same as face before
the fold.
(org-set-font-lock-defaults): Add the new fontification function.
* lisp/org-fold-core.el (org-fold-core-region): Refresh fontification
of newlines after folds when folding/unfolding.

Reported-by: StrawberryTea <look@strawberrytea.xyz>
Link: https://orgmode.org/list/87plwoqrfv.fsf@strawberrytea.xyz
2024-02-25 11:42:44 +03:00
Rick Lupton 95554543b9
org-id.el: Add search strings, inherit parent IDs
* lisp/ol.el (org-store-link): Refactor org-id links to use standard
`org-store-link-functions'.
(org-link-search): Create new headings at appropriate level.
(org-link-precise-link-target): New function extracting logic to
identify a precise link target, e.g. a heading, named object, or text
search.
(org-link-try-link-store-functions): Extract logic to call external
link store functions. Pass them a new `interactive?' argument.
* lisp/ol-bbdb.el (org-bbdb-store-link):
* lisp/ol-bibtex.el (org-bibtex-store-link):
* lisp/ol-docview.el (org-docview-store-link):
* lisp/ol-eshell.el (org-eshell-store-link):
* lisp/ol-eww.el (org-eww-store-link):
* lisp/ol-gnus.el (org-gnus-store-link):
* lisp/ol-info.el (org-info-store-link):
* lisp/ol-irc.el (org-irc-store-link):
* lisp/ol-man.el (org-man-store-link):
* lisp/ol-mhe.el (org-mhe-store-link):
* lisp/ol-rmail.el (org-rmail-store-link): Accept optional arg.
* lisp/org-id.el (org-id-link-consider-parent-id): New option to allow
a parent heading with an id to be considered as a link target.
(org-id-link-use-context): New option to add context to org-id links.
(org-id-get): Add optional `inherit' argument which considers parents'
IDs if the current entry does not have one.
(org-id-store-link): Consider IDs of parent headings as link targets
when current heading has no ID and `org-id-link-consider-parent-id' is
set.  Add a search string to the link when enabled.
(org-id-store-link-maybe): Function set as :store option for custom id
link property. Move logic from `org-store-link' here to determine when
an org-id link should be stored using `org-id-store-link'.
(org-id-open): Recognise search strings after "::" in org-id links.
* lisp/org-lint.el: Add checker for "::" in ID properties.
* testing/lisp/test-ol.el: Add tests for
`org-link-precise-link-target' and `org-id-store-link' functions,
testing new options.
* doc/org-manual.org: Update documentation about links.
* etc/ORG-NEWS: Document changes and new options.

These feature allows for more precise links when using org-id to link to
org headings, without requiring every single headline to have an id.

Link: https://list.orgmode.org/118435e8-0b20-46fd-af6a-88de8e19fac6@app.fastmail.com/
2024-02-24 15:56:44 +03:00
Rick Lupton 6e7e0b2cd3
lisp/org.el (org-insert-heading): Allow specifying heading level
* lisp/org.el (org-insert-heading): Change optional argument TOP to
LEVEL, accepting a number to force a specific heading level.
* testing/lisp/test-org.el (test-org/insert-heading): Add tests.
* etc/ORG-NEWS: Document changes.
2024-02-24 15:56:43 +03:00
Ihor Radchenko f90322377c
ox: Fix exporting code blocks with missing language specifier
* lisp/ox-latex.el (org-latex-inline-src-block): Export code blocks
without language as verbatim.
* lisp/ox-man.el (org-man-inline-src-block):
(org-man-src-block): Do not try to call source-highlight executable
when code block language is missing.
* lisp/ox-odt.el (org-odt-do-format-code):
* lisp/ox-texinfo.el (org-texinfo-src-block): Do not throw error when
code block language is missing.

Reproducer (not available on the list):

  To reproduce my problem you can write create an org-mode buffer (C-x b
  1.org M-x org-mode) with the following content:

  #+begin_src lua :tangle yes  :noweb yes
    aaa
    <<second>>
  #+end_src

  #+name: second
  #+begin_src
   bbb
  #+end_src

  Then enable exporting to texinfo and export the buffer:
    M-: (require 'ox-texinfo)
    C-c C-e i t

  The folllowing backtrace appears (providing you already enabled
  debugging on errors):

  Debugger entered--Lisp error: (wrong-type-argument stringp nil)
    apply(debug error (wrong-type-argument stringp nil))
    edebug(error (wrong-type-argument stringp nil))
    signal(wrong-type-argument (stringp nil))
    edebug-signal(wrong-type-argument (stringp nil))
    string-match("lisp" nil nil t)
    string-match-p("lisp" nil)
    (edebug-after (edebug-before 1) 5 (string-match-p "lisp" (edebug-after (edebug-before 2) 4
  (org-element-property :language (edebug-after 0 3 src-block)))))

Reported-by: pva-outdoor@yandex.ru
Link: https://list.orgmode.org/orgmode/87jzn7oz1u.fsf@yandex.ru/
2024-02-24 12:52:51 +03:00
Ihor Radchenko 505778950a
org-persist: Disable reporting read/write time by default
* lisp/org-persist.el (org-persist--report-time): This information
might be interesting, but not really necessary for users.  At least
one user also asked to disable the time reporting.
2024-02-24 12:39:59 +03:00
Ihor Radchenko 19db45f2cc
org-agenda-filter-by-category: Fix using truncated category as a filter
* lisp/org-agenda.el (org-agenda-format-item): Do not store truncated
category in 'org-category property when `org-agenda-prefix-format'
causes truncation.  This leads to agenda filters assuming truncated
version of the category as the true value, which is incorrect.

Reported-by: John Mathena <jmmathena@gmail.com>
Link: https://orgmode.org/list/CADywB5Lonxw-t1H7cvK+Au3oWq=EsB=Bthj13pt03MRP-Fn-4w@mail.gmail.com
2024-02-24 12:33:27 +03:00
Ihor Radchenko 4b1399fb21
org-goto-map: Do not treat 0-9 as digital arguments by default
* lisp/org-goto.el (org-goto--set-map): Fix 0-9 being interpreted as
digital arguments when `org-goto-auto-isearch' is non-nil.

Reported-by: Pan Xie <xiepan@skyguard.com.cn>
Link: https://orgmode.org/list/5f491471-af08-49d5-a840-6b2f96be54cf@skyguard.com.cn
2024-02-24 12:07:55 +03:00
Ihor Radchenko e52a480f9f
Merge branch 'bugfix' 2024-02-23 16:24:45 +03:00
Ihor Radchenko bfe253f7eb
org-cycle: Fix "Overwriting value of ... by aliasing" warning
* lisp/org-cycle.el:
* lisp/org.el (org-cycle): Move variable aliases right before new
variable name definitions in org-cycle.el.  Otherwise, if users set
the aliased variable values before loading Org-mode, they might get
overwritten by the default value.

Reported-by: Ruiyang Wu <ywwry66@gmail.com>
Link: https://orgmode.org/list/F3A95086-B4BF-4C08-AF7A-8D7DE6FE30CF@gmail.com
2024-02-23 16:24:19 +03:00
Ihor Radchenko 256caa0823
org-cycle: Fix "Overwriting value of ... by aliasing" warning
* lisp/org-cycle.el:
* lisp/org.el (org-cycle): Move variable aliases right before new
variable name definitions in org-cycle.el.  Otherwise, if users set
the aliased variable values before loading Org-mode, they might get
overwritten by the default value.

Reported-by: Ruiyang Wu <ywwry66@gmail.com>
Link: https://orgmode.org/list/F3A95086-B4BF-4C08-AF7A-8D7DE6FE30CF@gmail.com
2024-02-23 16:21:13 +03:00
Ihor Radchenko 24ce9abce7
org-capture-set-target-location: Fix `pcase' conditions
* lisp/org-capture.el (org-capture-set-target-location): Do not demand
file path to be a string in capture targets.  Other non-string values
are also allowed and checked in `org-capture-expand-file' that is
called later.

This commit fixes the changes introduced in 0a58a53ed.

Reported-by: Derek Chen-Becker <derek@chen-becker.org>
Link: https://orgmode.org/list/CAMbmz5nnXY1V1cPObfdw9wyM_GbiuYQ5_rksJ6gcO3Af+HOmSQ@mail.gmail.com
2024-02-23 15:51:11 +03:00
Ihor Radchenko 67d9372487
org-read-date-display: Fix random overlay priority when selection is at today
* lisp/org.el (org-read-date-display): Assign `org-read-date-overlay'
non-default priority.  This makes sure that the overlay is displayed
over the overlay that calendar.el puts to indicate "today".

Reported-by: John Kitchin <jkitchin@andrew.cmu.edu>
Link: https://orgmode.org/list/CALEYq0-xbCc6ayEK3v7tKnuGU7=Npgh3raEjuB13qOTN6Eu07Q@mail.gmail.com
2024-02-21 14:15:24 +03:00
Leo Butler b3b35b03ff
lisp/ox-beamer.el: customize the beamer frame environment name
* lisp/ox-beamer.el (org-beamer-frame-environment): A new customize
variable.  It contains the name of an environment that serves as an
alias for the beamer frame environment.

(org-beamer-template): Insert the `org-beamer-frame-environment'
environment definition into the beamer document.

* lisp/org-lint.el (org-lint-beamer-frame): Check the body of each
frame for an occurrence of \begin{orgframe} or \end{orgframe}, or
whatever environment name is in `org-beamer-frame-environment' [4].
The warning includes advice to see `org-beamer-frame-environment'.

* etc/ORG-NEWS (New option ~org-beamer-frame-environment~): Announce
the new option.

Rationale: Code with \begin{frame} or \end{frame} cannot be embedded
in a verbatim environment inside a beamer frame due to a design
decision made by the beamer developers [1].  As suggested in that
report, defining an alias for the beamer frame environment will allow
such verbatim examples to compile correctly [2].

This solution also works with instances of \againframe.

Refs:
[1] https://github.com/josephwright/beamer/issues/360
[2] https://github.com/josephwright/beamer/issues/360#issuecomment-708705250
[3] https://list.orgmode.org/orgmode/87le8eg1hs.fsf@localhost/T/
[4] https://list.orgmode.org/orgmode/87il38i5tb.fsf@localhost/T/
2024-02-21 13:46:32 +03:00
65 changed files with 8438 additions and 1918 deletions

View File

@ -3300,10 +3300,6 @@ Here is the full set of built-in link types:
File links. File name may be remote, absolute, or relative.
Additionally, you can specify a line number, or a text search.
In Org files, you may link to a headline name, a custom ID, or a
code reference instead.
As a special case, "file" prefix may be omitted if the file name
is complete, e.g., it starts with =./=, or =/=.
@ -3367,44 +3363,50 @@ Here is the full set of built-in link types:
Execute a shell command upon activation.
For =file:= and =id:= links, you can additionally specify a line
number, or a text search string, separated by =::=. In Org files, you
may link to a headline name, a custom ID, or a code reference instead.
The following table illustrates the link types above, along with their
options:
| Link Type | Example |
|------------+----------------------------------------------------------|
| http | =http://staff.science.uva.nl/c.dominik/= |
| https | =https://orgmode.org/= |
| doi | =doi:10.1000/182= |
| file | =file:/home/dominik/images/jupiter.jpg= |
| | =/home/dominik/images/jupiter.jpg= (same as above) |
| | =file:papers/last.pdf= |
| | =./papers/last.pdf= (same as above) |
| | =file:/ssh:me@some.where:papers/last.pdf= (remote) |
| | =/ssh:me@some.where:papers/last.pdf= (same as above) |
| | =file:sometextfile::NNN= (jump to line number) |
| | =file:projects.org= |
| | =file:projects.org::some words= (text search)[fn:12] |
| | =file:projects.org::*task title= (headline search) |
| | =file:projects.org::#custom-id= (headline search) |
| attachment | =attachment:projects.org= |
| | =attachment:projects.org::some words= (text search) |
| docview | =docview:papers/last.pdf::NNN= |
| id | =id:B7423F4D-2E8A-471B-8810-C40F074717E9= |
| news | =news:comp.emacs= |
| mailto | =mailto:adent@galaxy.net= |
| mhe | =mhe:folder= (folder link) |
| | =mhe:folder#id= (message link) |
| rmail | =rmail:folder= (folder link) |
| | =rmail:folder#id= (message link) |
| gnus | =gnus:group= (group link) |
| | =gnus:group#id= (article link) |
| bbdb | =bbdb:R.*Stallman= (record with regexp) |
| irc | =irc:/irc.com/#emacs/bob= |
| help | =help:org-store-link= |
| info | =info:org#External links= |
| shell | =shell:ls *.org= |
| elisp | =elisp:(find-file "Elisp.org")= (Elisp form to evaluate) |
| | =elisp:org-agenda= (interactive Elisp command) |
| Link Type | Example |
|------------+--------------------------------------------------------------------|
| http | =http://staff.science.uva.nl/c.dominik/= |
| https | =https://orgmode.org/= |
| doi | =doi:10.1000/182= |
| file | =file:/home/dominik/images/jupiter.jpg= |
| | =/home/dominik/images/jupiter.jpg= (same as above) |
| | =file:papers/last.pdf= |
| | =./papers/last.pdf= (same as above) |
| | =file:/ssh:me@some.where:papers/last.pdf= (remote) |
| | =/ssh:me@some.where:papers/last.pdf= (same as above) |
| | =file:sometextfile::NNN= (jump to line number) |
| | =file:projects.org= |
| | =file:projects.org::some words= (text search)[fn:12] |
| | =file:projects.org::*task title= (headline search) |
| | =file:projects.org::#custom-id= (headline search) |
| attachment | =attachment:projects.org= |
| | =attachment:projects.org::some words= (text search) |
| docview | =docview:papers/last.pdf::NNN= |
| id | =id:B7423F4D-2E8A-471B-8810-C40F074717E9= |
| | =id:B7423F4D-2E8A-471B-8810-C40F074717E9::*task= (headline search) |
| news | =news:comp.emacs= |
| mailto | =mailto:adent@galaxy.net= |
| mhe | =mhe:folder= (folder link) |
| | =mhe:folder#id= (message link) |
| rmail | =rmail:folder= (folder link) |
| | =rmail:folder#id= (message link) |
| gnus | =gnus:group= (group link) |
| | =gnus:group#id= (article link) |
| bbdb | =bbdb:R.*Stallman= (record with regexp) |
| irc | =irc:/irc.com/#emacs/bob= |
| help | =help:org-store-link= |
| info | =info:org#External links= |
| shell | =shell:ls *.org= |
| elisp | =elisp:(find-file "Elisp.org")= (Elisp form to evaluate) |
| | =elisp:org-agenda= (interactive Elisp command) |
#+cindex: VM links
#+cindex: Wanderlust links
@ -3465,8 +3467,9 @@ current buffer:
- /Org mode buffers/ ::
For Org files, if there is a =<<target>>= at point, the link points
to the target. Otherwise it points to the current headline, which
is also the description.
to the target. If there is a named block (using =#+name:=) at
point, the link points to that name. Otherwise it points to the
current headline, which is also the description.
#+vindex: org-id-link-to-org-use-id
#+cindex: @samp{CUSTOM_ID}, property
@ -3484,6 +3487,32 @@ current buffer:
timestamp, depending on ~org-id-method~. Later, when inserting the
link, you need to decide which one to use.
#+vindex: org-id-link-consider-parent-id
#+vindex: org-id-link-use-context
#+vindex: org-link-context-for-files
When ~org-id-link-consider-parent-id~ is ~t~[fn:: Also,
~org-link-context-for-files~ and ~org-id-link-use-context~ should be
both enabled (which they are, by default).], parent =ID= properties
are considered. This allows linking to specific targets, named
blocks, or headlines (which may not have a globally unique =ID=
themselves) within the context of a parent headline or file which
does.
For example, given this org file:
#+begin_src org
,* Parent
:PROPERTIES:
:ID: abc
:END:
,** Child 1
,** Child 2
#+end_src
Storing a link with point at "Child 1" will produce a link
=<id:abc::*Child 1>=, which precisely links to the "Child 1"
headline even though it does not have its own ID.
- /Email/News clients: VM, Rmail, Wanderlust, MH-E, Gnus/ ::
#+vindex: org-link-email-description-format
@ -3763,7 +3792,9 @@ the link completion function like this:
:ALT_TITLE: Search Options
:END:
#+cindex: search option in file links
#+cindex: search option in id links
#+cindex: file links, searching
#+cindex: id links, searching
#+cindex: attachment links, searching
File links can contain additional information to make Emacs jump to a
@ -3775,8 +3806,8 @@ example, when the command ~org-store-link~ creates a link (see
line as a search string that can be used to find this line back later
when following the link with {{{kbd(C-c C-o)}}}.
Note that all search options apply for Attachment links in the same
way that they apply for File links.
Note that all search options apply for Attachment and ID links in the
same way that they apply for File links.
Here is the syntax of the different ways to attach a search to a file
link, together with explanations for each:
@ -11333,7 +11364,7 @@ pretty output for a number of export backends.
:END:
#+cindex: @LaTeX{} fragments
#+vindex: org-format-latex-header
#+vindex: org-latex-preview-header
Org mode can contain LaTeX math fragments, and it supports ways to
process these for several export backends. When exporting to LaTeX,
the code is left as it is. When exporting to HTML, Org can use either
@ -11390,20 +11421,19 @@ lines:
:END:
#+cindex: @LaTeX{} fragments, preview
#+vindex: org-preview-latex-default-process
#+vindex: org-latex-preview-process-default
If you have a working LaTeX installation and =dvipng=, =dvisvgm= or
=convert= installed[fn:38], LaTeX fragments can be processed to
produce images of the typeset expressions to be used for inclusion
while exporting to HTML (see [[*LaTeX fragments]]), or for inline
previewing within Org mode.
#+vindex: org-format-latex-options
#+vindex: org-format-latex-header
You can customize the variables ~org-format-latex-options~ and
~org-format-latex-header~ to influence some aspects of the preview.
In particular, the ~:scale~ (and for HTML export, ~:html-scale~)
property of the former can be used to adjust the size of the preview
images.
#+vindex: org-latex-preview-appearance-options
#+vindex: org-latex-preview-header
You can customize the variables ~org-latex-preview-appearance-options~ and
~org-latex-preview-header~ to influence some aspects of the preview.
In particular, the ~:scale~ and ~:zoom~ properties of the former can
be used to adjust the size of the preview images.
- {{{kbd(C-c C-x C-l)}}} (~org-latex-preview~) ::
#+kindex: C-c C-x C-l
@ -11427,6 +11457,24 @@ To disable it, simply use
: #+STARTUP: nolatexpreview
#+vindex: org-latex-preview-numbered
When generating previews, Org mode can track equation numbers and keep
them consistent by regenerating previews when necessary. This
behavior is controlled by the variable ~org-latex-preview-numbered~.
Org mode can automatically preview LaTeX fragments as you type them,
and hide and reveal preview images as the cursor moves into or out of
them. To enable this, turn on the minor mode
~org-latex-preview-auto-mode~.
- (~org-latex-preview-auto-mode~) ::
#+findex: org-latex-preview-auto-mode
Generate previews of LaTeX fragments as they are entered in the
buffer. Previews will be generated when there is any change to the
buffer that includes the insertion of a LaTeX environment or
fragment.
*** Using CDLaTeX to enter math
:PROPERTIES:
:DESCRIPTION: Speed up entering of formulas.
@ -12527,7 +12575,7 @@ Inclusions may specify a file-link to extract an object matched by
ranges for =:lines= keyword are relative to the requested element.
Therefore,
: #+INCLUDE: "./paper.org::*conclusion" :lines 1-20
: #+INCLUDE: "./paper.org::*conclusion" :lines "1-20"
#+texinfo: @noindent
includes the first 20 lines of the headline named =conclusion=.
@ -16740,6 +16788,378 @@ user-friendly improvements. See
<https://orgmode.org/worg/dev/org-export-reference.html> for more
details.
*** Export features
**** The underlying idea
Across export backends it is common to want to include certain chunks
of content that are only relevant in particular situations.
With static export templates, one is forced to choose between
including everything that /might/ be wanted, or including very little
by default and requiring common content to be manually added every
time it is wanted.
"Export features" allow for a third option, a much more sophisticated
method of resolving this dilemma. At the start of the export process,
the buffer being exported and the export communication plist (~info~)
are scanned to determine which capabilities are relevant to the
current export. During the construction of the final output this list
of capabilities is used to produce snippets of content to be included.
This can be thought of as the construction of a graph between conditions,
features, and feature implementations. For example, say we have three conditions
we want to support:
+ Say that images need some extra setup to be supported well, we can
just include it when image links are found in the buffer.
+ Say we can better support emojis by treating them as images in a
particular export backend. We could look for a signal in the buffer
that emojis should be handled as images, and then make use of some
"image support" and "emoji support" snippets.
+ Say that LaTeX maths requires some extra setup, we can just
do this when inline LaTeX fragments are found.
This situation can be crudely drawn with the following graph:
#+begin_example
condition feature implementation
========= ======= ===============
[emoji] -----------> emoji ------> [emoji plist]
\
'---->----.
\
[image link] ------> image ------> [image plist]
[inline LaTeX] ----> maths ------> [maths plist]
\____________________/ \__________________/
phase 1 phase 2
#+end_example
In phase 1 "feature detection" the relevant features are determined, and in
phase 2 "feature implementation" how those features can be provided is worked
out.
**** Feature detection
After the expansion of =#+include= statements and the removal of
comments, the export communication plist (~info~) is annotated. At the
very end of the annotation process, ~org-export-detect-features~ is
run to determine the list of capabilities relevant to the current export.
This operates by merging the global feature condition alist
(~org-export-conditional-features~) with the ~feature-conditions~ slot
of the current backend and each of its parents. This produces the
total feature conditions alist, which has the form:
#+begin_example
((condition . implied-features)
...)
#+end_example
Where =condition= is a test that implies that =implied-features= are
relevant. While =implied-features= is always a list of feature
symbols, for convenience =condition= can take a number of forms,
namely:
+ A regexp which is searched for in the export buffer.
+ A variable, if a string it is used as a regexp search, otherwise any
non-nil value is taken to imply =implied-features=.
+ A (unary) function, which is called with on the export communication
plist (~info~). A returned string is used as a regexp search,
otherwise any non-nil value is taken to imply =implied-features=.
As an example, a feature conditions alist which checks whether any
headings exist, and if the word "hello" appears could take the
following form:
#+begin_example
(((lambda (info)
(org-element-map (plist-get info :parse-tree) 'heading
#'identity info t))
headlines)
("hello" has-greeting))
#+end_example
Conditions are inherited from parent export backends and (for conditions general
enough to apply across backends) the variable ~org-export-conditional-features~.
#+begin_example
,--> beamer
/
html <----. ,----> latex --'
\ /
org-export-conditional-features
/ \
ascii <----' '----> odt
#+end_example
**** Feature implementations
The other half of the export feature system is of course producing
snippets of content from the list of features. Export backends can do
this at any point via ~org-export-expand-feature-snippets~. This
operates on a /feature implementation alist/. The implementation alist
is essentially a mirror of the condition alist, instead of =(condition
. feature-list)= elements it takes =(feature . implementation-plist)=
elements. This reversal of order may seem a bit odd, but it should
help make the condition--feature--implementation graph more apparent.
For example, considering this example condition alist and implementation alist
would be represented as the earlier graph.
#+begin_example
;; The condition alist
(([emoji predicate] emoji image)
([image predicate] image)
([inline LaTeX predicate] maths))
;; The implementation alist
((emoji [plist])
(image [plist])
(maths [plist])
#+end_example
The implementation plist recognises a number of keywords, but the
primary keyword is ~:snippet~. The snippet value provides the snippet
content used to provide the feature's capability. Much like
~condition~, it accepts a number of forms for convenience, namely:
+ A string, which is passed on.
+ A variable symbol, the value of which must be a string.
+ A (unary) function, which is called on the export communication
plist (~info~), and must return a string.
Note that no keys are mandatory in the implementation plist (not even
~:snippet~).
Like conditions, implementations are also inherited from parent backends, but
there is no "root" global list of implementations, as they are always
backend-specific.
#+begin_example
,--> beamer
/
html <---o o---> latex --'
ascii <---o o---> odt
#+end_example
**** Feature dependency and incompatibility
While just connecting features with snippets satisfies most use cases,
this system is designed to also allow for complex configurations of
inter-dependent snippets.
In slightly more complex examples, we may run across implementations
which either (a) only make sense when another feature is active, or
(b) require another implementation to be used in order to work. These
two situations are covered by the ~:when~ and ~:requires~ keywords
respectively. The accept either a single feature symbol or a list of
feature symbols.
For example, if an implementation contains ~:when featA~, it will only
be used when =featA= is active. If the implementation contains ~:when
(featA featB)~ it will require /both/ =featA= and =featB= to be
active. The ~:requires~ keyword works in the same way, but
unconditionally requires implementations instead of testing for them.
Occasionally one implementation may be incompatible with another. For
example, in LaTeX loading the same package with different options will
often produce an "options clash" error. To ensure that incompatible
implementations are not used, the ~:prevents~ keyword makes it as if
the feature were never used in the first place.
Circular ~:requires~ and ~:prevents~, or features that are
simultaneously required and prevented result in undefined behaviour.
Similarly, the behaviour of mutual ~:when~s (e.g. ~(a :when b) (b
:when a)~ is also undefined.
**** Feature ordering
In many scenarios it is not only /which/ snippets are included that
matters, but the /order/ in which they are placed. A requirement for a
certain snippet to appear before/after others can be specified through
the ~:before~ and ~:after~ keywords. Like ~:when~, ~:requires~, and
~:prevents~ they accept either a single feature symbol or a list of
feature symbols.
As an example, should an implementation plist contain ~:before featA
:after (featB featC)~ it will be placed after =featA= but before
=featB= and =featC=. It is possible to accidentally create circular
dependencies, in which case an error will be raised.
While ~:before~ and ~:after~ work well for specifying relative
ordering, it can also be useful to specify the /absolute/ ordering,
for instance to put something first or last. This can be controlled
via the ~:order~ keyword. Each implementation has an ~:order~ of zero
by default. Implementations with a higher ~:order~ come later.
# REVIEW maybe give a convention on :order ranges?
# Perhaps take inspiration from ~add-hook~.
-----
The overall ordering behaviour can be characterized as a ascending
sort of ~:order~ followed by a stable [[https://en.wikipedia.org/wiki/Topological_sorting][topological sort]] based on
~:before~ and ~:after~.
**** Adding or editing export features
The export features of a backend can be modified via the convenience
macro ~org-export-update-features~. This is invoked with the following
form:
#+begin_example
(org-export-update-features 'BACKEND
(FEATURE-NAME
:PROPERTY VALUE
...)
...)
#+end_example
For each feature mentioned, it sets the each =:PROPERTY= to =VALUE= in
the implementation plist. The one exception to this is ~:condition~ in
which case the backend's feature condition alist is modified so that
the condition is taken to imply the feature.
Setting ~:condition t~ will thus make the feature enabled by default. This is not
a special case, but rather an instance of the general behaviour of obtaining the
value of any non-function symbol provided, and as ~(symbol-value 't)~ is always
non-nil, the associated features will always be considered active. Conversely
setting ~:condition nil~ will make it so no conditions imply the feature. This is
possible thanks to special behavior that removes the feature from all other
conditions' associations when ~nil~ is given.
Since having a anonymous function (lambda) is expected to be
reasonably common with ~:condition~ and ~:snippet~, for those keywords
and sexp given is implicitly wrapped with ~(lambda (info) SEXP)~.
If the backend is not available at the time the feature update is run,
an error will be raised.
**** Custom export feature examples
To make the usage of ~org-export-update-features~ and the capabilities
of the export feature system clearer, here are a few examples
~org-export-update-features~ invocations.
Say you want to apply the [[https://ctan.org/pkg/chickenize][chickenize]] package to the word "wacky" when
the title starts with "wacky". We can implement that by testing for
the regexp =^#\\+title: Wacky= and including
src_latex{\usepackage[chickenstring[1]='wacky']{chickenize}} when it is
found.
#+begin_example
(org-export-update-features 'latex
(wacky-chicken
:condition "^#\\+title: Wacky"
:snippet "\\usepackage[chickenstring[1]='wacky']{chickenize}"))
#+end_example
However, if =#+title: Wacky= is placed inside an example block, this
regexp will match even though the match isn't actually parsed as a
keyword. To be more robust, we can inspect ~info~ instead.
#+begin_example
(org-export-update-features 'latex
(wacky-chicken
:condition (and (car (plist-get info :title))
(string-match-p "^Wacky" (car (plist-get info :title))))
:snippet "\\usepackage[chickenstring[1]='wacky']{chickenize}"))
#+end_example
=chickenize= is a LuaLaTeX only package, and so trying to use this
when compiling with pdfLaTeX or XeLaTeX will cause issues. This may
well apply to other snippets too, so it could make sense to make a
=lualatex= feature to indicate when LuaLaTeX is being used.
#+begin_example
(org-export-update-features 'latex
(lualatex
:condition (equal (plist-get info :latex-compiler) "lualatex")))
#+end_example
To only use the chickenize snippet with LuaLaTeX, we can now add
=lualatex= as a ~:when~ clause.
#+begin_example
(org-export-update-features 'latex
(wacky-chicken
:when lualatex))
#+end_example
Hopefully this has given you an impression of how ~:condition~, ~:when~, and
~:snippet~ can look in practice. To demonstrate ~:requires~, ~:prevents~, and
~:after~ we will consider another LaTeX example.
Say that you wanted a few named special blocks to automatically export nicely,
such as =#+begin_warning=, =#+begin_info=, and =#+begin_note=. All three of
these can be individually detected and handled appropriately. For the sake of
simplicity, a crude regexp will be used here, however examining the parse tree
would be a more robust solution.
#+begin_example
(org-export-update-features 'latex
(box-warning
:condition "^[ \t]*#\\+begin_warning"
:snippet "\\mysetupbox{warning}"
:requires mysetupbox
:after mysetupbox)
(box-info
:condition "^[ \t]*#\\+begin_info"
:snippet "\\mysetupbox{info}"
:requires mysetupbox
:after mysetupbox)
(box-note
:condition "^[ \t]*#\\+begin_note"
:snippet "\\mysetupbox{note}"
:requires mysetupbox
:after mysetupbox)
(mysetupbox
:snippet "\newcommand{\mysetupbox}...")
#+end_example
Here, all three box types make use of LaTeX command ~\mysetupbox~ which is
provided by the =mysetupbox= feature implementation. By using a ~:requires~ for
this, we can make sure it is availible that it is loaded once, and thanks to the
~:after mysetupbox~ lines the specific box setup invocations will occur after
the ~\newcommand{\mysetupbox}...~ definition.
Say that when using beamer you use a package that defines its own
warning/info/note environments and you'd like to use those. In that case one can
make an on-by-default beamer feature that prevents the box features from being
used.
#+begin_example
(org-export-update-features 'beamer
(no-custom-boxes
:condition t
:prevents (box-warning box-info box-note)))
#+end_example
**** Detailed explanation of the implementation resolution process
The previous descriptions of the "export feature" behaviour should give a clear
overview of how this feature works. In case more detail is wanted, or should
there be any ambiguity, here is a more technical description of the overall
feature implementation resolution process.
1. The list of detected features is used to obtain all applicable corresponding
feature implementations. Detected feature symbols may have /no/ corresponding
implementation.
2. The list of feature implementations is sorted according to ~:order~.
3. Using a queue, all required features (~:requires~) are added to the list of feature
implementations, as are their requirements recursively. Should a feature that
has no implementation be required, an ~org-missing-feature-dependency~ error is raised.
4. The implementations with a ~:when~ condition are scanned and marked as
confirmed when all of the ~:when~ conditions are known to be satisfied. This is
repeated until there is no change in the list of confirmed implementations,
at which point all non-confirmed implementations are removed.
5. For each of the feature implementations in turn, prevented features
(~:prevents~) are removed from the list. Feature implementations that are only
present because of a feature that has now been removed are themselves
removed, recursively.
6. The list of feature implementations is sorted according to ~:order~, again.
7. A stable topological sort is performed using ~:before~ and ~:after~. Should any
circular dependencies be found, a ~org-circular-feature-dependency~ error is raised.
** Export Region
:PROPERTIES:
:DESCRIPTION: Author tables and lists in Org syntax.
@ -18090,9 +18510,9 @@ Here are examples of passing values by reference:
names---because the second row is a horizontal rule---then Org
removes the column names, processes the table, puts back the column
names, and then writes the table to the results block. Using =yes=,
Org does the same to the first row, even if the initial table does
not contain any horizontal rule. When set to =no=, Org does not
pre-process column names at all.
Org does the same to the first non-hline row, even if the initial
table does not contain any horizontal rule. When set to =no=, Org
does not pre-process column names at all.
# We keep python blocks unindented on purpose - to keep the example
# working even for users who changed the default value of ~org-src-preserve-indentation~
@ -21522,7 +21942,7 @@ The following =ol-man.el= file implements it
PATH should be a topic that can be thrown at the man command."
(funcall org-man-command path))
(defun org-man-store-link ()
(defun org-man-store-link (&optional _interactive?)
"Store a link to a man page."
(when (memq major-mode '(Man-mode woman-mode))
;; This is a man page, we do make this link.
@ -21582,13 +22002,15 @@ A review of =ol-man.el=:
For example, ~org-man-store-link~ is responsible for storing a link
when ~org-store-link~ (see [[*Handling Links]]) is called from a buffer
displaying a man page. It first checks if the major mode is
appropriate. If check fails, the function returns ~nil~, which
means it isn't responsible for creating a link to the current
buffer. Otherwise the function makes a link string by combining
the =man:= prefix with the man topic. It also provides a default
description. The function ~org-insert-link~ can insert it back
into an Org buffer later on.
displaying a man page. It is passed an argument ~interactive?~
which this function does not use, but other store functions use to
behave differently when a link is stored interactively by the user.
It first checks if the major mode is appropriate. If check fails,
the function returns ~nil~, which means it isn't responsible for
creating a link to the current buffer. Otherwise the function
makes a link string by combining the =man:= prefix with the man
topic. It also provides a default description. The function
~org-insert-link~ can insert it back into an Org buffer later on.
** Adding Export Backends
:PROPERTIES:
@ -23102,7 +23524,7 @@ used to create images, any LaTeX environment is handled.
[fn:38] These are respectively available at
[[https://sourceforge.net/projects/dvipng/]], [[http://dvisvgm.bplaced.net/]]
and from the ImageMagick suite. Choose the converter by setting the
variable ~org-preview-latex-default-process~ accordingly.
variable ~org-latex-preview-process-default~ accordingly.
[fn:39] This works automatically for the HTML backend (it requires
version 1.34 of the =htmlize.el= package, which you need to install).

View File

@ -460,6 +460,14 @@ timestamp object. Possible values: ~timerange~, ~daterange~, ~nil~.
~org-element-timestamp-interpreter~ takes into account this property
and returns an appropriate timestamp string.
**** =org-link= store functions are passed an ~interactive?~ argument
The ~:store:~ functions set for link types using
~org-link-set-parameters~ are now passed an ~interactive?~ argument,
indicating whether ~org-store-link~ was called interactively.
Existing store functions will continue to work.
*** ~org-priority=show~ command no longer adjusts for scheduled/deadline
In agenda views, ~org-priority=show~ command previously displayed the
@ -493,6 +501,157 @@ constant.
If you still want to use python-mode with ob-python, you might
consider [[https://gitlab.com/jackkamm/ob-python-mode-mode][ob-python-mode-mode]], where the code to support python-mode
has been ported to.
*** The LaTeX preview system has been overhauled
Org's LaTeX preview system has been overhauled
Org mode's LaTeX preview system has been rewritten with several improvements and new features.
- LaTeX Previews are generated in bulk, and hundreds of LaTeX fragments can be processed per second.
- Images are placed in the buffer continuously as they are generated.
- Preview generation is asynchronous and will not block Emacs.
- Inline previews are aligned and scaled to match the font baseline and size.
- Previews scale along with text when the text scale is changed.
- Previews are coloured to match surrounding text and the active theme.
- SVG previews automatically change colors when the active theme changes.
- Error encountered when compiling LaTeX fragments can be accessed by mousing over preview images.
- Preview overlays can hide and show themselves dynamically based on cursor position.
- Org mode can auto-generate LaTeX previews as you type or edit the text of existing ones.
- Org mode can keep equation numbering consistent by regenerating previews as needed.
**** New features
***** New minor mode ~org-latex-preview-auto-mode~
When this mode is turned on, LaTeX preview overlays in the buffer can be temporarily disabled by moving the cursor into them. Moving the cursor out will display the LaTeX preview image overlay again. Additionally, editing LaTeX fragments that have previews will cause the preview images to be updated.
If the option ~org-latex-preview-auto-track-inserts~ is non-nil (which see), previews for LaTeX fragments will be auto-generated as you type.
**** New options
***** A new option ~org-latex-preview-compiler-command-map~ for setting precompile commands
This alist maps compilers in ~org-latex-compilers~ to command strings
used for LaTeX precompilation when creating previews or LaTeX exports.
***** New option ~org-latex-preview-cache~ to enable preview image caching
When set to =persist=, images produced using ~org-latex-preview~ will
be cached and persisted across Emacs sessions using
~org-persist~. Temporary or custom directories can also be used.
***** New option ~org-latex-preview-persist-expiry~ to set persistence period
This is the number of days for which LaTeX preview images will be
cached, assuming persistence is turned on with
~org-latex-preview-cache~.
***** New option ~org-latex-preview-process-active-indicator~ to indicate preview processing for LaTeX fragments
This option controls the style of visual indicator for LaTeX fragments
currently being processed.
***** New option ~org-latex-preview-numbered~ to automatically renumber previewed LaTeX environments
When non-nil, previewed LaTeX environments in the buffer are
renumbered automatically as required.
***** New option =:page-width= in ~org-latex-preview-appearance-options~ to control the text width for LaTeX fragment previews
This option controls the width of text in preview images. This is
relevant for displaymath-type LaTeX environments, and can be specified
in multiple ways.
***** New option ~org-latex-preview-process-precompiled~ to control LaTeX precompilation
This option controls whether LaTeX headers for buffers are precompiled
when generating the first preview for the buffer. Precompilation can
speed up preview generation by a moderate to large amount depending on
the complexity of the header.
***** New option ~org-latex-preview-auto-track-inserts~ to auto-generate LaTeX previews when typing
When using ~org-latex-preview-auto-mode~ (which see), setting this option to a non-nil value will cause LaTeX previews to be rendered as you type LaTeX fragments. This works however new text is added to the buffer, for example by yanking. When this option is nil and ~org-latex-preview-auto-mode~ is on, existing previews for LaTeX fragments will still be auto-regenerated when edited but no new ones will be auto-generated.
**** Removed or renamed functions and variables
***** Mark ~org-format-latex-options~ obsolete
Use ~org-latex-preview-appearance-options~ instead. The replacement acts in the
same way, except that
- the preview image render size and display size can be independently
controlled with the =:scale= and =:zoom= plist keys, and
- the HTML export-specific parameters =:html-scale=,
=:html-foreground= and =:html-background= have been moved to
~org-html-latex-image-options~, which see.
- the new option =:page-width= (mentioned earlier)
***** Mark ~org-format-latex-header~ obsolete
Use ~org-latex-preview-header~ instead. The replacement variable
behaves the exact same way and is renamed for consistency.
***** Mark ~org-preview-latex-default-process~ obsolete
Use ~org-latex-preview-process-default~ instead. The replacement
variable behaves the exact same way and is renamed for consistency.
***** Mark ~org-preview-latex-process-alist~ obsolete
Use ~org-latex-preview-process-alist~ instead. The replacement
variable acts in the same way, except that =:latex-compiler= must now
be a single command or a list containing a single command. The
placeholder ="%S"= in the format string for =:latex-compiler= is
ignored.
***** Mark ~org-clear-latex-preview~ obsolete
Use ~org-latex-preview-clear-overlays~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-place-formula-image~ obsolete
This functionality is replaced with ~org-latex-preview-place-image~
(for images) and ~org-latex-preview-place-image-link~ (for links).
***** Mark ~org-latex-color-format~ obsolete
Use ~org-latex-preview--format-color~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-latex-color~ obsolete
Use ~org-latex-preview--attr-color~ instead. The replacement function
acts in the exact same way and is renamed for consistency.
***** Mark ~org-normalize-color~ obsolete
Use ~org-latex-preview--normalize-color~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-latex-to-mathml-jar-file~ obsolete
Use ~org-mathml-converter-jar-file~ instead. The replacement variable
acts in the exact same way and is renamed for consistency.
***** Mark ~org-latex-to-mathml-convert-command~ obsolete
Use ~org-mathml-convert-command~ instead. The replacement variable
acts in the exact same way and is renamed for consistency.
***** Mark ~org-format-latex-mathml-available-p~ obsolete
Use ~org-mathml-converter-available-p~ instead. The replacement
function acts in the exact same way and is renamed for consistency.
***** Mark ~org-create-math-formula~ obsolete
Use ~org-mathml-convert-latex~ instead. The replacement function acts
in the exact same way and is renamed for consistency.
*** =ox-icalendar.el= line ending fix may affect downstream packages
@ -538,7 +697,45 @@ The change is breaking when ~org-use-property-inheritance~ is set to ~t~.
*** ~org-babel-lilypond-compile-lilyfile~ ignores optional second argument
The =TEST= parameter is better served by Emacs debugging tools.
*** =id:= links support search options; ~org-id-store-link~ adds search option by default
Adding search option by ~org-id-store-link~ can be disabled by setting
~org-id-link-use-context~ to ~nil~, or toggled for a single call by
passing universal argument.
When using this feature, IDs should not include =::=, which is used in
links to indicate the start of the search string. For backwards
compability, existing IDs including =::= will still be matched (but
cannot be used together with search option). A new org-lint checker
has been added to warn about this.
*** ~org-store-link~ behaviour storing additional =CUSTOM_ID= links has changed
Previously, when storing =id:= link, ~org-store-link~ stored an
additional "human readable" link using a node's =CUSTOM_ID= property.
This behaviour has been expanded to store an additional =CUSTOM_ID=
link when storing any type of external link type in an Org file, not
just =id:= links.
** New and changed options
*** New option ~org-beamer-frame-environment~
The new option defines name of an alternative environment to be used
for fragile beamer frames. This option is needed to work around
beamer bug with frame contents containing literal =\end{frame}= string
(for example, inside example blocks). See
https://github.com/josephwright/beamer/issues/360
The default value is =orgframe=.
The option should normally not be changed, except when you need to put
=\end{orgframe}= string inside beamer frames.
A checker has been added to =M-x org-lint= to detect instances of
~org-beamer-frame-environment~ in Org documents.
*** New option ~org-export-process-citations~
The new option controls whether to use citation processors to process
@ -852,7 +1049,51 @@ This option starts the agenda to automatically include archives,
propagating the value for this variable to ~org-agenda-archives-mode~.
For acceptable values and their meaning, see the value of that variable.
*** New option ~org-id-link-consider-parent-id~ to allow =id:= links to parent headlines
For =id:= links, when this option is enabled, ~org-store-link~ will
look for ids from parent/ancestor headlines, if the current headline
does not have an id.
Combined with the new ability for =id:= links to use search options
[fn:: when =org-id-link-use-context= is =t=, which is the default],
this allows linking to specific headlines without requiring every
headline to have an id property, as long as the headline is unique
within a subtree that does have an id property.
For example, given this org file:
#+begin_src org
,* Parent
:PROPERTIES:
:ID: abc
:END:
,** Child 1
,** Child 2
#+end_src
Storing a link with point at "Child 1" will produce a link
=<id:abc::*Child 1>=, which precisely links to the "Child 1" headline
even though it does not have its own ID. By giving files top-level id
properties, links to headlines in the file can also be made more
robust by using the file id instead of the file path.
** New features
*** =ob-tangle.el=: New flag to remove tangle targets before writing
When ~org-babel-tangle-remove-file-before-write~ is set to ~t~ the
tangle target is removed before writing. This will allow overwriting
read-only tangle targets. However, when tangle target is a symlink,
this will convert the tangle target into an ordinary file.
The default value is ~auto~ -- overwrite tangle targets when they are
read-only.
*** ~org-bibtex-yank~ accepts a prefix argument
When called with a prefix argument, ~org-bibtex-yank~ adds data to the
headline of the entry at point instead of creating a new one.
*** =ob-plantuml.el=: Support tikz file format output
=ob-plantuml.el= now output =tikz= :file format via
@ -1052,6 +1293,11 @@ The same can be done via startup options:
: #+STARTUP: fnanon
** New functions and changes in function arguments
*** New optional argument =UPDATE-HEADING= for ~org-bibtex-yank~
When the new argument is non-nil, add data to the headline of the
entry at point.
*** New API functions to store data within ~org-element-cache~
Elisp programs can now store data inside Org element cache.
@ -1142,6 +1388,25 @@ as the function can also act on objects.
*** ~org-export-get-parent-element~ is renamed to ~org-element-parent-element~ and moved to =lisp/org-element.el=
*** ~org-insert-heading~ optional argument =TOP= is now =LEVEL=
A numeric value forces a heading at that level to be inserted. For
backwards compatibility, non-numeric non-nil values insert level 1
headings as before.
*** New optional argument for ~org-id-get~
New optional argument =INHERIT= means inherited ID properties from
parent entries are considered when getting an entry's ID (see
~org-id-link-consider-parent-id~ option).
*** New optional argument for ~org-link-search~
If a missing heading is created to match the search string, the new
optional argument =NEW-HEADING-CONTAINER= specifies where in the
buffer it will be added. If not specified, new headings are created
at level 1 at the end of the accessible part of the buffer, as before.
** Miscellaneous
*** =org-crypt.el= now applies initial visibility settings to decrypted entries
@ -1181,6 +1446,12 @@ For symmetry with =\S= and =\sect= for the section symbol, =\P= has
been added as an another form for the pilcrow symbol currently
available as =\para=.
*** ~org-table-to-lisp~ no longer clobbers the regexp global state
It does no longer use regexps.
It is also faster. Large tables can be read quickly.
* Version 9.6
** Important announcements and breaking changes

View File

@ -73,10 +73,12 @@
(declare-function org-element-parent "org-element-ast" (node))
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
(declare-function org-element-type-p "org-element-ast" (node &optional types))
(declare-function org-element-interpret-data "org-element" (data))
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
(declare-function org-escape-code-in-region "org-src" (beg end))
(declare-function org-forward-heading-same-level "org" (arg &optional invisible-ok))
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance))
(declare-function org-indent-block "org" ())
(declare-function org-indent-line "org" ())
(declare-function org-list-get-list-end "org-list" (item struct prevs))
(declare-function org-list-prevs-alist "org-list" (struct))
@ -700,8 +702,9 @@ By default, consider the block at point. However, when optional
argument DATUM is provided, extract information from that parsed
object instead.
Return nil if point is not on a source block. Otherwise, return
a list with the following pattern:
Return nil if point is not on a source block (blank lines after a
source block are considered a part of that source block).
Otherwise, return a list with the following pattern:
(language body arguments switches name start coderef)"
(let* ((datum (or datum (org-element-context)))
@ -1192,11 +1195,16 @@ Return t if a code block was found at point, nil otherwise."
;; we want to restore this location after executing BODY.
(outside-position
(and (<= (line-beginning-position)
(org-element-post-affiliated element))
(org-element-post-affiliated element))
(point-marker)))
(org-src-window-setup 'switch-invisibly))
(when (and (org-babel-where-is-src-block-head element)
(org-edit-src-code))
(condition-case nil
(org-edit-src-code)
(t
(org-edit-src-exit)
(when outside-position (goto-char outside-position))
nil)))
(unwind-protect (progn ,@body)
(org-edit-src-exit)
(when outside-position (goto-char outside-position)))
@ -1826,6 +1834,8 @@ HEADER-ARGUMENTS is alist of all the arguments."
Return a cons cell, the `car' of which contains the TABLE less
colnames, and the `cdr' of which contains a list of the column
names."
;; Skip over leading hlines.
(while (eq 'hline (car table)) (pop table))
(if (eq 'hline (nth 1 table))
(cons (cddr table) (car table))
(cons (cdr table) (car table))))
@ -1887,9 +1897,16 @@ of the vars, cnames and rnames."
(when (and (not (equal colnames "no"))
;; Compatibility note: avoid `length>', which
;; isn't available until Emacs 28.
(or colnames (and (> (length (cdr var)) 1)
(eq (nth 1 (cdr var)) 'hline)
(not (member 'hline (cddr (cdr var)))))))
(or colnames
;; :colnames nil (default)
;; Auto-assign column names when the table
;; has hline as the second line after
;; non-hline row.
(and (> (length (cdr var)) 1)
(not (eq (car (cdr var)) 'hline)) ; first row
(eq (nth 1 (cdr var)) 'hline) ; second row
(not (member 'hline (cddr (cdr var)))) ; other rows
)))
(let ((both (org-babel-get-colnames (cdr var))))
(setq cnames (cons (cons (car var) (cdr both))
cnames))
@ -2075,7 +2092,7 @@ With optional prefix argument ARG, jump backward ARG many source blocks."
(goto-char (match-beginning 5)))))
(defun org-babel-demarcate-block (&optional arg)
"Wrap or split the code in the region or on the point.
"Wrap or split the code in an active region or at point.
With prefix argument ARG, also create a new heading at point.
@ -2085,41 +2102,76 @@ is created. In both cases if the region is demarcated and if the
region is not active then the point is demarcated.
When called within blank lines after a code block, create a new code
block of the same language with the previous."
block of the same language as the previous."
(interactive "P")
(let* ((info (org-babel-get-src-block-info 'no-eval))
(start (org-babel-where-is-src-block-head))
;; `start' will be nil when within space lines after src block.
(block (and start (match-string 0)))
(headers (and start (match-string 4)))
(body-beg (and start (match-beginning 5)))
(body-end (and start (match-end 5)))
(stars (concat (make-string (or (org-current-level) 1) ?*) " "))
(upper-case-p (and block
(let (case-fold-search)
(string-match-p "#\\+BEGIN_SRC" block)))))
(if (and info start) ;; At src block, but not within blank lines after it.
(mapc
(lambda (place)
(save-excursion
(goto-char place)
(let ((lang (nth 0 info))
(indent (make-string (org-current-text-indentation) ?\s)))
(when (string-match "^[[:space:]]*$"
(buffer-substring (line-beginning-position)
(line-end-position)))
(delete-region (line-beginning-position) (line-end-position)))
(insert (concat
(if (looking-at "^") "" "\n")
indent (if upper-case-p "#+END_SRC\n" "#+end_src\n")
(if arg stars indent) "\n"
indent (if upper-case-p "#+BEGIN_SRC " "#+begin_src ")
lang
(if (> (length headers) 1)
(concat " " headers) headers)
(if (looking-at "[\n\r]")
""
(concat "\n" (make-string (current-column) ? )))))))
(move-end-of-line 2))
(sort (if (org-region-active-p) (list (mark) (point)) (list (point))) #'>))
(let* ((copy (org-element-copy (org-element-at-point)))
(before (org-element-begin copy))
(beyond (org-element-end copy))
(parts
(if (org-region-active-p)
(list body-beg (region-beginning) (region-end) body-end)
(list body-beg (point) body-end)))
(pads ;; To calculate left-side white-space padding.
(if (org-region-active-p)
(list (region-beginning) (region-end))
(list (point))))
(n (- (length parts) 2)) ;; 1 or 2 parts in `dolist' below.
;; `post-blank' caches the property before setting it to 0.
(post-blank (org-element-property :post-blank copy)))
;; Point or region are within body when parts is in increasing order.
(unless (apply #'<= parts)
(user-error "Select within the source block body to split it"))
(setq parts (mapcar (lambda (p) (buffer-substring (car p) (cdr p)))
(seq-mapn #'cons parts (cdr parts))))
;; Map positions to columns for white-space padding.
(setq pads (mapcar (lambda (p) (save-excursion
(goto-char p)
(current-column)))
pads))
(push 0 pads) ;; The 1st part never requires white-space padding.
(setq parts (mapcar (lambda (p) (string-join
(list (make-string (car p) ?\s)
(cdr p))))
(seq-mapn #'cons pads parts)))
(delete-region before beyond)
;; Set `:post-blank' to 0. We take care of spacing between blocks.
(org-element-put-property copy :post-blank 0)
(org-element-put-property copy :value (car parts))
(insert (org-element-interpret-data copy))
;; `org-indent-block' may see another `org-element' (e.g. paragraph)
;; immediately after the block. Ensure to indent the inserted block
;; and move point to its end.
(org-babel-previous-src-block 1)
(org-indent-block)
(goto-char (org-element-end (org-element-at-point)))
(org-element-put-property copy :caption nil)
(org-element-put-property copy :name nil)
;; Insert the 2nd block, and the 3rd block when region is active.
(dolist (part (cdr parts))
(org-element-put-property copy :value part)
(insert (if arg (concat stars "\n") "\n"))
(cl-decf n)
(when (= n 0)
;; Use `post-blank' to reset the property of the last block.
(org-element-put-property copy :post-blank post-blank))
(insert (org-element-interpret-data copy))
;; Ensure to indent the inserted block and move point to its end.
(org-babel-previous-src-block 1)
(org-indent-block)
(goto-char (org-element-end (org-element-at-point))))
;; Leave point at the last inserted block.
(goto-char (org-babel-previous-src-block 1)))
(let ((start (point))
(lang (or (car info) ; Reuse language from previous block.
(completing-read

View File

@ -187,7 +187,7 @@ code."
;; value of the variable
(mapc (lambda (pair)
(setq body (replace-regexp-in-string
(format "\\$%s" (car pair)) (cdr pair) body)))
(format "\\$%s" (car pair)) (cdr pair) body t t)))
vars)
(when prologue (funcall add-to-body prologue))
(when epilogue (setq body (concat body "\n" epilogue)))

View File

@ -37,7 +37,7 @@
(require 'ob)
(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-guess-inputenc "ox-latex" (header))
(declare-function org-splice-latex-header "org" (tpl def-pkg pkg snippets-p &optional extra))
@ -48,10 +48,10 @@
(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex"))
(defvar org-format-latex-header) ; From org.el
(defvar org-format-latex-options) ; From org.el
(defvar org-latex-default-packages-alist) ; From org.el
(defvar org-latex-packages-alist) ; From org.el
(defvar org-latex-preview-header) ; From org-latex-preview.el
(defvar org-latex-preview-appearance-options) ; From org-latex-preview.el
(defvar org-latex-default-packages-alist) ; From org-latex-preview.el
(defvar org-latex-packages-alist) ; From org-latex-preview.el
(defvar org-babel-default-header-args:latex
'((:results . "latex") (:exports . "results"))
@ -136,7 +136,7 @@ exporting the literal LaTeX source."
(regexp-quote (format "%S" (car pair)))
(if (stringp (cdr pair))
(cdr pair) (format "%S" (cdr pair)))
body)))
body t t)))
(org-babel--get-vars params))
(let ((prologue (cdr (assq :prologue params)))
(epilogue (cdr (assq :epilogue params))))
@ -167,11 +167,11 @@ This function is called by `org-babel-execute-src-block'."
(append (cdr (assq :packages params)) org-latex-packages-alist)))
(cond
((and (string-suffix-p ".png" out-file) (not imagemagick))
(let ((org-format-latex-header
(concat org-format-latex-header "\n"
(let ((org-latex-preview-header
(concat org-latex-preview-header "\n"
(mapconcat #'identity headers "\n"))))
(org-create-formula-image
body out-file org-format-latex-options in-buffer)))
(org-latex-preview-create-image
body out-file org-latex-preview-appearance-options in-buffer)))
((string= "svg" extension)
(with-temp-file tex-file
(insert (concat (funcall org-babel-latex-preamble params)
@ -238,7 +238,7 @@ This function is called by `org-babel-execute-src-block'."
(insert
(org-latex-guess-inputenc
(org-splice-latex-header
org-format-latex-header
org-latex-preview-header
(delq
nil
(mapcar

View File

@ -142,7 +142,7 @@ blocks.")
(replace-regexp-in-string
(concat "$" (regexp-quote name))
(if (stringp value) value (format "%S" value))
body))))
body t t))))
vars)
(concat
(and prologue (concat prologue "\n"))

View File

@ -51,7 +51,7 @@ $VAR instances are replaced by VAR values defined in PARAMS."
(setq body (replace-regexp-in-string
(regexp-quote (format "$%s" (car var)))
(format "%s" (cdr var))
body nil 'literal)))
body 'fixedcase 'literal)))
(let ((prologue (cdr (assq :prologue params)))
(epilogue (cdr (assq :epilogue params))))

View File

@ -113,9 +113,7 @@
Set `sql-product' in Org edit buffer according to the
corresponding :engine source block header argument."
(let ((product (cdr (assq :engine (nth 2 info)))))
(condition-case nil
(sql-set-product product)
(user-error "Cannot set `sql-product' in Org Src edit buffer"))))
(sql-set-product product)))
(defun org-babel-sql-dbstring-mysql (host port user password database)
"Make MySQL cmd line args for database connection. Pass nil to omit that arg."
@ -409,11 +407,11 @@ argument mechanism."
val (if sqlite
nil
'(:fmt (lambda (el) (if (stringp el)
el
(format "%S" el))))))))
el
(format "%S" el))))))))
data-file)
(if (stringp val) val (format "%S" val))))
body)))
body t t)))
vars)
body)

View File

@ -166,6 +166,23 @@ read-write permissions for the user, read-only for everyone else."
:package-version '(Org . "9.6")
:type 'integer)
(defcustom org-babel-tangle-remove-file-before-write 'auto
"How to overwrite the existing tangle target.
When set to nil, `org-babel-tangle' will replace contents of an existing
tangle target (and fail when tangle target is read-only).
When set to t, the tangle target (including read-only) will be deleted
first and a new file, possibly with different ownership and
permissions, will be created.
When set to symbol `auto', overwrite read-only tangle targets and
replace contents otherwise."
:group 'org-babel-tangle
:package-version '(Org . "9.7")
:type '(choice
(const :tag "Replace contents, but keep the same file" nil)
(const :tag "Re-create file" t)
(const :tag "Re-create when read-only" auto))
:safe t)
(defun org-babel-find-file-noselect-refresh (file)
"Find file ensuring that the latest changes on disk are
represented in the file."
@ -323,7 +340,19 @@ matching a regular expression."
(error "Not allowed to tangle into the same file as self"))
;; We do not erase, but overwrite previous file
;; to preserve any existing symlinks.
(write-region nil nil file-name)
;; This behavior is modified using
;; `org-babel-tangle-remove-file-before-write' to
;; tangle to read-only files.
(when (and
(file-exists-p file-name)
(pcase org-babel-tangle-remove-file-before-write
(`auto (not (file-writable-p file-name)))
(`t t)
(`nil nil)
(_ (error "Invalid value of `org-babel-tangle-remove-file-before-write': %S"
org-babel-tangle-remove-file-before-write))))
(delete-file file-name))
(write-region nil nil file-name)
(mapc (lambda (mode) (set-file-modes file-name mode)) modes))
(push file-name path-collector))))))
(if (equal arg '(4))

View File

@ -79,6 +79,12 @@
(declare-function org-open-file "org" (path &optional in-emacs line search))
(declare-function org-element-interpret-data "org-element" (data))
(declare-function org-element-parse-secondary-string "org-element" (string restriction &optional parent))
(declare-function org-element-map "org-element"
( data types fun
&optional
info first-match no-recursion
with-affiliated no-undefer))
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-element-contents "org-element-ast" (node))
@ -367,7 +373,7 @@ personal names of the form \"family, given\"."
(cond
((stringp names) (setq names-string names))
((org-element-type-p names 'raw)
(setq names-string (mapconcat #'identity (org-element-contents names))
(setq names-string (mapconcat #'identity (org-element-contents names) "")
raw-p t)))
(when names-string
(setq names-string
@ -463,6 +469,38 @@ necessary, unless optional argument NO-SUFFIX is non-nil."
new))))
(if no-suffix year (concat year suffix)))))))
(defun org-cite-basic--print-bibtex-string (element &optional info)
"Print Bibtex formatted string ELEMENT, according to Bibtex syntax.
Remove all the {...} that are not a part of LaTeX macros and parse the
LaTeX fragments. Do nothing when current backend is derived from
LaTeX, according to INFO.
Return updated ELEMENT."
(if (org-export-derived-backend-p (plist-get info :back-end) 'latex)
;; Derived from LaTeX, no need to use manual ad-hoc LaTeX
;; parser.
element
;; Convert ELEMENT to anonymous when ELEMENT is string.
;; Otherwise, we cannot modify ELEMENT by side effect.
(when (org-element-type-p element 'plain-text)
(setq element (org-element-create 'anonymous nil element)))
;; Approximately parse LaTeX fragments, assuming Org mode syntax
;; (it is close to original LaTeX, and we do not want to
;; re-implement complete LaTeX parser here))
(org-element-map element t
(lambda (str)
(when (stringp str)
(org-element-set
str
(org-element-parse-secondary-string
str '(latex-fragment entity))))))
;; Strip the remaining { and }.
(org-element-map element t
(lambda (str)
(when (stringp str)
(org-element-set str (replace-regexp-in-string "[{}]" "" str)))))
element))
(defun org-cite-basic--print-entry (entry style &optional info)
"Format ENTRY according to STYLE string.
ENTRY is an alist, as returned by `org-cite-basic--get-entry'.
@ -474,27 +512,29 @@ Optional argument INFO is the export state, as a property list."
(org-cite-basic--get-field 'journal entry info)
(org-cite-basic--get-field 'institution entry info)
(org-cite-basic--get-field 'school entry info))))
(pcase style
("plain"
(let ((year (org-cite-basic--get-year entry info 'no-suffix)))
(org-cite-concat
(org-cite-basic--shorten-names author) ". "
title (and from (list ", " from)) ", " year ".")))
("numeric"
(let ((n (org-cite-basic--key-number (cdr (assq 'id entry)) info))
(year (org-cite-basic--get-year entry info 'no-suffix)))
(org-cite-concat
(format "[%d] " n) author ", "
(org-cite-emphasize 'italic title)
(and from (list ", " from)) ", "
year ".")))
;; Default to author-year. Use year disambiguation there.
(_
(let ((year (org-cite-basic--get-year entry info)))
(org-cite-concat
author " (" year "). "
(org-cite-emphasize 'italic title)
(and from (list ", " from)) "."))))))
(org-cite-basic--print-bibtex-string
(pcase style
("plain"
(let ((year (org-cite-basic--get-year entry info 'no-suffix)))
(org-cite-concat
(org-cite-basic--shorten-names author) ". "
title (and from (list ", " from)) ", " year ".")))
("numeric"
(let ((n (org-cite-basic--key-number (cdr (assq 'id entry)) info))
(year (org-cite-basic--get-year entry info 'no-suffix)))
(org-cite-concat
(format "[%d] " n) author ", "
(org-cite-emphasize 'italic title)
(and from (list ", " from)) ", "
year ".")))
;; Default to author-year. Use year disambiguation there.
(_
(let ((year (org-cite-basic--get-year entry info)))
(org-cite-concat
author " (" year "). "
(org-cite-emphasize 'italic title)
(and from (list ", " from)) "."))))
info)))
;;; "Activate" capability

View File

@ -376,61 +376,47 @@ INFO is the export state, as a property list."
(other
(user-error "Invalid entry %S in `org-cite-biblatex-styles'" other))))))
(defun org-cite-biblatex-prepare-preamble (output _keys files style &rest _)
"Prepare document preamble for \"biblatex\" usage.
(defun org-cite-biblatex--generate-latex-usepackage (info)
"Ensure that the biblatex package is loaded.
This is performed by extracting relevant information from the
INFO export plist, and modifying any existing
\\usepackage{biblatex} statement in the LaTeX header."
(let ((style (org-cite-bibliography-style info))
(usepackage-rx (rx "\\usepackage"
(opt (group "[" (*? anything) "]"))
"{biblatex}")))
(concat
(if (string-match usepackage-rx (plist-get info :latex-full-header))
;; "biblatex" package loaded, but with none (or different) options.
;; Replace with style-including command.
(plist-put info :latex-full-header
(replace-match
(format "\\usepackage%s{biblatex}"
(save-match-data
(org-cite-biblatex--package-options nil style)))
t t
(plist-get info :latex-full-header)))
;; No "biblatex" package loaded. Insert "usepackage" command
;; with appropriate options, including style.
(format "\\usepackage%s{biblatex}\n"
(org-cite-biblatex--package-options
org-cite-biblatex-options style))))))
OUTPUT is the final output of the export process. FILES is the list of file
names used as the bibliography.
This function ensures \"biblatex\" package is required. It also adds resources
to the document, and set styles."
(with-temp-buffer
(save-excursion (insert output))
(when (search-forward "\\begin{document}" nil t)
;; Ensure there is a \usepackage{biblatex} somewhere or add one.
;; Then set options.
(goto-char (match-beginning 0))
(let ((re (rx "\\usepackage"
(opt (group "[" (*? anything) "]"))
"{biblatex}")))
(cond
;; No "biblatex" package loaded. Insert "usepackage" command
;; with appropriate options, including style.
((not (re-search-backward re nil t))
(save-excursion
(insert
(format "\\usepackage%s{biblatex}\n"
(org-cite-biblatex--package-options
org-cite-biblatex-options style)))))
;; "biblatex" package loaded, but without any option.
;; Include style only.
((not (match-beginning 1))
(search-forward "{" nil t)
(insert (org-cite-biblatex--package-options nil style)))
;; "biblatex" package loaded with some options set. Override
;; style-related options with ours.
(t
(replace-match
(save-match-data
(org-cite-biblatex--package-options (match-string 1) style))
nil nil nil 1))))
;; Insert resources below.
(forward-line)
(insert (mapconcat (lambda (f)
(format "\\addbibresource%s{%s}"
(if (org-url-p f) "[location=remote]" "")
f))
files
"\n")
"\n"))
(buffer-string)))
(defun org-cite-biblatex--generate-latex-bibresources (info)
"From INFO generate LaTeX that loads the relevant bibliography resource files."
(let ((files (plist-get info :bibliography)))
(mapconcat (lambda (f)
(format "\\addbibresource%s{%s}"
(if (org-url-p f) "[location=remote]" "")
f))
files
"\n")))
;;; Register `biblatex' processor
(org-cite-register-processor 'biblatex
:export-bibliography #'org-cite-biblatex-export-bibliography
:export-citation #'org-cite-biblatex-export-citation
:export-finalizer #'org-cite-biblatex-prepare-preamble
:cite-styles #'org-cite-biblatex-list-styles)
(provide 'oc-biblatex)

View File

@ -847,27 +847,11 @@ INFO is the export state, as a property list."
;; process.
(org-cite-parse-elements output)))))
(defun org-cite-csl-finalizer (output _keys _files _style _backend info)
"Add \"hanging\" package if missing from LaTeX output.
OUTPUT is the export document, as a string. INFO is the export state, as a
property list."
(org-cite-csl--barf-without-citeproc)
(if (not (eq 'org-latex (org-cite-csl--output-format info)))
output
(with-temp-buffer
(save-excursion (insert output))
(when (search-forward "\\begin{document}" nil t)
(goto-char (match-beginning 0))
;; Insert the CSL-specific parts of the LaTeX preamble.
(insert (org-cite-csl--generate-latex-preamble info)))
(buffer-string))))
;;; Register `csl' processor
(org-cite-register-processor 'csl
:export-citation #'org-cite-csl-render-citation
:export-bibliography #'org-cite-csl-render-bibliography
:export-finalizer #'org-cite-csl-finalizer
:cite-styles
'((("author" "a") ("bare" "b") ("caps" "c") ("full" "f") ("bare-caps" "bc") ("caps-full" "cf") ("bare-caps-full" "bcf"))
(("noauthor" "na") ("bare" "b") ("caps" "c") ("bare-caps" "bc"))

View File

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

View File

@ -226,7 +226,7 @@ date year)."
;;; Implementation
(defun org-bbdb-store-link ()
(defun org-bbdb-store-link (&optional _interactive?)
"Store a link to a BBDB database entry."
(when (eq major-mode 'bbdb-mode)
;; This is BBDB, we make this link!

View File

@ -507,7 +507,7 @@ ARG, when non-nil, is a universal prefix argument. See
`org-open-file' for details."
(org-link-open-as-file path arg))
(defun org-bibtex-store-link ()
(defun org-bibtex-store-link (&optional _interactive?)
"Store a link to a BibTeX entry."
(when (eq major-mode 'bibtex-mode)
(let* ((search (org-create-file-search-in-bibtex))
@ -640,22 +640,23 @@ With prefix argument OPTIONAL also prompt for optional fields."
"Return headline text according to ENTRY title."
(cdr (assq :title entry)))
(defun org-bibtex-create (&optional arg nonew)
(defun org-bibtex-create (&optional arg update-heading)
"Create a new entry at the given level.
With a prefix arg, query for optional fields as well.
If nonew is t, add data to the headline of the entry at point."
With a prefix ARG, query for optional fields as well.
If UPDATE-HEADING is non-nil, add data to the headline of the entry at
point."
(interactive "P")
(let* ((type (completing-read
"Type: " (mapcar (lambda (type)
(substring (symbol-name (car type)) 1))
org-bibtex-types)
nil nil (when nonew
(org-bibtex-get org-bibtex-type-property-name))))
nil nil (when update-heading
(org-bibtex-get org-bibtex-type-property-name))))
(type (if (keywordp type) type (intern (concat ":" type))))
(org-bibtex-treat-headline-as-title (if nonew nil t)))
(org-bibtex-treat-headline-as-title (if update-heading nil t)))
(unless (assoc type org-bibtex-types)
(error "Type:%s is not known" type))
(if nonew
(if update-heading
(org-back-to-heading)
(org-insert-heading)
(let ((title (org-bibtex-ask :title)))
@ -722,29 +723,32 @@ Return the number of saved entries."
(interactive "fFile: ")
(org-bibtex-read-buffer (find-file-noselect file 'nowarn 'rawfile)))
(defun org-bibtex-write (&optional noindent)
(defun org-bibtex-write (&optional noindent update-heading)
"Insert a heading built from the first element of `org-bibtex-entries'.
When optional argument NOINDENT is non-nil, do not indent the properties
drawer."
drawer. If UPDATE-HEADING is non-nil, add data to the headline of the
entry at point."
(interactive)
(unless org-bibtex-entries
(error "No entries in `org-bibtex-entries'"))
(let* ((entry (pop org-bibtex-entries))
(org-special-properties nil) ; avoids errors with `org-entry-put'
(val (lambda (field) (cdr (assoc field entry))))
(togtag (lambda (tag) (org-toggle-tag tag 'on))))
(org-insert-heading)
(insert (funcall org-bibtex-headline-format-function entry))
(insert "\n:PROPERTIES:\n")
(org-bibtex-put "TITLE" (funcall val :title) 'insert)
(togtag (lambda (tag) (org-toggle-tag tag 'on)))
(insert-raw (not update-heading)))
(unless update-heading
(org-insert-heading)
(insert (funcall org-bibtex-headline-format-function entry))
(insert "\n:PROPERTIES:\n"))
(org-bibtex-put "TITLE" (funcall val :title) insert-raw)
(org-bibtex-put org-bibtex-type-property-name
(downcase (funcall val :type))
'insert)
insert-raw)
(dolist (pair entry)
(pcase (car pair)
(:title nil)
(:type nil)
(:key (org-bibtex-put org-bibtex-key-property (cdr pair) 'insert))
(:key (org-bibtex-put org-bibtex-key-property (cdr pair) insert-raw))
(:keywords (if org-bibtex-tags-are-keywords
(dolist (kw (split-string (cdr pair) ", *"))
(funcall
@ -752,25 +756,28 @@ drawer."
(replace-regexp-in-string
"[^[:alnum:]_@#%]" ""
(replace-regexp-in-string "[ \t]+" "_" kw))))
(org-bibtex-put (car pair) (cdr pair) 'insert)))
(_ (org-bibtex-put (car pair) (cdr pair) 'insert))))
(insert ":END:\n")
(org-bibtex-put (car pair) (cdr pair) insert-raw)))
(_ (org-bibtex-put (car pair) (cdr pair) insert-raw))))
(unless update-heading
(insert ":END:\n"))
(mapc togtag org-bibtex-tags)
(unless noindent
(org-indent-region
(save-excursion (org-back-to-heading t) (point))
(point)))))
(defun org-bibtex-yank ()
"If kill ring holds a bibtex entry yank it as an Org headline."
(interactive)
(defun org-bibtex-yank (&optional update-heading)
"If kill ring holds a bibtex entry yank it as an Org headline.
When called with non-nil prefix argument UPDATE-HEADING, add data to the
headline of the entry at point."
(interactive "P")
(let (entry)
(with-temp-buffer
(yank 1)
(bibtex-mode)
(setf entry (org-bibtex-read)))
(if entry
(org-bibtex-write)
(org-bibtex-write nil update-heading)
(error "Yanked text does not appear to contain a BibTeX entry"))))
(defun org-bibtex-import-from-file (file)

View File

@ -83,7 +83,7 @@
(error "No such file: %s" path))
(when page (doc-view-goto-page page))))
(defun org-docview-store-link ()
(defun org-docview-store-link (&optional _interactive?)
"Store a link to a docview buffer."
(when (eq major-mode 'doc-view-mode)
;; This buffer is in doc-view-mode

View File

@ -60,7 +60,7 @@ followed by a colon."
(insert command)
(eshell-send-input)))
(defun org-eshell-store-link ()
(defun org-eshell-store-link (&optional _interactive?)
"Store eshell link.
When opened, the link switches back to the current eshell buffer and
the current working directory."

View File

@ -62,7 +62,7 @@
"Open URL with Eww in the current buffer."
(eww url))
(defun org-eww-store-link ()
(defun org-eww-store-link (&optional _interactive?)
"Store a link to the url of an EWW buffer."
(when (eq major-mode 'eww-mode)
(org-link-store-props

View File

@ -123,7 +123,7 @@ If `org-store-link' was called with a prefix arg the meaning of
(url-encode-url message-id))
(concat "gnus:" group "#" message-id)))
(defun org-gnus-store-link ()
(defun org-gnus-store-link (&optional _interactive?)
"Store a link to a Gnus folder or message."
(pcase major-mode
(`gnus-group-mode

View File

@ -50,7 +50,7 @@
:insert-description #'org-info-description-as-command)
;; Implementation
(defun org-info-store-link ()
(defun org-info-store-link (&optional _interactive?)
"Store a link to an Info file and node."
(when (eq major-mode 'Info-mode)
(let ((link (concat "info:"

View File

@ -103,7 +103,7 @@ attributes that are found."
parts))
;;;###autoload
(defun org-irc-store-link ()
(defun org-irc-store-link (&optional _interactive?)
"Dispatch to the appropriate function to store a link to an IRC session."
(cond
((eq major-mode 'erc-mode)

View File

@ -82,7 +82,7 @@ matched strings in man buffer."
(set-window-point window point)
(set-window-start window point)))))))
(defun org-man-store-link ()
(defun org-man-store-link (&optional _interactive?)
"Store a link to a README file."
(when (memq major-mode '(Man-mode woman-mode))
;; This is a man page, we do make this link

View File

@ -80,7 +80,7 @@ supported by MH-E."
(org-link-set-parameters "mhe" :follow #'org-mhe-open :store #'org-mhe-store-link)
;; Implementation
(defun org-mhe-store-link ()
(defun org-mhe-store-link (&optional _interactive?)
"Store a link to an MH-E folder or message."
(when (or (eq major-mode 'mh-folder-mode)
(eq major-mode 'mh-show-mode))

View File

@ -51,7 +51,7 @@
:store #'org-rmail-store-link)
;; Implementation
(defun org-rmail-store-link ()
(defun org-rmail-store-link (&optional _interactive?)
"Store a link to an Rmail folder or message."
(when (or (eq major-mode 'rmail-mode)
(eq major-mode 'rmail-summary-mode))

View File

@ -52,18 +52,19 @@
(declare-function org-do-occur "org" (regexp &optional cleanup))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-cache-refresh "org-element" (pos))
(declare-function org-element-cache-reset "org-element" (&optional all no-persistence))
(declare-function org-element-context "org-element" (&optional element))
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
(declare-function org-element-link-parser "org-element" ())
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-begin "org-element" (node))
(declare-function org-element-end "org-element" (node))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-element-update-syntax "org-element" ())
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
(declare-function org-find-property "org" (property &optional value))
(declare-function org-get-heading "org" (&optional no-tags no-todo no-priority no-comment))
(declare-function org-id-find-id-file "org-id" (id))
(declare-function org-id-store-link "org-id" ())
(declare-function org-insert-heading "org" (&optional arg invisible-ok top))
(declare-function org-load-modules-maybe "org" (&optional force))
(declare-function org-mark-ring-push "org" (&optional pos buffer))
@ -532,6 +533,16 @@ links more efficient."
(defvar-local org-target-link-regexp nil
"Regular expression matching radio targets in plain text.")
(defconst org-target-link-regexp-limit (ash 2 12)
"Maximum allowed length of regexp.
The number should generally be ~order of magnitude smaller than
MAX_BUF_SIZE in src/regex-emacs.c. The number of regexp-emacs.c is
for processed regexp, which appears to be larger compared to the
original string length.")
(defvar-local org-target-link-regexps nil
"List of regular expressions matching radio targets in plain text.
This list is non-nil, when a single regexp would be too long to match
all the possible targets, exceeding Emacs' regexp length limit.")
(defvar org-link-types-re nil
"Matches a link that has a url-like prefix like \"http:\".")
@ -818,6 +829,74 @@ spec."
(org-with-point-at (car region)
(not (org-in-regexp org-link-any-re))))
(defun org-link--try-link-store-functions (interactive?)
"Try storing external links, prompting if more than one is possible.
Each function returned by `org-store-link-functions' is called in
turn. If multiple functions return non-nil, prompt for which
link should be stored.
Argument INTERACTIVE? indicates whether `org-store-link' was
called interactively and is passed to the link store functions.
Return t when a link has been stored in `org-link-store-props'."
(let ((results-alist nil))
(dolist (f (org-store-link-functions))
(when (condition-case nil
(funcall f interactive?)
;; FIXME: The store function used (< Org 9.7) to accept
;; no arguments; provide backward compatibility support
;; for them.
(wrong-number-of-arguments
(funcall f)))
;; FIXME: return value is not link's plist, so we store the
;; new value before it is modified. It would be cleaner to
;; ask store link functions to return the plist instead.
(push (cons f (copy-sequence org-store-link-plist))
results-alist)))
(pcase results-alist
(`nil nil)
(`((,_ . ,_)) t) ;single choice: nothing to do
(`((,name . ,_) . ,_)
;; Reinstate link plist associated to the chosen
;; function.
(apply #'org-link-store-props
(cdr (assoc-string
(completing-read
(format "Store link with (default %s): " name)
(mapcar #'car results-alist)
nil t nil nil (symbol-name name))
results-alist)))
t))))
(defun org-link--add-to-stored-links (link desc)
"Add LINK to `org-stored-links' with description DESC."
(cond
((not (member (list link desc) org-stored-links))
(push (list link desc) org-stored-links)
(message "Stored: %s" (or desc link)))
((equal (list link desc) (car org-stored-links))
(message "This link has already been stored"))
(t
(setq org-stored-links
(delete (list link desc) org-stored-links))
(push (list link desc) org-stored-links)
(message "Link moved to front: %s" (or desc link)))))
(defun org-link--file-link-to-here ()
"Return as (LINK . DESC) a file link with search string to here."
(let ((link (concat "file:"
(abbreviate-file-name
(buffer-file-name (buffer-base-buffer)))))
desc)
(when org-link-context-for-files
(pcase (org-link-precise-link-target)
(`nil nil)
(`(,search-string ,search-desc ,_position)
(setq link (format "%s::%s" link search-string))
(setq desc search-desc))))
(cons link desc)))
;;; Public API
@ -1044,7 +1123,9 @@ LINK is escaped with backslashes for inclusion in buffer."
"List of functions that are called to create and store a link.
The functions are defined in the `:store' property of
`org-link-parameters'.
`org-link-parameters'. Each function should accept an argument
INTERACTIVE? which indicates whether the user has initiated
`org-store-link' interactively.
Each function will be called in turn until one returns a non-nil
value. Each function should check if it is responsible for
@ -1068,6 +1149,9 @@ and then used in capture templates."
if store-func
collect store-func))
(defvar org-link--abbrev-functions nil
"Alist of abbrev link expressions and functions.")
(defun org-link-expand-abbrev (link)
"Replace link abbreviations in LINK string.
Abbreviations are defined in `org-link-abbrev-alist'."
@ -1082,14 +1166,27 @@ Abbreviations are defined in `org-link-abbrev-alist'."
(setq rpl (cdr as))
(cond
((symbolp rpl) (funcall rpl tag))
((string-match "%(\\([^)]+\\))" rpl)
((string-match "%(\\([^) ]+\\))" rpl) ; %(function)
(replace-match
(save-match-data
(funcall (intern-soft (match-string 1 rpl)) tag))
t t rpl))
((string-match "%s" rpl) (replace-match (or tag "") t t rpl))
((string-match "%h" rpl)
(replace-match (url-hexify-string (or tag "")) t t rpl))
((string-match "%(\\(.+\\))" rpl) ; %(sexpr using tag)
(replace-match
(save-match-data
(funcall (or (cdr (assoc (match-string 1 rpl)
org-link--abbrev-functions))
(cdar (push (cons (match-string 1 rpl)
(eval (read (format
"(lambda (tag) (%s))"
(match-string 1 rpl)))))
org-link--abbrev-functions)))
tag))
t t rpl))
((string-match-p "%[0-<>^_]?[0-9]*\\(?:\\.[0-9]+\\)?s" rpl)
(format-spec rpl `((?s . ,(or tag "")))))
((string-match-p "%[0-<>^_]?[0-9]*\\(?:\\.[0-9]+\\)?h" rpl)
(format-spec rpl `((?h . ,(url-hexify-string (or tag ""))))))
(t (concat rpl tag)))))))
(defun org-link-open (link &optional arg)
@ -1163,7 +1260,7 @@ Optional argument ARG is passed to `org-open-file' when S is a
(`nil (user-error "No valid link in %S" s))
(link (org-link-open link arg))))
(defun org-link-search (s &optional avoid-pos stealth)
(defun org-link-search (s &optional avoid-pos stealth new-heading-container)
"Search for a search string S in the accessible part of the buffer.
If S starts with \"#\", it triggers a custom ID search.
@ -1183,6 +1280,13 @@ When optional argument STEALTH is non-nil, do not modify
visibility around point, thus ignoring `org-show-context-detail'
variable.
When optional argument NEW-HEADING-CONTAINER is an element, any
new heading that is created (see
`org-link-search-must-match-exact-headline') will be added as a
subheading of NEW-HEADING-CONTAINER. Otherwise, new headings are
created at level 1 at the end of the accessible part of the
buffer.
Search is case-insensitive and ignores white spaces. Return type
of matched result, which is either `dedicated' or `fuzzy'. Search
respects buffer narrowing."
@ -1281,11 +1385,24 @@ respects buffer narrowing."
((and (derived-mode-p 'org-mode)
(eq org-link-search-must-match-exact-headline 'query-to-create)
(yes-or-no-p "No match - create this as a new heading? "))
(goto-char (point-max))
(unless (bolp) (newline))
(org-insert-heading nil t t)
(insert s "\n")
(forward-line -1))
(let* ((container-ok (and new-heading-container
(org-element-type-p new-heading-container '(headline))))
(new-heading-position (if container-ok
(- (org-element-end new-heading-container) 1)
(point-max)))
(new-heading-level (if container-ok
(+ 1 (org-element-property :level new-heading-container))
1)))
;; Need to widen when target is outside accessible portion of
;; buffer, since the we want the user to end up there.
(unless (and (<= (point-min) new-heading-position)
(>= (point-max) new-heading-position))
(widen))
(goto-char new-heading-position)
(unless (bolp) (newline))
(org-insert-heading nil t new-heading-level)
(insert (if starred (substring s 1) s) "\n")
(forward-line -1)))
;; Only headlines are looked after. No need to process
;; further: throw an error.
((and (derived-mode-p 'org-mode)
@ -1335,6 +1452,70 @@ priority cookie or tag."
(org-link--normalize-string
(or string (org-get-heading t t t t)))))
(defun org-link-precise-link-target ()
"Determine search string and description for storing a link.
If a search string (see `org-link-search') is found, return
list (SEARCH-STRING DESC POSITION). Otherwise, return nil.
If there is an active region, the contents (or a part of it, see
`org-link-context-for-files') is used as the search string.
In Org buffers, if point is at a named element (such as a source
block), the name is used for the search string. If at a heading,
its CUSTOM_ID is used to form a search string of the form
\"#id\", if present, otherwise the current heading text is used
in the form \"*Heading\".
If none of those finds a suitable search string, the current line
is used as the search string.
The description DESC is nil (meaning the user will be prompted
for a description when inserting the link) for search strings
based on a region or the current line. For other cases, DESC is
a cleaned-up version of the name or heading at point.
POSITION is the buffer position at which the search string
matches."
(let* ((region (org-link--context-from-region))
(result
(cond
(region
(list (org-link--normalize-string region t)
nil
(region-beginning)))
((derived-mode-p 'org-mode)
(let* ((element (org-element-at-point))
(name (org-element-property :name element))
(heading (org-element-lineage element '(headline inlinetask) t))
(custom-id (org-entry-get heading "CUSTOM_ID")))
(cond
(name
(list name
name
(org-element-begin element)))
((org-before-first-heading-p)
(list (org-link--normalize-string (org-current-line-string) t)
nil
(line-beginning-position)))
(heading
(list (if custom-id (concat "#" custom-id)
(org-link-heading-search-string))
(org-link--normalize-string
(org-get-heading t t t t))
(org-element-begin heading))))))
;; Not in an org-mode buffer, no region
(t
(list (org-link--normalize-string (org-current-line-string) t)
nil
(line-beginning-position))))))
;; Only use search option if there is some text.
(when (org-string-nw-p (car result))
result)))
(defun org-link-open-as-file (path in-emacs)
"Pretend PATH is a file name and open it.
@ -1407,7 +1588,7 @@ PATH is a symbol name, as a string."
((and (pred boundp) variable) (describe-variable variable))
(name (user-error "Unknown function or variable: %s" name))))
(defun org-link--store-help ()
(defun org-link--store-help (&optional _interactive?)
"Store \"help\" type link."
(when (eq major-mode 'help-mode)
(let ((symbol
@ -1542,7 +1723,12 @@ prefix ARG forces storing a link for each line in the
active region.
Assume the function is called interactively if INTERACTIVE? is
non-nil."
non-nil.
In Org buffers, an additional \"human-readable\" simple file link
is stored as an alternative to persistent org-id or other links,
if at a heading with a CUSTOM_ID property or an element with a
NAME."
(interactive "P\np")
(org-load-modules-maybe)
(if (and (equal arg '(64)) (org-region-active-p))
@ -1557,36 +1743,19 @@ non-nil."
(move-beginning-of-line 2)
(set-mark (point)))))
(setq org-store-link-plist nil)
(let (link cpltxt desc search custom-id agenda-link) ;; description
;; Negate `org-context-in-file-links' when given a single universal arg.
(let ((org-link-context-for-files (org-xor org-link-context-for-files
(equal arg '(4))))
link cpltxt desc search agenda-link) ;; description
(cond
;; Store a link using an external link type, if any function is
;; available. If more than one can generate a link from current
;; location, ask which one to use.
;; available, unless external link types are skipped for this
;; call using two universal args. If more than one function
;; can generate a link from current location, ask the user
;; which one to use.
((and (not (equal arg '(16)))
(let ((results-alist nil))
(dolist (f (org-store-link-functions))
(when (funcall f)
;; XXX: return value is not link's plist, so we
;; store the new value before it is modified. It
;; would be cleaner to ask store link functions to
;; return the plist instead.
(push (cons f (copy-sequence org-store-link-plist))
results-alist)))
(pcase results-alist
(`nil nil)
(`((,_ . ,_)) t) ;single choice: nothing to do
(`((,name . ,_) . ,_)
;; Reinstate link plist associated to the chosen
;; function.
(apply #'org-link-store-props
(cdr (assoc-string
(completing-read
(format "Store link with (default %s): " name)
(mapcar #'car results-alist)
nil t nil nil (symbol-name name))
results-alist)))
t))))
(setq link (plist-get org-store-link-plist :link))
(org-link--try-link-store-functions interactive?))
(setq link (plist-get org-store-link-plist :link))
;; If store function actually set `:description' property, use
;; it, even if it is nil. Otherwise, fallback to nil (ask user).
(setq desc (plist-get org-store-link-plist :description)))
@ -1637,6 +1806,7 @@ non-nil."
(org-with-point-at m
(setq agenda-link (org-store-link nil interactive?))))))
;; Calendar mode
((eq major-mode 'calendar-mode)
(let ((cd (calendar-cursor-to-date)))
(setq link
@ -1645,6 +1815,7 @@ non-nil."
(org-encode-time 0 0 0 (nth 1 cd) (nth 0 cd) (nth 2 cd))))
(org-link-store-props :type "calendar" :date cd)))
;; Image mode
((eq major-mode 'image-mode)
(setq cpltxt (concat "file:"
(abbreviate-file-name buffer-file-name))
@ -1662,15 +1833,22 @@ non-nil."
(setq cpltxt (concat "file:" file)
link cpltxt)))
;; Try `org-create-file-search-functions`. If any are
;; successful, create a file link to the current buffer with
;; the provided search string. (sets `link` and `cpltxt` to
;; the same thing; it looks like the intention originally was
;; that cpltxt was a description, which might have been set by
;; the search-function (removed in switch to lexical binding)).
((setq search (run-hook-with-args-until-success
'org-create-file-search-functions))
(setq link (concat "file:" (abbreviate-file-name buffer-file-name)
"::" search))
(setq cpltxt (or link))) ;; description
;; Main logic for storing built-in link types in org-mode
;; buffers
((and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode))
(org-with-limited-levels
(setq custom-id (org-entry-get nil "CUSTOM_ID"))
(cond
;; Store a link using the target at point
((org-in-regexp "[^<]<<\\([^<>]+\\)>>[^>]" 1)
@ -1684,74 +1862,21 @@ non-nil."
;; links. Maybe the case of identical target and
;; description should be handled by `org-insert-link'.
cpltxt nil
desc nil
;; Do not append #CUSTOM_ID link below.
custom-id nil))
((and (featurep 'org-id)
(or (eq org-id-link-to-org-use-id t)
(and interactive?
(or (eq org-id-link-to-org-use-id 'create-if-interactive)
(and (eq org-id-link-to-org-use-id
'create-if-interactive-and-no-custom-id)
(not custom-id))))
(and org-id-link-to-org-use-id (org-entry-get nil "ID"))))
;; Store a link using the ID at point
(setq link (condition-case nil
(prog1 (org-id-store-link)
(setq desc (plist-get org-store-link-plist :description)))
(error
;; Probably before first headline, link only to file
(concat "file:"
(abbreviate-file-name
(buffer-file-name (buffer-base-buffer))))))))
(t
desc nil))
(t
;; Just link to current headline.
(setq cpltxt (concat "file:"
(abbreviate-file-name
(buffer-file-name (buffer-base-buffer)))))
;; Add a context search string.
(when (org-xor org-link-context-for-files (equal arg '(4)))
(let* ((element (org-element-at-point))
(name (org-element-property :name element))
(context
(cond
((let ((region (org-link--context-from-region)))
(and region (org-link--normalize-string region t))))
(name)
((org-before-first-heading-p)
(org-link--normalize-string (org-current-line-string) t))
(t (org-link-heading-search-string)))))
(when (org-string-nw-p context)
(setq cpltxt (format "%s::%s" cpltxt context))
(setq desc
(or name
;; Although description is not a search
;; string, use `org-link--normalize-string'
;; to prettify it (contiguous white spaces)
;; and remove volatile contents (statistics
;; cookies).
(and (not (org-before-first-heading-p))
(org-link--normalize-string
(org-get-heading t t t t)))
"NONE")))))
(setq link cpltxt)))))
(let ((here (org-link--file-link-to-here)))
(setq cpltxt (car here))
(setq desc (cdr here)))
(setq link cpltxt)))))
;; Buffer linked to file, but not an org-mode buffer.
((buffer-file-name (buffer-base-buffer))
;; Just link to this file here.
(setq cpltxt (concat "file:"
(abbreviate-file-name
(buffer-file-name (buffer-base-buffer)))))
;; Add a context search string.
(when (org-xor org-link-context-for-files (equal arg '(4)))
(let ((context (org-link--normalize-string
(or (org-link--context-from-region)
(org-current-line-string))
t)))
;; Only use search option if there is some text.
(when (org-string-nw-p context)
(setq cpltxt (format "%s::%s" cpltxt context))
(setq desc "NONE"))))
(setq link cpltxt))
(let ((here (org-link--file-link-to-here)))
(setq cpltxt (car here))
(setq desc (cdr here)))
(setq link cpltxt))
(interactive?
(user-error "No method for storing a link from this buffer"))
@ -1767,24 +1892,18 @@ non-nil."
;; Store and return the link
(if (not (and interactive? link))
(or agenda-link (and link (org-link-make-string link desc)))
(dotimes (_ (if custom-id 2 1)) ; Store 2 links when CUSTOM-ID is non-nil.
(cond
((not (member (list link desc) org-stored-links))
(push (list link desc) org-stored-links)
(message "Stored: %s" (or desc link)))
((equal (list link desc) (car org-stored-links))
(message "This link has already been stored"))
(t
(setq org-stored-links
(delete (list link desc) org-stored-links))
(push (list link desc) org-stored-links)
(message "Link moved to front: %s" (or desc link))))
(when custom-id
(setq link (concat "file:"
(abbreviate-file-name
(buffer-file-name (buffer-base-buffer)))
"::#" custom-id))))
(car org-stored-links)))))
(org-link--add-to-stored-links link desc)
;; In org buffers, store an additional "human-readable" link
;; using custom id, if available.
(when (and (buffer-file-name (buffer-base-buffer))
(derived-mode-p 'org-mode)
(org-entry-get nil "CUSTOM_ID"))
(let ((here (org-link--file-link-to-here)))
(setq link (car here))
(setq desc (cdr here)))
(unless (equal (list link desc) (car org-stored-links))
(org-link--add-to-stored-links link desc)))
(car org-stored-links)))))
;;;###autoload
(defun org-insert-link (&optional complete-file link-location description)
@ -2078,6 +2197,39 @@ This command can be called in any mode to insert a link in Org syntax."
(org-load-modules-maybe)
(org-run-like-in-org-mode 'org-insert-link))
(defun org--re-list-search-forward (regexp-list &optional bound noerror count)
"Like `re-search-forward', but REGEXP-LIST is a list of regexps.
BOUND, NOERROR, and COUNT are passed to `re-search-forward'."
(let (result (min-found most-positive-fixnum)
(pos-found nil)
(min-found-data nil)
(tail regexp-list))
(while tail
(setq result (save-excursion (re-search-forward (pop tail) bound t count)))
(when (and result (< result min-found))
(setq min-found result
pos-found (match-end 0)
min-found-data (match-data))))
(if (= most-positive-fixnum min-found)
(pcase noerror
(`t nil)
(_ (re-search-forward (car regexp-list) bound noerror count)))
(set-match-data min-found-data)
(goto-char pos-found))))
(defun org--re-list-looking-at (regexp-list &optional inhibit-modify)
"Like `looking-at', but REGEXP-LIST is a list of regexps.
INHIBIT-MODIFY is passed to `looking-at'."
(catch :found
(while regexp-list
(when
(if inhibit-modify
(looking-at-p (pop regexp-list))
;; FIXME: In Emacs <29, `looking-at' does not accept
;; optional INHIBIT-MODIFY argument.
(looking-at (pop regexp-list)))
(throw :found t)))))
;;;###autoload
(defun org-update-radio-target-regexp ()
"Find all radio targets in this file and update the regular expression.
@ -2115,6 +2267,30 @@ Also refresh fontification if needed."
targets
"\\|")
after-re)))
(setq org-target-link-regexps nil)
(let (current-length sub-targets)
(when (<= org-target-link-regexp-limit (length org-target-link-regexp))
(while (or targets sub-targets)
(when (and sub-targets
(or (not targets)
(>= (+ current-length (length (car targets)))
org-target-link-regexp-limit)))
(push (concat before-re
(mapconcat
(lambda (x)
(replace-regexp-in-string
" +" "\\s-+" (regexp-quote x) t t))
(nreverse sub-targets)
"\\|")
after-re)
org-target-link-regexps)
(setq current-length nil
sub-targets nil))
(unless current-length
(setq current-length (+ (length before-re) (length after-re))))
(when targets (push (pop targets) sub-targets))
(cl-incf current-length (length (car sub-targets))))
(setq org-target-link-regexps (nreverse org-target-link-regexps))))
(unless (equal old-regexp org-target-link-regexp)
;; Clean-up cache.
(let ((regexp (cond ((not old-regexp) org-target-link-regexp)
@ -2130,9 +2306,11 @@ Also refresh fontification if needed."
after-re)))))
(when (and (featurep 'org-element)
(not (bound-and-true-p org-mode-loading)))
(org-with-point-at 1
(while (re-search-forward regexp nil t)
(org-element-cache-refresh (match-beginning 1))))))
(if org-target-link-regexps
(org-element-cache-reset)
(org-with-point-at 1
(while (re-search-forward regexp nil t)
(org-element-cache-refresh (match-beginning 1)))))))
;; Re fontify buffer.
(when (memq 'radio org-highlight-links)
(org-restart-font-lock)))))

View File

@ -3148,169 +3148,172 @@ s Search for keywords S Like s, but only TODO entries
'(face bold) header))
header)))
(setq header-end (point-marker))
(while t
(setq custom1 custom)
(when (eq rmheader t)
(org-goto-line 1)
(re-search-forward ":" nil t)
(delete-region (match-end 0) (line-end-position))
(forward-char 1)
(looking-at "-+")
(delete-region (match-end 0) (line-end-position))
(move-marker header-end (match-end 0)))
(goto-char header-end)
(delete-region (point) (point-max))
(unwind-protect
(while t
(setq custom1 custom)
(when (eq rmheader t)
(org-goto-line 1)
(re-search-forward ":" nil t)
(delete-region (match-end 0) (line-end-position))
(forward-char 1)
(looking-at "-+")
(delete-region (match-end 0) (line-end-position))
(move-marker header-end (match-end 0)))
(goto-char header-end)
(delete-region (point) (point-max))
;; Produce all the lines that describe custom commands and prefixes
(setq lines nil)
(while (setq entry (pop custom1))
(setq key (car entry) desc (nth 1 entry)
type (nth 2 entry)
match (nth 3 entry))
(if (> (length key) 1)
(cl-pushnew (string-to-char key) prefixes :test #'equal)
(setq line
(format
"%-4s%-14s"
(org-add-props (copy-sequence key)
'(face bold))
(cond
((string-match "\\S-" desc) desc)
((eq type 'agenda) "Agenda for current week or day")
((eq type 'agenda*) "Appointments for current week or day")
((eq type 'alltodo) "List of all TODO entries")
((eq type 'search) "Word search")
((eq type 'stuck) "List of stuck projects")
((eq type 'todo) "TODO keyword")
((eq type 'tags) "Tags query")
((eq type 'tags-todo) "Tags (TODO)")
((eq type 'tags-tree) "Tags tree")
((eq type 'todo-tree) "TODO kwd tree")
((eq type 'occur-tree) "Occur tree")
((functionp type) (if (symbolp type)
(symbol-name type)
"Lambda expression"))
(t "???"))))
;; Produce all the lines that describe custom commands and prefixes
(setq lines nil)
(while (setq entry (pop custom1))
(setq key (car entry) desc (nth 1 entry)
type (nth 2 entry)
match (nth 3 entry))
(if (> (length key) 1)
(cl-pushnew (string-to-char key) prefixes :test #'equal)
(setq line
(format
"%-4s%-14s"
(org-add-props (copy-sequence key)
'(face bold))
(cond
((string-match "\\S-" desc) desc)
((eq type 'agenda) "Agenda for current week or day")
((eq type 'agenda*) "Appointments for current week or day")
((eq type 'alltodo) "List of all TODO entries")
((eq type 'search) "Word search")
((eq type 'stuck) "List of stuck projects")
((eq type 'todo) "TODO keyword")
((eq type 'tags) "Tags query")
((eq type 'tags-todo) "Tags (TODO)")
((eq type 'tags-tree) "Tags tree")
((eq type 'todo-tree) "TODO kwd tree")
((eq type 'occur-tree) "Occur tree")
((functionp type) (if (symbolp type)
(symbol-name type)
"Lambda expression"))
(t "???"))))
(cond
((not (org-string-nw-p match)) nil)
(org-agenda-menu-show-matcher
(setq line
(concat line ": "
(cond
((stringp match)
(propertize match 'face 'org-warning))
((listp type)
(format "set of %d commands" (length type)))))))
(t
(org-add-props line nil 'help-echo (concat "Matcher: " match))))
(push line lines)))
(setq lines (nreverse lines))
(when prefixes
(mapc (lambda (x)
(push
(format "%s %s"
(org-add-props (char-to-string x)
nil 'face 'bold)
(or (cdr (assoc (concat selstring
(char-to-string x))
prefix-descriptions))
"Prefix key"))
lines))
prefixes))
;; Check if we should display in two columns
(if org-agenda-menu-two-columns
(progn
(setq n (length lines)
n1 (+ (/ n 2) (mod n 2))
right (nthcdr n1 lines)
left (copy-sequence lines))
(setcdr (nthcdr (1- n1) left) nil))
(setq left lines right nil))
(while left
(insert "\n" (pop left))
(when right
(if (< (current-column) 40)
(move-to-column 40 t)
(insert " "))
(insert (pop right))))
;; Make the window the right size
(goto-char (point-min))
(if second-time
(when (not (pos-visible-in-window-p (point-max)))
(org-fit-window-to-buffer))
(setq second-time t)
(org-fit-window-to-buffer))
;; Hint to navigation if window too small for all information
(setq header-line-format
(when (not (pos-visible-in-window-p (point-max)))
"Use C-v, M-v, C-n or C-p to navigate."))
;; Ask for selection
(cl-loop
do (progn
(message "Press key for agenda command%s:"
(if (or restrict-ok org-agenda-overriding-restriction)
(if org-agenda-overriding-restriction
" (restriction lock active)"
(if restriction
(format " (restricted to %s)" restriction)
" (unrestricted)"))
""))
(setq c (read-char-exclusive)))
until (not (memq c '(14 16 22 134217846)))
do (org-scroll c))
(message "")
(cond
((not (org-string-nw-p match)) nil)
(org-agenda-menu-show-matcher
(setq line
(concat line ": "
(cond
((stringp match)
(propertize match 'face 'org-warning))
((listp type)
(format "set of %d commands" (length type)))))))
(t
(org-add-props line nil 'help-echo (concat "Matcher: " match))))
(push line lines)))
(setq lines (nreverse lines))
(when prefixes
(mapc (lambda (x)
(push
(format "%s %s"
(org-add-props (char-to-string x)
nil 'face 'bold)
(or (cdr (assoc (concat selstring
(char-to-string x))
prefix-descriptions))
"Prefix key"))
lines))
prefixes))
((assoc (char-to-string c) custom)
(setq selstring (concat selstring (char-to-string c)))
(throw 'exit (cons selstring restriction)))
((memq c prefixes)
(setq selstring (concat selstring (char-to-string c))
prefixes nil
rmheader (or rmheader t)
custom (delq nil (mapcar
(lambda (x)
(if (or (= (length (car x)) 1)
(/= (string-to-char (car x)) c))
nil
(cons (substring (car x) 1) (cdr x))))
custom))))
((eq c ?*)
(call-interactively 'org-toggle-sticky-agenda)
(sit-for 2))
((and (not restrict-ok) (memq c '(?1 ?0 ?<)))
(message "Restriction is only possible in Org buffers")
(ding) (sit-for 1))
((eq c ?1)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction 'buffer))
((eq c ?0)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction (if region-p 'region 'subtree)))
((eq c ?<)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction
(cond
((eq restriction 'buffer)
(if region-p 'region 'subtree))
((memq restriction '(subtree region))
nil)
(t 'buffer))))
((eq c ?>)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction nil))
((and (equal selstring "") (memq c '(?s ?S ?a ?t ?m ?L ?C ?e ?T ?M ?# ?! ?/ ??)))
(throw 'exit (cons (setq selstring (char-to-string c)) restriction)))
((and (> (length selstring) 0) (eq c ?\d))
(delete-window)
(org-agenda-get-restriction-and-command prefix-descriptions))
;; Check if we should display in two columns
(if org-agenda-menu-two-columns
(progn
(setq n (length lines)
n1 (+ (/ n 2) (mod n 2))
right (nthcdr n1 lines)
left (copy-sequence lines))
(setcdr (nthcdr (1- n1) left) nil))
(setq left lines right nil))
(while left
(insert "\n" (pop left))
(when right
(if (< (current-column) 40)
(move-to-column 40 t)
(insert " "))
(insert (pop right))))
;; Make the window the right size
(goto-char (point-min))
(if second-time
(when (not (pos-visible-in-window-p (point-max)))
(org-fit-window-to-buffer))
(setq second-time t)
(org-fit-window-to-buffer))
;; Hint to navigation if window too small for all information
(setq header-line-format
(when (not (pos-visible-in-window-p (point-max)))
"Use C-v, M-v, C-n or C-p to navigate."))
;; Ask for selection
(cl-loop
do (progn
(message "Press key for agenda command%s:"
(if (or restrict-ok org-agenda-overriding-restriction)
(if org-agenda-overriding-restriction
" (restriction lock active)"
(if restriction
(format " (restricted to %s)" restriction)
" (unrestricted)"))
""))
(setq c (read-char-exclusive)))
until (not (memq c '(14 16 22 134217846)))
do (org-scroll c))
(message "")
(cond
((assoc (char-to-string c) custom)
(setq selstring (concat selstring (char-to-string c)))
(throw 'exit (cons selstring restriction)))
((memq c prefixes)
(setq selstring (concat selstring (char-to-string c))
prefixes nil
rmheader (or rmheader t)
custom (delq nil (mapcar
(lambda (x)
(if (or (= (length (car x)) 1)
(/= (string-to-char (car x)) c))
nil
(cons (substring (car x) 1) (cdr x))))
custom))))
((eq c ?*)
(call-interactively 'org-toggle-sticky-agenda)
(sit-for 2))
((and (not restrict-ok) (memq c '(?1 ?0 ?<)))
(message "Restriction is only possible in Org buffers")
(ding) (sit-for 1))
((eq c ?1)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction 'buffer))
((eq c ?0)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction (if region-p 'region 'subtree)))
((eq c ?<)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction
(cond
((eq restriction 'buffer)
(if region-p 'region 'subtree))
((memq restriction '(subtree region))
nil)
(t 'buffer))))
((eq c ?>)
(org-agenda-remove-restriction-lock 'noupdate)
(setq restriction nil))
((and (equal selstring "") (memq c '(?s ?S ?a ?t ?m ?L ?C ?e ?T ?M ?# ?! ?/ ??)))
(throw 'exit (cons (setq selstring (char-to-string c)) restriction)))
((and (> (length selstring) 0) (eq c ?\d))
(delete-window)
(org-agenda-get-restriction-and-command prefix-descriptions))
((equal c ?q) (user-error "Abort"))
(t (user-error "Invalid key %c" c))))))))
((equal c ?q) (user-error "Abort"))
(t (user-error "Invalid key %c" c))))
;; Close *Agenda Commands* window.
(quit-window))))))
(defun org-agenda-fit-window-to-buffer ()
"Fit the window to the buffer size."
@ -3638,11 +3641,12 @@ the agenda to write."
(goto-char p)
(setq m (get-text-property (point) 'org-hd-marker))
(when m
(push (with-current-buffer (marker-buffer m)
(goto-char m)
(org-copy-subtree 1 nil t t)
org-subtree-clip)
content)))
(cl-pushnew (with-current-buffer (marker-buffer m)
(goto-char m)
(org-copy-subtree 1 nil t t)
org-subtree-clip)
content
:test #'equal)))
(find-file file)
(erase-buffer)
(dolist (s content) (org-paste-subtree 1 s))
@ -6399,14 +6403,14 @@ specification like [h]h:mm."
(org-agenda--timestamp-to-absolute
s base 'future (current-buffer) pos)))))
(diff (- deadline current))
(suppress-prewarning
(max-warning-days
(let ((scheduled
(and org-agenda-skip-deadline-prewarning-if-scheduled
(org-element-property
:raw-value
(org-element-property :scheduled el)))))
(cond
((not scheduled) nil)
((not scheduled) most-positive-fixnum)
;; The current item has a scheduled date, so
;; evaluate its prewarning lead time.
((integerp org-agenda-skip-deadline-prewarning-if-scheduled)
@ -6420,15 +6424,15 @@ specification like [h]h:mm."
org-deadline-warning-days))
;; Set pre-warning to deadline.
(t 0))))
(wdays (or suppress-prewarning (org-get-wdays s))))
(warning-days (min max-warning-days (org-get-wdays s))))
(cond
;; Only display deadlines at their base date, at future
;; repeat occurrences or in today agenda.
((= current deadline) nil)
((= current repeat) nil)
((not today?) (throw :skip nil))
;; Upcoming deadline: display within warning period WDAYS.
((> deadline current) (when (> diff wdays) (throw :skip nil)))
;; Upcoming deadline: display within warning period WARNING-DAYS.
((> deadline current) (when (> diff warning-days) (throw :skip nil)))
;; Overdue deadline: warn about it for
;; `org-deadline-past-days' duration.
(t (when (< org-deadline-past-days (- diff)) (throw :skip nil))))
@ -6481,7 +6485,7 @@ specification like [h]h:mm."
'effort-minutes effort-minutes)
level category tags time))
(face (org-agenda-deadline-face
(- 1 (/ (float diff) (max wdays 1)))))
(- 1 (/ (float diff) (max warning-days 1)))))
(upcoming? (and today? (> deadline today)))
(warntime (org-entry-get (point) "APPT_WARNTIME" 'selective)))
(org-add-props item props
@ -6610,13 +6614,13 @@ scheduled items with an hour specification like [h]h:mm."
(futureschedp (> schedule today))
(habitp (and (fboundp 'org-is-habit-p)
(string= "habit" (org-element-property :STYLE el))))
(suppress-delay
(max-delay-days
(let ((deadline (and org-agenda-skip-scheduled-delay-if-deadline
(org-element-property
:raw-value
(org-element-property :deadline el)))))
(cond
((not deadline) nil)
((not deadline) most-positive-fixnum)
;; The current item has a deadline date, so
;; evaluate its delay time.
((integerp org-agenda-skip-scheduled-delay-if-deadline)
@ -6629,17 +6633,14 @@ scheduled items with an hour specification like [h]h:mm."
(org-agenda--timestamp-to-absolute deadline))
org-scheduled-delay-days))
(t 0))))
(ddays
(delay-days
(cond
;; Nullify delay when a repeater triggered already
;; and the delay is of the form --Xd.
((and (string-match-p "--[0-9]+[hdwmy]" s)
(> schedule (org-agenda--timestamp-to-absolute s)))
0)
(suppress-delay
(let ((org-scheduled-delay-days suppress-delay))
(org-get-wdays s t t)))
(t (org-get-wdays s t)))))
(t (min max-delay-days (org-get-wdays s t))))))
;; Display scheduled items at base date (SCHEDULE), today if
;; scheduled before the current date, and at any repeat past
;; today. However, skip delayed items and items that have
@ -6647,7 +6648,7 @@ scheduled items with an hour specification like [h]h:mm."
(unless (and todayp
habitp
(bound-and-true-p org-habit-show-all-today))
(when (or (and (> ddays 0) (< diff ddays))
(when (or (and (> delay-days 0) (< diff delay-days))
(> diff (or (and habitp org-habit-scheduled-past-days)
org-scheduled-past-days))
(> schedule current)
@ -6966,6 +6967,7 @@ Any match of REMOVE-RE will be removed from TXT."
(file-name-sans-extension
(file-name-nondirectory buffer-file-name))
"")))
(full-category category)
(category-icon (org-agenda-get-category-icon category))
(category-icon (if category-icon
(propertize " " 'display category-icon)
@ -7093,7 +7095,9 @@ Any match of REMOVE-RE will be removed from TXT."
;; And finally add the text properties
(remove-text-properties 0 (length rtn) '(line-prefix t wrap-prefix t) rtn)
(org-add-props rtn nil
'org-category category
;; CATEGORY might be truncated. Store the full category in
;; the properties.
'org-category full-category
'tags tags
'org-priority-highest org-priority-highest
'org-priority-lowest org-priority-lowest

View File

@ -157,7 +157,7 @@ archive location, but not yet deleted from the original file.")
"Splice the archive FILES into the list of files.
This implies visiting all these files and finding out what the
archive file is."
(org-uniquify
(seq-uniq
(apply
'append
(mapcar
@ -166,7 +166,9 @@ archive file is."
nil
(with-current-buffer (org-get-agenda-file-buffer f)
(cons f (org-all-archive-files)))))
files))))
files))
#'file-equal-p
))
(defun org-all-archive-files ()
"List of all archive files used in the current buffer."

View File

@ -995,7 +995,7 @@ Store them in the capture property list."
((or `here
`(here))
(org-capture-put :exact-position (point) :insert-here t))
(`(file ,(and path (pred stringp)))
(`(file ,path)
(set-buffer (org-capture-target-buffer path))
(org-capture-put-target-region-and-position)
(widen)
@ -1008,7 +1008,7 @@ Store them in the capture property list."
(org-capture-put-target-region-and-position)
(goto-char position))
(_ (error "Cannot find target ID \"%s\"" id))))
(`(file+headline ,(and path (pred stringp)) ,(and headline (pred stringp)))
(`(file+headline ,path ,(and headline (pred stringp)))
(set-buffer (org-capture-target-buffer path))
;; Org expects the target file to be in Org mode, otherwise
;; it throws an error. However, the default notes files
@ -1030,7 +1030,7 @@ Store them in the capture property list."
(unless (bolp) (insert "\n"))
(insert "* " headline "\n")
(forward-line -1)))
(`(file+olp ,(and path (pred stringp)) . ,(and outline-path (guard outline-path)))
(`(file+olp ,path . ,(and outline-path (guard outline-path)))
(let ((m (org-find-olp (cons (org-capture-expand-file path)
outline-path))))
(set-buffer (marker-buffer m))
@ -1038,7 +1038,7 @@ Store them in the capture property list."
(widen)
(goto-char m)
(set-marker m nil)))
(`(file+regexp ,(and path (pred stringp)) ,(and regexp (pred stringp)))
(`(file+regexp ,path ,(and regexp (pred stringp)))
(set-buffer (org-capture-target-buffer path))
(org-capture-put-target-region-and-position)
(widen)
@ -1051,7 +1051,7 @@ Store them in the capture property list."
(org-capture-put :exact-position (point))
(setq target-entry-p
(and (derived-mode-p 'org-mode) (org-at-heading-p)))))
(`(file+olp+datetree ,(and path (pred stringp)) . ,outline-path)
(`(file+olp+datetree ,path . ,outline-path)
(let ((m (if outline-path
(org-find-olp (cons (org-capture-expand-file path)
outline-path))
@ -1106,7 +1106,7 @@ Store them in the capture property list."
;; the following is the keep-restriction argument for
;; org-datetree-find-date-create
(when outline-path 'subtree-at-point))))
(`(file+function ,(and path (pred stringp)) ,(and function (pred functionp)))
(`(file+function ,path ,(and function (pred functionp)))
(set-buffer (org-capture-target-buffer path))
(org-capture-put-target-region-and-position)
(widen)
@ -1508,7 +1508,7 @@ Of course, if exact position has been required, just put it there."
(condition-case err
(bookmark-set bookmark)
(error
(message (format "Bookmark set error: %S" err)))))))
(message "Bookmark set error: %S" err))))))
(move-marker org-capture-last-stored-marker (point))))))
(defun org-capture-narrow (beg end)

View File

@ -1253,9 +1253,9 @@ properties drawers."
;; property `org-summaries', in alist whose key is SPEC.
(let* ((summary
(and summarize
(let ((values (append (and (/= last-level inminlevel)
(aref lvals last-level))
(aref lvals inminlevel))))
(let ((values
(cl-loop for l from (1+ level) to lmax
append (aref lvals l))))
(and values (funcall summarize values printf))))))
;; Leaf values are not summaries: do not mark them.
(when summary

View File

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

View File

@ -246,7 +246,7 @@ Assume `epg-context' is set."
;; contents in the buffer.
(error
(insert contents)
(error (error-message-string err)))))
(error "%s" (error-message-string err)))))
(when folded-heading
(goto-char folded-heading)
(org-fold-subtree t))

View File

@ -116,6 +116,7 @@ than its value."
(const :tag "No limit" nil)
(integer :tag "Maximum level")))
(defvaralias 'org-hide-block-startup 'org-cycle-hide-block-startup)
(defcustom org-cycle-hide-block-startup nil
"Non-nil means entering Org mode will fold all blocks.
This can also be set in on a per-file basis with
@ -126,6 +127,7 @@ This can also be set in on a per-file basis with
:group 'org-cycle
:type 'boolean)
(defvaralias 'org-hide-drawer-startup 'org-cycle-hide-drawer-startup)
(defcustom org-cycle-hide-drawer-startup t
"Non-nil means entering Org mode will fold all drawers.
This can also be set in on a per-file basis with
@ -201,6 +203,7 @@ Special case: when 0, never leave empty lines in collapsed view."
:type 'integer)
(put 'org-cycle-separator-lines 'safe-local-variable 'integerp)
(defvaralias 'org-pre-cycle-hook 'org-cycle-pre-hook)
(defcustom org-cycle-pre-hook nil
"Hook that is run before visibility cycling is happening.
The function(s) in this hook must accept a single argument which indicates
@ -241,6 +244,7 @@ normal outline commands like `show-all', but not with the cycling commands."
:package-version '(Org . "9.6")
:type 'boolean)
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
(defvar org-cycle-tab-first-hook nil
"Hook for functions to attach themselves to TAB.
See `org-ctrl-c-ctrl-c-hook' for more information.

View File

@ -1131,8 +1131,11 @@ The function takes care of setting `:parent' property for NEW."
(eq new-type 'plain-text))
;; We cannot replace OLD with NEW since strings are not mutable.
;; We take the long path.
(progn (org-element-insert-before new old)
(org-element-extract old))
(progn
(org-element-insert-before new old)
(org-element-extract old)
;; We will return OLD.
(setq old new))
;; Since OLD is going to be changed into NEW by side-effect, first
;; make sure that every element or object within NEW has OLD as
;; parent.

View File

@ -551,6 +551,8 @@ The resulting function can be evaluated at a later time, from
another buffer, effectively cloning the original buffer there.
The function assumes BUFFER's major mode is `org-mode'."
(declare-function org-fold-core--update-buffer-folds "org-fold-core" ())
(require 'org-fold-core)
(with-current-buffer buffer
(let ((str (unless drop-contents (org-with-wide-buffer (buffer-string))))
(narrowing
@ -592,7 +594,7 @@ The function assumes BUFFER's major mode is `org-mode'."
(let ((invis-prop (overlay-get ov 'invisible)))
(when invis-prop
(push (list (overlay-start ov) (overlay-end ov)
invis-prop)
(overlay-properties ov))
ov-set))))
ov-set))))
(lambda ()
@ -618,8 +620,12 @@ The function assumes BUFFER's major mode is `org-mode'."
;; Current position of point.
(goto-char pos)
;; Overlays with invisible property.
(pcase-dolist (`(,start ,end ,invis) ols)
(overlay-put (make-overlay start end) 'invisible invis))
(pcase-dolist (`(,start ,end ,props) ols)
(let ((ov (make-overlay start end)))
(while props
(overlay-put ov (pop props) (pop props)))))
;; Text property folds.
(unless drop-visibility (org-fold-core--update-buffer-folds))
;; Never write the buffer copy to disk, despite
;; `buffer-file-name' not being nil.
(setq write-contents-functions (list (lambda (&rest _) t))))))))
@ -3842,7 +3848,9 @@ Assume point is at the beginning of the link."
;; Type 1: Text targeted from a radio target.
((and org-target-link-regexp
(save-excursion (or (bolp) (backward-char))
(looking-at org-target-link-regexp)))
(if org-target-link-regexps
(org--re-list-looking-at org-target-link-regexps)
(looking-at org-target-link-regexp))))
(setq type "radio")
(setq format 'plain)
(setq link-end (match-end 1))
@ -5215,7 +5223,10 @@ to an appropriate container (e.g., a paragraph)."
((not (memq 'link restriction)) nil)
((progn
(unless (bolp) (forward-char -1))
(not (re-search-forward org-target-link-regexp nil t)))
(not
(if org-target-link-regexps
(org--re-list-search-forward org-target-link-regexps nil t)
(re-search-forward org-target-link-regexp nil t))))
nil)
;; Since we moved backward, we do not want to
;; match again an hypothetical 1-character long
@ -5224,8 +5235,11 @@ to an appropriate container (e.g., a paragraph)."
;; beginning of line, we prevent this here.
((and (= start (1+ (line-beginning-position)))
(= start (match-end 1)))
(and (re-search-forward org-target-link-regexp nil t)
(1+ (match-beginning 1))))
(and
(if org-target-link-regexps
(org--re-list-search-forward org-target-link-regexps nil t)
(re-search-forward org-target-link-regexp nil t))
(1+ (match-beginning 1))))
(t (1+ (match-beginning 1))))))
found)
(save-excursion

View File

@ -29,6 +29,7 @@
(require 'org-macs)
(org-assert-version)
(require 'seq) ; Emacs 27 does not preload seq.el; for `seq-every-p'.
(declare-function org-mode "org" ())
(declare-function org-toggle-pretty-entities "org" ())

View File

@ -634,6 +634,10 @@ unless RETURN-ONLY is non-nil."
text-property-default-nonsticky
full-prop-list))))))))))))))
(defun org-fold-core--update-buffer-folds ()
"Copy folding state in a new buffer with text copied from old buffer."
(org-fold-core--property-symbol-get-create (car (org-fold-core-folding-spec-list))))
(defun org-fold-core-decouple-indirect-buffer-folds ()
"Copy and decouple folding state in a newly created indirect buffer.
This function is mostly intended to be used in
@ -641,7 +645,7 @@ This function is mostly intended to be used in
(when (and (buffer-base-buffer)
(eq org-fold-core-style 'text-properties)
(not (memql 'ignore-indirect org-fold-core--optimise-for-huge-buffers)))
(org-fold-core--property-symbol-get-create (car (org-fold-core-folding-spec-list)))))
(org-fold-core--update-buffer-folds)))
;;; API
@ -784,19 +788,16 @@ corresponding folding spec (if the text is folded using that spec)."
(when (and spec (not (eq spec 'all))) (org-fold-core--check-spec spec))
(org-with-point-at pom
(cond
((eq spec 'all)
(let ((result))
(dolist (spec (org-fold-core-folding-spec-list))
(let ((val (if (eq org-fold-core-style 'text-properties)
(get-text-property (point) (org-fold-core--property-symbol-get-create spec nil t))
(get-char-property (point) (org-fold-core--property-symbol-get-create spec nil t)))))
(when val (push val result))))
(reverse result)))
((null spec)
(let ((result (if (eq org-fold-core-style 'text-properties)
(get-text-property (point) 'invisible)
(get-char-property (point) 'invisible))))
(when (org-fold-core-folding-spec-p result) result)))
((or (null spec) (eq spec 'all))
(catch :single-spec
(let ((result))
(dolist (lspec (org-fold-core-folding-spec-list))
(let ((val (if (eq org-fold-core-style 'text-properties)
(get-text-property (point) (org-fold-core--property-symbol-get-create lspec nil t))
(get-char-property (point) (org-fold-core--property-symbol-get-create lspec nil t)))))
(when (and val (null spec)) (throw :single-spec val))
(when val (push val result))))
(reverse result))))
(t (if (eq org-fold-core-style 'text-properties)
(get-text-property (point) (org-fold-core--property-symbol-get-create spec nil t))
(get-char-property (point) (org-fold-core--property-symbol-get-create spec nil t))))))))
@ -1034,26 +1035,23 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
(when spec (org-fold-core--check-spec spec))
(with-silent-modifications
(org-with-wide-buffer
;; Arrange face property of newlines after all the folds
;; between FROM and TO to match the first character before the
;; fold; not the last as per Emacs defaults. This makes
;; :extend faces span past the ellipsis.
;; See bug#65896.
(if flag ; folding
(when (equal ?\n (char-after to))
(put-text-property to (1+ to) 'face (get-text-property from 'face)))
;; unfolding
(dolist (region (org-fold-core-get-regions :from from :to to :specs spec))
(when (equal ?\n (char-after (cadr region)))
(if-let ((specs (remq spec (org-fold-core-get-folding-spec 'all (1- (cadr region))))))
;; There are multiple folds, re-apply 'face according
;; to the folds that will stay after unfolding SPEC.
(put-text-property
(cadr region) (1+ (cadr region)) 'face
(get-text-property
(car (org-fold-core-get-region-at-point (car specs) (1- (cadr region))))
'face))
(font-lock-flush (cadr region) (1+ (cadr region)))))))
;; Arrange fontifying newlines after all the folds between FROM
;; and TO to match the first character before the fold; not the
;; last as per Emacs defaults. This makes :extend faces span
;; past the ellipsis. See bug#65896. The face properties are
;; assigned via `org-activate-folds'.
(when (equal ?\n (char-after from))
(font-lock-flush from (1+ from)))
(when (equal ?\n (char-after to))
(font-lock-flush to (1+ to)))
(dolist (region (org-fold-core-get-regions :from from :to to :specs spec))
(when (equal ?\n (char-after (cadr region)))
(font-lock-flush (cadr region) (1+ (cadr region))))
;; Re-fontify beginning of the fold - we may
;; unfold inside an existing fold, with FROM begin a newline
;; after spliced fold.
(when (equal ?\n (char-after (car region)))
(font-lock-flush (car region) (1+ (car region)))))
(when (eq org-fold-core-style 'overlays)
(if org-fold-core--keep-overlays
(mapc
@ -1113,14 +1111,12 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
(setq pos next))
(setq pos (next-single-char-property-change pos 'invisible nil to)))))))
(when (eq org-fold-core-style 'text-properties)
(remove-text-properties from to (list (org-fold-core--property-symbol-get-create spec) nil)))
;; FROM..TO may represent only a part of the fold. Transfer
;; 'face from the new char before fold, if there is any.
(when-let ((trailing-fold (org-fold-core-get-region-at-point spec to)))
(when (equal ?\n (char-after (cdr trailing-fold)))
(put-text-property
(cdr trailing-fold) (1+ (cdr trailing-fold))
'face (get-text-property to 'face))))))))))
(remove-text-properties from to (list (org-fold-core--property-symbol-get-create spec) nil)))))
;; Re-calculate trailing faces for all the folds revealed
;; by unfolding or created by folding.
(dolist (region (org-fold-core-get-regions :from from :to to :specs spec))
(when (equal ?\n (char-after (cadr region)))
(font-lock-flush (cadr region) (1+ (cadr region)))))))))
(cl-defmacro org-fold-core-regions (regions &key override clean-markers relative)
"Fold every region in REGIONS list in current buffer.

View File

@ -102,7 +102,11 @@ When nil, you can use these keybindings to navigate the buffer:
mouse-drag-region universal-argument org-occur)))
(dolist (cmd cmds)
(substitute-key-definition cmd cmd map global-map)))
(suppress-keymap map)
(if org-goto-auto-isearch
;; Suppress 0-9 interpreted as digital arguments.
;; Make them initiate isearch instead.
(suppress-keymap map t)
(suppress-keymap map))
(org-defkey map "\C-m" 'org-goto-ret)
(org-defkey map [(return)] 'org-goto-ret)
(org-defkey map [(left)] 'org-goto-left)

View File

@ -129,6 +129,46 @@ nil Never use an ID to make a link, instead link using a text search for
(const :tag "Only use existing" use-existing)
(const :tag "Do not use ID to create link" nil)))
(defcustom org-id-link-consider-parent-id nil
"Non-nil means storing a link to an Org entry considers inherited IDs.
When this option is non-nil and `org-id-link-use-context' is
enabled, ID properties inherited from parent entries will be
considered when storing an ID link. If no ID is found in this
way, a new one may be created as normal (see
`org-id-link-to-org-use-id').
For example, given this org file:
* Parent
:PROPERTIES:
:ID: abc
:END:
** Child 1
** Child 2
With `org-id-link-consider-parent-id' and
`org-id-link-use-context' both enabled, storing a link with point
at \"Child 1\" will produce a link \"<id:abc::*Child 1>\". This
allows linking to uniquely-named sub-entries within a parent
entry with an ID, without requiring every sub-entry to have its
own ID."
:group 'org-link-store
:group 'org-id
:package-version '(Org . "9.7")
:type 'boolean)
(defcustom org-id-link-use-context t
"Non-nil means enables search string context in org-id links.
Search strings are added by `org-id-store-link' when both the
general option `org-link-context-for-files' and the org-id option
`org-id-link-use-context' are non-nil."
:group 'org-link-store
:group 'org-id
:package-version '(Org . "9.7")
:type 'boolean)
(defcustom org-id-uuid-program "uuidgen"
"The uuidgen program."
:group 'org-id
@ -280,15 +320,21 @@ This is useful when working with contents in a temporary buffer
that will be copied back to the original.")
;;;###autoload
(defun org-id-get (&optional epom create prefix)
"Get the ID property of the entry at EPOM.
EPOM is an element, marker, or buffer position.
If EPOM is nil, refer to the entry at point.
If the entry does not have an ID, the function returns nil.
However, when CREATE is non-nil, create an ID if none is present already.
PREFIX will be passed through to `org-id-new'.
In any case, the ID of the entry is returned."
(let ((id (org-entry-get epom "ID")))
(defun org-id-get (&optional epom create prefix inherit)
"Get the ID of the entry at EPOM.
EPOM is an element, marker, or buffer position. If EPOM is nil,
refer to the entry at point.
If INHERIT is non-nil, ID properties inherited from parent
entries are considered. Otherwise, only ID properties on the
entry itself are considered.
When CREATE is nil, return the ID of the entry if found,
otherwise nil. When CREATE is non-nil, create an ID if none has
been found, and return the new ID. PREFIX will be passed through
to `org-id-new'."
(let ((id (org-entry-get epom "ID" (and inherit t))))
(cond
((and id (stringp id) (string-match "\\S-" id))
id)
@ -700,21 +746,56 @@ optional argument MARKERP, return the position as a new marker."
;; id link type
;; Calling the following function is hard-coded into `org-store-link',
;; so we do have to add it to `org-store-link-functions'.
(defun org-id--get-id-to-store-link (&optional create)
"Get or create the relevant ID for storing a link.
Optional argument CREATE is passed to `org-id-get'.
Inherited IDs are only considered when
`org-id-link-consider-parent-id', `org-id-link-use-context' and
`org-link-context-for-files' are all enabled, since inherited IDs
are confusing without the additional search string context.
Note that this function resets the
`org-entry-property-inherited-from' marker: it will either point
to nil (if the id was not inherited) or to the point it was
inherited from."
(let* ((inherit-id (and org-id-link-consider-parent-id
org-id-link-use-context
org-link-context-for-files)))
(move-marker org-entry-property-inherited-from nil)
(org-id-get nil create nil inherit-id)))
;;;###autoload
(defun org-id-store-link ()
"Store a link to the current entry, using its ID.
If before first heading store first title-keyword as description
or filename if no title."
The link description is based on the heading, or if before the
first heading, the title keyword if available, or else the
filename.
When `org-link-context-for-files' and `org-id-link-use-context'
are non-nil, add a search string to the link. The link
description is then based on the search string target.
When in addition `org-id-link-consider-parent-id' is non-nil, the
ID can be inherited from a parent entry, with the search string
used to still link to the current location."
(interactive)
(when (and (buffer-file-name (buffer-base-buffer)) (derived-mode-p 'org-mode))
(let* ((link (concat "id:" (org-id-get-create)))
(when (and (buffer-file-name (buffer-base-buffer))
(derived-mode-p 'org-mode))
;; Get the precise target first, in case looking for an id causes
;; a properties drawer to be added at the current location.
(let* ((precise-target (and org-link-context-for-files
org-id-link-use-context
(org-link-precise-link-target)))
(link (concat "id:" (org-id--get-id-to-store-link 'create)))
(id-location (or (and org-entry-property-inherited-from
(marker-position org-entry-property-inherited-from))
(save-excursion (org-back-to-heading-or-point-min t) (point))))
(case-fold-search nil)
(desc (save-excursion
(org-back-to-heading-or-point-min t)
(goto-char id-location)
(cond ((org-before-first-heading-p)
(let ((keywords (org-collect-keywords '("TITLE"))))
(if keywords
@ -726,14 +807,59 @@ or filename if no title."
(match-string 4)
(match-string 0)))
(t link)))))
;; Precise targets should be after id-location to avoid
;; duplicating the current headline as a search string
(when (and precise-target
(> (nth 2 precise-target) id-location))
(setq link (concat link "::" (nth 0 precise-target)))
(setq desc (nth 1 precise-target)))
(org-link-store-props :link link :description desc :type "id")
link)))
(defun org-id-open (id _)
"Go to the entry with id ID."
(org-mark-ring-push)
(let ((m (org-id-find id 'marker))
cmd)
;;;###autoload
(defun org-id-store-link-maybe (&optional interactive?)
"Store a link to the current entry using its ID if enabled.
The value of `org-id-link-to-org-use-id' determines whether an ID
link should be stored, using `org-id-store-link'.
Assume the function is called interactively if INTERACTIVE? is
non-nil."
(when (and (buffer-file-name (buffer-base-buffer))
(derived-mode-p 'org-mode)
(or (eq org-id-link-to-org-use-id t)
(and interactive?
(or (eq org-id-link-to-org-use-id 'create-if-interactive)
(and (eq org-id-link-to-org-use-id
'create-if-interactive-and-no-custom-id)
(not (org-entry-get nil "CUSTOM_ID")))))
;; 'use-existing
(and org-id-link-to-org-use-id
(org-id--get-id-to-store-link))))
(org-id-store-link)))
(defun org-id-open (link _)
"Go to the entry indicated by id link LINK.
The link can include a search string after \"::\", which is
passed to `org-link-search'.
For backwards compatibility with IDs that contain \"::\", if no
match is found for the ID, the full link string including \"::\"
will be tried as an ID."
(let* ((option (and (string-match "::\\(.*\\)\\'" link)
(match-string 1 link)))
(id (if (not option) link
(substring link 0 (match-beginning 0))))
m cmd)
(org-mark-ring-push)
(setq m (org-id-find id 'marker))
(when (and (not m) option)
;; Backwards compatibility: if id is not found, try treating
;; whole link as an id.
(setq m (org-id-find link 'marker))
(when m
(setq option nil)))
(unless m
(error "Cannot find entry with ID \"%s\"" id))
;; Use a buffer-switching command in analogy to finding files
@ -750,9 +876,17 @@ or filename if no title."
(funcall cmd (marker-buffer m)))
(goto-char m)
(move-marker m nil)
(when option
(save-restriction
(unless (org-before-first-heading-p)
(org-narrow-to-subtree))
(org-link-search option nil nil
(org-element-lineage (org-element-at-point) 'headline t))))
(org-fold-show-context)))
(org-link-set-parameters "id" :follow #'org-id-open)
(org-link-set-parameters "id"
:follow #'org-id-open
:store #'org-id-store-link-maybe)
(provide 'org-id)

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

File diff suppressed because it is too large Load Diff

View File

@ -65,6 +65,7 @@
;; - special properties in properties drawers,
;; - obsolete syntax for properties drawers,
;; - invalid duration in EFFORT property,
;; - invalid ID property with a double colon,
;; - missing definition for footnote references,
;; - missing reference for footnote definitions,
;; - non-footnote definitions in footnote section,
@ -686,6 +687,16 @@ Use :header-args: instead"
(list (org-element-begin p)
(format "Invalid effort duration format: %S" value))))))))
(defun org-lint-invalid-id-property (ast)
(org-element-map ast 'node-property
(lambda (p)
(when (equal "ID" (org-element-property :key p))
(let ((value (org-element-property :value p)))
(and (org-string-nw-p value)
(string-match-p "::" value)
(list (org-element-begin p)
(format "IDs should not include \"::\": %S" value))))))))
(defun org-lint-link-to-local-file (ast)
(org-element-map ast 'link
(lambda (l)
@ -1507,6 +1518,19 @@ AST is the buffer parse tree."
((memq (org-element-property :type deadline) '(inactive inactive-range))
(list (org-element-begin planning) "Inactive timestamp in DEADLINE will not appear in agenda."))
(t nil))))))
(defvar org-beamer-frame-environment) ; defined in ox-beamer.el
(defun org-lint-beamer-frame (ast)
"Check for occurrences of begin or end frame."
(require 'ox-beamer)
(org-with-point-at ast
(goto-char (point-min))
(let (result)
(while (re-search-forward
(concat "\\\\\\(begin\\|end\\){" org-beamer-frame-environment "}") nil t)
(push (list (match-beginning 0) "Beamer frame name may cause error when exporting. Consider customizing `org-beamer-frame-environment'.") result))
result)))
;;; Checkers declaration
@ -1684,6 +1708,11 @@ AST is the buffer parse tree."
#'org-lint-invalid-effort-property
:categories '(properties))
(org-lint-add-checker 'invalid-id-property
"Report search string delimiter \"::\" in ID property"
#'org-lint-invalid-id-property
:categories '(properties))
(org-lint-add-checker 'undefined-footnote-reference
"Report missing definition for footnote references"
#'org-lint-undefined-footnote-reference
@ -1787,6 +1816,10 @@ AST is the buffer parse tree."
"Report $ that might be treated as LaTeX fragment boundary."
#'org-lint-LaTeX-$-ambiguous
:categories '(markup) :trust 'low)
(org-lint-add-checker 'beamer-frame
"Report that frame text contains beamer frame environment."
#'org-lint-beamer-frame
:categories '(export) :trust 'low)
(org-lint-add-checker 'timestamp-syntax
"Report malformed timestamps."
#'org-lint-timestamp-syntax

View File

@ -344,6 +344,278 @@ If EXCLUDE-TMP is non-nil, ignore temporary buffers."
nil))
(buffer-list)))))
;;; Async stack
(defvar org-async--stack nil
"List of async currently running task forms.
Each running task is represented by a list with the following structure:
(%PROCESS :success %FUN :failure %FUN
:filter %FUN :buffer %BUFFER
:timeout %FLOAT :start-time %FLOAT
:info %SEXP)")
(defvar org-async--wait-queue nil
"List of async queued task forms.
Each queued task is represented by a list with the following structure:
(%PROCESS :success %FUN :failure %FUN
:filter %FUN :buffer %BUFFER
:info %SEXP :dir %STRING
:timeout %FLOAT :coding %SYMBOL)")
(defvar org-async-process-limit 4
"Maximum number of processes to run at once.")
(defvar org-async-timeout 120
"Default timeout for a process started via `org-async-queue'.")
(defvar org-async-check-timeout-interval 1
"Check for processes which have exceeded their timeout every this many seconds.")
(defvar org-async--counter 0)
(cl-defun org-async-call (proc &key success failure filter buffer info timeout now process-variables
(dir default-directory) (coding 'utf-8))
"Start PROC and register it with callbacks SUCCESS and FAILURE.
PROC can be a process, string, or list. A string will be run as
a shell command, with `start-process-shell-command' and a list
run using `start-process' with the car as the command and the cdr
as the arguments. The process will be executed in DIR (if set)
or `default-directory'.
There is also a \"special form\" of PROC, namely a list where the
first item is the symbol org-async-task, and the rest constitutes
an argument list for `org-async-call'. This form allows for easy
specification of callbacks that are themselves async tasks, e.g.
(org-async-call \\='(\"sleep 1\")
:success \\='(org-async-task (\"notify-send\" \"done\")))
When using this form, all other arguments are ignored.
When BUFFER is provided, the output of PROC will be directed to it.
Shoud BUFFER be t, then a temp buffer will be created and removed
during `org-async--cleanup-process'.
SUCCESS and FAILURE can be any form accepted by `org-async--execute-callback',
namely:
- A string, which is used a `message' string with the exit-code,
process-buffer, and INFO as arguments.
- A function, which is called with exit-code, process-buffer,
and INFO as arguments.
- A list, which is used as an argument list for a new `org-async-call' call.
- nil, which does nothing.
When PROC succeeds by exiting with an exit code of zero, the SUCCESS
callback will be run. Should PROC fail, or be killed, or the process
runs for more than TIMEOUT seconds, the FAILURE callback will be run.
A function FILTER can be provided, in which case it will be
called in the same manner as a normal procecss filter, however
the function FILTER will be called with INFO as a third argument.
i.e. the call signature is (content new-content-string INFO)
When BUFFER is non-nil, there are two other major differences:
- The new content is silently inserted before FILTER is called
- Note that `point' is left alone and is not moved by this.
- The process buffer is the current buffer when FILTER is called.
When CODING is non-nil, both the process encode and decode system
will be set to CODING. If unset, UTF-8 is used.
When NOW is non-nil, the PROC is started immediately, regardless
of `org-async-process-limit'.
For improved performance, PROCESS-VARIABLES is a list of
let-style bindings that should be applied to the process.
Variables are supported on an individual basis (i.e. only certain
variables can be set), with the default value being equivalent to:
:process-variables ((process-adaptive-read-buffering nil)
(process-connection-type nil)
(read-process-output-max 65536))"
(cond
;; Called with a task (as can be used with callbacks), so re-call
;; with expanded arguments.
((and (consp proc)
(eq (car proc) 'org-async-task))
(apply #'org-async-call (cdr proc)))
;; Start the async process now.
((or now (< (length org-async--stack) org-async-process-limit))
(let ((proc
(let ((default-directory (or dir default-directory))
(process-adaptive-read-buffering ; No by default
(cadr (or (assoc 'process-adaptive-read-buffering process-variables) nil)))
(process-connection-type ; Use a pipe by default
(cadr (or (assoc 'process-connection-type process-variables) nil)))
(read-process-output-max ; Can be worth changing depending on the process
(or (assq 'read-process-output-max process-variables) read-process-output-max)))
(cond ((processp proc) proc)
((stringp proc)
(start-process-shell-command
(format "org-async-%d" (cl-incf org-async--counter))
buffer proc))
((consp proc)
(apply #'start-process
(format "org-async-%s-%d"
(car proc) (cl-incf org-async--counter))
buffer proc))
(t (error "Async process input %S not a recognised format"
proc)))))
(timeout (or timeout org-async-timeout)))
(set-process-sentinel proc #'org-async--sentinel)
(when filter
(set-process-filter proc #'org-async--filter))
(when coding
(set-process-coding-system proc coding coding))
(push (list proc
:success success
:failure failure
:filter filter
:buffer (if (eq buffer t)
(cons :temp (generate-new-buffer " *temp*" t))
buffer)
:info info
:timeout timeout
:start-time (float-time))
org-async--stack)
(org-async--monitor t)
(car org-async--stack)))
;; Queue the task to be run later.
(t
(setq org-async--wait-queue
(append org-async--wait-queue
(list (list proc
:success success
:failure failure
:filter filter
:buffer buffer
:info info
:dir dir
:timeout timeout
:coding coding))))
(last org-async--wait-queue))))
(defvar org-async--blocking-tasks nil
"List of async tasks currently being waited on.")
(defun org-async-wait-for (&rest tasks)
"Block until every task of TASKS has finished (including callback tasks)."
(setq org-async--blocking-tasks tasks)
(while org-async--blocking-tasks
(dolist (task org-async--blocking-tasks)
(accept-process-output (car task)))))
(defun org-async--filter (process string)
"After PROCESS recieves STRING, call the async filter.
This is implementated to satisfy the filter function documentation in
`org-async-call'."
(when-let ((proc-info (alist-get process org-async--stack)))
(let ((filter (plist-get proc-info :filter))
(buffer (plist-get proc-info :buffer)))
(if buffer
(with-current-buffer buffer
(save-excursion
(goto-char (point-max))
(insert string))
(funcall filter process string (plist-get proc-info :info)))
(funcall filter process string (plist-get proc-info :info))))))
(defun org-async--sentinel (process _signal)
"Watch PROCESS for death, and cleanup accordingly.
When a signal is recieved, the status of PROCESS is checked.
Should the it have an exit status, with status code 0,
`org-async--cleanup-process' is run with the \"failed\" argument
unset. Should the process have finished in any other manner,
`org-async--cleanup-process' is run with the \"failed\" argument."
(pcase (process-status process)
((and 'exit (guard (= 0 (process-exit-status process))))
(org-async--cleanup-process process))
((or 'exit 'signal 'failed)
(org-async--cleanup-process process 'failed))))
(defun org-async--cleanup-process (process &optional failed)
"Remove PROCESS from the async stack, and run its callback.
If the exit code of PROCESS is zero and FAILED is non-nil, then
the success callback is run (via `org-async--execute-callback').
Otherwise, the failure callback is run."
(when (assq process org-async--stack)
(let* ((proc-info (cdr (assq process org-async--stack)))
(buffer-val (plist-get proc-info :buffer))
(proc-buf (if (consp buffer-val) (cdr buffer-val) buffer-val))
(blocking-p (cl-member process org-async--blocking-tasks :key #'car)))
(setq org-async--stack
(delq (assq process org-async--stack) org-async--stack))
;; Ensure that any filter is called on the final output
;; prior to the callbacks.
(while (accept-process-output process))
(org-async--execute-callback
(plist-get
proc-info
(if (and (not failed)
(= 0 (process-exit-status process)))
:success :failure))
(process-exit-status process)
proc-buf
(plist-get proc-info :info)
blocking-p)
(when blocking-p
(setq org-async--blocking-tasks
(cl-delete process org-async--blocking-tasks :key #'car)))
(when (and (consp buffer-val) (eq :temp (car buffer-val)))
(kill-buffer proc-buf)))
(when (and org-async--wait-queue
(< org-async-process-limit (length org-async--stack)))
(apply #'org-async-call (pop org-async--wait-queue)))))
(defun org-async--execute-callback (callback exit-code process-buffer info &optional blocking)
"Run CALLBACK with EXIT-CODE, PROCESS-BUFFER, and INFO.
CALLBACK can take one of four forms:
- A string, which is used a `message' string with EXIT-CODE,
PROCESS-BUFFER, and INFO as arguments.
- A function, which is called with EXIT-CODE, PROCESS-BUFFER,
and INFO as arguments.
- A list, which is either:
- An (org-async-task ...) structure, which passed to an
`org-async-call' invocation.
- A list of callbacks, which are individually evaluated.
- nil, which does nothing.
When BLOCKING is set, all callback tasks are made blocking."
(cond
((stringp callback)
(message callback exit-code process-buffer info))
((functionp callback)
(funcall callback exit-code process-buffer info))
((consp callback)
(if (eq (car callback) 'org-async-task)
(if blocking
(push (org-async-call callback) org-async--blocking-tasks)
(org-async-call callback))
(dolist (clbk callback)
(org-async--execute-callback clbk exit-code process-buffer info blocking))))
((null callback)) ; Do nothing.
(t (message "Ignoring invalid `org-async-call' callback: %S" callback))))
(defvar org-async--monitor-scheduled nil)
(defun org-async--monitor (&optional force)
"Check each process against their timeouts, and kill any overdue.
The only runs when `org-async--monitor-scheduled' is nil, unless FORCE is set.
Should any processes still be alive after checking the stack, this will run
itself using a timer in `org-async-check-timeout-interval' seconds."
(when (or force (null org-async--monitor-scheduled))
(dolist (stack-proc org-async--stack)
(if (process-live-p (car stack-proc))
(let ((timeout (plist-get (cdr stack-proc) :timeout)))
(when (and (numberp timeout)
(< 0 timeout
(- (float-time)
(plist-get (cdr stack-proc) :start-time))))
(kill-process (car stack-proc))))
(org-async--cleanup-process (car stack-proc))))
(if org-async--stack
(setq org-async--monitor-scheduled
(run-at-time org-async-check-timeout-interval
nil #'org-async--monitor t))
(setq org-async--monitor-scheduled nil))))
;;; File
@ -991,7 +1263,7 @@ Otherwise, return nil."
"Splits STRING into substrings at SEPARATORS.
SEPARATORS is a regular expression. When nil, it defaults to
\"[ \f\t\n\r\v]+\".
\"[ \\f\\t\\n\\r\\v]+\".
Unlike `split-string', matching SEPARATORS at the beginning and
end of string are ignored."

View File

@ -470,7 +470,7 @@ agenda view showing the flagged items."
(insert "#+TAGS: " (mapconcat 'identity tags " ") "\n")
(insert "#+ALLPRIORITIES: " org-mobile-allpriorities "\n")
(when (file-exists-p (expand-file-name
org-mobile-directory "agendas.org"))
"agendas.org" org-mobile-directory))
(insert "* [[file:agendas.org][Agenda Views]]\n"))
(pcase-dolist (`(,_ . ,link-name) files-alist)
(insert (format "* [[file:%s][%s]]\n" link-name link-name)))

View File

@ -389,7 +389,7 @@ The keys are conses of (container . associated).")
(defvar org-persist--index-age nil
"The modification time of the index file, when it was loaded.")
(defvar org-persist--report-time 0.5
(defvar org-persist--report-time nil
"Whether to report read/write time.
When the value is a number, it is a threshold number of seconds. If
@ -602,22 +602,27 @@ MISC, if non-nil will be appended to the collection. It must be a plist."
;;;; Reading container data.
(defvar org-persist--inhibit-container-normalization nil
"Prevent `org-persist--normalize-container' from doing anything.")
(defun org-persist--normalize-container (container &optional inner)
"Normalize CONTAINER representation into (type . settings).
When INNER is non-nil, do not try to match as list of containers."
(pcase container
((or `elisp `elisp-data `version `file `index `url)
`(,container nil))
((or (pred keywordp) (pred stringp) `(quote . ,_))
`(elisp-data ,container))
((pred symbolp)
`(elisp ,container))
(`(,(or `elisp `elisp-data `version `file `index `url) . ,_)
container)
((and (pred listp) (guard (not inner)))
(mapcar (lambda (c) (org-persist--normalize-container c 'inner)) container))
(_ (error "org-persist: Unknown container type: %S" container))))
(if org-persist--inhibit-container-normalization
container
(pcase container
((or `elisp `elisp-data `version `file `index `url)
`(,container nil))
((or (pred keywordp) (pred stringp) `(quote . ,_))
`(elisp-data ,container))
((pred symbolp)
`(elisp ,container))
(`(,(or `elisp `elisp-data `version `file `index `url) . ,_)
container)
((and (pred listp) (guard (not inner)))
(mapcar (lambda (c) (org-persist--normalize-container c 'inner)) container))
(_ (error "org-persist: Unknown container type: %S" container)))))
(defvar org-persist--associated-buffer-cache (make-hash-table :weakness 'key)
"Buffer hash cache.")
@ -823,20 +828,23 @@ COLLECTION is the plist holding data collection."
(defun org-persist-write:file (c collection)
"Write file container C according to COLLECTION."
(org-persist-collection-let collection
(when (or (and path (file-exists-p path))
(and (stringp (cadr c)) (file-exists-p (cadr c))))
(when (and (stringp (cadr c)) (file-exists-p (cadr c)))
(setq path (cadr c)))
(let* ((persist-file (plist-get collection :persist-file))
(ext (file-name-extension path))
(file-copy (org-file-name-concat
org-persist-directory
(format "%s-%s.%s" persist-file (md5 path) ext))))
(unless (file-exists-p file-copy)
(unless (file-exists-p (file-name-directory file-copy))
(make-directory (file-name-directory file-copy) t))
(copy-file path file-copy 'overwrite))
(format "%s-%s.%s" persist-file (md5 path) ext)))))
(if (or (and path (file-exists-p path))
(and (stringp (cadr c)) (file-exists-p (cadr c))))
(progn
(when (and (stringp (cadr c)) (file-exists-p (cadr c)))
(setq path (cadr c)))
(let* ((persist-file (plist-get collection :persist-file))
(ext (file-name-extension path))
(file-copy (org-file-name-concat
org-persist-directory
(format "%s-%s.%s" persist-file (md5 path) ext))))
(unless (file-exists-p file-copy)
(unless (file-exists-p (file-name-directory file-copy))
(make-directory (file-name-directory file-copy) t))
(copy-file path file-copy 'overwrite))
(format "%s-%s.%s" persist-file (md5 path) ext)))
(when-let ((file-copy (org-persist-read c associated)))
(file-relative-name file-copy org-persist-directory)))))
(defun org-persist-write:url (c collection)
"Write url container C according to COLLECTION."
@ -941,26 +949,27 @@ VALUE pairs.
When WRITE-IMMEDIATELY is non-nil, the return value will be the same
with `org-persist-write'."
(unless org-persist--index (org-persist--load-index))
(setq container (org-persist--normalize-container container))
(when inherit
(setq inherit (org-persist--normalize-container inherit))
(let ((inherited-collection (org-persist--get-collection inherit associated))
new-collection)
(unless (member container (plist-get inherited-collection :container))
(setq new-collection
(plist-put (copy-sequence inherited-collection) :container
(cons container (plist-get inherited-collection :container))))
(org-persist--remove-from-index inherited-collection)
(org-persist--add-to-index new-collection))))
(let ((collection (org-persist--get-collection container associated misc)))
(when (and expiry (not inherit))
(when expiry (plist-put collection :expiry expiry))))
(when (or (bufferp associated) (bufferp (plist-get associated :buffer)))
(with-current-buffer (if (bufferp associated)
associated
(plist-get associated :buffer))
(add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local)))
(when write-immediately (org-persist-write container associated)))
(let ((container (org-persist--normalize-container container))
(inherit (and inherit (org-persist--normalize-container inherit)))
(org-persist--inhibit-container-normalization t))
(when inherit
(let ((inherited-collection (org-persist--get-collection inherit associated))
new-collection)
(unless (member container (plist-get inherited-collection :container))
(setq new-collection
(plist-put (copy-sequence inherited-collection) :container
(cons container (plist-get inherited-collection :container))))
(org-persist--remove-from-index inherited-collection)
(org-persist--add-to-index new-collection))))
(let ((collection (org-persist--get-collection container associated misc)))
(when (and expiry (not inherit))
(when expiry (plist-put collection :expiry expiry))))
(when (or (bufferp associated) (bufferp (plist-get associated :buffer)))
(with-current-buffer (if (bufferp associated)
associated
(plist-get associated :buffer))
(add-hook 'kill-buffer-hook #'org-persist-write-all-buffer nil 'local)))
(when write-immediately (org-persist-write container associated))))
(cl-defun org-persist-unregister (container &optional associated &key remove-related)
"Unregister CONTAINER in ASSOCIATED to be persistent.
@ -1075,6 +1084,9 @@ have the same meaning as in `org-persist-read'."
"Call `org-persist-load-all' in current buffer."
(org-persist-load-all (current-buffer)))
(defvar org-persist--inhibit-write nil
"Whether `org-persist-write' should be inhibited.")
(defun org-persist-write (container &optional associated ignore-return)
"Save CONTAINER according to ASSOCIATED.
ASSOCIATED can be a plist, a buffer, or a string.
@ -1084,31 +1096,33 @@ The return value is nil when writing fails and the written value (as
returned by `org-persist-read') on success.
When IGNORE-RETURN is non-nil, just return t on success without calling
`org-persist-read'."
(setq associated (org-persist--normalize-associated associated))
;; Update hash
(when (and (plist-get associated :file)
(plist-get associated :hash)
(get-file-buffer (plist-get associated :file)))
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
(let ((collection (org-persist--get-collection container associated)))
(setf collection (plist-put collection :associated associated))
(unless (or
;; Prevent data leakage from encrypted files.
;; We do it in somewhat paranoid manner and do not
;; allow anything related to encrypted files to be
;; written.
(and (plist-get associated :file)
(string-match-p epa-file-name-regexp (plist-get associated :file)))
(seq-find (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
(plist-get collection :container)))
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
(plist-get collection :container))))
(puthash file data org-persist--write-cache)
(org-persist--write-elisp-file file data)
(or ignore-return (org-persist-read container associated)))))))
(unless org-persist--inhibit-write
(setq associated (org-persist--normalize-associated associated))
;; Update hash
(when (and (plist-get associated :file)
(plist-get associated :hash)
(get-file-buffer (plist-get associated :file)))
(setq associated (org-persist--normalize-associated (get-file-buffer (plist-get associated :file)))))
(let ((collection (org-persist--get-collection container associated))
(org-persist--inhibit-write t))
(setf collection (plist-put collection :associated associated))
(unless (or
;; Prevent data leakage from encrypted files.
;; We do it in somewhat paranoid manner and do not
;; allow anything related to encrypted files to be
;; written.
(and (plist-get associated :file)
(string-match-p epa-file-name-regexp (plist-get associated :file)))
(cl-some (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-write-hook v associated))
(plist-get collection :container)))
(when (or (file-exists-p org-persist-directory) (org-persist--save-index))
(let ((file (org-file-name-concat org-persist-directory (plist-get collection :persist-file)))
(data (mapcar (lambda (c) (cons c (org-persist-write:generic c collection)))
(plist-get collection :container))))
(puthash file data org-persist--write-cache)
(org-persist--write-elisp-file file data)
(or ignore-return (org-persist-read container associated))))))))
(defun org-persist-write-all (&optional associated)
"Save all the persistent data.

View File

@ -809,9 +809,42 @@ as `org-src-fontify-natively' is non-nil."
(1+ pt) (1- (point)) 'face 'org-inline-src-block)))
(font-lock-append-text-property
(1- (point)) (point) 'face '(org-inline-src-block shadow))
(setq pt (point)))))
(setq pt (point)))
(when (and org-inline-src-prettify-results
(re-search-forward "\\= {{{results(" limit t))
(font-lock-append-text-property pt (1+ pt) 'face 'org-inline-src-block)
(goto-char pt))))
t)))
(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

View File

@ -5494,25 +5494,38 @@ for a horizontal separator line, or a list of field values as strings.
The table is taken from the parameter TXT, or from the buffer at point."
(if txt
(with-temp-buffer
(buffer-disable-undo)
(insert txt)
(goto-char (point-min))
(org-table-to-lisp))
(save-excursion
(goto-char (org-table-begin))
(let ((table nil))
(while (re-search-forward "\\=[ \t]*|" nil t)
(let ((row nil))
(if (looking-at "-")
(push 'hline table)
(while (not (progn (skip-chars-forward " \t") (eolp)))
(push (buffer-substring
(point)
(progn (re-search-forward "[ \t]*\\(|\\|$\\)")
(match-beginning 0)))
row))
(push (nreverse row) table)))
(let (table)
(while (progn (skip-chars-forward " \t")
(eq (following-char) ?|))
(forward-char)
(push
(if (eq (following-char) ?-)
'hline
(let (row)
(while (progn
(skip-chars-forward " \t")
(not (eolp)))
(let ((q (point)))
(skip-chars-forward "^|\n")
(goto-char
(prog1
(let ((p (point)))
(unless (eolp) (setq p (1+ p)))
p)
(skip-chars-backward " \t" q)
;; Preserve text properties. They are used when
;; calculating cell width.
(push (buffer-substring q (point)) row)))))
(nreverse row)))
table)
(forward-line))
(nreverse table)))))
(nreverse table)))))
(defun org-table-collapse-header (table &optional separator max-header-lines)
"Collapse the lines before `hline' into a single header.

File diff suppressed because it is too large Load Diff

View File

@ -148,6 +148,22 @@ which is replaced with the subtitle."
:package-version '(Org . "8.3")
:type '(string :tag "Format string"))
(defcustom org-beamer-frame-environment "orgframe"
"Name of the alternative beamer frame environment.
In frames marked as fragile, this environment is used in place of
the usual frame environment.
This permits insertion of a beamer frame inside example blocks,
working around beamer limitations. See
https://list.orgmode.org/87a5nux3zr.fsf@t14.reltub.ca/T/#mc7221e93f138bdd56c916b194b9230d3a6c3de09
This option may need to be changed when \"\\end{orgframe}\" string is
used inside beamer slides."
:group 'org-export-beamer
:package-version '(Org . "9.7")
:type '(string :tag "Beamer frame")
:safe (lambda (str) (string-match-p "^[A-Za-z]+$" str)))
;;; Internal Variables
@ -408,12 +424,14 @@ used as a communication channel."
"Format HEADLINE as a frame.
CONTENTS holds the contents of the headline. INFO is a plist
used as a communication channel."
(let ((fragilep
;; FRAGILEP is non-nil when HEADLINE contains an element
;; among `org-beamer-verbatim-elements'.
(org-element-map headline org-beamer-verbatim-elements 'identity
info 'first-match)))
(concat "\\begin{frame}"
(let* ((fragilep
;; FRAGILEP is non-nil when HEADLINE contains an element
;; among `org-beamer-verbatim-elements'.
(org-element-map headline org-beamer-verbatim-elements 'identity
info 'first-match))
(frame (or (and fragilep org-beamer-frame-environment)
"frame")))
(concat "\\begin{" frame "}"
;; Overlay specification, if any. When surrounded by
;; square brackets, consider it as a default
;; specification.
@ -480,7 +498,7 @@ used as a communication channel."
;; output.
(if (not fragilep) contents
(replace-regexp-in-string "\\`\n*" "\\& " (or contents "")))
"\\end{frame}")))
"\\end{" frame "}")))
(defun org-beamer--format-block (headline contents info)
"Format HEADLINE as a block.
@ -814,7 +832,6 @@ contextual information."
(org-export-get-reference radio-target info)
text))
;;;; Template
;;
;; Template used is similar to the one used in `latex' backend,
@ -830,10 +847,12 @@ holding export options."
;; Timestamp.
(and (plist-get info :time-stamp-file)
(format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
;; LaTeX compiler
(org-latex--insert-compiler info)
;; Document class and packages.
;; Document class, packages, and some configuration.
(org-latex-make-preamble info)
;; Define the alternative frame environment.
(unless (equal "frame" org-beamer-frame-environment)
(format "\\newenvironment<>{%s}[1][]{\\begin{frame}#2[environment=%1$s,#1]}{\\end{frame}}\n"
org-beamer-frame-environment))
;; Insert themes.
(let ((format-theme
(lambda (prop command)
@ -881,12 +900,6 @@ holding export options."
(let ((template (plist-get info :latex-hyperref-template)))
(and (stringp template)
(format-spec template (org-latex--format-spec info))))
;; engrave-faces-latex preamble
(when (and (eq (plist-get info :latex-src-block-backend) 'engraved)
(org-element-map (plist-get info :parse-tree)
'(src-block inline-src-block) #'identity
info t))
(org-latex-generate-engraved-preamble info))
;; Document start.
"\\begin{document}\n\n"
;; Title command.

View File

@ -39,6 +39,8 @@
(require 'ox)
(require 'ox-publish)
(require 'table)
(require 'org-latex-preview)
(require 'ox-mathml)
;;; Function Declarations
@ -107,7 +109,8 @@
(verbatim . org-html-verbatim)
(verse-block . org-html-verse-block))
:filters-alist '((:filter-options . org-html-infojs-install-script)
(:filter-parse-tree . org-html-image-link-filter)
(:filter-parse-tree org-html-image-link-filter
org-html-prepare-latex-images)
(:filter-final-output . org-html-final-function))
:menu-entry
'(?h "Export to HTML"
@ -155,6 +158,7 @@
(:html-infojs-template nil nil org-html-infojs-template)
(:html-inline-image-rules nil nil org-html-inline-image-rules)
(:html-link-org-files-as-html nil nil org-html-link-org-files-as-html)
(:html-latex-image-options nil nil org-html-latex-image-options)
(:html-mathjax-options nil nil org-html-mathjax-options)
(:html-mathjax-template nil nil org-html-mathjax-template)
(:html-metadata-timestamp-format nil nil org-html-metadata-timestamp-format)
@ -319,7 +323,7 @@ This affects IDs that are determined from the ID property.")
pre.src-awk:before { content: 'Awk'; }
pre.src-authinfo::before { content: 'Authinfo'; }
pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */
pre.src-C\\+\\+:before { content: 'C++'; }
pre.src-clojure:before { content: 'Clojure'; }
pre.src-css:before { content: 'CSS'; }
pre.src-D:before { content: 'D'; }
@ -822,7 +826,7 @@ e.g. \"tex:mathjax\". Allowed values are:
be loaded.
`html' Use `org-latex-to-html-convert-command' to convert
LaTeX fragments to HTML.
SYMBOL Any symbol defined in `org-preview-latex-process-alist',
SYMBOL Any symbol defined in `org-latex-preview-process-alist',
e.g., `dvipng'."
:group 'org-export-html
:version "24.4"
@ -1168,6 +1172,25 @@ See `format-time-string' for more information on its components."
:package-version '(Org . "8.0")
:type 'string)
(defcustom org-html-latex-image-options
'(:foreground "Black" :background "Transparent"
:page-width 1.0 :scale 1.0 :image-dir "ltximg" :inline nil)
"LaTeX preview options that apply to generated images.
This is a HTML-specific counterpart to
`org-latex-preview-appearance-options', which see.
This supports two extra properties,
:image-dir an html-export counterpart of `org-latex-preview-cache', and
:inline a list of image formats (or single format symbol) that
should not be saved according to :image-dir, but instead
inlined in the generated HTML. Valid format symbols are:
- png, to inline png images using <img> with a data URI
- svg, to inline svg images using <img> with a data URI
- svg-embed, to inline svg images using an <svg> element"
:group 'org-export-html
:package-version '(Org . "9.7")
:type 'plist)
;;;; Template :: Mathjax
(defcustom org-html-mathjax-options
@ -1653,6 +1676,36 @@ https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag"
:package-version '(Org . "9.1")
:type 'string)
;;;; LaTeX Fragments
(defcustom org-latex-to-html-convert-command nil
"Shell command to convert LaTeX fragments to HTML.
This command is very open-ended: the output of the command will
directly replace the LaTeX fragment in the resulting HTML.
Replace format-specifiers in the command as noted below and use
`shell-command' to convert LaTeX to HTML.
%i: The LaTeX fragment to be converted.
For example, this could be used with LaTeXML as
\"latexmlc \\='literal:%i\\=' --profile=math --preload=siunitx.sty 2>/dev/null\".
The LaTeX fragment is replaced as is, without escaping special shell
syntax. It may be necessary to use single-quotes around \\='%i\\=', not
double-quotes. Else a math fragment such as \"$y = 200$\" may be
expanded to \" = 200\"."
:group 'org-latex
:package-version '(Org . "9.4")
:type '(choice
(const :tag "None" nil)
(string :tag "Shell command")))
(defun org-format-latex-as-html (latex-fragment)
"Convert LATEX-FRAGMENT to HTML.
This uses `org-latex-to-html-convert-command', which see."
(let ((cmd (format-spec org-latex-to-html-convert-command
`((?i . ,latex-fragment)))))
(message "Running %s" cmd)
(shell-command-to-string cmd)))
;;;; Todos
@ -1777,11 +1830,7 @@ a communication channel."
(org-html--make-attribute-string
(org-combine-plists
(list :src source
:alt (if (string-match-p
(concat "^" org-preview-latex-image-directory) source)
(org-html-encode-plain-text
(org-find-text-property-in-string 'org-latex-src source))
(file-name-nondirectory source)))
:alt (file-name-nondirectory source))
(if (string= "svg" (file-name-extension source))
(org-combine-plists '(:class "org-svg") attributes '(:fallback nil))
attributes)))
@ -2994,58 +3043,102 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; LaTeX Environment
(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))))
(defun org-html-prepare-latex-images (parse-tree _backend info)
"Make sure that appropriate preview images exist for all LaTeX.
TODO."
(when (assq (plist-get info :with-latex) org-latex-preview-process-alist)
(let* ((latex-preamble
(or org-latex-preview--preamble-content
(setq org-latex-preview--preamble-content
(org-latex-preview--get-preamble))))
(elements
(org-element-map parse-tree
'(latex-fragment latex-environment)
#'identity
info))
(entries-and-numbering
(org-latex-preview--construct-entries
elements t parse-tree))
(processing-type (plist-get info :with-latex))
(processing-info
(cdr (assq processing-type org-latex-preview-process-alist)))
(imagetype (or (plist-get processing-info :image-output-type) "png"))
(numbering-offsets (cons nil (cadr entries-and-numbering)))
(html-options (plist-get info :html-latex-image-options))
(element-hash-table (make-hash-table :test #'eq :size (length elements)))
fragment-info prev-fg prev-bg)
(cl-loop
for entry in (car entries-and-numbering)
for element in elements
do
(pcase-let* ((`(,beg ,end ,provided-value) entry)
(value (or provided-value
(buffer-substring-no-properties beg end)))
(fg (plist-get html-options :foreground))
(bg (plist-get html-options :background))
(number (car (setq numbering-offsets (cdr numbering-offsets))))
(hash (org-latex-preview--hash
processing-type latex-preamble value imagetype fg bg number))
(options (org-combine-plists
org-latex-preview-appearance-options
html-options
(list :number number
:continue-color
(and (equal prev-bg bg)
(equal prev-fg fg))))))
(puthash element hash element-hash-table)
(unless (org-latex-preview--get-cached hash)
(push (list :string (org-latex-preview--tex-styled
processing-type value options)
:overlay (org-latex-preview--ensure-overlay beg end)
:key hash)
fragment-info))
(setq prev-fg fg prev-bg bg)))
(when fragment-info
(apply #'org-async-wait-for
(org-latex-preview--create-image-async
processing-type
(nreverse fragment-info)
:latex-preamble latex-preamble
:appearance-options html-options)))
(plist-put info :html-latex-preview-hash-table element-hash-table)
nil)))
(defun org-html--wrap-latex-environment (contents _ &optional caption label)
(defun org-html--as-latex (element info &optional content)
(let ((content (or content (org-element-property :value element))))
(pcase (plist-get info :with-latex)
('verbatim ; Do nothing.
content)
((or 't 'mathjax)
(cond ; Prepare for MathJax processing.
((string-match-p "\\`\\$\\$" content)
(concat "\\[" (substring content 2 -2) "\\]"))
((string-match-p "\\`\\$" content)
(concat "\\(" (substring content 1 -1) "\\)"))
(t content)))
('html
(org-format-latex-as-html content))
('mathml
(if-let ((path (org-mathml-convert-latex-cached content)))
(with-temp-buffer
(insert-file-contents path)
(buffer-string))
content))
((and ptype (guard (assq ptype org-latex-preview-process-alist)))
(org-html-latex-image element info))
(processing-type
(warn "LaTeX fragment processor `%s' is unknown" processing-type)
content))))
(defun org-html--wrap-latex-environment (contents &optional label)
"Wrap CONTENTS string within appropriate environment for equations.
When optional arguments CAPTION and LABEL are given, use them for
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.
(if (org-string-nw-p label) (format " id=\"%s\"" label) "")
;; 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))))
(format "<span class=\"equation\">\n%s\n</span>" contents)))
(defun org-html--math-environment-p (element &optional _)
"Non-nil when ELEMENT is a LaTeX math environment.
@ -3077,57 +3170,141 @@ For instance, change an `equation' environment to `equation*'."
"Transcode a LATEX-ENVIRONMENT element from Org to HTML.
CONTENTS is nil. INFO is a plist holding contextual information."
(let ((processing-type (plist-get info :with-latex))
(latex-frag (org-remove-indentation
(org-element-property :value latex-environment)))
(attributes (org-export-read-attribute :attr_html latex-environment))
(label (org-html--reference latex-environment info t))
(caption (and (org-html--latex-environment-numbered-p latex-environment)
(number-to-string
(org-export-get-ordinal
latex-environment info nil
(lambda (l _)
(and (org-html--math-environment-p l)
(org-html--latex-environment-numbered-p l))))))))
(cond
((memq processing-type '(t mathjax))
(org-html-format-latex
(if (org-string-nw-p label)
(replace-regexp-in-string "\\`.*"
(format "\\&\n\\\\label{%s}" label)
latex-frag)
latex-frag)
'mathjax info))
((assq processing-type org-preview-latex-process-alist)
(let ((formula-link
(org-html-format-latex
(org-html--unlabel-latex-environment latex-frag)
processing-type info)))
(when (and formula-link (string-match "file:\\([^]]*\\)" formula-link))
(let ((source (org-export-file-uri (match-string 1 formula-link))))
(org-html--wrap-latex-environment
(org-html--format-image source attributes info)
info caption label)))))
(t (org-html--wrap-latex-environment latex-frag info caption label)))))
(latex-frag (org-remove-indentation
(org-element-property :value latex-environment)))
(label (org-html--reference latex-environment info t)))
(if (memq processing-type '(t mathjax))
(org-html--as-latex
latex-environment info
(if (org-string-nw-p label)
(replace-regexp-in-string "\\`.*"
(format "\\&\n\\\\label{%s}" label)
latex-frag)
latex-frag))
(org-html--wrap-latex-environment
(org-html--as-latex latex-environment info latex-frag)
label))))
;;;; LaTeX Fragment
(defun org-html-latex-fragment (latex-fragment _contents info)
"Transcode a LATEX-FRAGMENT object from Org to HTML.
CONTENTS is nil. INFO is a plist holding contextual information."
(let ((latex-frag (org-element-property :value latex-fragment))
(processing-type (plist-get info :with-latex)))
(org-html--as-latex latex-fragment info))
(defun org-html-latex-image (element info)
"Transcode the LaTeX fragment or environment ELEMENT from Org to HTML.
INFO is a plist holding contextual information, and it is assumed
that an image for ELEMENT already exists within it."
(let* ((hash (or (gethash element (plist-get info :html-latex-preview-hash-table))
(error "Expected LaTeX preview hash to exist for element, but none found")))
(path-info (or (org-latex-preview--get-cached hash)
(error "Expected LaTeX preview %S to exist in the cache" hash)))
(image-options (plist-get info :html-latex-image-options))
(block-p (memq (aref (org-element-property :value element) 1) '(?$ ?\[)))
(image-source
(org-html-latex-image--data path-info hash info block-p)))
(unless (and (plist-get (cdr path-info) :height)
(plist-get (cdr path-info) :depth))
(error "Something went wrong during image generation"))
(if (and (eq (plist-get image-options :inline) 'svg-embed)
(eq (plist-get (cdr path-info) :image-type) 'svg))
image-source
(let ((scaling (org-html-latex-image--scaling path-info info)))
(org-html-close-tag
"img"
(org-html--make-attribute-string
(list :src image-source
:alt (org-html-encode-plain-text
(org-element-property :value element))
:style (if block-p
(format "height: %.4fem; display: block" (plist-get scaling :height))
(format "height: %.4fem; vertical-align: -%.4fem; display: inline-block"
(plist-get scaling :height) (plist-get scaling :depth)))
:class (format "org-latex org-latex-%s" (if block-p "block" "inline"))))
info)))))
(defun org-html-latex-image--scaling (image-path-info info)
"Determine the appropriate (<height> . <depth>) of IMAGE-PATH-INFO given INFO."
(let* ((image-options (plist-get info :html-latex-image-options))
(rescale-factor (if (eq (plist-get (cdr image-path-info) :image-type) 'svg)
(plist-get image-options :scale)
1)))
(list :height (* rescale-factor (plist-get (cdr image-path-info) :height))
:depth (* rescale-factor (plist-get (cdr image-path-info) :depth)))))
(defun org-html-latex-image--data (image-path-info hash info &optional block-p)
"Obtaine the image source for IMAGE-PATH-INFO as a string.
This can take the form of a path, data URI, or <svg> element
depending on HASH and INFO. BLOCK-P signals that the image
should be a block element."
(let* ((image-options (plist-get info :html-latex-image-options))
(inline-condition (plist-get image-options :inline))
(image-dir (plist-get image-options :image-dir))
(image-format (plist-get (cdr image-path-info) :image-type))
(source-file (car image-path-info)))
(cond
((memq processing-type '(t mathjax))
(org-html-format-latex latex-frag 'mathjax info))
((memq processing-type '(t html))
(org-html-format-latex latex-frag 'html info))
((assq processing-type org-preview-latex-process-alist)
(let ((formula-link
(org-html-format-latex latex-frag processing-type info)))
(when (and formula-link (string-match "file:\\([^]]*\\)" formula-link))
(let ((source (org-export-file-uri (match-string 1 formula-link))))
(org-html--format-image source nil info)))))
(t latex-frag))))
((or inline-condition
(member (file-name-extension source-file)
(org-ensure-list inline-condition)))
(let ((coding-system-for-read 'utf-8)
(file-name-handler-alist nil))
(with-temp-buffer
(insert-file-contents-literally source-file)
(cond
((and (eq inline-condition 'svg-embed)
(eq image-format 'svg))
(goto-char (point-min))
(let ((svg-closing-tag (and (search-forward "<svg" nil t)
(search-forward ">" nil t))))
(dolist (search '("<!-- This file was generated by dvisvgm [^\n]+ -->"
" height=['\"][^\"']+[\"']"
" width=['\"][^\"']+[\"']"))
(goto-char (point-min))
(when (re-search-forward search svg-closing-tag t)
(replace-match "")))
(goto-char (point-min))
(when (re-search-forward "viewBox=['\"][^\"']+[\"']" svg-closing-tag t)
(insert
" style=\""
(let ((scaling (org-html-latex-image--scaling image-path-info info)))
(if block-p
(format "height: %.4fem; display: block" (plist-get scaling :height))
(format "height: %.4fem; vertical-align: -%.4fem; display: inline-block"
(plist-get scaling :height) (plist-get scaling :depth))))
"\" class=\"org-latex org-latex-"
(if block-p "block" "inline")
"\"")))
(buffer-string))
((eq image-format 'svg)
;; Modelled after <https://codepen.io/tigt/post/optimizing-svgs-in-data-uris>.
(concat "data:image/svg+xml,"
(url-hexify-string
(subst-char-in-string ?\" ?\' (buffer-string))
'(?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n
?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z ?A ?B
?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P
?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z ?0 ?1 ?2 ?3
?4 ?5 ?6 ?7 ?8 ?9 ?- ?_ ?. ?~
;;Special additions
?\s ?= ?: ?/))))
(t
(base64-encode-region (point-min) (point-max))
(goto-char (point-min))
(insert "data:image/" (symbol-name image-format) ";base64,")
(buffer-string))))))
((stringp image-dir)
(let* ((image-dir (expand-file-name image-dir))
(image-path (file-name-with-extension
(file-name-concat image-dir (substring hash 0 11))
(file-name-extension source-file))))
(unless (file-directory-p image-dir)
(mkdir image-dir t))
(unless (file-exists-p image-path)
(copy-file source-file image-path))
image-path))
(t source-file))))
;;;; Line Break

View File

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

View File

@ -510,8 +510,9 @@ contextual information."
(expand-file-name "reshilite" tmpdir)))
(org-lang (org-element-property :language inline-src-block))
(lst-lang
(cadr (assq (intern org-lang)
(plist-get info :man-source-highlight-langs))))
(and org-lang
(cadr (assq (intern org-lang)
(plist-get info :man-source-highlight-langs)))))
(cmd (concat (expand-file-name "source-highlight")
" -s " lst-lang
@ -757,8 +758,9 @@ contextual information."
(code (org-element-property :value src-block))
(org-lang (org-element-property :language src-block))
(lst-lang
(cadr (assq (intern org-lang)
(plist-get info :man-source-highlight-langs))))
(and org-lang
(cadr (assq (intern org-lang)
(plist-get info :man-source-highlight-langs)))))
(cmd (concat "source-highlight"
" -s " lst-lang
" -f groff_man "

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

@ -0,0 +1,155 @@
;;; ox-mathml.el --- Support for MathML exports -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2023 TEC
;;
;; Author: TEC <contact@tecosaur.net>
;; Maintainer: TEC <contact@tecosaur.net>
;; Created: February 27, 2023
;; Modified: February 27, 2023
;; Version: 0.0.1
;; Keywords: abbrev bib c calendar comm convenience data docs emulations extensions faces files frames games hardware help hypermedia i18n internal languages lisp local maint mail matching mouse multimedia news outlines processes terminals tex tools unix vc wp
;; Homepage: https://github.com/tecosaur/ox-mathml
;; Package-Requires: ((emacs "24.3"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; Support for MathML exports
;;
;;; Code:
(defgroup org-mathml nil
"Options for generation of MathML representations of LaTeX math."
:tag "Org MathML export"
:group 'org-export)
(defcustom org-mathml-converter-jar-file nil
"Value of\"%j\" in `org-mathml-convert-command'.
Use this to specify additional executable file say a jar file.
When using MathToWeb as the converter, specify the full-path to
your mathtoweb.jar file."
:group 'org-mathml
:version "24.1"
:type '(choice
(const :tag "None" nil)
(file :tag "JAR file" :must-match t)))
(defcustom org-mathml-convert-command nil
"Command to convert LaTeX fragments to MathML.
Replace format-specifiers in the command as noted below and use
`shell-command' to convert LaTeX to MathML.
%j: Executable file in fully expanded form as specified by
`org-latex-to-mathml-jar-file'.
%I: Input LaTeX file in fully expanded form.
%i: The latex fragment to be converted.
%o: Output MathML file.
This command is used by `org-mathml-convert-latex'.
When using MathToWeb as the converter, set this option to
\"java -jar %j -unicode -force -df %o %I\".
When using LaTeXML set this option to
\"latexmlmath \"%i\" --preload=amsmath.sty --preload=amssymb.sty --presentationmathml=%o\"."
:group 'org-mathml
:version "24.1"
:type '(choice
(const :tag "None" nil)
(string :tag "\nShell command")))
(defun org-mathml-converter-available-p ()
"Return t if `org-mathml-convert-command' is usable."
(save-match-data
(when (and (boundp 'org-mathml-convert-command)
org-mathml-convert-command)
(let ((executable (car (split-string
org-mathml-convert-command))))
(when (executable-find executable)
(if (string-match
"%j" org-mathml-convert-command)
(file-readable-p org-mathml-converter-jar-file)
t))))))
(defun org-mathml-convert-latex (latex-frag &optional mathml-file)
"Convert LATEX-FRAG to MathML and store it in MATHML-FILE.
Use `org-latex-to-mathml-convert-command'. If the conversion is
successful, return the portion between \"<math...> </math>\"
elements otherwise return nil. When MATHML-FILE is specified,
write the results in to that file. When invoked as an
interactive command, prompt for LATEX-FRAG, with initial value
set to the current active region and echo the results for user
inspection."
(interactive (list (let ((frag (when (org-region-active-p)
(buffer-substring-no-properties
(region-beginning) (region-end)))))
(read-string "LaTeX Fragment: " frag nil frag))))
(unless latex-frag (user-error "Invalid LaTeX fragment"))
(let* ((tmp-in-file
(let ((file (file-relative-name
(make-temp-name (expand-file-name "ltxmathml-in")))))
(write-region latex-frag nil file)
file))
(tmp-out-file (file-relative-name
(make-temp-name (expand-file-name "ltxmathml-out"))))
(cmd (format-spec
org-mathml-convert-command
`((?j . ,(and org-mathml-converter-jar-file
(shell-quote-argument
(expand-file-name
org-mathml-converter-jar-file))))
(?I . ,(shell-quote-argument tmp-in-file))
(?i . ,latex-frag)
(?o . ,(shell-quote-argument tmp-out-file)))))
mathml shell-command-output)
(when (called-interactively-p 'any)
(unless (org-mathml-converter-available-p)
(user-error "LaTeX to MathML converter not configured")))
(message "Running %s" cmd)
(setq shell-command-output (shell-command-to-string cmd))
(setq mathml
(when (file-readable-p tmp-out-file)
(with-current-buffer (find-file-noselect tmp-out-file t)
(goto-char (point-min))
(when (re-search-forward
(format "<math[^>]*?%s[^>]*?>\\(.\\|\n\\)*</math>"
(regexp-quote
"xmlns=\"http://www.w3.org/1998/Math/MathML\""))
nil t)
(prog1 (match-string 0) (kill-buffer))))))
(cond
(mathml
(setq mathml
(concat "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" mathml))
(when mathml-file
(write-region mathml nil mathml-file))
(when (called-interactively-p 'any)
(message mathml)))
((warn "LaTeX to MathML conversion failed")
(message shell-command-output)))
(delete-file tmp-in-file)
(when (file-exists-p tmp-out-file)
(delete-file tmp-out-file))
mathml))
(defun org-mathml-convert-latex-cached (latex-frag)
"Use `org-mathml-convert-latex' but check local cache first."
(let ((latex-hash-path
(expand-file-name
(concat
"org-mathml-formula-"
(sha1
(prin1-to-string
(list latex-frag
org-mathml-convert-command)))
".mathml")
temporary-file-directory))
print-length print-level)
(unless (file-exists-p latex-hash-path)
(org-mathml-convert-latex latex-frag latex-hash-path))
(and (file-exists-p latex-hash-path)
latex-hash-path)))
(provide 'ox-mathml)
;;; ox-mathml.el ends here

View File

@ -34,6 +34,7 @@
(require 'org-macs)
(require 'ox)
(require 'table nil 'noerror)
(require 'ox-mathml)
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
@ -3111,7 +3112,7 @@ and prefix with \"OrgSrc\". For example,
(defun org-odt-do-format-code
(code info &optional lang refs retain-labels num-start)
(let* ((lang (or (assoc-default lang org-src-lang-modes) lang))
(lang-mode (and lang (intern (format "%s-mode" lang))))
(lang-mode (if lang (intern (format "%s-mode" lang)) #'ignore))
(code-lines (org-split-string code "\n"))
(code-length (length code-lines))
(use-htmlfontify-p (and (functionp lang-mode)
@ -3712,114 +3713,147 @@ contextual information."
(defun org-odt--translate-latex-fragments (tree _backend info)
(let ((processing-type (plist-get info :with-latex))
(count 0)
(count 0)
(warning nil))
;; Normalize processing-type to one of dvipng, mathml or verbatim.
;; If the desired converter is not available, force verbatim
;; processing.
(cl-case processing-type
((t mathml)
(if (and (fboundp 'org-format-latex-mathml-available-p)
(org-format-latex-mathml-available-p))
(setq processing-type 'mathml)
(setq warning "`org-odt-with-latex': LaTeX to MathML converter not available. Falling back to verbatim.")
(setq processing-type 'verbatim)))
((dvipng imagemagick)
(unless (and (org-check-external-command "latex" "" t)
(org-check-external-command
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
(setq warning "`org-odt-with-latex': LaTeX to PNG converter not available. Falling back to verbatim.")
(setq processing-type 'verbatim)))
(otherwise
(setq warning "`org-odt-with-latex': Unknown LaTeX option. Forcing verbatim.")
(setq processing-type 'verbatim)))
;; Display warning if the selected PROCESSING-TYPE is not
;; available, but there are fragments to be converted.
(when warning
(org-element-map tree '(latex-fragment latex-environment)
(lambda (_) (warn warning))
info 'first-match nil t))
;; Store normalized value for later use.
(when (plist-get info :with-latex)
(plist-put info :with-latex processing-type))
(message "Formatting LaTeX using %s" processing-type)
;; Convert `latex-fragment's and `latex-environment's.
(when (memq processing-type '(mathml dvipng imagemagick))
(org-element-map tree '(latex-fragment latex-environment)
(lambda (latex-*)
(cl-incf count)
(let* ((latex-frag (org-element-property :value latex-*))
(input-file (plist-get info :input-file))
(cache-dir (file-name-directory input-file))
(cache-subdir (concat
(cl-case processing-type
((dvipng imagemagick)
org-preview-latex-image-directory)
(mathml "ltxmathml/"))
(file-name-sans-extension
(file-name-nondirectory input-file))))
(display-msg
(cl-case processing-type
((dvipng imagemagick)
(format "Creating LaTeX Image %d..." count))
(mathml (format "Creating MathML snippet %d..." count))))
;; Get an Org-style link to PNG image or the MathML
;; file.
(link
(with-temp-buffer
(insert latex-frag)
(delay-mode-hooks (let ((org-inhibit-startup t)) (org-mode)))
;; When converting to a PNG image, make sure to
;; copy all LaTeX header specifications from the
;; Org source.
(unless (eq processing-type 'mathml)
(let ((h (plist-get info :latex-header)))
(when h
(insert "\n"
(replace-regexp-in-string
"^" "#+LATEX_HEADER: " h)))))
(org-format-latex cache-subdir nil nil cache-dir
nil display-msg nil
processing-type)
(goto-char (point-min))
(skip-chars-forward " \t\n")
(org-element-link-parser))))
(if (not (org-element-type-p link 'link))
(message "LaTeX Conversion failed.")
;; Conversion succeeded. Parse above Org-style link to
;; a `link' object.
(let ((replacement
(cl-case (org-element-type latex-*)
;;LaTeX environment. Mimic a "standalone image
;; or formula" by enclosing the `link' in
;; a `paragraph'. Copy over original
;; attributes, captions to the enclosing
;; paragraph.
(latex-environment
(org-element-adopt
(list 'paragraph
(list :style "OrgFormula"
:name
(org-element-property :name latex-*)
:caption
(org-element-property :caption latex-*)))
link))
;; LaTeX fragment. No special action.
(latex-fragment link))))
;; Note down the object that link replaces.
(org-element-put-property replacement :replaces
(list (org-element-type latex-*)
(list :value latex-frag)))
;; Restore blank after initial element or object.
(org-element-put-property
replacement :post-blank
(org-element-property :post-blank latex-*))
;; Replace now.
(org-element-set latex-* replacement)))))
info nil nil t)))
;; MathML will be handled seperately.
(if (and (memq processing-type '(t mathml))
(fboundp 'org-format-latex-mathml-available-p)
(org-format-latex-mathml-available-p)
(plist-put info :with-latex 'mathml))
(org-element-map tree '(latex-fragment latex-environment)
(lambda (latex)
(cl-incf count)
(if-let ((latex-frag (org-element-property :value latex))
(path (org-mathml-convert-latex-cached latex-frag))
(link (list 'link
(list :type "file"
:path path
:format 'bracket
:raw-link (format "file:%s" path))))
(replacement
(if (eq (org-element-type latex) 'latex-environment)
;;LaTeX environment. Mimic a "standalone image
;; or formula" by enclosing the `link' in
;; a `paragraph'. Copy over original
;; attributes, captions to the enclosing
;; paragraph.
(org-element-adopt-elements
(list 'paragraph
(list :style "OrgFormula"
:name
(org-element-property :name latex)
:caption
(org-element-property :caption latex)))
link)
link)))
(progn
;; Note down the object that link replaces.
(org-element-put-property replacement :replaces
(list (org-element-type latex)
(list :value latex-frag)))
;; Restore blank after initial element or object.
(org-element-put-property
replacement :post-blank
(org-element-property :post-blank latex))
;; Replace now.
(org-element-set-element latex replacement))
(setq warning "Conversion of LaTeX to MathML failed. Falling back to verbatim.")))
info nil nil)
;; Normalize processing-type to one of dvipng or verbatim.
;; If the desired converter is not available, force verbatim
;; processing.
(cl-case processing-type
((t mathml)
(setq warning "LaTeX to MathML converter not available. Falling back to verbatim."
processing-type 'verbatim))
((dvipng imagemagick)
(unless (and (org-check-external-command "latex" "" t)
(org-check-external-command
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
(setq warning "LaTeX to PNG converter not available. Falling back to verbatim."
processing-type 'verbatim)))
(otherwise
(setq warning "Unknown LaTeX option. Forcing verbatim."
processing-type 'verbatim)))
;; Display warning if the selected PROCESSING-TYPE is not
;; available, but there are fragments to be converted.
(when warning
(org-element-map tree '(latex-fragment latex-environment)
(lambda (_) (warn warning))
info 'first-match nil t))
;; Store normalized value for later use.
(when (plist-get info :with-latex)
(plist-put info :with-latex processing-type))
(message "Formatting LaTeX using %s" processing-type)
;; Convert `latex-fragment's and `latex-environment's.
(when (memq processing-type '(dvipng imagemagick))
(org-element-map tree '(latex-fragment latex-environment)
(lambda (latex-*)
(cl-incf count)
(let* ((latex-frag (org-element-property :value latex-*))
(input-file (plist-get info :input-file))
(cache-dir (file-name-directory input-file))
(cache-subdir (concat
(cl-case processing-type
((dvipng imagemagick)
org-preview-latex-image-directory))
(file-name-sans-extension
(file-name-nondirectory input-file))))
(display-msg
(cl-case processing-type
((dvipng imagemagick)
(format "Creating LaTeX Image %d..." count))))
;; Get an Org-style link to PNG image.
(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.
(let ((h (plist-get info :latex-header)))
(when h
(insert "\n"
(replace-regexp-in-string
"^" "#+LATEX_HEADER: " h))))
(org-latex-preview-replace-fragments
cache-subdir processing-type cache-dir display-msg)
(goto-char (point-min))
(skip-chars-forward " \t\n")
(org-element-link-parser))))
(if (not (eq 'link (org-element-type link)))
(message "LaTeX Conversion failed.")
;; Conversion succeeded. Parse above Org-style link to
;; a `link' object.
(let ((replacement
(cl-case (org-element-type latex-*)
;;LaTeX environment. Mimic a "standalone image
;; or formula" by enclosing the `link' in
;; a `paragraph'. Copy over original
;; attributes, captions to the enclosing
;; paragraph.
(latex-environment
(org-element-adopt-elements
(list 'paragraph
(list :style "OrgFormula"
:name
(org-element-property :name latex-*)
:caption
(org-element-property :caption latex-*)))
link))
;; LaTeX fragment. No special action.
(latex-fragment link))))
;; Note down the object that link replaces.
(org-element-put-property replacement :replaces
(list (org-element-type latex-*)
(list :value latex-frag)))
;; Restore blank after initial element or object.
(org-element-put-property
replacement :post-blank
(org-element-property :post-blank latex-*))
;; Replace now.
(org-element-set-element latex-* replacement)))))
info nil nil t))))
tree)
@ -4146,7 +4180,7 @@ MathML source to kill ring depending on the value of
(save-buffer-coding-system 'utf-8))
(set-buffer buffer)
(set-buffer-file-coding-system coding-system-for-write)
(let ((mathml (org-create-math-formula latex-frag)))
(let ((mathml (org-mathml-convert-latex-cached latex-frag)))
(unless mathml (error "No Math formula created"))
(insert mathml)
;; Add MathML to kill ring, if needed.

View File

@ -1681,8 +1681,9 @@ as a communication channel."
"Transcode a SRC-BLOCK element from Org to Texinfo.
CONTENTS holds the contents of the item. INFO is a plist holding
contextual information."
(let* ((lisp (string-match-p "lisp"
(org-element-property :language src-block)))
(let* ((lisp (string-match-p
"lisp"
(or (org-element-property :language src-block) "")))
(code (org-texinfo--sanitize-content
(org-export-format-code-default src-block info)))
(value (format

View File

@ -1060,7 +1060,7 @@ mode."
(cl-defstruct (org-export-backend (:constructor org-export-create-backend)
(:copier nil))
name parent transcoders options filters blocks menu)
name parent transcoders options filters blocks menu feature-conditions feature-implementations)
;;;###autoload
(defun org-export-get-backend (name)
@ -1166,6 +1166,62 @@ returns filters inherited from parent backends, if any."
(setq filters (append filters (org-export-backend-filters backend))))
filters)))
(defvar org-export-conditional-features)
(defun org-export-get-all-feature-conditions (backend)
"Return full feature condition alist for BACKEND.
BACKEND is an export back-end, as return by, e.g,,
`org-export-create-backend'. Return value is an alist where keys
are feature conditions, and values are feature symbols.
Unlike `org-export-backend-feature-conditions', this function
also returns conditions inherited from parent back-ends, if any."
(when (symbolp backend) (setq backend (org-export-get-backend backend)))
(and backend
(let ((conditions (org-export-backend-feature-conditions backend))
parent)
(while (setq parent (org-export-backend-parent backend))
(setq backend (org-export-get-backend parent))
(dolist (condition (org-export-backend-feature-conditions backend))
(push condition conditions)))
(dolist (condition org-export-conditional-features)
(unless (assq (car condition) conditions)
(push condition conditions)))
conditions)))
(defun org-export-get-all-feature-implementations (backend)
"Return full feature implementation alist for BACKEND.
BACKEND is an export back-end, as return by, e.g,,
`org-export-create-backend'. Return value is an alist where keys
are feature symbols, and values are an implementation
specification plist.
Unlike `org-export-backend-feature-implementations', this function
also returns implementations inherited from parent back-ends, if any."
(when (symbolp backend) (setq backend (org-export-get-backend backend)))
(and backend
(let ((implementations (org-export-backend-feature-implementations backend))
parent)
(while (setq parent (org-export-backend-parent backend))
(setq backend (org-export-get-backend parent))
(dolist (implementation (org-export-backend-feature-implementations backend))
(unless (assq (car implementation) implementations)
(push implementation implementations))))
implementations)))
(defun org-export-install-features (info)
"Install feature conditions and implementations in the communication channel.
INFO is a plist containing the current communication channel.
Return the updated communication channel."
(plist-put info :feature-conditions
(org-export-get-all-feature-conditions
(plist-get info :back-end)))
(plist-put info :feature-implementations
(org-export-get-all-feature-implementations
(plist-get info :back-end))))
(defun org-export-define-backend (backend transcoders &rest body)
"Define a new backend BACKEND.
@ -1277,20 +1333,24 @@ keywords are understood:
`org-export-options-alist' for more information about
structure of the values."
(declare (indent 1))
(let (filters menu-entry options)
(let (filters menu-entry options feature-conditions feature-implementations)
(while (keywordp (car body))
(let ((keyword (pop body)))
(pcase keyword
(:filters-alist (setq filters (pop body)))
(:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body)))
(:feature-conditions-alist (setq feature-conditions (pop body)))
(:feature-implementations-alist (setq feature-implementations (pop body)))
(_ (error "Unknown keyword: %s" keyword)))))
(org-export-register-backend
(org-export-create-backend :name backend
:transcoders transcoders
:options options
:filters filters
:menu menu-entry))))
:menu menu-entry
:feature-conditions feature-conditions
:feature-implementations feature-implementations))))
(defun org-export-define-derived-backend (child parent &rest body)
"Create a new backend as a variant of an existing one.
@ -1337,7 +1397,7 @@ The backend could then be called with, for example:
(org-export-to-buffer \\='my-latex \"*Test my-latex*\")"
(declare (indent 2))
(let (filters menu-entry options transcoders)
(let (filters menu-entry options transcoders feature-conditions feature-implementations)
(while (keywordp (car body))
(let ((keyword (pop body)))
(pcase keyword
@ -1345,6 +1405,8 @@ The backend could then be called with, for example:
(:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body)))
(:translate-alist (setq transcoders (pop body)))
(:feature-conditions-alist (setq feature-conditions (pop body)))
(:feature-implementations-alist (setq feature-implementations (pop body)))
(_ (error "Unknown keyword: %s" keyword)))))
(org-export-register-backend
(org-export-create-backend :name child
@ -1352,7 +1414,9 @@ The backend could then be called with, for example:
:transcoders transcoders
:options options
:filters filters
:menu menu-entry))))
:menu menu-entry
:feature-conditions feature-conditions
:feature-implementations feature-implementations))))
@ -2055,6 +2119,550 @@ keywords before output."
(funcall (intern (format "org-element-%s-interpreter" type))
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
@ -3129,6 +3737,8 @@ still inferior to file-local settings."
(when (plist-get info :with-cite-processors)
(org-cite-process-citations info)
(org-cite-process-bibliography info))
;; Install all the feature conditions and implementations.
(org-export-process-features info)
info))
;;;###autoload

View File

@ -7,3 +7,14 @@
doi = {10.1007/978-3-642-23816-1},
isbn = {9783642238161}
}
@InCollection{Geyer2011,
author = {Geyer, Charles J},
title = {{Introduction to Markov\plus Chain Monte Carlo}},
year = 2011,
booktitle = {{Handbook of Markov Chain Monte Carlo}},
editor = {Brooks, Steve and Gelman, Andrew and Jones, Galin and Meng,
Xiao-Li},
publisher = {CRC press},
pages = 45,
}

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

@ -2545,6 +2545,225 @@ abc
(lambda (&rest _) (error "No warnings should occur"))))
(org-babel-import-elisp-from-file (buffer-file-name))))))
(ert-deftest test-ob/demarcate-block-split-duplication ()
"Test duplication of language, body, switches, and headers in splitting."
(let ((caption "#+caption: caption.")
(regexp (rx "#+caption: caption."))
(org-adapt-indentation nil))
(org-test-with-temp-text (format "
%s
#+header: :var edge=\"also duplicated\"
#+header: :wrap \"src any-spanish -n\"
#+name: Nobody
#+begin_src any-english -i -n :var here=\"duplicated\" :wrap \"src any-english -n\"
above split
<point>
below split
#+end_src
do not org-indent-block text here
" caption)
(let ((wrap-val "src any-spanish -n") above below avars bvars)
(org-babel-demarcate-block)
(goto-char (point-min))
(org-babel-next-src-block) ;; upper source block
(setq above (org-babel-get-src-block-info))
(setq avars (org-babel--get-vars (nth 2 above)))
(org-babel-next-src-block) ;; lower source block
(setq below (org-babel-get-src-block-info))
(setq bvars (org-babel--get-vars (nth 2 below)))
;; duplicated multi-line header arguments:
(should (string= "also duplicated" (cdr (assq 'edge avars))))
(should (string= "also duplicated" (cdr (assq 'edge bvars))))
(should (string= wrap-val (cdr (assq :wrap (nth 2 above)))))
(should (string= wrap-val (cdr (assq :wrap (nth 2 below)))))
;; duplicated language, other header arguments, and switches:
(should (string= "any-english" (nth 0 above)))
(should (string= "any-english" (nth 0 below)))
(should (string= "above split" (org-trim (nth 1 above))))
(should (string= "below split" (org-trim (nth 1 below))))
(should (string= "duplicated" (cdr (assq 'here avars))))
(should (string= "duplicated" (cdr (assq 'here bvars))))
(should (string= "-i -n" (nth 3 above)))
(should (string= "-i -n" (nth 3 below)))
;; non-duplication of name and caption, which is not in above/below.
(should (string= "Nobody" (nth 4 above)))
(should-not (string= "" (nth 4 below)))
(goto-char (point-min))
(should (re-search-forward regexp))
(should-not (re-search-forward regexp nil 'noerror))))))
(ert-deftest test-ob/demarcate-block-split-prefix-point ()
"Test prefix argument point splitting."
(let ((org-adapt-indentation t)
(org-edit-src-content-indentation 2)
(org-src-preserve-indentation nil)
(ok-col 11)
(stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*"))
(org-test-with-temp-text "
********** 10 stars with point between two lines
#+begin_src emacs-lisp
;; to upper block
<point>
;; to lower block
#+end_src
"
(org-babel-demarcate-block 'a-prefix-arg)
(goto-char (point-min))
(dolist (regexp `(,stars
"#\\+beg" ";; to upper block" "#\\+end"
,stars
"#\\+beg" ";; to lower block" "#\\+end"))
(should (re-search-forward regexp))
(goto-char (match-beginning 0))
(cond ((string= regexp stars)
(should (= 0 (current-column))))
((string-prefix-p ";;" regexp)
(should (= (+ ok-col org-edit-src-content-indentation)
(current-column))))
(t (should (= ok-col (current-column)))))))))
(ert-deftest test-ob/demarcate-block-split-prefix-region ()
"Test prefix argument region splitting."
(let ((org-adapt-indentation t)
(org-edit-src-content-indentation 2)
(org-src-preserve-indentation nil)
(ok-col 11)
(stars "^\\*\\*\\*\\*\\*\\*\\*\\*\\*\\*")
(parts '("to upper block" "mark those words as region" "to lower block")))
(org-test-with-temp-text (format "
********** 10 stars with region between two lines
#+header: :var b=\"also seen\"
#+begin_src any-language -i -n :var a=\"seen\"
%s
<point>%s
%s
#+end_src
" (nth 0 parts) (nth 1 parts) (nth 2 parts))
(let ((n 0) info vars)
(transient-mark-mode 1)
(push-mark (point) t t)
(search-forward (nth 1 parts))
(org-babel-demarcate-block 'a-prefix-argument)
(goto-char (point-min))
(while (< n (length parts))
(org-babel-next-src-block)
(setq info (org-babel-get-src-block-info))
(setq vars (org-babel--get-vars (nth 2 info)))
(should (string= "any-language" (nth 0 info)))
(should (string= (nth n parts) (org-trim (nth 1 info))))
(should (string= "seen" (cdr (assq 'a vars))))
(should (string= "also seen" (cdr (assq 'b vars))))
(should (string= "-i -n" (nth 3 info)))
(cl-incf n)))
(goto-char (point-min))
(dolist (regexp `(,stars
"#\\+beg" ,(nth 0 parts) "#\\+end"
,stars
"#\\+beg" ,(nth 1 parts) "#\\+end"
,stars
"#\\+beg" ,(nth 2 parts) "#\\+end"))
(should (re-search-forward regexp))
(goto-char (match-beginning 0))
(cond ((string= regexp stars)
(should (= 0 (current-column))))
((memq regexp parts)
(should (= (+ ok-col org-edit-src-content-indentation)
(current-column))))
(t (should (= ok-col (current-column)))))))))
(ert-deftest test-ob/demarcate-block-split-user-errors ()
"Test for `user-error's in splitting"
(let ((org-adapt-indentation t)
(org-edit-src-content-indentation 2)
(org-src-preserve-indentation))
(let* ((caption "#+caption: caption.")
(within-body ";; within-body")
(below-block "# below block")
(template "
%s%s
#+begin_src emacs-lisp
%s
#+end_src
%s%s
"))
;; Test point at caption.
(org-test-with-temp-text
(format template "<point>" caption within-body below-block "")
(should-error (org-babel-demarcate-block) :type 'user-error))
;; Test region from below the block (mark) to within the body (point).
(org-test-with-temp-text
(format template "" caption within-body below-block "<point>")
;; Set mark.
(transient-mark-mode 1)
(push-mark (point) t t)
;; Set point.
(should (search-backward within-body nil 'noerror))
(goto-char (match-beginning 0))
(should-error (org-babel-demarcate-block) :type 'user-error)))))
(ert-deftest test-ob/demarcate-block-wrap-point ()
"Test wrapping point in blank lines below a source block."
(org-test-with-temp-text "
#+begin_src any-language -i -n :var here=\"not duplicated\"
to upper block
#+end_src
<point>
"
(let (info vars)
(org-babel-demarcate-block)
(goto-char (point-min))
(org-babel-next-src-block)
(setq info (org-babel-get-src-block-info)) ;; upper source block info
(setq vars (org-babel--get-vars (nth 2 info)))
(should (string= "any-language" (nth 0 info)))
(should (string= "to upper block" (org-trim (nth 1 info))))
(should (string= "not duplicated" (cdr (assq 'here vars))))
(should (string= "-i -n" (nth 3 info)))
(org-babel-next-src-block)
(setq info (org-babel-get-src-block-info)) ;; lower source block info
(setq vars (org-babel--get-vars (nth 2 info)))
(should (string= "any-language" (nth 0 info)))
(should (string= "" (org-trim (nth 1 info))))
(should-not vars)
(should (string= "" (nth 3 info))))))
(ert-deftest test-ob/demarcate-block-wrap-region ()
"Test wrapping region in blank lines below a source block."
(let ((region-text "mark this line as region leaving point in blank lines"))
(org-test-with-temp-text (format "
#+begin_src any-language -i -n :var here=\"not duplicated\"
to upper block
#+end_src
<point>
%s
" region-text)
(let (info vars)
(transient-mark-mode 1)
(push-mark (point) t t)
(search-forward region-text)
(exchange-point-and-mark)
(org-babel-demarcate-block)
(goto-char (point-min))
(org-babel-next-src-block)
(setq info (org-babel-get-src-block-info)) ;; upper source block info
(setq vars (org-babel--get-vars (nth 2 info)))
(should (string= "any-language" (nth 0 info)))
(should (string= "to upper block" (org-trim (nth 1 info))))
(should (string= "not duplicated" (cdr (assq 'here vars))))
(should (string= "-i -n" (nth 3 info)))
(org-babel-next-src-block)
(setq info (org-babel-get-src-block-info)) ;; lower source block info
(setq vars (org-babel--get-vars (nth 2 info)))
(should (string= "any-language" (nth 0 info)))
(should (string= region-text (org-trim (nth 1 info))))
(should-not vars)
(should (string= "" (nth 3 info)))))))
(provide 'test-ob)
;;; test-ob ends here

View File

@ -156,5 +156,24 @@ Text: [cite/t: Citing ; @friends; and @friends also; is duplication.]"
(search-forward "Text: Citing van Dongen, M.R.C. (2012), and van Dongen, M.R.C. (2012)
also is duplication." nil t)))))))
(ert-deftest test-org-cite-basic/export-bibliography ()
"Test `org-cite-basic-export-bibliography'."
;; Drop {...} Bibtex brackets and render entities.
(org-test-with-temp-text
(format
"#+bibliography: %s
#+cite_export: basic
Foo [cite/plain:@Geyer2011]
#+print_bibliography:"
(expand-file-name "examples/Basic.bib" org-test-dir))
(let ((export-buffer "*Test ASCII Export*")
(org-export-show-temporary-export-buffer nil))
(org-export-to-buffer 'ascii export-buffer)
(with-current-buffer export-buffer
(let ((case-fold-search t))
(should
;; Rendered from {Introduction to Markov\plus Chain Monte Carlo}
(search-forward "Introduction to Markov+ Chain Monte Carlo" nil t)))))))
(provide 'test-oc-basic)
;;; test-oc-basic.el ends here

View File

@ -381,6 +381,128 @@ See https://github.com/yantar92/org/issues/4."
(equal (format "[[file:%s::*foo bar][foo bar]]" file file)
(org-store-link nil)))))))
(ert-deftest test-org-link/precise-link-target ()
"Test `org-link-precise-link-target` specifications."
(org-test-with-temp-text "* H1<point>\n* H2\n"
(should
(equal '("*H1" "H1" 1)
(org-link-precise-link-target))))
(org-test-with-temp-text "* H1\n#+name: foo<point>\n#+begin_example\nhi\n#+end_example\n"
(should
(equal '("foo" "foo" 6)
(org-link-precise-link-target))))
(org-test-with-temp-text "\nText<point>\n* H1\n"
(should
(equal '("Text" nil 2)
(org-link-precise-link-target))))
(org-test-with-temp-text "\n<point>\n* H1\n"
(should
(equal nil (org-link-precise-link-target)))))
(defmacro test-ol-stored-link-with-text (text &rest body)
"Return :link and :description from link stored in body."
(declare (indent 1))
`(let (org-store-link-plist)
(org-test-with-temp-text-in-file ,text
,@body
(list (plist-get org-store-link-plist :link)
(plist-get org-store-link-plist :description)))))
(ert-deftest test-org-link/id-store-link ()
"Test `org-id-store-link' specifications."
(let ((org-id-link-to-org-use-id nil))
(should
(equal '(nil nil)
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n"
(org-id-store-link-maybe t)))))
;; On a headline, link to that headline's ID. Use heading as the
;; description of the link.
(let ((org-id-link-to-org-use-id t))
(should
(equal '("id:abc" "H1")
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n"
(org-id-store-link-maybe t)))))
;; Remove TODO keywords etc from description of the link.
(let ((org-id-link-to-org-use-id t))
(should
(equal '("id:abc" "H1")
(test-ol-stored-link-with-text "* TODO [#A] H1 :tag:\n:PROPERTIES:\n:ID: abc\n:END:\n"
(org-id-store-link-maybe t)))))
;; create-if-interactive
(let ((org-id-link-to-org-use-id 'create-if-interactive))
(should
(equal '("id:abc" "H1")
(cl-letf (((symbol-function 'org-id-new)
(lambda (&rest _rest) "abc")))
(test-ol-stored-link-with-text "* H1\n"
(org-id-store-link-maybe t)))))
(should
(equal '(nil nil)
(test-ol-stored-link-with-text "* H1\n"
(org-id-store-link-maybe nil)))))
;; create-if-interactive-and-no-custom-id
(let ((org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
(should
(equal '("id:abc" "H1")
(cl-letf (((symbol-function 'org-id-new)
(lambda (&rest _rest) "abc")))
(test-ol-stored-link-with-text "* H1\n"
(org-id-store-link-maybe t)))))
(should
(equal '(nil nil)
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:CUSTOM_ID: xyz\n:END:\n"
(org-id-store-link-maybe t))))
(should
(equal '(nil nil)
(test-ol-stored-link-with-text "* H1\n"
(org-id-store-link-maybe nil)))))
;; use-context should have no effect when on the headline with an id
(let ((org-id-link-to-org-use-id t)
(org-id-link-use-context t))
(should
(equal '("id:abc" "H2")
(test-ol-stored-link-with-text "* H1\n** H2<point>\n:PROPERTIES:\n:ID: abc\n:END:\n"
;; simulate previously getting an inherited value
(move-marker org-entry-property-inherited-from 1)
(org-id-store-link-maybe t))))))
(ert-deftest test-org-link/id-store-link-using-parent ()
"Test `org-id-store-link' specifications with `org-id-link-consider-parent-id` set."
;; when using context to still find specific heading
(let ((org-id-link-to-org-use-id t)
(org-id-link-consider-parent-id t)
(org-id-link-use-context t))
(should
(equal '("id:abc::*H2" "H2")
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n** H2\n<point>"
(org-id-store-link))))
(should
(equal '("id:abc::name" "name")
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n\n#+name: name\n<point>#+begin_example\nhi\n#+end_example\n"
(org-id-store-link))))
(should
(equal '("id:abc" "H1")
(test-ol-stored-link-with-text "* H1<point>\n:PROPERTIES:\n:ID: abc\n:END:\n** H2\n"
(org-id-store-link))))
;; should not use newly added ids as search string, e.g. in an empty file
(should
(let (name result)
(setq result
(cl-letf (((symbol-function 'org-id-new)
(lambda (&rest _rest) "abc")))
(test-ol-stored-link-with-text "<point>"
(setq name (buffer-name))
(org-id-store-link))))
(equal `("id:abc" ,name) result))))
;; should not find targets in the next section
(let ((org-id-link-to-org-use-id 'use-existing)
(org-id-link-consider-parent-id t)
(org-id-link-use-context t))
(should
(equal '(nil nil)
(test-ol-stored-link-with-text "* H1\n:PROPERTIES:\n:ID: abc\n:END:\n* H2\n** <point>Target\n"
(org-id-store-link-maybe t))))))
;;; Radio Targets

View File

@ -687,6 +687,47 @@ Sunday 7 January 2024
(should-not (org-agenda-files)))
(org-test-agenda--kill-all-agendas))
(ert-deftest test-org-agenda/skip-deadline-prewarning-if-scheduled ()
"Test `org-agenda-skip-deadline-prewarning-if-scheduled'."
(org-test-at-time
"2024-01-15"
(let ((org-agenda-skip-deadline-prewarning-if-scheduled t))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should (search-forward "In " nil t))))
;; Custom prewarning cookie "-3d", so there should be no warning anyway.
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 10))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat -3d> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 3))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled nil))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-19 Fri>"
(org-agenda-list nil nil 1)
(should (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-16 Tue>"
(org-agenda-list nil nil 1)
(should-not (search-forward "In " nil t))))
(let ((org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
(org-test-agenda-with-agenda
"* TODO foo\nDEADLINE: <2024-01-20 Sat> SCHEDULED: <2024-01-15 Mon>"
(org-agenda-list nil nil 1)
(should (search-forward "In " nil t))))))
;; agenda redo

View File

@ -755,6 +755,15 @@ Some other text
(text (org-element-map tree 'plain-text 'identity nil t)))
(org-element-set text "b")
(org-element-map tree 'plain-text 'identity nil t)))))
;; Replace string inside anonymous element with another string.
(let* ((parent (org-element-create 'anonymous nil "test"))
(str (car (org-element-contents parent))))
(let ((return (org-element-set str "repl"))
(new (car (org-element-contents parent))))
;; Return the modified value.
(should (eq return new))
(should (equal new "repl"))
(should (eq (org-element-parent new) parent))))
;; KEEP-PROPS
(should
(org-element-property
@ -2378,6 +2387,23 @@ e^{i\\pi}+1=0
(org-element-property
:type
(org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))
(should
(equal
"radio"
(org-test-with-temp-text "<<<radio>>><<<radio2>>><<<foo>>>A radio link"
(org-update-radio-target-regexp)
(org-element-property
:type
(org-element-map (org-element-parse-buffer) 'link #'identity nil t)))))
(should
(equal
"radio"
(let ((org-target-link-regexp-limit 9))
(org-test-with-temp-text "<<<radio>>><<<radio2>>><<<foo>>>A radio link"
(org-update-radio-target-regexp)
(org-element-property
:type
(org-element-map (org-element-parse-buffer) 'link #'identity nil t))))))
;; Pathological case: radio target of length 1 at beginning of line
;; not followed by spaces.
(should

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

@ -1849,7 +1849,21 @@ See also `test-org-table/copy-field'."
(should
(org-test-with-temp-text "|-<point>--|---------|\n|---|---|-----|"
(org-table-align)
t)))
t))
;; Adjust table width.
(should
(equal
(let ((org-link-descriptive t))
(org-test-with-temp-text "
| a | b |
|----------+---|
| [[c][c]] | d |<point>"
(org-table-align)
(buffer-string)))
"
| a | b |
|---+---|
| [[c][c]] | d |")))
(ert-deftest test-org-table/align-buffer-tables ()
"Align all tables when updating buffer."

View File

@ -1980,8 +1980,30 @@ CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46"
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(16)))
(buffer-string))))
;; When optional TOP-LEVEL argument is non-nil, always insert
;; a level 1 heading.
;; When optional LEVEL argument is a number, insert a heading at
;; that level.
(should
(equal "* H1\n** H2\n* "
(org-test-with-temp-text "* H1\n** H2<point>"
(org-insert-heading nil nil 1)
(buffer-string))))
(should
(equal "* H1\n** H2\n** "
(org-test-with-temp-text "* H1\n** H2<point>"
(org-insert-heading nil nil 2)
(buffer-string))))
(should
(equal "* H1\n** H2\n*** "
(org-test-with-temp-text "* H1\n** H2<point>"
(org-insert-heading nil nil 3)
(buffer-string))))
(should
(equal "* H1\n- item\n* "
(org-test-with-temp-text "* H1\n- item<point>"
(org-insert-heading nil nil 1)
(buffer-string))))
;; When optional LEVEL argument is non-nil, always insert a level 1
;; heading.
(should
(equal "* H1\n** H2\n* "
(org-test-with-temp-text "* H1\n** H2<point>"
@ -2436,6 +2458,13 @@ Test
** H2<point>"
(should (= 1 (org-up-heading-safe)))
(should (looking-at-p "^\\* H1")))
;; Return true level. Ignore `org-odd-levels-only'.
(let ((org-odd-levels-only t))
(org-test-with-temp-text "
*** H1
***** H2<point>"
(should (= 3 (org-up-heading-safe)))
(should (looking-at-p "^\\*\\{3\\} H1"))))
;; Do not jump beyond the level 1 heading.
(org-test-with-temp-text "
Text.

View File

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