Update to Org 9.8.1

This commit is contained in:
Kyle Meyer 2026-04-04 23:45:28 -04:00
parent 0a62e6c304
commit 6c22780955
103 changed files with 9454 additions and 5087 deletions

View file

@ -29,9 +29,9 @@
# Contact Info
#+texinfo_header: @set MAINTAINERSITE @uref{https://orgmode.org,maintainers webpage}
#+texinfo_header: @set MAINTAINER Bastien Guerry
#+texinfo_header: @set MAINTAINEREMAIL @email{bzg@gnu.org}
#+texinfo_header: @set MAINTAINERCONTACT @uref{mailto:bzg@gnu.org,contact the maintainer}
#+texinfo_header: @set MAINTAINER Ihor Radchenko
#+texinfo_header: @set MAINTAINEREMAIL @email{yantar92@posteo.net}
#+texinfo_header: @set MAINTAINERCONTACT @uref{mailto:yantar92@posteo.net,contact the maintainer}
#+options: H:4 num:t toc:t author:t \n:nil ::t |:t ^:nil -:t f:t *:t <:t e:t ':t
#+options: d:nil todo:nil pri:nil tags:not-in-toc stat:nil broken-links:mark

File diff suppressed because it is too large Load diff

View file

@ -4,13 +4,930 @@ ORG NEWS -- history of user-visible changes. -*- mode: org; coding: utf-8 -*-
#+LINK: doc https://orgmode.org/worg/doc.html#%s
#+LINK: msg https://list.orgmode.org/%s/
#+LINK: git https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=%s
#+LINK: git https://git.savannah.nongnu.org/cgit/org-mode.git/commit/?id=%s
#+macro: kbd (eval (org-texinfo-kbd-macro $1))
Copyright (C) 2012-2026 Free Software Foundation, Inc.
See the end of the file for license conditions.
Please send Org bug reports to mailto:emacs-orgmode@gnu.org.
* Version 9.8
** Important announcements and breaking changes
# Here, we list the *most important* changes and changes that _likely_
# require user action for most Org mode users.
# Sorted from most important to least important.
*** You may need to update =org-protocol= bookmarklets for browsers
In Firefox 133 and Firefox 128.5 ESR, the previously suggested
JavaScript bookmarklets replace the current page with the bookmarklet URL
text. We have updated the manual with new bookmarklets that do not
have this problem.
The new bookmarklets have ~void(0);~ appended at the end, so that they
do not return anything.
Example:
#+begin_example
javascript:location.href='org-protocol://store-link?url='+
encodeURIComponent(location.href);void(0);
#+end_example
*** =C-c C-x C-v= command toggling inline image display has been reworked
Previously, =C-c C-x C-v= always toggled image display in the whole
buffer (or narrowed part of the buffer). With prefix argument, it
also forced displaying image links with description.
Now, =C-c C-x C-v= is bound to a new command ~org-link-preview~, which
uses different defaults:
1. When the region is active, images in the region are previewed
2. Otherwise, if there is an image at point, it is toggled. If there
is no image at point, images in the current entry are previewed
3. With the =C-u= argument, image previews in the active region or at
point are cleared instead
4. The =C-u C-u= argument unconditionally shows all images in the
accessible portion of the buffer
5. The =C-u C-u C-u= argument unconditionally clears all images in the
accessible portion of the buffer
6. Displaying images over links with description can be forced using
numeric argument:
- ~C-u 1~ for toggling all images at point/current entry
- ~C-u 11~ for toggling all images in buffer
(The first five of these prefix argument behaviors are the same as that of
the ~org-latex-preview~ command.)
In addition to images, ~org-link-preview~ can also be used to preview
Org links of all types for which preview behavior is defined, see
[[#link-preview][previews for arbitrary link types]].
The old ~org-toggle-inline-images~ command is obsolete but still
available. You can bind it back to =C-c C-x C-v= by adding the
following to your config:
#+begin_src emacs-lisp
(eval-after-load 'org-keys
(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images))
#+end_src
*** Org mode no longer treats =:results drawer= as verbatim output
Previously, =:results drawer= left the code block results verbatim in
some scenarios. This is no longer the case, in line with the manual
and the intended purpose of this option. However, the fix may have
brought subtle changes in the code block output for users who made use
of the previous erroneous behavior. If you use =:results drawer= in your
Org documents, please watch out for potential changes in the code evaluation.
*** Diary-style timestamps are exported together with active timestamps
~org-export-with-timestamps~ and ~org-icalendar-with-timestamps~ now
treat diary-style timestamps as a type of active timestamp for
purposes of export.
This mainly affects iCalendar export, where diary timestamps will now
be included when only active timestamps are exported (the default).
This should have minimal impact on non-iCalendar exporters, since
~org-export-with-timestamps~ was already ~t~ by default. However,
users who manually set ~org-export-with-timestamps~ to ~active~ will
now have diary timestamps included as well.
To use the old behavior and export active timestamps only without
diary timestamps, users can set ~org-export-with-timestamps~ and
~org-icalendar-with-timestamps~ to ~active-exclude-diary~.
*** ~org-element-drawer-parser~ assigns ~:pre-blank~ property
Previously, the whole contents of drawer, including blank lines at the beginning were
parsed as paragraph. Now, the blank lines at the beginning are stored in ~:pre-blank~
property, just as in other greater elements.
*** ~org-element-org-data-parser~ now returns syntax node with ~:pre-blank~ property
Previously, parsing ~org-data~ syntax node did not record information
about blank lines at the beginning of the document. Now, the number
of blank lines is recorded in ~:pre-blank~ property.
~org-element-org-data-interpreter~ takes into account this information.
*** Emacs 26 and Emacs 27 support has been dropped
We maintain compatibility with the latest Emacs release, and two
versions prior the latest. The latest is Emacs 30, so we drop
everything before Emacs 28.
** New features
# We list the most important features, and the features that may
# require user action to be used.
*** Some navigation commands can now be repeated
When ~repeat-mode~ is turned on, the following navigation commands can
be repeated:
| Command | Key binding | Repeat key |
|-----------------------------------+------------------------+--------------|
| ~org-next-visible-heading~ | {{{kbd(C-c C-n)}}} | {{{kbd(n)}}} |
| ~org-previous-visible-heading~ | {{{kbd(C-c C-p)}}} | {{{kbd(p)}}} |
| ~org-forward-heading-same-level~ | {{{kbd(C-c C-f)}}} | {{{kbd(f)}}} |
| ~org-backward-heading-same-level~ | {{{kbd(C-c C-b)}}} | {{{kbd(b)}}} |
| ~org-up-heading~ | {{{kbd(C-c C-u)}}} | {{{kbd(u)}}} |
| ~org-next-block~ | {{{kbd(C-c M-f)}}} | {{{kbd(f)}}} |
| ~org-previous-block~ | {{{kbd(C-c M-b)}}} | {{{kbd(b)}}} |
| ~org-next-link~ | {{{kbd(C-c C-x C-n)}}} | {{{kbd(n)}}} |
| ~org-previous-link~ | {{{kbd(C-c C-x C-p)}}} | {{{kbd(p)}}} |
The keybindings in the repeat-maps can be changed by customizing
~org-navigation-repeat-map~, ~org-link-navigation-repeat-map~, and
~org-block-navigation-repeat-map~.
See the new [[info:org#Repeating commands]["Repeating commands"]] section in Org mode manual.
*** New babel backend for C# code blocks
Org now officially enables C# code block evaluation based on the .NET SDK.
The old backend that does not use .NET SDK remains in org-contrib
and will be removed in a future release. The built-in =ob-csharp.el= should be
considered the official successor.
*** All Org link types can be previewed
:PROPERTIES:
:CUSTOM_ID: link-preview
:END:
Org links support a new parameter =:preview= that can be used to
preview arbitrary link types. The value of this parameter should be a
function that is called to preview links of the corresponding type
(see ~org-link-parameters~).
The new preview system does not bring any brand-new link previews, but
open the possibility for third-party packages to implement custom previews
without having to use non-ideal hacks.
On the user side, the existing image link previews should now run
smoother, especially in Org buffers that have a lot of image links to
preview. The new previews are less blocking, previewing link in
batches, without fully blocking Emacs. See the new
~org-link-preview-batch-size~ and ~org-link-preview-delay~ options.
*** New =%\*N= placeholder in ~org-capture-templates~
The new placeholder is like =%\N=, but gives access not only to the
=%^{prompt}= values, but also to =%^{prompt}X= values.
*** Alignment of image previews can be customized
This feature was added in Org 9.7 but was not documented in the release notes. See [[https://orgmode.org/worg/org-release-notes.html#preview-align][retrospectively added news entry]].
*** ~org-open-at-point-global~ now accepts prefix argument
The argument is passed through to ~org-link-open~, allowing alternative
way to open links, if link ~:follow~ function supports it.
*** ox-latex: Table of contents generation has been fixed and augmented
The LaTeX exporter differs from other exporters in that it does not
include unnumbered sections in the table of contents by default. To
include an unnumbered section, set the property =:UNNUMBERED: toc= on
the section.
Alternatively, you can set the new custom variable
~org-latex-toc-include-unnumbered~ to include unnumbered sections by
default, aligning with other exporters' behavior. In that case, to
exclude a section from the table of contents, mark it as =:UNNUMBERED:
notoc= in its properties.
*** Tables copied from LibreOffice Calc documents can be pasted as Org tables
Tables copied into the clipboard from LibreOffice Calc documents can
now be pasted as an Org table using ~yank-media~.
*** Ditaa code blocks can use ditaa executable, and can produce SVG output
In order to use a ditaa executable instead of a JAR file, you can set
~org-ditaa-default-exec-mode~ to ~'ditaa~. The location of the
executable can be configured via ~org-ditaa-exec~.
SVG output can now be generated; note, however, that this requires a
ditaa version of at least 0.11.0.
*** New datetree capture ~:tree-type~ options
:PROPERTIES:
:CUSTOM_ID: 9.8-datetree-treetype
:END:
For datetree capture, ~:tree-type~ can now be any subset of ~(year
quarter month week day)~ to construct a datetree with the specified
levels. For back-compatibility, the default value of ~nil~ is an
alias for ~(year month day)~, ~month~ is an alias for ~(year month)~,
and ~week~ is an alias for ~(year week day)~.
If ~:tree-type~ is a superset of ~(month week)~, then weeks are
assigned to the month containing Thursday, to be consistent with the
ISO-8601 year-week rule. If ~:tree-type~ contains ~(quarter week)~
but does not contain ~month~, then quarters are defined as 13-week
periods (the final quarter of a 53-week year has 14-weeks).
Otherwise, quarters are defined as 3-month periods.
Additionally, ~:tree-type~ can be a function, in which case it should
take the date as an argument, and generate a list of pairs for
~org-datetree-find-create-hierarchy~. This allows for creating new
types of datetrees (e.g. for lunar calendars, academic calendars,
retail 4-4-5 calendars, etc).
*** New =shortdoc= link type
You can now create links to =shortdoc= documentation groups for Emacs
Lisp functions (see =M-x shortdoc-display-group=). Requires Emacs 28
or newer.
*** Beamer export supports setting frame subtitles
If a headline is exported as a frame, and has its =BEAMER_SUBTITLE=
property set, the value is used as the subtitle.
*** =:wrap= header argument can now be explicitly disabled
Previously, presence of =:wrap= argument (inherited or not) in code
block headers always made the block results wrapped. There was no way
to disable wrapping if =:wrap= was specified in the inherited header
arguments. Now, =:wrap no= or =:wrap nil= will explicitly disable
wrapping.
*** =ob-sqlite=: Added ability to open a database in readonly mode
Added option ~:readonly~ to =ob-sqlite=.
With ~:readonly yes~, the database is opened in readonly mode. For
example:
#+begin_example
,#+begin_src sqlite :db /tmp/rip.db :readonly yes :exports both
create table rip(a,b);
,#+end_src
#+end_example
This results in an error such as:
#+begin_example
Runtime error near line 2: attempt to write a readonly database (8)
[ Babel evaluation exited with code 1 ]
#+end_example
** New and changed options
# Changes dealing with changing default values of customizations,
# adding new customizations, or changing the interpretation of the
# existing customizations.
*** New option ~org-edit-keep-region~
Since Org 9.7, structure editing commands do not deactivate region
after editing. Now, this is configurable via the new option.
*** =xelatex= can be used for LaTeX previews
A new process =xelatex= is added to ~org-preview-process-alist~ to
allow generating LaTeX fragment preview through =xdv= file produced by
XeLaTeX, which has better support for Unicode.
You can now set ~org-preview-latex-default-process~ to ~'xelatex~.
*** New link preview system
**** New option ~org-link-preview-batch-size~
Org link previews are generated asynchronously and a few at a time, in
batches. This option controls the number of links that are previewed
in each batch.
**** New option ~org-link-preview-delay~
Org link previews are generated asynchronously. This option controls
the minimum idle time in seconds between previews of batches of links.
*** New and changed export options
**** ~org-html-style-default~ now highlights =#+begin_src c= (lowercase) blocks
The default value has been changed, adding ~pre.src-c:before {
content: 'C'; }~ (lowercase =c=) that parallels ~pre.src-C:before {
content: 'C'; }~ (uppercase =C=).
**** ~org-odt-with-latex~ accepts any method from ~org-preview-latex-process-alist~
Previously, only a few conversion methods (~dvipng~, ~imagemagick~,
~dvisvgm~) could be used to render LaTeX fragments as images when
exporting to ODT. Now any method in ~org-preview-latex-process-alist~
can be used.
**** New environment =onlyenv= in ~org-beamer-environments-default~
The =onlyenv= environment limits showing parts of an animated Beamer
slide to specific animation steps.
#+begin_example
,***** Comment
:PROPERTIES:
:BEAMER_env: onlyenv
:BEAMER_act: <2->
:END:
This text will be displayed on animation step 2 and later.
#+end_example
**** =ox-html=: Headline self links can be enabled from an Org mode file
Previously HTML export could add, to each headline, a link to itself.
To enable it, you had to use the variable
~org-html-self-link-headlines~.
Now, it's also possible to enable it per Org mode file by adding:
: #+OPTIONS: html-self-link-headlines:t
**** Allow disabling macro replacement during export
New custom option ~org-export-replace-macros~ controls whether Org
mode replaces macros in the buffer before export. Set it to nil to
disable macro replacement.
This variable has no effect on the ={{{results...}}}= macros for inline
code block results.
**** New option ~org-cite-bibtex-bibliography-style~
This option adds a fallback bibliography style for BibTeX when none is
provided in the =#+CITE_EXPORT= options. The default style is
"plain".
**** New option ~org-cite-csl-bibtex-titles-to-sentence-case~
When this option is non-nil then title fields in BibTeX bibliography
entries are converted to sentence-case before being formatted
according to a CSL style, except for entries with a =langid= field
specifying a non-English language. When nil, this conversion is
limited to entries having a =langid= field specifying a variant of
English. The default value is ~t~ as the CSL standard assumes that
English titles are specified in sentence-case but the BibTeX
bibliography format requires them to be written in title-case.
**** New option ~org-latex-mathml-directory~
This option specifies the path where MathML files generated from LaTeX
fragments are stored.
**** New option ~org-latex-use-sans~
This option specifies the PDF should be typeset using the Sans font
specified in the document class (or the user) instead of the default
font (i.e. the Roman font).
**** New option ~#+LATEX_CLASS_PRE~
This option prepends LaTeX code before the LaTeX preamble.
**** New option ~org-odt-with-forbidden-chars~
The new export option controls how to deal with characters that are forbidden
inside ODT documents during export.
The ODT documents must follow XML1.0 specification and cannot contain
certain Unicode characters. For example, form feed characters like ^L
are disallowed.
By default, =ox-odt= will strip such characters and display warning.
You may return to the previous behavior by setting
~org-odt-with-forbidden-chars~ to t.
Note that Emacs warnings can always be suppressed by clicking on ⛔
symbol or by customizing ~warning-suppress-types~.
**** New option ~org-md-link-org-files-as-md~
This option makes it possible to disable mapping of linked org files
to markdown during export to Markdown. This is analogous to how
~org-html-link-org-files-as-html~ works in export to HTML.
*** New context available to save in archived headings
~org-archive-save-context-info~ can now contain ~olid~ symbol to save
parent heading ID in the archived heading.
*** New hook ~org-archive-finalize-hook~
Hook run after successfully archiving a subtree in final location.
Unlike ~org-archive-hook~, which runs in the source Org buffer, the
new hook is called with point on the subtree in the destination file.
*** Headline/olp target in ~org-capture-templates~ can be a function/variable
The variable ~org-capture-templates~ accepts a target specification as
function or symbol for headline (~file+headline~) and olp (~file+olp~
and ~file+olp+datetree~).
*** The default value of ~org-babel-latex-process-alist~ is no longer taken from ~org-preview-latex-process-alist~
The default value used to be pulled from =dvipng= process type from
~org-preview-latex-process-alist~. Now, it defaults to using
=latexmk= (when available), or running =latex= multiple times, so that
all the references are resolved in the generated PNG.
*** ~org-tags-sort-function~ can now be a list of functions
~org-tags-sort-function~ can now be set to a list of functions.
Subsequent sorting functions will be used if two tags are found to be
equivalent. See docstring for more information.
*** New tags sorting function ~org-tags-sort-hierarchy~
By setting ~org-tags-sort-function~ to ~org-tags-sort-hierarchy~, tags
are sorted taking their hierarchy into account. See
[[info:org#Tag Hierarchy][Tag Hierarchy]] for how to set up a tag
hierarchy.
*** New option ~org-cite-basic-complete-key-crm-separator~
This option makes ~org-cite~'s ~basic~ insert processor use
~completing-read-multiple~ instead of the default consecutive prompts.
It can also be set to dynamically compute ~crm-separator~ so that the
separator does not appear in completion candidates.
*** ~org-yank-image-save-method~ can be a function producing directory name
In previous versions, ~org-yank-image-save-method~ could be either a
symbol ~attach~ or a string -- directory name. Now it can also be a
function, which will be called with no arguments and its return value
will be used as a directory to save the image to.
*** ~org-refile-targets~ can now match all headlines in the target file(s)
Candidate refile targets may now be specified with the symbol ~t~ to
indicate that all headlines within the specified file are to be
considered. For example, setting ~org-refile-targets~ to ~((nil . t))~
will allow one to refile to any heading within the current buffer.
*** In =ob-ditaa=, the output type is now controlled consistently with other babel backends
Output file type is determined as specified in Babel documentation:
the suffix of =:file= is the primary determinant, and =:file-ext=
secondary. Header arguments =:pdf= and =:eps= are supported for
backwards compatibility. Default output type is still PNG.
** New functions and changes in function arguments
# This also includes changes in function behavior from Elisp perspective.
*** The deprecated =show= parameter to =org-priority= has been removed
The =show= parameter for the =org-priority= function was deprecated in
Org 9.2 (released in 2017). Sufficient time has passed, and it is being
removed as part of refactoring for numeric priorities.
*** ~org-attach-attach~ now returns a link to file stored
Previously, ~org-attach-attach~ did not have any specified default value.
Now, it returns a list =(LINK DESCRIPTION)= to the file stored.
The link obeys non-nil ~org-attach-store-link-p~ setting.
When ~org-attach-store-link-p~ is nil, an =attachment:= link is returned.
*** New functions exposing link formatting done by ~org-insert-link~
New function ~org-link-get-description~ exposes handling ~:insert-description~
link parameter and ~org-link-make-description-function~.
New function ~org-link-make-string-for-buffer~ exposes link and description
cleanups performed by ~org-insert-link~, including cleaning up =<...>= brackets,
stripping current buffer file path from the link, and adjusting =file:= links
according to ~org-link-file-path-type~.
*** ob-comint: New optional arguments controlling prompt handling
The new argument ~prompt-handling~ in ~org-babel-comint-with-output~
and ~org-babel-comint-async-register~ allows Babel languages to
specify how prompts should be handled in comint output. If equal to
~filter-prompts~, prompts are removed from output before it is passed
on to language-specific processing. If equal to
~disable-prompt-filtering~, then the prompt filtering is skipped. If
unset, then the default behavior is the same as ~filter-prompts~ for
backwards compatibility.
Prompt filtering is needed for some Babel languages, such as ob-shell,
which leave extra prompts in the output as a side effect of
evaluation. However, other Babel languages, like ob-python, don't
leave extra prompts after evaluation, and skipping the prompt
filtering can be more robust for such languages (as this avoids
removing false positive prompts).
*** Elisp functions for new datetree tree-types
Accompanying the [[#9.8-datetree-treetype][new datetree capture ~:tree-type~ options]], on the
elisp level ~org-datetree-find-create-entry~ and
~org-datetree-find-create-hierarchy~ generalize
~org-datetree-find-date-create~, ~org-datetree-find-month-create~, and
~org-datetree-find-iso-week-create~ to new datetree types.
*** New function ~org-src-get-lang-mode-if-bound~
The new function is like ~org-src-get-lang-mode~, except that it
ensures the returned major mode for the given language is bound, and
so available to the user. If the mode is not bound, the function can
optionally return a fallback mode and display a message when doing so.
The function was added so that Org can fall back to Fundamental mode
for source blocks where the appropriate major mode is unavailable.
*** New function ~org-gnus-no-new-news-other-frame~ (to be used in ~org-link-frame-setup~)
The new function is like ~org-gnus-no-new-news~, but always opens the
link in other frame.
*** New function ~org-string-width-invisibility-spec~
The new function constructs an invisibility spec without folds and
ellipses, suitable for ~org-string-width~. This can be helpful for
performance if ~org-string-width~ is called multiple times.
*** New command ~org-link-preview~ to preview Org links
This command replaces ~org-toggle-inline-images~, which is now
obsolete.
*** New command ~org-link-preview-region~ to preview Org links in a region or the buffer
This command replaces ~org-display-inline-images~, which is now
obsolete.
*** New command ~org-link-preview-clear~ to clear Org link previews in a region or the buffer
This command replaces ~org-remove-inline-images~, which is now
obsolete.
*** New command ~org-link-preview-refresh~ to refresh Org link previews in the buffer
This command replaces ~org-redisplay-inline-images~, which is now
obsolete.
*** ~org-html-head~ and ~org-html-head-extra~ can now be specified as functions
Previously, ~org-html-head~ and ~org-html-head-extra~ could only be
specified directly as strings. Now, they can be set to functions that
accept the INFO channel and return a string. This makes it possible
to dynamically generate the content of the resulting ~<head>~ tag in
the resulting HTML document.
*** ~org-element-create~ now ignores ~nil~s in CHILDREN argument
When CHILDREN contains ~nil~ elements, they are skipped. This way,
#+begin_src emacs-lisp
(let ((children nil))
(org-element-create 'section nil children)) ; => (section nil)
#+end_src
will yield expected results rather than assigning literal ~nil~ as a child.
*** ~org-clock-get-clock-string~ now takes an optional ~max-length~ argument
When a ~max-length~ is passed to ~org-clock-get-clock-string~, it will first
attempt to truncate the headline and add an ellipsis in order to make the entire
clock string fit under the length limit. If the length limit is too small to
accommodate even a single character of the headline, after accounting for spaces
and the surrounding parentheses, it will omit the headline entirely and just
show as much of the clock as fits under the limit.
*** ~org-string-width~ now takes an optional ~invisibility-spec~ argument
For performance, if the invisibility spec has been constructed, it can
be passed in as ~invisibility-spec~ instead of having it be
constructed again.
** Removed or renamed functions and variables
*** Obsolete functions and variables removed from ~org-datetree~
Due to the refactoring of ~org-datetree~ to support the [[#9.8-datetree-treetype][new datetree
capture ~:tree-type~ options]], the internal variable
~org-datetree-base-level~ has been removed, as well as the
undocumented helper function ~org-datetree-insert-line~.
*** Obsolete functions ~org-let~ and ~org-let2~ are removed
If any code is still using these ancient functions, it should move to ~cl-progv~.
*** ~org-show-empty-lines-in-parent~ is now obsolete
This function is unused in Org code and does not appear to be used in third-party code.
To be removed in future releases.
*** ~org-edit-src-content-indentation~ is renamed to ~org-src-content-indentation~
The new name highlights that the customization affects more than
editing. ~org-src-content-indentation~ also affects detangling,
printing Org syntax tree (for example, during export to Org), and
indentation of src and example blocks in Org buffers.
*** ~org-cycle-display-inline-images~ is renamed to ~org-cycle-display-link-previews~
Inline image previews in Org mode are now provided by the more general
link previews feature. The behavior with regard to image links is
unchanged.
*** ~org-cycle-inline-images-display~ is renamed to ~org-cycle-link-previews-display~
The behavior is unchanged, except in that the new variable now affects
previews of supported link types besides image links.
*** ~org-startup-with-inline-images~ is renamed to ~org-startup-with-link-previews~
The behavior is unchanged, except in that the new variable now affects
previews of supported link types besides image links.
*** =ob-ditaa=: =org-babel-ditaa-java-cmd= renamed and =org-ditaa-jar-option= made obsolete
To align with other customizable variable names, which do not contain
the word =babel=, variable =org-babel-ditaa-java-cmd= has been renamed
to =org-ditaa-java-exec=. The old variable =org-babel-ditaa-java-cmd=
is still available as an obsolete alias.
Variable =org-ditaa-jar-option= did not serve any sensible purpose and
has been made obsolete. Its value is still used in place of default
parameter -jar if the variable is defined.
** Miscellaneous
*** =ob-calc.el=: Vector and matrix are now inserted as Org tables by default
~ob-calc~ now formats vector and matrix results as Org tables. This
conversion can be overridden using the ~:results verbatim~ keyword on
a per source block basis.
To get back the old behavior, add
#+begin_example
(with-eval-after-load 'ob-calc
(setq org-babel-header-args:calc
(append '(:results . "verbatim") org-babel-header-args:calc)))
#+end_example
to your configuration.
The new behavior follows general babel backend rules (auto-detecting
result type), but may affect the existing usage.
*** ~orgtbl-to-generic~ retains special rows when exporting to Org
Previously, special table rows were unconditionally removed when
export to Org. Now, the defaults follow what ox-org does - to retain
special rows by default. See [[https://orgmode.org/worg/org-release-notes.html#ox-org-special-table-rows][previous change]].
To retain the old behavior, add ~:with-special-rows nil~ to PARAMS argument:
: (orgtbl-to-generic table '(:with-special-rows nil)
*** ~org-babel-lob-ingest~ no longer performs noweb expansion when ingesting blocks
Previously, ~org-babel-lob-ingest~ would expand noweb references when
adding source blocks to the Library of Babel. Now, blocks are stored
with unexpanded noweb references.
Noweb expansion is handled appropriately when blocks are actually used
via ~org-babel-execute-src-block~ or ~org-babel-exp-do-export~, with
the correct context (~:tangle~, ~:export~, or ~:eval~).
This change is unlikely to affect most users, but code that directly
accesses ~org-babel-library-of-babel~ may observe the difference.
*** Trailing =-= is now allowed in plain links
Previously, plain links like
: https://domain/test-
did not include the trailing =-= punctuation.
Now, the =-= is allowed at the end, and is considered a part of the plain link.
#+begin_quote
These types of links will likely be encountered for sites where anchor
targets are automatically generated from documentation headings which
are questions.
https://list.orgmode.org/orgmode/87sexh9ddv.fsf@ice9.digital/
#+end_quote
*** Update of statistics cookies now respects narrowing
Calling ~org-update-statistics-cookies~ with a prefix argument will
now only update cookies in the accessible portion of the buffer.
*** ox-man: Support specifying =#+DATE:= and ~org-export-with-date~
Previously, ox-man ignored =#+DATE:= keyword even when
~org-export-with-date~ is set to non-nil. Now, the date is exported
and specified in the =footer-middle= argument of =.TH= macro (see ~man
7 man~).
*** ox-man: Support specifying =:release= and =:header= in =#+MAN_CLASS_OPTIONS:= in addition to =:section-id=
The newly added =:release= and =:header= options of =#+MAN_CLASS_OPTIONS=
are respectively mapped to the =footer-inside= and =header-middle=
arguments of the =.TH= macro (see ~man 7 groff_man~).
*** ~org-capture~ target pointing to headline is now handled uniformly for =plain= entry type
Previously, when using ~file+regexp~, ~file+function~ or ~function~, =plain= entries
were inserted right at the point according to regexp/function, even when point is
on an existing headline.
Now, when target points to an existing headline, =plain= entries are
inserted inside its body, honoring ~:prepend~ property. This is more
consistent with how ~item~, ~checkitem~, and ~table-line~ templates
are handled.
*** ~org-lint~ now checks priorities
Warnings are raised on headlines containing out-of-bounds, invalid
(e.g., =[#-1]=, =[#AA]=), or malformed (e.g., =[#1=, =[#A=)
priorities.
*** In Dot code blocks, ~graphviz-dot-mode~ is used if available
Previously, when editing Dot code blocks with =M-x org-edit-special=,
Dot code would open in Fundamental mode, even when specialized mode is
installed. The new behavior is more DWIM.
*** Source blocks fall back to Fundamental mode
Org now falls back to Fundamental mode for source blocks when the
appropriate major mode is unavailable.
*** Priority speed commands adapt to user options
Previously, =1=, =2=, and =3= would insert priorities =A=, =B=, and
=C=, which causes errors when using numeric priorities. These now
insert ~org-priority-highest~, ~org-priority-default~, and
~org-priority-lowest~, respectively.
*** ~org-store-link~ no longer asks to select store function when called noninteractively
Previously, when multiple store functions are available to store link
at point, ~org-store-link~ would always ask user which store function
to use.
Now, when ~org-store-link~ is called noninteractively (~interactive?~
argument is nil), the first matching store function is used.
Interactively, the previous behavior is retained.
*** Org mode may throw an error when attempting to include remote unsafe resource noninteractively
Previously, when ~org-resource-download-policy~ is ~ask~ (default),
and Emacs is running in batch mode, Org mode simply skipped unsafe
remote resources in the =#+include:='s. Now, an error is thrown to
avoid seemingly ignored =#+include= statements when publishing via
batch scripts.
*** HTML export wraps ~<code>~ around all the exported src blocks
HTML export always uses ~<pre>~ tag around exported src blocks.
In addition, previously, HTML export used ~<code>~ tag around src
blocks when ~org-html-klipsify-src~ is non-nil.
Now, both ~<pre>~ and ~<code>~ tags are *always* wrapped around the
export src blocks.
*** ~yank-media~ and DND handlers now honor the user option ~org-file-link-type~
When inserting file: links, ~yank-media~ and DND handlers now respect
the user option ~org-file-link-type~.
*** ~org-timer-done-hook~ is now run before the timer is stopped
Previously, ~org-timer-countdown-timer~ and ~org-timer-start-time~
were unset when the hook is run. Now, they still hold the timer info.
*** ox-latex: LaTeX images are now stored alongside the exported =.html= file
Previously, LaTeX images (when HTML export does use images for LaTeX)
were stored alongside the original =.org= file. Now, they are stored
alongside the =.html= file.
This change will make links to LaTeX images point to the folder
containing =.html= file, not the =.org= file.
*** Org mode no longer prevents =flyspell= from spell-checking inside =LOGBOOK= drawers
Previously, spell-checking via =flyspell= was disabled inside
=LOGBOOK= (or ~org-log-into-drawer~) drawers. Now, it is no longer
the case. It can be useful to see spelling mistakes inside notes
added via ~org-add-note~ command.
*** ~ob-R~ and ~ob-julia~ no longer use ESS settings for working directory
Previously, without =:dir= parameter, R and Julia code blocks could
query for working directory during evaluation. This was because
~ess-ask-for-ess-directory~ setting was obeyed.
Now, ~ess-ask-for-ess-directory~, ~ess-directory-function~, and
~ess-directory~ are all ignored during code block evaluation (except
when session is already running). In other words, R and Julia code
blocks now conform to the "16.4 Environment of a Code Block" section
of Org mode manual that prescribes Org buffer directory or ~:dir~
value to be used as working dir to run the code blocks.
*** ~org-cancel-repeater~ now cancels all the repeaters inside entry
Previously, ~org-cancel-repeater~ only canceled repeater in the first
active timestamp inside heading. Now, all the repeaters are
canceled.
The function is renamed to ~org-cancel-repeaters~ accordingly (the old
name is still kept as an alias).
*** ~org-refile~ now saves current position to Org mark ring when jumping to heading
When ~org-refile~ is called with =C-u= or =C-u C-u= prefix argument
(to jump to heading or to jump to the last refiled heading), it saves
point to Org mark ring before jumping. Then, the user can return back
via ~org-mark-ring-goto~.
*** =org-attach= now considers symlinked files when searching pre-existing attach dirs
When Org buffer is opened from a symlink, Org mode looks into the
original file directory when searching if an attachment directory already exists.
This way, attachments will remain accessible when opening symlinked Org file.
When no attach dir exists, Org mode will still prefer creating it in
the "default" directory - where the symlink is located.
*** Texinfo exporter now supports links in headings
The Texinfo exporter no longer removes links from headings. This
applies to all headings, below and above the =H= and =toc= export
=#+OPTIONS:=.
*** Texinfo exporter now considers numeric =toc= values in =#+OPTIONS:=
For example, given =H:3= and =toc:2= in =#+OPTIONS:=, all headings at
the 1st and 2nd level appear in the table of contents and those at the
3rd level do not.
*** =ob-tangle= now tangles source blocks that do not specify a =language= if an inherited property sets a tangle filename
Previously, all source blocks that did not specify a =language= where
ignored by ~org-babel-tangle-collect-blocks~. Now, if it inherits a
:tangle header argument with a value other than =no= or =yes= (that is, a
filename), a source block without =language= will get tangled to that
file.
*** BibTeX is tangled with the standard =.bib= file extension
Previously, =bibtex= source blocks located in a file named =NAME.org=
were tangled into a file named =NAME.bibtex=. Now, they are tangled
into a file named =FILE.bib=, using the standard extension =.bib=,
matching the rest of the ecosystem, including BibTeX and LaTeX.
*** LaTeX export now respects ~org-latex-with...~ options in the PDF metadata
Previously, the LaTeX exporter handled the PDF metadata =pdfcreator=,
=pdfauthor= and =pdftitle= as defined in
~org-latex-hyperref-template~. This has changed, and these three fields
will be defined as empty and not produce any metadata if their
corresponding ~org-latex-with-author~, ~org-latex-with-title~, or
~org-latex-with-creator~ option is set to ~nil~.
*** Fancy HTML5 export uses ~<time>~ element for timestamps
Previously, timestamps would always be rendered inside a ~<span
class="timestamp">~. Now, if both ~org-html-doctype~ is ~html5~ and
~org-html-html5-fancy~ is enabled, org will use the semantic
~<time>~ element. This will also have the ~timestamp~ class, but
additionally set the ~datetime~ attribute with a machine-readable
variant of the timestamp. The format used for the attribute can be
customized using ~org-html-datetime-formats~.
*** Export dispatcher supports arrow keys and mouse wheel
Previously, the arrow keys and the mouse wheel were ignored in the
export dispatcher, ~org-export-dispatch~ bound to =C-c C-e=. Now,
they scroll the text in the export dispatcher window.
*** Add completion for ID links
Completion is enabled for ID links inserted with ~org-insert-link~.
Completion candidates, by default, will be sourced from all files
known to contain headlines with IDs. You can use new option
~org-id-completion-targets~ to change where the candidates are
searched.
*** Pre-populate the description for BBDB links
When inserting BBDB links, use the full name as the default link
description, instead of nothing.
* Version 9.7
** Important announcements and breaking changes
@ -285,6 +1202,9 @@ Images dropped also respect the value of ~org-yank-image-save-method~
when ~org-yank-dnd-method~ is =attach=.
*** Alignment of image previews can be customized
:PROPERTIES:
:CUSTOM_ID: preview-align
:END:
Previously, all the image previews were always left-aligned.
@ -1634,6 +2554,9 @@ Previously, all the header arguments where stripped from src blocks
during export. Now, header arguments are preserved.
*** =ox-org= now exports special table rows by default
:PROPERTIES:
:CUSTOM_ID: ox-org-special-table-rows
:END:
Previously, when exporting to Org, special table rows (for example,
width cookies) were not exported. Now, they are exported by default.
@ -4205,10 +5128,13 @@ wget -c "https://ben.akrin.com/crackzor/crackzor_1.0.c.gz"
#+end_example
*** Add ~:session~ support of ob-js for js-comint
#+begin_src js :session "*Javascript REPL*"
console.log("stardiviner")
#+end_src
# SIC, JavaScript miscapitalized in `js-comint.el'.
*** Add ~:session~ support of ob-js for Indium
#+begin_src js :session "*JS REPL*"
console.log("stardiviner")

View file

@ -404,9 +404,6 @@
<style:style style:name="OrgTags" style:family="text"/>
<style:style style:name="OrgPriority" style:family="text"/>
<style:style style:name="OrgPriority-A" style:family="text" style:parent-style-name="OrgPriority"/>
<style:style style:name="OrgPriority-B" style:family="text" style:parent-style-name="OrgPriority"/>
<style:style style:name="OrgPriority-C" style:family="text" style:parent-style-name="OrgPriority"/>
<style:style style:name="OrgTimestamp" style:display-name="OrgTimestamp" style:family="text">
<style:text-properties style:font-name="Courier New" fo:background-color="transparent" style:font-name-asian="NSimSun" style:font-name-complex="Courier New"/>

View file

@ -1,6 +1,6 @@
% Reference Card for Org Mode
\def\orgversionnumber{9.7.11}
\def\versionyear{2024} % latest update
\def\orgversionnumber{9.8.1}
\def\versionyear{2026} % latest update
\input emacsver.tex
%**start of header
@ -401,7 +401,7 @@ \section{Tables}
formula, \kbd{:=} a field formula.
\key{Example: Add Col1 and Col2}{|=\$1+\$2 |}
\key{... with printf format specification}{|=\$1+\$2;\%.2f|}
\key{... with printf-style format specification}{|=\$1+\$2;\%.2f|}
\key{... with constants from constants.el}{|=\$1/\$c/\$cm |}
\metax{sum from 2nd to 3rd hline}{|:=vsum(@II..@III)|}
\key{apply current column formula}{| = |}

View file

@ -482,7 +482,6 @@ int get_column_num (int nbcols, const char** header, const char* column)
(defun org-babel-C-header-to-C (head type)
"Convert an elisp list of header table into a C or D vector
specifying a variable with the name of the table."
(unless noninteractive (message "%S" type))
(let ((table (car head))
(headers (cdr head))
(typename (pcase type

View file

@ -35,13 +35,24 @@
(require 'cl-lib)
(require 'ob)
(require 'subr-x) ; For `string-trim-right', Emacs < 28
(declare-function orgtbl-to-tsv "org-table" (table params))
(declare-function run-ess-r "ext:ess-r-mode" (&optional start-args))
(declare-function inferior-ess-send-input "ext:ess-inf" ())
(declare-function ess-make-buffer-current "ext:ess-inf" ())
(declare-function ess-eval-buffer "ext:ess-inf" (vis))
(declare-function ess-wait-for-process "ext:ess-inf"
(&optional proc sec-prompt wait force-redisplay))
(declare-function ess-send-string "ext:ess-inf"
(process string &optional visibly message type))
(defvar ess-current-process-name) ; ess-custom.el
(defvar ess-local-process-name) ; ess-custom.el
(defvar ess-ask-for-ess-directory) ; ess-custom.el
(defvar ess-directory-function) ; ess-custom.el
(defvar ess-directory) ; ess-custom.el
(defvar ess-gen-proc-buffer-name-function) ; ess-custom.el
(defvar ess-eval-visibly) ; ess-custom.el
(defconst org-babel-header-args:R
'((width . :any)
@ -239,7 +250,10 @@ Retrieve variables from PARAMS."
(min (if lengths (apply 'min lengths) 0)))
;; Ensure VALUE has an orgtbl structure (depth of at least 2).
(unless (listp (car value)) (setq value (mapcar 'list value)))
(let ((file (orgtbl-to-tsv value '(:fmt org-babel-R-quote-tsv-field)))
(let ((file (orgtbl-to-tsv
value
'( :fmt org-babel-R-quote-tsv-field
:with-special-rows nil)))
(header (if (or (eq (nth 1 value) 'hline) colnames-p)
"TRUE" "FALSE"))
(row-names (if rownames-p "1" "NULL")))
@ -254,19 +268,17 @@ Retrieve variables from PARAMS."
(t (format "%s <- %S" name (prin1-to-string value))))))
(defvar ess-current-process-name) ; dynamically scoped
(defvar ess-local-process-name) ; dynamically scoped
(defvar ess-ask-for-ess-directory) ; dynamically scoped
(defvar ess-gen-proc-buffer-name-function) ; defined in ess-inf.el
(defun org-babel-R-initiate-session (session params)
"Create or return the current R SESSION buffer.
Use PARAMS to set default directory when creating a new session."
(defun org-babel-R-initiate-session (session _params)
"Create or return the current R SESSION buffer."
(unless (string= session "none")
(let* ((session (or session "*R*"))
(ess-ask-for-ess-directory
(and (boundp 'ess-ask-for-ess-directory)
ess-ask-for-ess-directory
(not (cdr (assq :dir params)))))
;; Force using `default-directory', as we promise in the
;; manual. The caller should have taken care about setting
;; it according to :dir if necessary.
;; https://ess.r-project.org/Manual/ess.html#Changing-the-startup-actions
(ess-ask-for-ess-directory nil)
(ess-directory-function nil)
(ess-directory nil)
;; Make ESS name the process buffer as SESSION.
(ess-gen-proc-buffer-name-function
(lambda (_) session)))
@ -442,26 +454,20 @@ last statement in BODY, as elisp."
(org-babel-import-elisp-from-file tmp-file '(16)))
column-names-p)))
(output
(mapconcat
'org-babel-chomp
(butlast
(delq nil
(mapcar
(lambda (line) (when (> (length line) 0) line))
(mapcar
(lambda (line) ;; cleanup extra prompts left in output
(if (string-match
"^\\([>+.]\\([ ][>.+]\\)*[ ]\\)"
(car (split-string line "\n")))
(substring line (match-end 1))
line))
(let ((tmp-src-file (org-babel-temp-file "R-")))
(with-temp-file tmp-src-file
(insert (concat
(org-babel-chomp body) "\n" org-babel-R-eoe-indicator)))
(with-current-buffer session
(let ((comint-prompt-regexp (concat "^" comint-prompt-regexp)))
(org-babel-comint-with-output (session org-babel-R-eoe-output)
(insert (mapconcat 'org-babel-chomp
(list body org-babel-R-eoe-indicator)
"\n"))
(inferior-ess-send-input)))))))) "\n"))))
(org-babel-chomp
(string-trim-right
(org-babel-comint-with-output
(session org-babel-R-eoe-output nil nil 'disable-prompt-filtering)
(ess-send-string (get-buffer-process (current-buffer))
(format "source('%s', echo=F, print.eval=T)"
(org-babel-process-file-name
tmp-src-file 'noquote))))
(rx (literal org-babel-R-eoe-output) (zero-or-more anychar)))))))))
(defun org-babel-R-process-value-result (result column-names-p)
"R-specific processing of return value.
@ -486,7 +492,8 @@ by `org-babel-comint-async-filter'."
session (current-buffer)
"^\\(?:[>.+] \\)*\\[1\\] \"ob_comint_async_R_\\(start\\|end\\|file\\)_\\(.+\\)\"$"
'org-babel-chomp
'ob-session-async-R-value-callback)
'ob-session-async-R-value-callback
'disable-prompt-filtering)
(cl-case result-type
(value
(let ((tmp-file (org-babel-temp-file "R-")))

View file

@ -61,7 +61,6 @@
"Execute a block of Awk code BODY with org-babel.
PARAMS is a plist of src block parameters .
This function is called by `org-babel-execute-src-block'."
(unless noninteractive (message "Executing Awk source code block"))
(let* ((result-params (cdr (assq :result-params params)))
(cmd-line (cdr (assq :cmd-line params)))
(in-file (cdr (assq :in-file params)))
@ -112,7 +111,11 @@ table."
(let ((echo-var (lambda (v) (if (stringp v) v (format "%S" v)))))
(cond
((and (listp var) (listp (car var)))
(orgtbl-to-generic var (list :sep (or sep "\t") :fmt echo-var)))
(orgtbl-to-generic
var
(list :sep (or sep "\t")
:fmt echo-var
:with-special-rows nil)))
((listp var)
(mapconcat echo-var var "\n"))
(t (funcall echo-var var)))))

View file

@ -37,8 +37,6 @@
(require 'calc-trail)
(require 'calc-store)
(declare-function calc-store-into "calc-store" (&optional var))
(declare-function calc-recall "calc-store" (&optional var))
(declare-function math-evaluate-expr "calc-ext" (x))
(defvar org-babel-default-header-args:calc nil
@ -114,11 +112,30 @@
))))))
(mapcar #'org-trim
(split-string (org-babel-expand-body:calc body params) "[\n\r]"))))
(save-excursion
(with-current-buffer "*Calculator*"
(prog1
(calc-eval (calc-top 1))
(calc-pop 1)))))
(let ((result (prog1
;; Cannot use 'top' as SEPARATOR reliably when the
;; top of the stack has a vector.
(calc-eval (calc-top 1) 'raw)
(calc-eval 1 'pop)))
(calc-line-numbering)
lisp-table)
(org-babel-reassemble-table
(org-babel-result-cond (cdr (assq :result-params params))
(calc-eval result)
(if (Math-vectorp result)
(progn
(dolist (r (if (math-matrixp result)
(cdr result) ; Ignore the 'vec item.
(list result)))
(setq r (cdr r)) ; Ignore the 'vec item.
(push (mapcar (lambda (x) (math-format-stack-value (list x 1 nil))) r)
lisp-table))
(setq lisp-table (nreverse lisp-table)))
(calc-eval result)))
(org-babel-pick-name
(cdr (assq :colname-names params)) (cdr (assq :colnames params)))
(org-babel-pick-name
(cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))
(defun org-babel-calc-maybe-resolve-var (el)
"Resolve user variables in EL.

View file

@ -53,7 +53,6 @@
(require 'ob)
(declare-function cider-current-connection "ext:cider-client" (&optional type))
(declare-function cider-current-ns "ext:cider-client" ())
(declare-function inf-clojure "ext:inf-clojure" (cmd))
(declare-function inf-clojure-cmd "ext:inf-clojure" (project-type))
(declare-function inf-clojure-eval-string "ext:inf-clojure" (code))
@ -155,21 +154,21 @@ or set the `:backend' header argument"))))
(result-params (cdr (assq :result-params params)))
(print-level nil)
(print-length nil)
;; Remove comments, they break (let [...] ...) bindings
(body (replace-regexp-in-string "^[ ]*;+.*$" "" body))
(body (org-trim
(concat
;; Source block specified namespace :ns.
(and (cdr (assq :ns params)) (format "(ns %s)\n" ns))
;; Variables binding.
(if (null vars) (org-trim body)
;; Remove comments, they break (let [...] ...) bindings
(let ((body (replace-regexp-in-string "^[ ]*;+.*$" "" body)))
(format "(let [%s]\n%s)"
(mapconcat
(lambda (var)
(format "%S '%S" (car var) (cdr var)))
vars
"\n ")
body))))))
body)))))))
;; If the result param is set to "output" we don't have to do
;; anything special and just let the backend handle everything
(if (member "output" result-params)
@ -237,8 +236,8 @@ or set the `:backend' header argument"))))
"clojure" (format "clojure -A%s" alias)
cmd0)
cmd0)))
(setq
org-babel-comint-prompt-regexp-old comint-prompt-regexp
(setq-local
org-babel-comint-prompt-regexp-fallback comint-prompt-regexp
comint-prompt-regexp inf-clojure-comint-prompt-regexp)
(funcall-interactively #'inf-clojure cmd)
(goto-char (point-max))))

View file

@ -36,8 +36,13 @@
(require 'ob-core)
(require 'org-compat)
(require 'org-element-ast)
(require 'comint)
(declare-function org-element-context "org-element" (&optional element))
(defvar org-element-inline-src-block-regexp)
(defun org-babel-comint-buffer-livep (buffer)
"Check if BUFFER is a comint buffer with a live process."
(let ((buffer (when buffer (get-buffer buffer))))
@ -58,7 +63,8 @@ executed inside the protection of `save-excursion' and
(let ((comint-input-filter (lambda (_input) nil)))
,@body))))))
(defvar-local org-babel-comint-prompt-regexp-old nil
(defvaralias 'org-babel-comint-prompt-regexp-old 'org-babel-comint-prompt-regexp-fallback)
(defvar org-babel-comint-prompt-regexp-fallback nil
"Fallback regexp used to detect prompt.")
(defcustom org-babel-comint-fallback-regexp-threshold 5.0
@ -69,11 +75,11 @@ This is useful when prompt unexpectedly changes."
:package-version '(Org . "9.7"))
(defun org-babel-comint--set-fallback-prompt ()
"Swap `comint-prompt-regexp' and `org-babel-comint-prompt-regexp-old'."
(when org-babel-comint-prompt-regexp-old
"Swap `comint-prompt-regexp' and `org-babel-comint-prompt-regexp-fallback'."
(when org-babel-comint-prompt-regexp-fallback
(let ((tmp comint-prompt-regexp))
(setq comint-prompt-regexp org-babel-comint-prompt-regexp-old
org-babel-comint-prompt-regexp-old tmp))))
(setq comint-prompt-regexp org-babel-comint-prompt-regexp-fallback
org-babel-comint-prompt-regexp-fallback tmp))))
(defun org-babel-comint--prompt-filter (string &optional prompt-regexp)
"Remove PROMPT-REGEXP from STRING.
@ -100,15 +106,33 @@ PROMPT-REGEXP defaults to `comint-prompt-regexp'."
(setq string (substring string (match-end 0))))
string)
(defun org-babel-comint--remove-prompts-p (prompt-handling)
"Helper to decide whether to remove prompts from comint output.
Parses the symbol in PROMPT-HANDLING, which can be
`filter-prompts', in which case prompts should be removed; or
`disable-prompt-filtering', in which case prompt filtering is
skipped. For backward-compatibility, the default value of `nil'
is equivalent to `filter-prompts'."
(cond
((eq prompt-handling 'disable-prompt-filtering) nil)
((eq prompt-handling 'filter-prompts) t)
((eq prompt-handling nil) t)
(t (error (format "Unrecognized prompt handling behavior %s"
prompt-handling)))))
(defmacro org-babel-comint-with-output (meta &rest body)
"Evaluate BODY in BUFFER and return process output.
Will wait until EOE-INDICATOR appears in the output, then return
all process output. If REMOVE-ECHO and FULL-BODY are present and
non-nil, then strip echo'd body from the returned output. META
should be a list containing the following where the last two
elements are optional.
non-nil, then strip echo'd body from the returned output.
PROMPT-HANDLING may be either of the symbols `filter-prompts', in
which case the output is split by `comint-prompt-regexp' and
returned as a list; or, `disable-prompt-filtering', which
suppresses this behavior and returns the full output as a string.
META should be a list containing the following where the last
three elements are optional.
(BUFFER EOE-INDICATOR REMOVE-ECHO FULL-BODY)
(BUFFER EOE-INDICATOR REMOVE-ECHO FULL-BODY PROMPT-HANDLING)
This macro ensures that the filter is removed in case of an error
or user `keyboard-quit' during execution of body."
@ -116,7 +140,8 @@ or user `keyboard-quit' during execution of body."
(let ((buffer (nth 0 meta))
(eoe-indicator (nth 1 meta))
(remove-echo (nth 2 meta))
(full-body (nth 3 meta)))
(full-body (nth 3 meta))
(prompt-handling (nth 4 meta)))
`(org-babel-comint-in-buffer ,buffer
(let* ((string-buffer "")
(comint-output-filter-functions
@ -134,27 +159,25 @@ or user `keyboard-quit' during execution of body."
,@body
;; wait for end-of-evaluation indicator
(let ((start-time (current-time)))
(while (progn
(goto-char comint-last-input-end)
(not (save-excursion
(and (re-search-forward
(regexp-quote ,eoe-indicator) nil t)
(re-search-forward
comint-prompt-regexp nil t)))))
(while (not (save-excursion
(and (string-match
(regexp-quote ,eoe-indicator) string-buffer)
(string-match
comint-prompt-regexp string-buffer))))
(accept-process-output
(get-buffer-process (current-buffer))
org-babel-comint-fallback-regexp-threshold)
(when (and org-babel-comint-prompt-regexp-old
(when (and org-babel-comint-prompt-regexp-fallback
(> (float-time (time-since start-time))
org-babel-comint-fallback-regexp-threshold)
(progn
(goto-char comint-last-input-end)
(save-excursion
(and
(re-search-forward
(regexp-quote ,eoe-indicator) nil t)
(re-search-forward
org-babel-comint-prompt-regexp-old nil t)))))
(string-match
(regexp-quote ,eoe-indicator) string-buffer)
(string-match
org-babel-comint-prompt-regexp-fallback string-buffer)))))
(org-babel-comint--set-fallback-prompt))))
;; replace cut dangling text
(goto-char (process-mark (get-buffer-process (current-buffer))))
@ -164,8 +187,9 @@ or user `keyboard-quit' during execution of body."
(and ,remove-echo ,full-body
(setq string-buffer (org-babel-comint--echo-filter string-buffer ,full-body)))
;; Filter out prompts.
(org-babel-comint--prompt-filter string-buffer)))))
(if (org-babel-comint--remove-prompts-p ,prompt-handling)
(org-babel-comint--prompt-filter string-buffer)
string-buffer)))))
(defun org-babel-comint-input-command (buffer cmd)
"Pass CMD to BUFFER.
@ -189,14 +213,14 @@ statement (not large blocks of code)."
(accept-process-output
(get-buffer-process buffer)
org-babel-comint-fallback-regexp-threshold)
(when (and org-babel-comint-prompt-regexp-old
(when (and org-babel-comint-prompt-regexp-fallback
(> (float-time (time-since start-time))
org-babel-comint-fallback-regexp-threshold)
(progn
(goto-char comint-last-input-end)
(save-excursion
(re-search-forward
org-babel-comint-prompt-regexp-old nil t))))
org-babel-comint-prompt-regexp-fallback nil t))))
(org-babel-comint--set-fallback-prompt))))))
(defun org-babel-comint-eval-invisibly-and-wait-for-file
@ -239,6 +263,9 @@ Its single argument is a string consisting of output from the
comint process. It should return a string that will be passed
to `org-babel-insert-result'.")
(defvar-local org-babel-comint-async-remove-prompts-p t
"Whether prompts should be detected and removed from async output.")
(defvar-local org-babel-comint-async-dangling nil
"Dangling piece of the last process output, as a string.
Used when `org-babel-comint-async-indicator' is spread across multiple
@ -290,8 +317,7 @@ STRING contains the output originally inserted into the comint buffer."
(with-current-buffer buf
(save-excursion
(goto-char (point-min))
(when (search-forward tmp-file nil t)
(org-babel-previous-src-block)
(when (org-babel-comint-async--find-src tmp-file)
(let* ((info (org-babel-get-src-block-info))
(params (nth 2 info))
(result-params
@ -326,17 +352,22 @@ STRING contains the output originally inserted into the comint buffer."
until (and (equal (match-string 1) "start")
(equal (match-string 2) uuid))
finally return (+ 1 (match-end 0)))))
;; Remove prompt
(res-promptless (org-trim (string-join (mapcar #'org-trim (org-babel-comint--prompt-filter res-str-raw)) "\n") "\n"))
;; Apply user callback
(res-str (funcall org-babel-comint-async-chunk-callback res-promptless)))
(res-str (funcall org-babel-comint-async-chunk-callback
(if org-babel-comint-async-remove-prompts-p
(org-trim (string-join
(mapcar #'org-trim
(org-babel-comint--prompt-filter
res-str-raw))
"\n")
t)
res-str-raw))))
;; Search for uuid in associated org-buffers to insert results
(cl-loop for buf in org-buffers
until (with-current-buffer buf
(save-excursion
(goto-char (point-min))
(when (search-forward uuid nil t)
(org-babel-previous-src-block)
(when (org-babel-comint-async--find-src uuid)
(let* ((info (org-babel-get-src-block-info))
(params (nth 2 info))
(result-params
@ -348,20 +379,63 @@ STRING contains the output originally inserted into the comint buffer."
;; Remove uuid from the list to search for
(setq uuid-list (delete uuid uuid-list)))))))))
(defun org-babel-comint-async--find-src (uuid-or-tmpfile)
"Find source block associated with an async comint result.
UUID-OR-TMPFILE is the uuid or tmpfile associated with the result.
Returns non-nil if the source block is succesfully found, and moves
point there.
This function assumes that UUID-OR-TMPFILE was previously inserted as
the source block's result, as a placeholder until the true result
becomes ready. It may fail to find the source block if the buffer was
modified so that UUID-OR-TMPFILE is no longer the result of the source
block, or if it has been copied elsewhere into the buffer (this is a
limitation of the current async implementation)."
(goto-char (point-min))
(when (search-forward uuid-or-tmpfile nil t)
(let ((uuid-pos (point)))
(and (re-search-backward
;; find the nearest preceding src or inline-src block
(rx (or (regexp org-babel-src-block-regexp)
(regexp org-element-inline-src-block-regexp)))
nil t)
;; check it's actually a src block and not verbatim text
(org-element-type-p (org-element-context)
'(inline-src-block src-block))
;; Check result contains the uuid. There isn't a simple way
;; to extract the result value that works in all cases
;; (e.g. inline blocks or results drawers), so instead
;; check the result region contains the found uuid position
(let ((result-where (org-babel-where-is-src-block-result)))
(when result-where
(save-excursion
(goto-char result-where)
(and
(>= uuid-pos (org-element-property :begin (org-element-context)))
(< uuid-pos (org-element-property :end (org-element-context)))))))))))
(defun org-babel-comint-async-register
(session-buffer org-buffer indicator-regexp
chunk-callback file-callback)
chunk-callback file-callback
&optional prompt-handling)
"Set local org-babel-comint-async variables in SESSION-BUFFER.
ORG-BUFFER is added to `org-babel-comint-async-buffers' if not
present. `org-babel-comint-async-indicator',
`org-babel-comint-async-chunk-callback', and
`org-babel-comint-async-file-callback' are set to
INDICATOR-REGEXP, CHUNK-CALLBACK, and FILE-CALLBACK
respectively."
INDICATOR-REGEXP, CHUNK-CALLBACK, and FILE-CALLBACK respectively.
PROMPT-HANDLING may be either of the symbols `filter-prompts', in
which case prompts matching `comint-prompt-regexp' are filtered
from output before it is passed to CHUNK-CALLBACK, or
`disable-prompt-filtering', in which case this behavior is
disabled. For backward-compatibility, the default value of `nil'
is equivalent to `filter-prompts'."
(org-babel-comint-in-buffer session-buffer
(setq org-babel-comint-async-indicator indicator-regexp
org-babel-comint-async-chunk-callback chunk-callback
org-babel-comint-async-file-callback file-callback)
(setq org-babel-comint-async-remove-prompts-p
(org-babel-comint--remove-prompts-p prompt-handling))
(unless (memq org-buffer org-babel-comint-async-buffers)
(setq org-babel-comint-async-buffers
(cons org-buffer org-babel-comint-async-buffers)))

View file

@ -42,30 +42,26 @@
nil))
(defvar org-babel-library-of-babel)
(defvar org-edit-src-content-indentation)
(defvar org-src-content-indentation)
(defvar org-link-file-path-type)
(defvar org-src-lang-modes)
(defvar org-babel-tangle-uncomment-comments)
(declare-function org-attach-dir "org-attach" (&optional create-if-not-exists-p no-fs-check))
(declare-function org-at-item-p "org-list" ())
(declare-function org-at-table-p "org" (&optional table-type))
(declare-function org-at-table-p "org-table" (&optional table-type))
(declare-function org-babel-lob-execute-maybe "ob-lob" ())
(declare-function org-babel-ref-goto-headline-id "ob-ref" (id))
(declare-function org-babel-ref-headline-body "ob-ref" ())
(declare-function org-babel-ref-parse "ob-ref" (assignment))
(declare-function org-babel-ref-resolve "ob-ref" (ref))
(declare-function org-babel-ref-split-args "ob-ref" (arg-string))
(declare-function org-babel-tangle-comment-links "ob-tangle" (&optional info))
(declare-function org-current-level "org" ())
(declare-function org-cycle "org-cycle" (&optional arg))
(declare-function org-edit-src-code "org-src" (&optional code edit-buffer-name))
(declare-function org-edit-src-exit "org-src" ())
(declare-function org-src-preserve-indentation-p "org-src" (node))
(declare-function org-src-preserve-indentation-p "org-src" (&optional node))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-at-point-no-context "org-element" (&optional pom))
(declare-function org-element-context "org-element" (&optional element))
(declare-function org-element-normalize-string "org-element" (s))
(declare-function org-element-normalize-string "org-element" (s &optional keep-newlines))
(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))
@ -79,34 +75,26 @@
(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-in-commented-heading-p "org" (&optional no-inheritance element))
(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))
(declare-function org-list-struct "org-list" ())
(declare-function org-list-to-generic "org-list" (LIST PARAMS))
(declare-function org-list-to-lisp "org-list" (&optional delete))
(declare-function org-list-to-org "org-list" (list &optional params))
(declare-function org-macro-escape-arguments "org-macro" (&rest args))
(declare-function org-mark-ring-push "org" (&optional pos buffer))
(declare-function org-narrow-to-subtree "org" (&optional element))
(declare-function org-next-block "org" (arg &optional backward block-regexp))
(declare-function org-open-at-point "org" (&optional in-emacs reference-buffer))
(declare-function org-open-at-point "org" (&optional arg))
(declare-function org-previous-block "org" (arg &optional block-regexp))
(declare-function org-fold-show-context "org-fold" (&optional key))
(declare-function org-src-coderef-format "org-src" (&optional element))
(declare-function org-src-coderef-regexp "org-src" (fmt &optional label))
(declare-function org-src-get-lang-mode "org-src" (lang))
(declare-function org-table-align "org-table" ())
(declare-function org-table-convert-region "org-table" (beg0 end0 &optional separator))
(declare-function org-table-end "org-table" (&optional table-type))
(declare-function org-table-import "org-table" (file arg))
(declare-function org-table-to-lisp "org-table" (&optional txt))
(declare-function org-unescape-code-in-string "org-src" (s))
(declare-function orgtbl-to-generic "org-table" (table params))
(declare-function orgtbl-to-orgtbl "org-table" (table params))
(declare-function tramp-compat-make-temp-file "tramp-compat" (filename &optional dir-flag))
(defgroup org-babel nil
"Code block evaluation and management in `org-mode' documents."
@ -217,7 +205,7 @@ When matching, reference is stored in match group 1."
(defvar org-babel-src-block-regexp
(concat
;; (1) indentation (2) lang
"^\\([ \t]*\\)#\\+begin_src[ \t]+\\([^ \f\t\n\r\v]+\\)[ \t]*"
"^\\([ \t]*\\)#\\+begin_src\\(?:[ \t]+\\([^ \f\t\n\r\v]*\\)\\)?[ \t]*"
;; (3) switches
"\\([^\":\n]*\"[^\"\n*]*\"[^\":\n]*\\|[^\":\n]*\\)"
;; (4) header arguments
@ -358,18 +346,27 @@ a window into the `org-babel-get-src-block-info' function."
(full (lambda (it) (> (length it) 0)))
(printf (lambda (fmt &rest args) (princ (apply #'format fmt args)))))
(when info
(with-help-window (help-buffer)
(let ((name (nth 4 info))
(lang (nth 0 info))
(let* ((name (nth 4 info))
(language (nth 0 info))
(switches (nth 3 info))
(header-args (nth 2 info)))
(header-args (nth 2 info))
(property-header-args
(org-entry-get (point) "header-args" t))
(property-header-args-language
(org-entry-get (point) (concat "header-args:" language) t)))
(with-help-window (help-buffer)
(when name (funcall printf "Name: %s\n" name))
(when lang (funcall printf "Lang: %s\n" lang))
(when language (funcall printf "Language: %s\n" language))
;; Show header arguments that have been set through
;; properties (i.e. in property drawers or through
;; #+PROPERTY)
(funcall printf "Properties:\n")
(funcall printf "\t:header-args \t%s\n" (org-entry-get (point) "header-args" t))
(funcall printf "\t:header-args:%s \t%s\n" lang (org-entry-get (point) (concat "header-args:" lang) t))
(funcall printf "\t:header-args \t%s\n" property-header-args)
(funcall printf "\t:header-args:%s \t%s\n" language property-header-args-language)
;; Show switches
(when (funcall full switches) (funcall printf "Switches: %s\n" switches))
;; Show default header arguments and header arguments that
;; have been explicitly set in the current code block.
(funcall printf "Header Arguments:\n")
(dolist (pair (sort header-args
(lambda (a b) (string< (symbol-name (car a))
@ -722,9 +719,17 @@ Otherwise, return a list with the following pattern:
lang
(org-babel--normalize-body datum)
(apply #'org-babel-merge-params
;; Use `copy-tree' to avoid creating shared structure
;; with the `org-babel-default-header-args-*' variables:
;; modifications by `org-babel-generate-file-param'
;; below would modify the shared structure, thereby
;; modifying the variables.
(copy-tree
(if inline org-babel-default-inline-header-args
org-babel-default-header-args)
(and (boundp lang-headers) (eval lang-headers t))
t)
(and (boundp lang-headers)
(copy-tree (eval lang-headers t) t))
(append
;; If DATUM is provided, make sure we get node
;; properties applicable to its location within
@ -748,11 +753,19 @@ Otherwise, return a list with the following pattern:
(defun org-babel--expand-body (info)
"Expand noweb references in src block and remove any coderefs.
The src block is defined by its INFO, as returned by
`org-babel-get-src-block-info'."
`org-babel-get-src-block-info'.
This function uses the :eval context for noweb expansion because it is
called when code needs to be evaluated, either by
`org-babel-execute-src-block' or `org-babel-confirm-evaluate'. The :eval
context is appropriate even during export or tangling when the code block
needs to be evaluated to generate results. It is distinct from the :export
and :tangle contexts which are used when generating the source code body for
display in exported documents or tangled files."
(let ((coderef (nth 6 info))
(expand
(if (org-babel-noweb-p (nth 2 info) :eval)
(org-babel-expand-noweb-references info)
(org-babel-expand-noweb-references info nil :eval)
(nth 1 info))))
(if (not coderef) expand
(replace-regexp-in-string
@ -986,7 +999,7 @@ arguments and pop open the results in a preview buffer."
(symbol-name (car el2)))))))
(body (setf (nth 1 info)
(if (org-babel-noweb-p params :eval)
(org-babel-expand-noweb-references info) (nth 1 info))))
(org-babel-expand-noweb-references info nil :eval) (nth 1 info))))
(expand-cmd (intern (concat "org-babel-expand-body:" lang)))
(assignments-cmd (intern (concat "org-babel-variable-assignments:"
lang)))
@ -1076,10 +1089,19 @@ completion from lists of common args and values."
(unless (= (char-before (point)) ?\ ) (insert " "))
(insert ":" header-arg) (when value (insert " " value)))))
(defun org-babel-in-src-block-header-p ()
"Return non-nil when `point' is in the header line of the source block."
(let ((beg (org-babel-where-is-src-block-head)))
(when beg
(let ((end (save-excursion (goto-char beg) (end-of-line) (point))))
(and (>= (point) beg) (<= (point) end))))))
;; Add support for completing-read insertion of header arguments after ":"
(defun org-babel-header-arg-expand ()
"Call `org-babel-enter-header-arg-w-completion' in appropriate contexts."
(when (and (equal (char-before) ?\:) (org-babel-where-is-src-block-head))
"Call `org-babel-enter-header-arg-w-completion' in appropriate contexts
(the header line of a source block)."
(when (and (equal (char-before) ?\:)
(org-babel-in-src-block-header-p))
(org-babel-enter-header-arg-w-completion (match-string 2))))
(defun org-babel-enter-header-arg-w-completion (&optional lang)
@ -1117,7 +1139,7 @@ session."
(user-error "No src code block at point")
(setf (nth 1 info)
(if (org-babel-noweb-p params :eval)
(org-babel-expand-noweb-references info)
(org-babel-expand-noweb-references info nil :eval)
(nth 1 info)))))
(session (cdr (assq :session params)))
(dir (cdr (assq :dir params)))
@ -1240,6 +1262,9 @@ evaluation mechanisms."
(save-match-data (org-element-context))
'(babel-call inline-babel-call inline-src-block src-block)))
(defvar org-babel-results-buffer-name "*Org Babel Results*"
"The buffer name of Org Babel evaluate results.")
;;;###autoload
(defun org-babel-open-src-block-result (&optional re-run)
"Open results of source block at point.
@ -1263,7 +1288,7 @@ exist."
(if (looking-at org-link-bracket-re) (org-open-at-point)
(let ((r (org-babel-format-result (org-babel-read-result)
(cdr (assq :sep arguments)))))
(pop-to-buffer (get-buffer-create "*Org Babel Results*"))
(pop-to-buffer (get-buffer-create org-babel-results-buffer-name))
(erase-buffer)
(insert r)))
t))
@ -1379,8 +1404,8 @@ buffer."
(while (re-search-forward "call_\\S-\\|^[ \t]*#\\+CALL:" nil t)
(let ((,datum (org-element-context)))
(when (org-element-type-p ,datum '(babel-call inline-babel-call))
(goto-char (or (org-element-post-affiliated datum)
(org-element-begin datum)))
(goto-char (or (org-element-post-affiliated ,datum)
(org-element-begin ,datum)))
(let ((,end (copy-marker (org-element-end ,datum))))
,@body
(goto-char ,end)
@ -1484,7 +1509,7 @@ CONTEXT specifies the context of evaluation. It can be `:eval',
(lang (nth 0 info))
(params (nth 2 info))
(body (if (org-babel-noweb-p params context)
(org-babel-expand-noweb-references info)
(org-babel-expand-noweb-references info nil context)
(nth 1 info)))
(expand-cmd (intern (concat "org-babel-expand-body:" lang)))
(assignments-cmd (intern (concat "org-babel-variable-assignments:"
@ -2136,7 +2161,14 @@ block of the same language as the previous."
(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)))
(post-blank (org-element-property :post-blank copy))
(to-uppercase
(lambda (str)
(string-match "^[ \t]*#\\+\\(begin_src\\)" str)
(setq str (replace-match "BEGIN_SRC" t t str 1))
(string-match "^[ \t]*#\\+\\(end_src\\)" str)
(setq str (replace-match "END_SRC" t t str 1))
str)))
;; 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"))
@ -2156,7 +2188,12 @@ block of the same language as the previous."
;; 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))
(let ((copy-str (org-element-interpret-data copy)))
;; `org-element-interpret-data' produces lower-case
;; #+begin_src .. #+end_src
(when upper-case-p
(setq copy-str (funcall to-uppercase copy-str)))
(insert copy-str))
;; `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.
@ -2173,7 +2210,12 @@ block of the same language as the previous."
(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))
(let ((copy-str (org-element-interpret-data copy)))
;; `org-element-interpret-data' produces lower-case
;; #+begin_src .. #+end_src
(when upper-case-p
(setq copy-str (funcall to-uppercase copy-str)))
(insert copy-str))
;; Ensure to indent the inserted block and move point to its end.
(org-babel-previous-src-block 1)
(org-indent-block)
@ -2683,6 +2725,10 @@ result:
(opening-line (concat "#+begin_" full))
(closing-line (concat "#+end_" type)))
(cond
;; Do nothing if type is "no" or "nil"
((or (org-string-equal-ignore-case type "nil")
(org-string-equal-ignore-case type "no"))
nil)
;; Escape contents from "export" wrap. Wrap
;; inline results within an export snippet with
;; appropriate value.
@ -2835,7 +2881,9 @@ If called with prefix argument ARG, remove all result blocks in the
buffer."
(interactive "P")
(if arg
(progn
(org-babel-map-src-blocks nil (org-babel-remove-result))
(org-babel-map-call-lines nil (org-babel-remove-result)))
(org-babel-remove-result)))
(defun org-babel-result-end ()
@ -2953,7 +3001,7 @@ used as a string to be appended to #+begin_example line."
(indent-rigidly
(point-min)
(point-max)
(+ ind org-edit-src-content-indentation))
(+ ind org-src-content-indentation))
(buffer-string))))))
(delete-region body-start
(org-with-wide-buffer
@ -3025,6 +3073,8 @@ parameters when merging lists."
(setf (cddr (nth variable-index vars))
(concat (symbol-name name) "=" value))
(cl-incf variable-index)))
((null (cdr pair)) ;; Empty :var, ignore
nil)
(t (error "Variable \"%s\" must be assigned a default value"
(cdr pair))))))
(`(:results . ,value)
@ -3089,7 +3139,7 @@ CONTEXT may be one of :tangle, :export or :eval."
(defvar org-babel-expand-noweb-references--cache-buffer nil
"Cons (BUFFER . MODIFIED-TICK) for cached noweb references.
See `org-babel-expand-noweb-references--cache'.")
(defun org-babel-expand-noweb-references (&optional info parent-buffer)
(defun org-babel-expand-noweb-references (&optional info parent-buffer context)
"Expand Noweb references in the body of the current source code block.
When optional argument INFO is non-nil, use the block defined by INFO
@ -3098,6 +3148,24 @@ instead.
The block is assumed to be located in PARENT-BUFFER or current buffer
\(when PARENT-BUFFER is nil).
CONTEXT specifies the context of expansion and can be one of :tangle,
:export, or :eval. When CONTEXT is nil, it defaults to :eval.
Note: CONTEXT does not affect whether the top-level block is expanded -
that is determined by the caller and the block's own :noweb setting.
The context only determines which noweb header arguments are honored when
recursively expanding nested references within referenced blocks.
For recursive expansion:
- :tangle context: expands blocks with :noweb tangle, :noweb yes, etc.
- :export context: expands blocks with :noweb export, :noweb yes, etc.
- :eval context: expands blocks with :noweb eval, :noweb yes, etc.
This is important for recursive expansion: when a block with :noweb tangle
references another block that also contains noweb references, those nested
references should only be expanded if the referenced block's :noweb setting
permits expansion in the tangle context.
For example the following reference would be replaced with the
body of the source-code block named `example-block'.
@ -3125,7 +3193,8 @@ defined by `org-babel-lob'. For example
would set the value of argument \"a\" equal to \"9\". Note that
these arguments are not evaluated in the current source-code
block but are passed literally to the \"example-block\"."
(let* ((parent-buffer (or parent-buffer (current-buffer)))
(let* ((context (or context :eval))
(parent-buffer (or parent-buffer (current-buffer)))
(info (or info (org-babel-get-src-block-info 'no-eval)))
(lang (nth 0 info))
(body (nth 1 info))
@ -3159,8 +3228,9 @@ block but are passed literally to the \"example-block\"."
(expand-body
(i)
;; Expand body of code represented by block info I.
`(let ((b (if (org-babel-noweb-p (nth 2 ,i) :eval)
(org-babel-expand-noweb-references ,i)
`(let ((b (if (org-babel-noweb-p (nth 2 ,i) context)
(org-babel-expand-noweb-references
,i parent-buffer context)
(nth 1 ,i))))
(if (not comment) b
(let ((cs (org-babel-tangle-comment-links ,i)))
@ -3501,8 +3571,7 @@ Execute TABLE-FORMS when result should be considered sexp and parsed."
(member "file" ,params)
(and (or (member "output" ,params)
(member "raw" ,params)
(member "org" ,params)
(member "drawer" ,params))
(member "org" ,params))
(not (member "table" ,params))))
,scalar-form
,@table-forms)))))

304
lisp/org/ob-csharp.el Normal file
View file

@ -0,0 +1,304 @@
;;; ob-csharp.el --- org-babel functions for csharp evaluation -*- lexical-binding: t -*-
;; Copyright (C) 2024-2026 Free Software Foundation, Inc.
;; Author: Maximilian Kueffner
;; Maintainer: Maximilian Kueffner <poverobuosodonati@gmail.com>
;; Keywords: literate programming, reproducible research
;; Homepage: https://orgmode.org
;; This file is part of GNU Emacs.
;; GNU Emacs 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.
;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;; Requirements:
;; Some .NET runtime environment should be installed.
;; The `dotnet' command should be available to the system's environment
;; (PATH discoverable for example).
;;; Code:
(require 'ob)
;; file extension for C#
(add-to-list 'org-babel-tangle-lang-exts '("csharp" . "cs"))
;; default header arguments for C#
(defvar org-babel-default-header-args:csharp
'((main . ((no)))
(nugetconfig . :any)
(framework . :any)
(class . ((no nil :any)))
(references . :any)
(usings . :any)
(cmdline . :any))
"Csharp specific header arguments.")
(defcustom org-babel-csharp-compiler "dotnet"
"The program to call for compiling a csharp project."
:group 'org-babel
:package-version '(Org. "9.8")
:type 'string
:risky t)
(defun org-babel-csharp--default-compile-command (dir-proj-sln bin-dir)
"Construct the default compilation command for C#.
DIR-PROJ-SLN is either a directory containing a \".csproj\" or \".sln\" file
or a full path to either of these.
BIN-DIR is the directory for the compiled output."
(format "%s build --output %S %S"
org-babel-csharp-compiler bin-dir dir-proj-sln))
(defun org-babel-csharp--default-restore-command (project-file)
"Construct the default restore command for C# projects.
PROJECT-FILE is a path to a \".csproj\" file on which the restore command
takes effect."
(format "%s restore %S" org-babel-csharp-compiler project-file))
(defun org-babel-csharp--find-dotnet-version ()
"Get a list of dotnet major versions from a list of dotnet sdks."
(cl-delete-if #'(lambda (v) (= 0 v))
(delete-dups
(mapcar #'(lambda (n)
(let ((fr (string-match "^[0-9.]+\\." n))
(to (string-match "\\." n)))
(string-to-number (substring n fr to))))
(split-string
(shell-command-to-string
(format "%s --list-sdks" org-babel-csharp-compiler))
"\n")))))
(defcustom org-babel-csharp-default-target-framework
(format "net%s.0"
(let ((net-sdks (org-babel-csharp--find-dotnet-version)))
(when net-sdks
(apply #'max net-sdks))))
"The desired target framework to use."
:group 'org-babel
:package-version '(Org. "9.8")
:type 'string
:safe #'stringp)
(defcustom org-babel-csharp-generate-compile-command
#'org-babel-csharp--default-compile-command
"A function creating the compile command.
It must take two parameters intended for the target binary directory and
a .sln file, .csproj file, or a base directory where either can be found."
:group 'org-babel
:package-version '(Org. "9.8")
:type 'function
:risky t)
(defcustom org-babel-csharp-generate-restore-command
#'org-babel-csharp--default-restore-command
"A function creating a project restore command.
It must take one parameter defining the project to perform a restore on."
:group 'org-babel
:package-version '(Org. "9.8")
:type 'function
:risky t)
(defcustom org-babel-csharp-additional-project-flags nil
"Will be passed in the \"PropertyGroup\" defining the project.
This is taken as-is. It should be a string in XML-format."
:group 'org-babel
:package-version '(Org. "9.8")
:type '(choice string (const nil))
:safe (lambda (x) (or (eq x nil) (stringp x))))
(defun org-babel-csharp--generate-project-file (refs framework)
"Generate the file content to be used in a csproj-file.
REFS is a list of references. Check `org-babel-csharp--format-refs' for
the allowed semantics.
FRAMEWORK is the target framework."
(unless framework
(error "framework cannot be nil"))
(concat "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n "
(when refs
(org-babel-csharp--format-refs refs))
"\n\n <PropertyGroup>"
"\n <OutputType>Exe</OutputType>\n"
(format "\n <TargetFramework>%s</TargetFramework>" framework)
"\n <ImplicitUsings>enable</ImplicitUsings>"
"\n <Nullable>enable</Nullable>"
(when org-babel-csharp-additional-project-flags
(format "\n %s" org-babel-csharp-additional-project-flags))
"\n </PropertyGroup>"
"\n</Project>"))
(defun org-babel-csharp--format-usings (usings)
"Format USINGS into a string suitable for inclusion in a C# source file.
USINGS should be a list of strings, each representing a using directive.
Returns a string with each using directive on a new line."
(mapconcat
(lambda (u)
(unless (stringp u) (error "Usings must be of type string."))
(format "using %s;" u))
usings "\n"))
(defun org-babel-expand-body:csharp (body params)
"Expand a block of C# code in BODY according to PARAMS.
See `org-babel-default-header-args:csharp' for available parameters."
(let* ((main-p (not (string= (cdr (assq :main params)) "no")))
(class (pcase (alist-get :class params)
("no" nil)
(`nil "Program")
(_ (alist-get :class params))))
(namespace "org.babel.autogen")
(usings (alist-get :usings params)))
(with-temp-buffer
(when (alist-get :prologue params)
(insert (alist-get :prologue params) "\n"))
(insert "namespace " namespace ";\n")
(when usings
(insert (format "\n%s\n" (org-babel-csharp--format-usings usings))))
(when class
(insert "\nclass " class "\n{\n"))
(when main-p
(insert "static void Main(string[] args)\n{\n"))
(insert (if (alist-get :var params)
(mapconcat #'identity (org-babel-variable-assignments:csharp params) "\n")
"")
"\n")
(insert body)
(when main-p
(insert "\n}"))
(when class
(insert "\n}"))
(when (alist-get :epilogue params)
(insert "\n" (alist-get :epilogue params)))
(buffer-string))))
(defun org-babel-csharp--format-refs (refs)
"Format REFS into a string suitable for inclusion in a .csproj file.
REFS should be a list of strings or cons cells, each representing a reference.
If an entry is a cons cell, the car denotes the reference name and
the cdr is the version.
Returns a formatted string representing the references, categorized into
project reference, assembly reference, and package reference.
Reference types are distinguished by their file extension.
'.csproj' is interpreted as a project reference,
'.dll' as an assembly reference.
When a version is present, it will be treated as a package reference."
(let ((projectref)
(assemblyref)
(systemref))
(dolist (ref refs)
(let* ((version (if (consp ref)
(cdr ref)
nil))
(ref-string (if (consp ref)
(car ref)
ref))
(full-ref (if version
(file-truename (car ref))
(file-truename ref))))
(cond
((string= "csproj" (file-name-extension full-ref))
(setf projectref
(concat projectref
(format "\n <ProjectReference Include=\"%s\" />"
full-ref))))
((string= "dll" (file-name-extension full-ref))
(setf assemblyref
(concat assemblyref
(format "\n <Reference Include=%S>\n <HintPath>%s</HintPath>\n </Reference>"
(file-name-base full-ref) full-ref))))
(t (setf systemref
(concat systemref
(format "\n <PackageReference Include=%s />"
(if version
(format "%S Version=%S" ref-string version)
(format "%S" ref-string)))))))))
(format "%s\n\n %s\n\n %s"
(if projectref
(format "<ItemGroup>%s\n </ItemGroup>" projectref)
"")
(if assemblyref
(format "<ItemGroup>%s\n </ItemGroup>" assemblyref)
"")
(if systemref
(format "<ItemGroup>%s\n </ItemGroup>" systemref)
""))))
(defun org-babel-execute:csharp (body params)
"Execute a block of Csharp code with org-babel.
This function is called by `org-babel-execute-src-block'"
(let* ((full-body (org-babel-expand-body:csharp body params))
(base-dir (make-temp-name (file-name-concat org-babel-temporary-directory "obcs")))
(project-name (file-name-base base-dir))
(bin-dir (file-name-concat base-dir "bin"))
(framework (or (alist-get :framework params) org-babel-csharp-default-target-framework))
(program-file (file-name-concat base-dir "Program.cs"))
(project-file (file-name-concat base-dir (concat project-name ".csproj")))
(nuget-file (alist-get :nugetconfig params))
(cmdline (alist-get :cmdline params))
(cmdline (if cmdline cmdline ""))
(restore-cmd (funcall org-babel-csharp-generate-restore-command project-file))
(compile-cmd (funcall org-babel-csharp-generate-compile-command
(file-truename project-file)
(file-truename bin-dir)))
(run-cmd (format "%S %S" (file-truename (file-name-concat bin-dir project-name)) cmdline)))
(unless (org-babel-csharp--find-dotnet-version)
(error "Could not find a .NET SDK for compiling."))
(unless (file-exists-p base-dir)
(make-directory base-dir))
(with-temp-file program-file
(insert full-body))
(with-temp-file project-file
(insert
(let ((refs (alist-get :references params)))
(org-babel-csharp--generate-project-file refs framework))))
(when (and nuget-file (file-exists-p (file-truename nuget-file)))
(copy-file nuget-file (file-name-concat base-dir (file-name-nondirectory (file-truename nuget-file)))))
;; nuget restore
(org-babel-eval restore-cmd "")
(let ((compile-result (org-babel-eval compile-cmd "")))
(when (string-match ": error" compile-result)
(org-babel-eval-error-notify 1 compile-result)))
(let ((results (org-babel-eval run-cmd "")))
(when results
(setq results (org-remove-indentation results))
;; results
(org-babel-reassemble-table
(org-babel-result-cond (cdr (assq :result-params params))
results
(let ((tmp-file (org-babel-temp-file "c-")))
(with-temp-file tmp-file (insert results))
(org-babel-import-elisp-from-file tmp-file)))
(org-babel-pick-name
(cdr (assq :colname-names params)) (cdr (assq :colnames params)))
(org-babel-pick-name
(cdr (assq :rowname-names params)) (cdr (assq :rownames params))))))))
(defun org-babel-variable-assignments:csharp (params)
"Return a list of C# variable assignments from header arguments."
(mapcar
#'(lambda (pair) (format "var %s = %S;" (car pair) (cdr pair)))
(org-babel--get-vars params)))
(provide 'ob-csharp)
;;; ob-csharp.el ends here

View file

@ -2,7 +2,7 @@
;; Copyright (C) 2009-2026 Free Software Foundation, Inc.
;; Author: Eric Schulte
;; Authors: Eric Schulte, Jarmo Hurri
;; Keywords: literate programming, reproducible research
;; URL: https://orgmode.org
@ -25,15 +25,52 @@
;; Org-Babel support for evaluating ditaa source code.
;;
;; This differs from most standard languages in that
;; Source code blocks of type ditaa have some special features:
;;
;; 1) there is no such thing as a "session" in ditaa
;; - there is no such thing as a "session"
;;
;; 2) we are generally only going to return results of type "file"
;; - :export results is the default
;;
;; 3) we are adding the "file" and "cmdline" header arguments
;; - only results of type "file" are returned
;;
;; 4) there are no variables (at least for now)
;; - there are no variables
;;
;; - three different variants of "ditaa" exist: a ditaa executable
;; (shell script), ditaa.jar Java archive and DitaaEPS.jar Java
;; archive; the third one is a fork generating eps output, and is
;; also a prerequisite for producing pdf output; ob-ditaa supports
;; all three of these; if ditaa.jar or DitaaEPS.jar is used, paths
;; to file(s) must be set; the following table summarizes which
;; variant is used in which case; column mode refers to
;; `org-ditaa-default-exec-mode'
;;
;; | mode | output | command |
;; |----------------+----------+-----------------------------------------------------|
;; | `ditaa' | png, svg | `org-ditaa-exec' |
;; | `jar' | png, svg | `org-ditaa-java-exec' -jar `org-ditaa-jar-path' |
;; | `ditaa', `jar' | eps | `org-ditaa-java-exec' -jar `org-ditaa-eps-jar-path' |
;; | `ditaa', `jar' | pdf | `org-ditaa-java-exec' -jar `org-ditaa-eps-jar-path' |
;;
;; - standard header argument "cmdline" controls command line parameters passed to ditaa
;; - the following header arguments are added:
;; "java" : additional parameters passed to java if ditaa run via a jar
;;
;;; Requirements:
;; at least one of the following:
;;
;; ditaa (executable)
;; - packaged in some distributions
;; - configurable via `org-ditaa-exec'
;;
;; ditaa.jar | when exec mode is `jar'
;; - `org-ditaa-jar-path' must point to this jar file
;; - see https://github.com/stathissideris/ditaa
;;
;; DitaaEps.jar | when generating eps or pdf output
;; - `org-ditaa-eps-jar-path' must point to this jar file
;; - see https://sourceforge.net/projects/ditaa-addons/files/DitaaEps/
;;; Code:
@ -44,11 +81,39 @@
(require 'org-compat)
(defvar org-babel-default-header-args:ditaa
'((:results . "file")
'((:results . "file graphics")
(:exports . "results")
(:java . "-Dfile.encoding=UTF-8"))
(:file-ext . "png"))
"Default arguments for evaluating a ditaa source block.")
(defcustom org-ditaa-default-exec-mode 'jar
"Method to use for ditaa diagram generation when generating png or svg output.
`jar' means to use java together with a JAR.
The JAR must be set via `org-ditaa-jar-path'.
`ditaa' means to use the ditaa executable.
The executable can be configured via `org-ditaa-exec'."
:group 'org-babel
:package-version '(Org . "9.8")
:type '(choice (const :tag "Use java together with a JAR file." jar)
(const :tag "Use ditaa executable." ditaa))
:safe (lambda (x) (memq x '(jar ditaa))))
(defcustom org-ditaa-exec "ditaa"
"File name of the ditaa executable."
:group 'org-babel
:package-version '(Org . "9.8")
:type 'string
:risky t)
(define-obsolete-variable-alias 'org-babel-ditaa-java-cmd 'org-ditaa-java-exec "9.8")
(defcustom org-ditaa-java-exec "java"
"Java executable to use when evaluating ditaa blocks using a JAR."
:group 'org-babel
:type 'string
:risky t)
(defcustom org-ditaa-jar-path (expand-file-name
"ditaa.jar"
(file-name-as-directory
@ -58,65 +123,82 @@
(expand-file-name
"../contrib"
(file-name-directory (org-find-library-dir "org")))))))
"Path to the ditaa jar executable."
:group 'org-babel
:type 'string)
(defcustom org-babel-ditaa-java-cmd "java"
"Java executable to use when evaluating ditaa blocks."
"Path to the ditaa.jar file."
:group 'org-babel
:type 'string)
(defcustom org-ditaa-eps-jar-path
(expand-file-name "DitaaEps.jar" (file-name-directory org-ditaa-jar-path))
"Path to the DitaaEps.jar executable."
"Path to the DitaaEps.jar executable.
Used when generating eps or pdf output."
:group 'org-babel
:version "24.4"
:package-version '(Org . "8.0")
:type 'string)
(defcustom org-ditaa-jar-option "-jar"
"Option for the ditaa jar file.
Do not leave leading or trailing spaces in this string."
:group 'org-babel
:version "24.1"
:type 'string)
(make-obsolete-variable 'org-ditaa-jar-option
"(ob-ditaa) variable org-ditaa-jar-option is obsolete"
"9.8")
(defun ob-ditaa--ensure-jar-file (file)
"Return FILE if it exists, signal error otherwise."
(if (file-exists-p file)
file
(error "(ob-ditaa) Could not find jar file %s" file)))
(defun org-babel-execute:ditaa (body params)
"Execute BODY of Ditaa code with org-babel according to PARAMS.
"Execute BODY of ditaa code with org-babel according to PARAMS.
This function is called by `org-babel-execute-src-block'."
(let* ((out-file (or (cdr (assq :file params))
(error
"Ditaa code block requires :file header argument")))
(cmdline (cdr (assq :cmdline params)))
(java (cdr (assq :java params)))
(in-file (org-babel-temp-file "ditaa-"))
(eps (cdr (assq :eps params)))
(eps-file (when eps
(org-babel-process-file-name (concat in-file ".eps"))))
(pdf-cmd (when (and (or (string= (file-name-extension out-file) "pdf")
(cdr (assq :pdf params))))
(concat
"epstopdf"
" " eps-file
" -o=" (org-babel-process-file-name out-file))))
(cmd (concat org-babel-ditaa-java-cmd
" " java " " org-ditaa-jar-option " "
(let* ((out-file (org-babel-graphical-output-file params))
(out-file-suffix (file-name-extension out-file))
;; backwards-compatibility of :eps and :pdf header arguments
;; notice that these take precedence over file type (suffix)
(legacy-eps (cdr (assq :eps params)))
(legacy-pdf (cdr (assq :pdf params))))
(when (and legacy-eps legacy-pdf)
(error "(ob-ditaa) Both :eps and :pdf legacy output types specified"))
(let* ((legacy-output-type (or legacy-eps legacy-pdf))
(eps (or legacy-eps (string= out-file-suffix "eps")))
(pdf (or legacy-pdf (string= out-file-suffix "pdf")))
(svg (and (not legacy-output-type) (string= out-file-suffix "svg")))
(png (and (not legacy-output-type)
(or (string= out-file-suffix "png")
(not (or svg pdf eps))))) ;; default output type is png
(ditaa-options (cdr (assq :cmdline params)))
(java-options (cdr (assq :java params)))
(use-eps-jar (or eps pdf))
(exec-form (if (or (equal org-ditaa-default-exec-mode 'jar) use-eps-jar)
(concat org-ditaa-java-exec
(when java-options (concat " " java-options))
" "
;; use obsolete variable instead of default param if defined
(if (boundp 'org-ditaa-jar-option) org-ditaa-jar-option "-jar")
" "
(shell-quote-argument
(expand-file-name
(if eps org-ditaa-eps-jar-path org-ditaa-jar-path)))
" " cmdline
" " (org-babel-process-file-name in-file)
" " (if pdf-cmd
eps-file
(org-babel-process-file-name out-file)))))
(unless (file-exists-p org-ditaa-jar-path)
(error "Could not find ditaa.jar at %s" org-ditaa-jar-path))
(with-temp-file in-file (insert body))
(unless noninteractive (message cmd))
(ob-ditaa--ensure-jar-file (if use-eps-jar org-ditaa-eps-jar-path
org-ditaa-jar-path))))
org-ditaa-exec))
(in-file (org-babel-temp-file "ditaa-"))
(ditaa-out-file (org-babel-process-file-name (if pdf (concat in-file ".eps") out-file)))
(ditaa-coding-system 'utf-8)
(cmd (concat exec-form
(when ditaa-options (concat " " ditaa-options))
(when svg (concat " " "--svg"))
" " "-e" " " (symbol-name ditaa-coding-system)
" " in-file " " ditaa-out-file)))
;; verify that output file type is specified - note that this
;; error should in fact never happen, since default png type is
;; set above if no other supported type is specified
(unless (or eps pdf svg png)
(error (concat "(ob-ditaa) Unknown output file extension: " out-file-suffix)))
(with-temp-file in-file
(set-buffer-file-coding-system ditaa-coding-system)
(insert body))
(shell-command cmd)
(when pdf-cmd (unless noninteractive (message pdf-cmd)) (shell-command pdf-cmd))
nil)) ;; signal that output has already been written to file
(when pdf
(shell-command (concat "epstopdf" " " ditaa-out-file " "
"-o=" (org-babel-process-file-name out-file))))
nil))) ;; signal that output has already been written to file
(defun org-babel-prep-session:ditaa (_session _params)
"Return an error because ditaa does not support sessions."

View file

@ -32,11 +32,6 @@
(require 'ob-core)
(declare-function org-babel--get-vars "ob" (params))
(declare-function org-babel-result-cond "ob" (result-params scalar-form &rest table-forms))
(declare-function org-babel-reassemble-table "ob" (table colnames rownames))
(declare-function org-babel-pick-name "ob" (names selector))
(defconst org-babel-header-args:emacs-lisp '((lexical . :any))
"Emacs-lisp specific header arguments.")
@ -77,7 +72,7 @@ by `org-edit-src-code'.")
(org-babel-expand-body:emacs-lisp body params)))
(result (eval (read (if (or (member "code" result-params)
(member "pp" result-params))
(concat "(pp " body ")")
(concat "(pp-to-string " body ")")
body))
(org-babel-emacs-lisp-lexical lexical))))
(when (and session (not (equal session "none")))
@ -102,7 +97,7 @@ Convert LEXICAL into the form appropriate for `lexical-binding'
and the LEXICAL argument to `eval'."
(if (listp lexical)
lexical
(not (null (member lexical '("yes" "t"))))))
(not (null (member lexical '("yes" "t" t))))))
(defun org-babel-edit-prep:emacs-lisp (info)
"Set `lexical-binding' in Org edit buffer.

View file

@ -33,7 +33,8 @@
(eval-when-compile (require 'subr-x)) ; For `string-empty-p', Emacs < 29
(defvar org-babel-error-buffer-name "*Org-Babel Error Output*")
(defvar org-babel-error-buffer-name "*Org-Babel Error Output*"
"The buffer name Org Babel evaluate error output.")
(declare-function org-babel-temp-file "ob-core" (prefix &optional suffix))
(defun org-babel-eval-error-notify (exit-code stderr)
@ -160,15 +161,8 @@ This buffer is named by `org-babel-error-buffer-name'."
"Return system `shell-file-name', defaulting to /bin/sh.
Unfortunately, `executable-find' does not support file name
handlers. Therefore, we could use it in the local case only."
;; FIXME: Since Emacs 27, `executable-find' accepts optional second
;; argument supporting remote hosts.
(cond ((and (not (file-remote-p default-directory))
(executable-find shell-file-name))
shell-file-name)
((file-executable-p
(concat (file-remote-p default-directory) shell-file-name))
shell-file-name)
("/bin/sh")))
(if (executable-find shell-file-name 'respect-remote)
shell-file-name "/bin/sh"))
(provide 'ob-eval)

View file

@ -45,7 +45,7 @@
drop-locals))
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance element))
(declare-function org-in-archived-heading-p "org" (&optional no-inheritance element))
(declare-function org-src-preserve-indentation-p "org-src" (node))
(declare-function org-src-preserve-indentation-p "org-src" (&optional node))
(defcustom org-export-use-babel t
"Switch controlling code evaluation and header processing during export.
@ -215,7 +215,7 @@ this template."
(string= "yes"
(cdr (assq :noweb params))))
(org-babel-expand-noweb-references
info org-babel-exp-reference-buffer)
info org-babel-exp-reference-buffer :export)
(nth 1 info)))
(goto-char begin)
(let ((replacement
@ -306,10 +306,7 @@ this template."
;; Do not use tabs for block
;; indentation.
(when (fboundp 'indent-tabs-mode)
(indent-tabs-mode -1)
;; FIXME: Emacs 26
;; compatibility.
(setq-local indent-tabs-mode nil))
(indent-tabs-mode -1))
(insert replacement)
(skip-chars-backward " \r\t\n")
(indent-line-to ind)
@ -321,10 +318,7 @@ this template."
;; Do not use tabs for block
;; indentation.
(when (fboundp 'indent-tabs-mode)
(indent-tabs-mode -1)
;; FIXME: Emacs 26
;; compatibility.
(setq-local indent-tabs-mode nil))
(indent-tabs-mode -1))
(insert replacement)
(indent-rigidly
1 (point) ind)
@ -423,7 +417,7 @@ replaced with its value."
(org-babel-noweb-wrap) "" (nth 1 info))
(if (org-babel-noweb-p (nth 2 info) :export)
(org-babel-expand-noweb-references
info org-babel-exp-reference-buffer)
info org-babel-exp-reference-buffer :export)
(nth 1 info))))
(org-fill-template
(if (eq type 'inline)
@ -462,7 +456,7 @@ inhibit insertion of results into the buffer."
(let ((lang (nth 0 info))
(body (if (org-babel-noweb-p (nth 2 info) :eval)
(org-babel-expand-noweb-references
info org-babel-exp-reference-buffer)
info org-babel-exp-reference-buffer :eval)
(nth 1 info)))
(info (copy-sequence info))
(org-babel-current-src-block-location (point-marker)))

View file

@ -4,6 +4,7 @@
;; Authors: Sergey Litvinov
;; Eric Schulte
;; Maintainer: Ken Mankoff <km@kenmankoff.com>
;; Keywords: literate programming, reproducible research, fortran
;; URL: https://orgmode.org

View file

@ -209,7 +209,6 @@ This function is called by `org-babel-execute-src-block'."
(let ((script-file (org-babel-temp-file "gnuplot-script-")))
(with-temp-file script-file
(insert (concat body "\n")))
(unless noninteractive (message "gnuplot \"%s\"" script-file))
(setq output
(shell-command-to-string
(format
@ -217,8 +216,7 @@ This function is called by `org-babel-execute-src-block'."
(org-babel-process-file-name
script-file
(if (member system-type '(cygwin windows-nt ms-dos))
t nil)))))
(unless noninteractive (message "%s" output)))
t nil))))))
(with-temp-buffer
(insert (concat body "\n"))
(gnuplot-mode)
@ -231,7 +229,6 @@ This function is called by `org-babel-execute-src-block'."
"Prepare SESSION according to the header arguments in PARAMS."
(let* ((session (org-babel-gnuplot-initiate-session session))
(var-lines (org-babel-variable-assignments:gnuplot params)))
(unless noninteractive (message "%S" session))
(org-babel-comint-in-buffer session
(dolist (var-line var-lines)
(insert var-line)
@ -320,6 +317,7 @@ Pass PARAMS through to `orgtbl-to-generic' when exporting TABLE."
`( :sep "\t" :fmt org-babel-gnuplot-quote-tsv-field
;; Two setting below are needed to make :fmt work.
:raw t
:with-special-rows nil
:backend ,ob-gnuplot-data)
params)))))
data-file)

View file

@ -52,7 +52,6 @@ parameters may be used, like groovy -v"
(defun org-babel-execute:groovy (body params)
"Execute Groovy BODY according to PARAMS.
This function is called by `org-babel-execute-src-block'."
(unless noninteractive (message "Executing Groovy source code block"))
(let* ((processed-params (org-babel-process-params params))
(session (org-babel-groovy-initiate-session (nth 0 processed-params)))
(result-params (nth 2 processed-params))

View file

@ -57,7 +57,7 @@
(add-to-list 'org-babel-tangle-lang-exts '("haskell" . "hs"))
(defvar org-babel-default-header-args:haskell
'((:padlines . "no")))
'((:padline . "no")))
(defvar org-babel-haskell-lhs2tex-command "lhs2tex")
@ -153,7 +153,7 @@ This function should only be called by `org-babel-execute:haskell'."
(add-hook 'inferior-haskell-hook
(lambda ()
(setq-local
org-babel-comint-prompt-regexp-old comint-prompt-regexp
org-babel-comint-prompt-regexp-fallback comint-prompt-regexp
comint-prompt-regexp
(concat haskell-prompt-regexp "\\|^λ?> "))))
(org-babel-haskell-with-session session params

View file

@ -1,4 +1,4 @@
;;; ob-js.el --- Babel Functions for Javascript -*- lexical-binding: t; -*-
;;; ob-js.el --- Babel Functions for JavaScript -*- lexical-binding: t; -*-
;; Copyright (C) 2010-2026 Free Software Foundation, Inc.
@ -73,10 +73,10 @@
;; parenthesis are not shadowed if the last line of the body is a
;; line comment.
"require('process').stdout.write(require('util').inspect(function(){%s\n}()));"
"Javascript code to print value of body.")
"JavaScript code to print value of body.")
(defun org-babel-execute:js (body params)
"Execute Javascript BODY according to PARAMS.
"Execute JavaScript BODY according to PARAMS.
This function is called by `org-babel-execute-src-block'."
(let* ((org-babel-js-cmd (or (cdr (assq :cmd params)) org-babel-js-cmd))
(session (cdr (assq :session params)))
@ -158,7 +158,7 @@ specifying a variable of the same value."
session))
(defun org-babel-variable-assignments:js (params)
"Return list of Javascript statements assigning the block's variables.
"Return list of JavaScript statements assigning the block's variables.
The variables are defined in PARAMS."
(mapcar
(lambda (pair) (format "var %s=%s;"
@ -183,6 +183,7 @@ Return the initialized session."
(run-skewer)
(skewer-repl)
session-buffer)))
;; SIC, JavaScript miscapitalized in `js-comint.el'.
((string= "*Javascript REPL*" session)
(org-require-package 'js-comint)
(let ((session-buffer "*Javascript REPL*"))

View file

@ -43,8 +43,15 @@
(declare-function inferior-ess-send-input "ext:ess-inf" ())
(declare-function ess-make-buffer-current "ext:ess-inf" ())
(declare-function ess-eval-buffer "ext:ess-inf" (vis))
(declare-function ess-wait-for-process "ext:ess-inf"
(&optional proc sec-prompt wait force-redisplay))
(defvar ess-current-process-name) ; ess-custom.el
(defvar ess-local-process-name) ; ess-custom.el
(defvar ess-eval-visibly) ; ess-custom.el
(defvar ess-local-customize-alist); ess-custom.el
(defvar ess-gen-proc-buffer-name-function) ; ess-custom.el
(defvar ess-ask-for-ess-directory) ; ess-custom.el
(defvar ess-directory-function) ; ess-custom.el
(defvar ess-directory) ; ess-custom.el
(defvar org-babel-header-args:julia
'((width . :any)
@ -66,11 +73,6 @@
:group 'org-babel
:type 'string)
(defvar ess-current-process-name) ; dynamically scoped
(defvar ess-local-process-name) ; dynamically scoped
(defvar ess-eval-visibly-p) ; dynamically scoped
(defvar ess-local-customize-alist); dynamically scoped
(defvar ess-gen-proc-buffer-name-function) ; defined in ess-inf.el
(defun org-babel-julia-associate-session (session)
"Associate R code buffer with an R session.
Make SESSION be the inferior ESS process associated with the
@ -167,7 +169,10 @@ This function is called by `org-babel-execute-src-block'."
(min (if lengths (apply #'min lengths) 0)))
;; Ensure VALUE has an orgtbl structure (depth of at least 2).
(unless (listp (car value)) (setq value (list value)))
(let ((file (orgtbl-to-csv value '(:fmt org-babel-julia-quote-csv-field))))
(let ((file (orgtbl-to-csv
value
'( :fmt org-babel-julia-quote-csv-field
:with-special-rows nil))))
(if (= max min)
(format "%s = begin
using CSV
@ -181,13 +186,17 @@ end"
(format "%s = %s" name (org-babel-julia-quote-csv-field value))))
(defvar ess-ask-for-ess-directory) ; dynamically scoped
(defun org-babel-julia-initiate-session (session params)
(defun org-babel-julia-initiate-session (session _params)
"If there is not a current julia process then create one."
(unless (string= session "none")
(let* ((session (or session "*Julia*"))
(ess-ask-for-ess-directory
(and (bound-and-true-p ess-ask-for-ess-directory)
(not (cdr (assq :dir params)))))
;; Force using `default-directory', as we promise in the
;; manual. The caller should have taken care about setting
;; it according to :dir if necessary.
;; https://ess.r-project.org/Manual/ess.html#Changing-the-startup-actions
(ess-ask-for-ess-directory nil)
(ess-directory-function nil)
(ess-directory nil)
;; Make ESS name the process buffer as SESSION.
(ess-gen-proc-buffer-name-function
(lambda (_) session)))
@ -293,7 +302,7 @@ last statement in BODY, as elisp."
(let ((ess-local-customize-alist t)
(ess-local-process-name
(process-name (get-buffer-process session)))
(ess-eval-visibly-p nil))
(ess-eval-visibly nil))
(ess-eval-buffer nil)))
(let ((tmp-file (org-babel-temp-file "julia-")))
(org-babel-comint-eval-invisibly-and-wait-for-file

View file

@ -41,9 +41,6 @@
(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))
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-next-visible-heading "org" (arg))
(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("latex" . "tex"))
@ -80,13 +77,14 @@
(defcustom org-babel-latex-preamble
(lambda (_)
"\\documentclass[preview]{standalone}
\\def\\pgfsysdriver{pgfsys-tex4ht.def}
")
"Closure which evaluates at runtime to the LaTeX preamble.
It takes 1 argument which is the parameters of the source block."
:group 'org-babel
:type 'function)
:package-version '(Org . "9.8")
:type 'function
:risky t)
(defcustom org-babel-latex-begin-env
(lambda (_)
@ -130,16 +128,29 @@ exporting the literal LaTeX source."
:type '(repeat (string)))
(defcustom org-babel-latex-process-alist
`(,(cons 'png (alist-get 'dvipng org-preview-latex-process-alist)))
`((png :programs ("latex" "dvipng") :description "dvi > png"
:message
"you need to install the programs: latex and dvipng."
:image-input-type "dvi" :image-output-type "png"
:image-size-adjust (1.0 . 1.0) :latex-compiler
,(if (and (executable-find "latexmk") (executable-find "perl"))
'("latexmk -f -pdf -latex -interaction=nonstopmode -output-directory=%o %f")
'("latex -interaction nonstopmode -output-directory %o %f"
"latex -interaction nonstopmode -output-directory %o %f"
"latex -interaction nonstopmode -output-directory %o %f"))
:image-converter ("dvipng -D %D -T tight -o %O %f")
:transparent-image-converter
("dvipng -D %D -T tight -bg Transparent -o %O %f")))
"Definitions of external processes for LaTeX result generation.
See `org-preview-latex-process-alist' for more details.
The following process symbols are recognized:
- `png' :: Process used to produce .png output."
:group 'org-babel
:package-version '(Org . "9.7")
:package-version '(Org . "9.8")
:type '(alist :tag "LaTeX to image backends"
:value-type (plist)))
:value-type (plist))
:risky t)
(defun org-babel-expand-body:latex (body params)
"Expand BODY according to PARAMS, return the expanded body."
@ -249,6 +260,8 @@ This function is called by `org-babel-execute-src-block'."
((or (string= "pdf" extension) imagemagick)
(with-temp-file tex-file
(require 'ox-latex)
(defvar org-latex-compiler)
(declare-function org-latex--remove-packages "ox-latex" (pkg-alist info))
(insert
(org-latex-guess-inputenc
(org-splice-latex-header
@ -259,8 +272,12 @@ This function is called by `org-babel-execute-src-block'."
(lambda (el)
(unless (and (listp el) (string= "hyperref" (cadr el)))
el))
org-latex-default-packages-alist))
(org-latex--remove-packages
org-latex-default-packages-alist
(list :latex-compiler org-latex-compiler))))
(org-latex--remove-packages
org-latex-packages-alist
(list :latex-compiler org-latex-compiler))
nil))
(if fit "\n\\usepackage[active, tightpage]{preview}\n" "")
(if border (format "\\setlength{\\PreviewBorder}{%s}" border) "")

View file

@ -61,13 +61,14 @@ Valid values include `slime-eval' and `sly-eval'."
:type 'symbol)
(defcustom org-babel-lisp-dir-fmt
"(let ((*default-pathname-defaults* #P%S\n)) %%s\n)"
"(cl:let ((cl:*default-pathname-defaults* #P%S\n)) %%s\n)"
"Format string used to wrap code bodies to set the current directory.
For example a value of \"(progn ;; %s\\n %%s)\" would ignore the
current directory string."
:group 'org-babel
:version "24.1"
:type 'string)
:package-version '(Org . "9.8")
:type 'string
:risky t)
(defun org-babel-expand-body:lisp (body params)
"Expand BODY according to PARAMS, return the expanded body."
@ -77,10 +78,10 @@ current directory string."
(prologue (cdr (assq :prologue params)))
(epilogue (cdr (assq :epilogue params)))
(body (if (null vars) (org-trim body)
(concat "(let ("
(concat "(cl:let ("
(mapconcat
(lambda (var)
(format "(%S (quote %S))" (car var) (cdr var)))
(format "(%S (cl:quote %S))" (car var) (cdr var)))
vars "\n ")
")\n"
(and prologue (concat prologue "\n"))
@ -89,7 +90,7 @@ current directory string."
")"))))
(if (or (member "code" result-params)
(member "pp" result-params))
(format "(pprint %s)" body)
(format "(cl:pprint %s)" body)
body)))
(defun org-babel-execute:lisp (body params)
@ -115,7 +116,7 @@ a property list containing the parameters of the block."
default-directory)))
(format
(if dir (format org-babel-lisp-dir-fmt dir)
"(progn %s\n)")
"(cl:progn %s\n)")
(buffer-substring-no-properties
(point-min) (point-max)))))
(cdr (assq :package params)))))))

View file

@ -59,10 +59,6 @@ should not be inherited from a source block.")
(let* ((info (org-babel-get-src-block-info 'no-eval))
(source-name (nth 4 info)))
(when source-name
(setf (nth 1 info)
(if (org-babel-noweb-p (nth 2 info) :eval)
(org-babel-expand-noweb-references info)
(nth 1 info)))
(let ((source (intern source-name)))
(setq org-babel-library-of-babel
(cons (cons source info)
@ -86,11 +82,13 @@ if so then run the appropriate source block from the Library."
(org-babel-execute-src-block nil info nil (org-element-type datum))
t)))
(defun org-babel-lob--src-info (ref)
(defun org-babel-lob--src-info (ref &optional eval)
"Return internal representation for Babel data referenced as REF.
REF is a string. This function looks into the current document
for a Babel call or source block. If none is found, it looks
after REF in the Library of Babel."
after REF in the Library of Babel.
When EVAL is non-nil, evaluate src block parameters.
"
(let ((name ref)
(file nil))
;; Extract the remote file, if specified in the reference.
@ -112,7 +110,7 @@ after REF in the Library of Babel."
(when (equal name (org-element-property :name element))
(throw :found
(pcase (org-element-type element)
(`src-block (org-babel-get-src-block-info t element))
(`src-block (org-babel-get-src-block-info (not eval) element))
(`babel-call (org-babel-lob-get-info element))
;; Non-executable data found. Since names
;; are supposed to be unique throughout
@ -131,6 +129,12 @@ remote variable references; a process which could likely result
in the execution of other code blocks, and do not evaluate Lisp
values in parameters.
The evaluation happens in the context of DATUM (babel call or inline
babel call) for its local arguments, while evaluation of the references
code block happens in the context (with point at) of that block.
`org-babel-current-src-block-location' is bound to DATUM position
during evaluation.
Return nil when not on an appropriate location. Otherwise return
a list compatible with `org-babel-get-src-block-info', which
see."
@ -138,12 +142,14 @@ see."
(type (org-element-type context))
(reference (org-element-property :call context)))
(when (memq type '(babel-call inline-babel-call))
(pcase (org-babel-lob--src-info reference)
(`(,language ,body ,header ,_ ,_ ,_ ,coderef)
(let ((begin (org-element-property (if (eq type 'inline-babel-call)
(let* ((begin (org-element-property (if (eq type 'inline-babel-call)
:begin
:post-affiliated)
context)))
context))
;; Signal downstream to lisp parameter values about current location.
(org-babel-current-src-block-location begin))
(pcase (org-babel-lob--src-info reference (if no-eval nil 'eval))
(`(,language ,body ,header ,_ ,_ ,_ ,coderef)
(list language
body
(apply #'org-babel-merge-params
@ -164,8 +170,8 @@ see."
nil
(org-element-property :name context)
begin
coderef)))
(_ nil)))))
coderef))
(_ nil))))))
(provide 'ob-lob)

View file

@ -23,18 +23,7 @@
;;; Commentary:
;; Org-Babel support for evaluating Lua source code.
;; Requirements:
;; for session support, lua-mode is needed.
;;
;; lua-mode is not part of GNU Emacs/orgmode, but can be obtained
;; from NonGNU ELPA (see `M-x list-packages').
;;
;; The source repository is here:
;; https://github.com/immerrr/lua-mode
;; However, sessions are not yet working.
;; Org Babel support for evaluating Lua source code.
;;; Code:
@ -45,10 +34,6 @@
(require 'org-macs)
(require 'cl-lib)
(declare-function lua-shell "ext:lua-mode" (&optional argprompt))
(declare-function lua-toggle-shells "ext:lua-mode" (arg))
(declare-function run-lua "ext:lua" (cmd &optional dedicated show))
(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("lua" . "lua"))
@ -62,7 +47,7 @@
:type 'string)
(defcustom org-babel-lua-mode 'lua-mode
"Preferred lua mode for use in running lua interactively.
"Preferred Lua mode for use in running Lua interactively.
This will typically be `lua-mode'."
:group 'org-babel
:version "26.1"
@ -70,7 +55,7 @@ This will typically be `lua-mode'."
:type 'symbol)
(defcustom org-babel-lua-hline-to "None"
"Replace hlines in incoming tables with this when translating to Lua."
"Replace `hlines' in incoming tables with this when translating to Lua."
:group 'org-babel
:version "26.1"
:package-version '(Org . "8.3")
@ -92,11 +77,10 @@ This will typically be `lua-mode'."
(defun org-babel-execute:lua (body params)
"Execute Lua BODY according to PARAMS.
This function is called by `org-babel-execute-src-block'."
(let* ((session (org-babel-lua-initiate-session
(cdr (assq :session params))))
(let* ((session (cdr (assq :session params)))
(result-params (cdr (assq :result-params params)))
(result-type (cdr (assq :result-type params)))
(return-val (when (and (eq result-type 'value) (not session))
(return-val (when (eq result-type 'value)
(cdr (assq :return params))))
(preamble (cdr (assq :preamble params)))
(full-body
@ -104,7 +88,9 @@ This function is called by `org-babel-execute-src-block'."
(concat body (if return-val (format "\nreturn %s" return-val) ""))
params (org-babel-variable-assignments:lua params)))
(result (org-babel-lua-evaluate
session full-body result-type result-params preamble)))
full-body result-type result-params preamble)))
(when (and session (not (equal session "none")))
(user-error "Sessions not supported for Lua"))
(org-babel-reassemble-table
result
(org-babel-pick-name (cdr (assq :colname-names params))
@ -112,28 +98,6 @@ This function is called by `org-babel-execute-src-block'."
(org-babel-pick-name (cdr (assq :rowname-names params))
(cdr (assq :rownames params))))))
(defun org-babel-prep-session:lua (session params)
"Prepare SESSION according to the header arguments in PARAMS.
VARS contains resolved variable references."
(let* ((session (org-babel-lua-initiate-session session))
(var-lines
(org-babel-variable-assignments:lua params)))
(org-babel-comint-in-buffer session
(mapc (lambda (var)
(end-of-line 1) (insert var) (comint-send-input)
(org-babel-comint-wait-for-output session))
var-lines))
session))
(defun org-babel-load-session:lua (session body params)
"Load BODY into SESSION."
(save-window-excursion
(let ((buffer (org-babel-prep-session:lua session params)))
(with-current-buffer buffer
(goto-char (process-mark (get-buffer-process (current-buffer))))
(insert (org-babel-chomp body)))
buffer)))
;; helper functions
(defun org-babel-variable-assignments:lua (params)
@ -168,9 +132,9 @@ specifying a variable of the same value."
(if (stringp var) (substring-no-properties var) var)))))
(defun org-babel-lua-table-or-string (results)
"Convert RESULTS into an appropriate elisp value.
"Convert RESULTS into an appropriate Emacs Lisp value.
If the results look like a list or tuple, then convert them into an
Emacs-lisp table, otherwise return the results as a string."
Emacs Lisp table, otherwise return the results as a string."
(let ((res (org-babel-script-escape results)))
(if (listp res)
(mapcar (lambda (el) (if (eq el 'None)
@ -180,76 +144,6 @@ Emacs-lisp table, otherwise return the results as a string."
(defvar org-babel-lua-buffers '((:default . "*Lua*")))
(defun org-babel-lua-session-buffer (session)
"Return the buffer associated with SESSION."
(cdr (assoc session org-babel-lua-buffers)))
(defun org-babel-lua-with-earmuffs (session)
"Return buffer name for SESSION, as *SESSION*."
(let ((name (if (stringp session) session (format "%s" session))))
(if (and (string= "*" (substring name 0 1))
(string= "*" (substring name (- (length name) 1))))
name
(format "*%s*" name))))
(defun org-babel-session-buffer:lua (session &optional _)
"Return session buffer name for SESSION."
(or (org-babel-lua-session-buffer session)
(org-babel-lua-with-earmuffs session)))
(defun org-babel-lua-without-earmuffs (session)
"Remove stars around *SESSION*, leaving SESSION."
(let ((name (if (stringp session) session (format "%s" session))))
(if (and (string= "*" (substring name 0 1))
(string= "*" (substring name (- (length name) 1))))
(substring name 1 (- (length name) 1))
name)))
(defvar lua-default-interpreter)
(defvar lua-which-bufname)
(defvar lua-shell-buffer-name)
(defun org-babel-lua-initiate-session-by-key (&optional session)
"Initiate a Lua session.
If there is not a current inferior-process-buffer in SESSION
then create. Return the initialized session."
;; (require org-babel-lua-mode)
(save-window-excursion
(let* ((session (if session (intern session) :default))
(lua-buffer (org-babel-lua-session-buffer session))
;; (cmd (if (member system-type '(cygwin windows-nt ms-dos))
;; (concat org-babel-lua-command " -i")
;; org-babel-lua-command))
)
(cond
((and (eq 'lua-mode org-babel-lua-mode)
(fboundp 'lua-start-process)) ; lua-mode.el
;; Make sure that lua-which-bufname is initialized, as otherwise
;; it will be overwritten the first time a Lua buffer is
;; created.
;;(lua-toggle-shells lua-default-interpreter)
;; `lua-shell' creates a buffer whose name is the value of
;; `lua-which-bufname' with '*'s at the beginning and end
(let* ((bufname (if (and lua-buffer (buffer-live-p lua-buffer))
(replace-regexp-in-string ;; zap surrounding *
"^\\*\\([^*]+\\)\\*$" "\\1" (buffer-name lua-buffer))
(concat "Lua-" (symbol-name session))))
(lua-which-bufname bufname))
(lua-start-process)
(setq lua-buffer (org-babel-lua-with-earmuffs bufname))))
(t
(error "No function available for running an inferior Lua")))
(setq org-babel-lua-buffers
(cons (cons session lua-buffer)
(assq-delete-all session org-babel-lua-buffers)))
session)))
(defun org-babel-lua-initiate-session (&optional session _params)
"Create a session named SESSION according to PARAMS."
(unless (string= session "none")
(error "Sessions currently not supported, work in progress")
(org-babel-lua-session-buffer
(org-babel-lua-initiate-session-by-key session))))
(defvar org-babel-lua-eoe-indicator "--eoe"
"A string to indicate that evaluation has completed.")
@ -263,38 +157,56 @@ function dump(it, indent)
if indent == nil then
indent = ''
end
if type(it) == 'table' and %s then
local count = 0
for _ in pairs(it) do
count = count + 1
end
local result = ''
if #indent ~= 0 then
result = result .. '\\n'
end
for key, value in pairs(it) do
local keys = {}
for key in pairs(it) do
table.insert(keys, key)
end
table.sort(keys)
for index, key in pairs(keys) do
local value = it[key]
result = result
.. indent
.. dump(key)
.. ' = '
.. dump(value, indent .. ' ')
count = count - 1
if count ~= 0 then
if index ~= #keys then
result = result .. '\\n'
end
end
return result
else
return tostring(it)
return string.gsub(tostring(it), '\"', '\\\"')
end
end
function combine(...)
local result = {}
for index = 1, select('#', ...) do
result[index] = dump(select(index, ...))
end
return table.concat(result, '%s')
if #result == 1 then
local value = result[1]
if string.find(value, '[%%(%%[{]') == 1 then
return '\"' .. value .. '\"'
else
return value
end
end
return '\"' .. table.concat(result, '%s') .. '\"'
end
output = io.open('%s', 'w')
@ -302,19 +214,6 @@ output:write(combine(main()))
output:close()")
(defun org-babel-lua-evaluate
(session body &optional result-type result-params preamble)
"Evaluate BODY in SESSION as Lua code.
RESULT-TYPE and RESULT-PARAMS are passed to
`org-babel-lua-evaluate-session' or
`org-babel-lua-evaluate-external-process'.
PREAMBLE is passed to `org-babel-lua-evaluate-external-process'."
(if session
(org-babel-lua-evaluate-session
session body result-type result-params)
(org-babel-lua-evaluate-external-process
body result-type result-params preamble)))
(defun org-babel-lua-evaluate-external-process
(body &optional result-type result-params preamble)
"Evaluate BODY in external Lua process.
If RESULT-TYPE equals `output' then return standard output as a
@ -350,88 +249,6 @@ PREAMBLE string is appended to BODY."
raw
(org-babel-lua-table-or-string (org-trim raw)))))
(defun org-babel-lua-evaluate-session
(session body &optional result-type result-params)
"Pass BODY to the Lua process in SESSION.
If RESULT-TYPE equals `output' then return standard output as a
string. If RESULT-TYPE equals `value' then return the value of the
last statement in BODY, as Emacs Lisp."
(let* ((send-wait (lambda () (comint-send-input nil t) (sleep-for 0.005)))
(dump-last-value
(lambda
(tmp-file pp)
(mapc
(lambda (statement) (insert statement) (funcall send-wait))
(if pp
(list
"-- table to string
function t2s(t, indent)
if indent == nil then
indent = \"\"
end
if type(t) == \"table\" then
ts = \"\"
for k,v in pairs(t) do
if type(v) == \"table\" then
ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \\n\" ..
t2s(v, indent .. \" \")
else
ts = ts .. indent .. t2s(k,indent .. \" \") .. \" = \" ..
t2s(v, indent .. \" \") .. \"\\n\"
end
end
return ts
else
return tostring(t)
end
end
"
(concat "fd:write(_))
fd:close()"
(org-babel-process-file-name tmp-file 'noquote)))
(list (format "fd=io.open(\"%s\", \"w\")
fd:write( _ )
fd:close()"
(org-babel-process-file-name tmp-file
'noquote)))))))
(input-body (lambda (body)
(mapc (lambda (line) (insert line) (funcall send-wait))
(split-string body "[\r\n]"))
(funcall send-wait)))
(results
(pcase result-type
(`output
(mapconcat
#'org-trim
(butlast
(org-babel-comint-with-output
(session org-babel-lua-eoe-indicator t body)
(funcall input-body body)
(funcall send-wait) (funcall send-wait)
(insert org-babel-lua-eoe-indicator)
(funcall send-wait))
2) "\n"))
(`value
(let ((tmp-file (org-babel-temp-file "lua-")))
(org-babel-comint-with-output
(session org-babel-lua-eoe-indicator nil body)
(let ((comint-process-echoes nil))
(funcall input-body body)
(funcall dump-last-value tmp-file
(member "pp" result-params))
(funcall send-wait) (funcall send-wait)
(insert org-babel-lua-eoe-indicator)
(funcall send-wait)))
(org-babel-eval-read-file tmp-file))))))
(unless (string= (substring org-babel-lua-eoe-indicator 1 -1) results)
(org-babel-result-cond result-params
results
(org-babel-lua-table-or-string results)))))
(defun org-babel-lua-read-string (string)
"Strip single quotes from around Lua STRING."
(org-unbracket-string "'" "'" string))
(provide 'ob-lua)
;;; ob-lua.el ends here

View file

@ -110,11 +110,13 @@ output. See `org-babel-maxima-expand'.")
"\n")))
(defvar org-babel-maxima--output-filter-regexps
'("batch" ;; remove the `batch' or `batchload' line
'("(linenum:0,$" ;; remove fragment from command-line (see `org-babel-execute:maxima')
"batch" ;; remove the `batch' or `batchload' line
"^rat: replaced .*$" ;; remove notices from `rat'
"^;;; Loading #P" ;; remove notices from the lisp implementation
"^read and interpret" ;; remove notice from `batch'
"^(%\\([i]-?[0-9]+\\))[ ]$" ;; remove empty input lines from `batch'-ing
"^Loading .+maxima-init\\.mac" ;; remove message about loading init file
)
"Regexps to remove extraneous lines from Maxima's output.
See `org-babel-maxima--output-filter'.")
@ -131,7 +133,6 @@ Return nil if LINE is zero-length or it matches a regexp in
(defun org-babel-execute:maxima (body params)
"Execute Maxima BODY according to PARAMS.
This function is called by `org-babel-execute-src-block'."
(unless noninteractive (message "Executing Maxima source code block"))
(let ((result-params (split-string (or (cdr (assq :results params)) "")))
(result
(let* ((cmdline (or (cdr (assq :cmdline params)) ""))
@ -151,7 +152,6 @@ This function is called by `org-babel-execute-src-block'."
(format "(linenum:0, %s(%S))$" batch/load in-file))
cmdline)))
(with-temp-file in-file (insert (org-babel-maxima-expand body params)))
(unless noninteractive (message cmd))
;; " | grep -v batch | grep -v 'replaced' | sed '/^$/d' "
(let ((raw (org-babel-eval cmd "")))
(mapconcat

View file

@ -44,7 +44,6 @@
(require 'org-macs)
(declare-function tuareg-run-caml "ext:tuareg" ())
(declare-function tuareg-run-ocaml "ext:tuareg" ())
(declare-function tuareg-interactive-send-input "ext:tuareg" ())
(defvar org-babel-tangle-lang-exts)

View file

@ -36,6 +36,8 @@
(require 'org-macs)
(require 'python)
(require 'subr-x) ; For `string-trim-right', Emacs < 28
(defvar org-babel-tangle-lang-exts)
(add-to-list 'org-babel-tangle-lang-exts '("python" . "py"))
@ -158,14 +160,14 @@ file to save the graphics to.")
(defconst org-babel-python--def-format-value "\
def __org_babel_python_format_value(result, result_file, result_params):
with open(result_file, 'w') as f:
with open(result_file, 'w') as __org_babel_python_tmpfile:
if 'graphics' in result_params:
result.savefig(result_file)
elif 'pp' in result_params:
import pprint
f.write(pprint.pformat(result))
__org_babel_python_tmpfile.write(pprint.pformat(result))
elif 'list' in result_params and isinstance(result, dict):
f.write(str(['{} :: {}'.format(k, v) for k, v in result.items()]))
__org_babel_python_tmpfile.write(str(['{} :: {}'.format(k, v) for k, v in result.items()]))
else:
if not set(result_params).intersection(\
['scalar', 'verbatim', 'raw']):
@ -198,7 +200,7 @@ def __org_babel_python_format_value(result, result_file, result_params):
result = result.tolist()
else:
result = repr(result)
f.write(str(result))"
__org_babel_python_tmpfile.write(str(result))"
"Python function to format value result and save it to file.")
(defun org-babel-variable-assignments:python (params)
@ -451,13 +453,13 @@ __org_babel_python_format_value(main(), '%s', %s)")
(defun org-babel-python-send-string (session body)
"Pass BODY to the Python process in SESSION.
Return output."
(with-current-buffer session
(let* ((string-buffer "")
(comint-output-filter-functions
(cons (lambda (text) (setq string-buffer
(concat string-buffer text)))
comint-output-filter-functions))
(body (format "\
(org-babel-chomp
(string-trim-right
(org-babel-comint-with-output
((org-babel-session-buffer:python session)
org-babel-python-eoe-indicator
nil nil 'disable-prompt-filtering)
(python-shell-send-string (format "\
try:
%s
except:
@ -466,16 +468,7 @@ finally:
print('%s')"
(org-babel-python--shift-right body 4)
org-babel-python-eoe-indicator)))
(let ((python-shell-buffer-name
(org-babel-python-without-earmuffs session)))
(python-shell-send-string body))
;; same as `python-shell-comint-end-of-output-p' in emacs-25.1+
(while (not (and (python-shell-comint-end-of-output-p string-buffer)
(string-match
org-babel-python-eoe-indicator
string-buffer)))
(accept-process-output (get-buffer-process (current-buffer))))
(org-babel-chomp (substring string-buffer 0 (match-beginning 0))))))
(rx (literal org-babel-python-eoe-indicator) (zero-or-more anychar)))))
(defun org-babel-python-evaluate-session
(session body &optional result-type result-params graphics-file)
@ -495,8 +488,8 @@ non-nil, then save graphical results to that file instead."
(pcase result-type
(`output
(let ((body (format "\
with open('%s') as f:
exec(compile(f.read(), f.name, 'exec'))"
with open('%s') as __org_babel_python_tmpfile:
exec(compile(__org_babel_python_tmpfile.read(), __org_babel_python_tmpfile.name, 'exec'))"
(org-babel-process-file-name
tmp-src-file 'noquote))))
(org-babel-python-send-string session body)))
@ -538,7 +531,8 @@ by `org-babel-comint-async-filter'."
(org-babel-comint-async-register
session (current-buffer)
"ob_comint_async_python_\\(start\\|end\\|file\\)_\\(.+\\)"
'org-babel-chomp 'org-babel-python-async-value-callback)
'org-babel-chomp 'org-babel-python-async-value-callback
'disable-prompt-filtering)
(pcase result-type
(`output
(let ((uuid (org-id-uuid)))

View file

@ -66,7 +66,7 @@
(declare-function org-find-property "org" (property &optional value))
(declare-function org-id-find-id-file "org-id" (id))
(declare-function org-id-find-id-in-file "org-id" (id file &optional markerp))
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance))
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance element))
(declare-function org-narrow-to-subtree "org" (&optional element))
(declare-function org-fold-show-context "org-fold" (&optional key))
@ -236,7 +236,7 @@ to \"0:-1\"."
(if (or (= 0 (length portion)) (string-match ind-re portion))
(mapcar
(lambda (n) (nth n lis))
(apply 'org-number-sequence
(apply #'number-sequence
(if (and (> (length portion) 0) (match-string 2 portion))
(list
(funcall wrap (string-to-number (match-string 2 portion)))

View file

@ -192,7 +192,7 @@ Session settings (`:ruby' header arg value) are taken from PARAMS."
(when new-session?
(with-current-buffer session-buffer
(setq-local
org-babel-comint-prompt-regexp-old comint-prompt-regexp
org-babel-comint-prompt-regexp-fallback comint-prompt-regexp
comint-prompt-regexp (concat "^" org-babel-ruby-prompt))
(insert org-babel-ruby-define-prompt ";")
(insert "_org_prompt_mode=conf.prompt_mode;conf.prompt_mode=:CUSTOM;")
@ -213,20 +213,18 @@ Session settings (`:ruby' header arg value) are taken from PARAMS."
(defvar org-babel-ruby-wrapper-method
"
def main()
results = (lambda do
%s
end
results = main()
end).call
File.open('%s', 'w'){ |f| f.write((results.class == String) ? results : results.inspect) }
")
(defvar org-babel-ruby-pp-wrapper-method
"
require 'pp'
def main()
results = (lambda do
%s
end
results = main()
end).call
File.open('%s', 'w') do |f|
$stdout = f
pp results

View file

@ -66,11 +66,10 @@
(declare-function geiser-eval--retort-output "ext:geiser-eval" (ret))
(declare-function geiser-eval--retort-result-str "ext:geiser-eval" (ret prefix))
(declare-function geiser-eval--retort-error "ext:geiser-eval" (ret))
(declare-function geiser-eval--retort-error-msg "ext:geiser-eval" (err))
(declare-function geiser-eval--error-msg "ext:geiser-eval" (err))
(defcustom org-babel-scheme-null-to 'hline
"Replace `null' and empty lists in scheme tables with this before returning."
"Replacement for `()' and `null' in tabulated Scheme results."
:group 'org-babel
:version "26.1"
:package-version '(Org . "9.1")
@ -86,7 +85,7 @@
"Expand :var header arguments given as VARS."
(mapconcat
(lambda (var)
(format "(define %S %S)" (car var) (cdr var)))
(format "(define %S '%S)" (car var) (cdr var)))
vars
"\n"))
@ -131,12 +130,17 @@ If the variables HOST and PORT are set, connect to the running Scheme REPL."
(let ((buffer (org-babel-scheme-get-session-buffer name)))
(or buffer
(progn
(let ((geiser-repl-window-allow-split nil)
(geiser-repl-use-other-window nil)
(switch-to-buffer-obey-display-actions t)
(display-buffer-overriding-action '(display-buffer-no-window
(allow-no-window t))))
(if (fboundp 'geiser)
(if (and host port)
(geiser-connect impl host port)
(geiser impl))
;; Obsolete since Geiser 0.26.
(run-geiser impl))
(run-geiser impl)))
(when name
(rename-buffer name t)
(org-babel-scheme-set-session-buffer name (current-buffer)))
@ -185,8 +189,6 @@ is true; otherwise returns the last value."
(let ((beg (point)))
(insert code)
(geiser-mode)
(let ((geiser-repl-window-allow-split nil)
(geiser-repl-use-other-window nil))
(let ((repl-buffer (save-current-buffer
(org-babel-scheme-get-repl impl repl host port))))
(when (not (eq impl (org-babel-scheme-get-buffer-impl
@ -226,15 +228,15 @@ is true; otherwise returns the last value."
(let ((msg (geiser-eval--error-msg err)))
(org-babel-eval-error-notify
nil
(concat (if (listp msg) (car msg) msg) "\n")))))))))))
(concat (if (listp msg) (car msg) msg) "\n"))))))))))
result))
(defun org-babel-scheme--table-or-string (results)
"Convert RESULTS into an appropriate elisp value.
If the results look like a list or tuple, then convert them into an
Emacs-lisp table, otherwise return the results as a string."
"Convert RESULTS into an appropriate Elisp value.
If the results are in the form of a proper list, then convert them into
an Emacs Lisp table, otherwise return the results as a string."
(let ((res (and results (org-babel-script-escape results))))
(cond ((listp res)
(cond ((proper-list-p res)
(mapcar (lambda (el)
(if (or (null el) (eq el 'null))
org-babel-scheme-null-to
@ -242,6 +244,14 @@ Emacs-lisp table, otherwise return the results as a string."
res))
(t res))))
(defun org-babel-scheme--get-impl (&optional params)
"Return Scheme implementation to be used according to PARAMS."
(or (when (cdr (assq :scheme params))
(intern (cdr (assq :scheme params))))
geiser-scheme-implementation
geiser-default-implementation
(car geiser-active-implementations)))
(defun org-babel-execute:scheme (body params)
"Execute a block of Scheme code with org-babel.
This function is called by `org-babel-execute-src-block'."
@ -251,11 +261,7 @@ This function is called by `org-babel-execute-src-block'."
(buffer-name source-buffer))))
(save-excursion
(let* ((result-type (cdr (assq :result-type params)))
(impl (or (when (cdr (assq :scheme params))
(intern (cdr (assq :scheme params))))
geiser-scheme-implementation
geiser-default-implementation
(car geiser-active-implementations)))
(impl (org-babel-scheme--get-impl params))
(host (cdr (assq :host params)))
(port (cdr (assq :port params)))
(session (org-babel-scheme-make-session-name
@ -281,6 +287,38 @@ This function is called by `org-babel-execute-src-block'."
result
(org-babel-scheme--table-or-string table)))))))
(defun org-babel-scheme-initiate-session (session params)
"Return scheme buffer for SESSION according to PARAMS.
Creates new session buffer if necessary."
(let ((impl (org-babel-scheme--get-impl params))
(host (cdr (assq :host params)))
(port (cdr (assq :port params))))
(save-current-buffer
(org-babel-scheme-get-repl impl
session
host
port))))
(defun org-babel-prep-session:scheme (session params)
"Prepare SESSION according to header arguments in PARAMS."
(let ((repl (org-babel-scheme-initiate-session session params)))
(org-babel-scheme-execute-with-geiser
(org-babel-scheme-expand-header-arg-vars (org-babel--get-vars params))
nil
(org-babel-scheme--get-impl params)
session)
repl))
(defun org-babel-load-session:scheme (session body params)
"Load BODY into SESSION.
Also evaluates any variable assignments in PARAMS before loading BODY."
(with-current-buffer (org-babel-prep-session:scheme session params)
(goto-char (process-mark (get-buffer-process (current-buffer))))
(insert (org-babel-chomp body))
(current-buffer)))
(provide 'ob-scheme)
;;; ob-scheme.el ends here

View file

@ -52,7 +52,6 @@ In case you want to use a different screen than one selected by your $PATH")
(defun org-babel-execute:screen (body params)
"Send BODY via screen to a terminal using Babel, according to PARAMS.
\"default\" session is used when none is specified in the PARAMS."
(unless noninteractive (message "Sending source code block to interactive terminal session..."))
(save-window-excursion
(let* ((session (cdr (assq :session params)))
(socket (org-babel-screen-session-socketname session)))

View file

@ -65,7 +65,6 @@
BODY is the source inside a sed source block and PARAMS is an
association list over the source block configurations. This
function is called by `org-babel-execute-src-block'."
(unless noninteractive (message "Executing sed source code block"))
(let* ((result-params (cdr (assq :result-params params)))
(cmd-line (cdr (assq :cmd-line params)))
(in-file (cdr (assq :in-file params)))

View file

@ -57,6 +57,9 @@
("fish" . "function fish_prompt\n\techo \"%s\"\nend")
;; prompt2 is like PS2 in POSIX shells.
("csh" . "set prompt=\"%s\"\nset prompt2=\"\"")
;; Disable bracketed paste - it messes up out processing and,
;; apparently, comint.el. Also, unset "% \r" prompts.
("zsh" . "setopt nopromptcr;unset zle_bracketed_paste;PROMPT_COMMAND=;PS1=\"%s\";PS2=")
;; PROMPT_COMMAND can override PS1 settings. Disable it.
;; Disable PS2 to avoid garbage in multi-line inputs.
(t . "PROMPT_COMMAND=;PS1=\"%s\";PS2="))
@ -85,6 +88,24 @@ is modified outside the Customize interface."
(shell-file-name name))
(org-babel-execute:shell body params))))
(put fname 'definition-name 'org-babel-shell-initialize))
(let ((fname (intern (concat "org-babel-prep-session:" name))))
(defalias fname
(lambda (session params)
(:documentation
(format "Prepare %s SESSION according to the header arguments specified in PARAMS." name))
(let ((explicit-shell-file-name name)
(shell-file-name name))
(org-babel-prep-session:shell session params))))
(put fname 'definition-name 'org-babel-shell-initialize))
(let ((fname (intern (format "org-babel-%s-initiate-session" name))))
(defalias fname
(lambda (session _params)
(:documentation
(format "Initiate %s session named SESSION." name))
(let ((explicit-shell-file-name name)
(shell-file-name name))
(org-babel-sh-initiate-session session))))
(put fname 'definition-name 'org-babel-shell-initialize))
(defalias (intern (concat "org-babel-variable-assignments:" name))
#'org-babel-variable-assignments:shell
(format "Return list of %s statements assigning to the block's \
@ -245,8 +266,11 @@ var of the same value."
(let ((echo-var (lambda (v) (if (stringp v) v (format "%S" v)))))
(cond
((and (listp var) (or (listp (car var)) (eq (car var) 'hline)))
(orgtbl-to-generic var (list :sep (or sep "\t") :fmt echo-var
:hline hline)))
(orgtbl-to-generic
var
(list :sep (or sep "\t") :fmt echo-var
:hline hline
:with-special-rows nil)))
((listp var)
(mapconcat echo-var var "\n"))
(t (funcall echo-var var)))))
@ -255,18 +279,36 @@ var of the same value."
"String to indicate that evaluation has completed.")
(defvar org-babel-sh-eoe-output "org_babel_sh_eoe"
"String to indicate that evaluation has completed.")
(defvar org-babel-sh-prompt "org_babel_sh_prompt> "
(defvar org-babel-sh-prompt
;; FIXME: Emacs 27 CI fails non-interactively. Play it safe and
;; keep the old prompt until we drop Emacs 27 support.
(if (version< emacs-version "28") "org_babel_sh_prompt> " "𒆸 ")
"String to set prompt in session shell.")
(defvar-local org-babel-sh--prompt-initialized nil
"When non-nil, ob-shell already initialized the prompt in current buffer.")
(defalias 'org-babel-shell-initiate-session #'org-babel-sh-initiate-session)
(defun org-babel-sh-initiate-session (&optional session _params)
"Initiate a session named SESSION according to PARAMS."
(when (and session (not (string= session "none")))
(save-window-excursion
(or (org-babel-comint-buffer-livep session)
(or (and (org-babel-comint-buffer-livep session)
(buffer-local-value
'org-babel-sh--prompt-initialized
(get-buffer session))
session)
(progn
(if (org-babel-comint-buffer-livep session)
(set-buffer session)
(shell session)
;; Set unique prompt for easier analysis of the output.
(org-babel-comint-wait-for-output (current-buffer))
(org-babel-comint-wait-for-output (current-buffer)))
(setq-local
org-babel-comint-prompt-regexp-fallback comint-prompt-regexp
comint-prompt-regexp
(concat "^" (regexp-quote org-babel-sh-prompt)
" *"))
(org-babel-comint-input-command
(current-buffer)
(format
@ -274,11 +316,7 @@ var of the same value."
org-babel-shell-set-prompt-commands))
(alist-get t org-babel-shell-set-prompt-commands))
org-babel-sh-prompt))
(setq-local
org-babel-comint-prompt-regexp-old comint-prompt-regexp
comint-prompt-regexp
(concat "^" (regexp-quote org-babel-sh-prompt)
" *"))
(setq org-babel-sh--prompt-initialized t)
;; Needed for Emacs 23 since the marker is initially
;; undefined and the filter functions try to use it without
;; checking.
@ -314,22 +352,28 @@ return the value of the last statement in BODY."
(stdin-file (org-babel-temp-file "sh-stdin-"))
(padline (not (string= "no" (cdr (assq :padline params))))))
(with-temp-file script-file
(when shebang (insert shebang "\n"))
(if shebang
(insert shebang "\n")
;; Provide shell name explicitly.
;; This is necessary because running, for example,
;; dash script-for-dash.sh will use /bin/sh.
(insert (format "#!/usr/bin/env %s" shell-file-name) "\n"))
(when padline (insert "\n"))
(insert body))
(set-file-modes script-file #o755)
(with-temp-file stdin-file (insert (or stdin "")))
(with-temp-buffer
(with-connection-local-variables
;; `with-connection-local-variables' will override
;; `shell-file-name' and `shell-command-swtich' as
;; needed for the remote connection.
(apply #'process-file
(if shebang (file-local-name script-file)
shell-file-name)
shell-file-name
stdin-file
(current-buffer)
nil
(if shebang (when cmdline (list cmdline))
(list shell-command-switch
(concat (file-local-name script-file) " " cmdline)))))
(concat (file-local-name script-file) " " (format "%s" cmdline)))))
(buffer-string))))
(session ; session evaluation
(if async

View file

@ -117,23 +117,27 @@ corresponding :engine source block header argument."
(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."
(combine-and-quote-strings
(mapconcat
#'identity
(delq nil
(list (when host (concat "-h" host))
(list (when host (concat "-h" (shell-quote-argument host)))
(when port (format "-P%d" port))
(when user (concat "-u" user))
(when password (concat "-p" password))
(when database (concat "-D" database))))))
(when user (concat "-u" (shell-quote-argument user)))
(when password (concat "-p" (shell-quote-argument password)))
(when database (concat "-D" (shell-quote-argument database)))))
" "))
(defun org-babel-sql-dbstring-postgresql (host port user database)
"Make PostgreSQL command line args for database connection.
Pass nil to omit that arg."
(combine-and-quote-strings
(mapconcat
#'identity
(delq nil
(list (when host (concat "-h" host))
(list (when host (concat "-h" (shell-quote-argument host)))
(when port (format "-p%d" port))
(when user (concat "-U" user))
(when database (concat "-d" database))))))
(when user (concat "-U" (shell-quote-argument user)))
(when database (concat "-d" (shell-quote-argument database)))))
" "))
(defun org-babel-sql-dbstring-oracle (host port user password database)
"Make Oracle command line arguments for database connection.
@ -149,8 +153,12 @@ or
<user>/<password>@<database>
using its alias."
(when user (setq user (shell-quote-argument user)))
(when password (setq password (shell-quote-argument password)))
(when database (setq database (shell-quote-argument database)))
(when host (setq host (shell-quote-argument host)))
(cond ((and user password database host port)
(format "%s/%s@%s:%s/%s" user password host port database))
(format "%s/%s@%s:%d/%s" user password host port database))
((and user password database)
(format "%s/%s@%s" user password database))
(t (user-error "Missing information to connect to database"))))
@ -161,10 +169,10 @@ using its alias."
SQL Server on Windows and Linux platform."
(mapconcat #'identity
(delq nil
(list (when host (format "-S \"%s\"" host))
(when user (format "-U \"%s\"" user))
(when password (format "-P \"%s\"" password))
(when database (format "-d \"%s\"" database))))
(list (when host (format "-S \"%s\"" (shell-quote-argument host)))
(when user (format "-U \"%s\"" (shell-quote-argument user)))
(when password (format "-P \"%s\"" (shell-quote-argument password)))
(when database (format "-d \"%s\"" (shell-quote-argument database)))))
" "))
(defun org-babel-sql-dbstring-sqsh (host user password database)
@ -172,10 +180,10 @@ SQL Server on Windows and Linux platform."
\"sqsh\" is one method to access Sybase or MS SQL via Linux platform"
(mapconcat #'identity
(delq nil
(list (when host (format "-S \"%s\"" host))
(when user (format "-U \"%s\"" user))
(when password (format "-P \"%s\"" password))
(when database (format "-D \"%s\"" database))))
(list (when host (format "-S \"%s\"" (shell-quote-argument host)))
(when user (format "-U \"%s\"" (shell-quote-argument user)))
(when password (format "-P \"%s\"" (shell-quote-argument password)))
(when database (format "-D \"%s\"" (shell-quote-argument database)))))
" "))
(defun org-babel-sql-dbstring-vertica (host port user password database)
@ -183,11 +191,11 @@ SQL Server on Windows and Linux platform."
Pass nil to omit that arg."
(mapconcat #'identity
(delq nil
(list (when host (format "-h %s" host))
(list (when host (format "-h %s" (shell-quote-argument host)))
(when port (format "-p %d" port))
(when user (format "-U %s" user))
(when user (format "-U %s" (shell-quote-argument user)))
(when password (format "-w %s" (shell-quote-argument password) ))
(when database (format "-d %s" database))))
(when database (format "-d %s" (shell-quote-argument database)))))
" "))
(defun org-babel-sql-dbstring-saphana (host port instance user password database)
@ -195,13 +203,15 @@ Pass nil to omit that arg."
Pass nil to omit that arg."
(mapconcat #'identity
(delq nil
(list (and host port (format "-n %s:%s" host port))
(and host (not port) (format "-n %s" host))
(list (and host port (format "-n %s:%s"
(shell-quote-argument host)
port))
(and host (not port) (format "-n %s" (shell-quote-argument host)))
(and instance (format "-i %d" instance))
(and user (format "-u %s" user))
(and user (format "-u %s" (shell-quote-argument user)))
(and password (format "-p %s"
(shell-quote-argument password)))
(and database (format "-d %s" database))))
(and database (format "-d %s" (shell-quote-argument database)))))
" "))
(defun org-babel-sql-convert-standard-filename (file)
@ -276,11 +286,13 @@ This function is called by `org-babel-execute-src-block'."
(or cmdline "")
(org-babel-process-file-name in-file)
(org-babel-process-file-name out-file)))
((postgresql postgres) (format
((postgresql postgres)
(format
"%s%s --set=\"ON_ERROR_STOP=1\" %s -A -P \
footer=off -F \"\t\" %s -f %s -o %s %s"
(if dbpassword
(format "PGPASSWORD=%s " dbpassword)
(format "PGPASSWORD=%s "
(shell-quote-argument dbpassword))
"")
(or (bound-and-true-p
sql-postgres-program)
@ -375,7 +387,7 @@ SET COLSEP '|'
(goto-char (point-max))
(forward-char -1))
(write-file out-file))))
(org-table-import out-file (if (string= engine "sqsh") '(4) '(16)))
(org-table-import out-file (if (memq (intern engine) '(saphana sqsh)) '(4) '(16)))
(org-babel-reassemble-table
(mapcar (lambda (x)
(if (string= (car x) header-delim)
@ -406,9 +418,11 @@ argument mechanism."
(insert (orgtbl-to-csv
val (if sqlite
nil
'(:fmt (lambda (el) (if (stringp el)
'( :fmt (lambda (el)
(if (stringp el)
el
(format "%S" el))))))))
(format "%S" el)))
:with-special-rows nil)))))
data-file)
(if (stringp val) val (format "%S" val))))
body t t)))

View file

@ -36,7 +36,6 @@
(declare-function org-table-convert-region "org-table"
(beg0 end0 &optional separator))
(declare-function orgtbl-to-csv "org-table" (table params))
(declare-function org-table-to-lisp "org-table" (&optional txt))
(defvar org-babel-default-header-args:sqlite '())
@ -52,7 +51,8 @@
(line . :any)
(list . :any)
(separator . :any)
(nullvalue . :any))
(nullvalue . :any)
(readonly-p . ((yes no))))
"Sqlite specific header args.")
(defun org-babel-expand-body:sqlite (body params)
@ -77,6 +77,7 @@ This function is called by `org-babel-execute-src-block'."
(separator (cdr (assq :separator params)))
(nullvalue (cdr (assq :nullvalue params)))
(headers-p (equal "yes" (cdr (assq :colnames params))))
(readonly-p (equal "yes" (cdr (assq :readonly params))))
(others (delq nil (mapcar
(lambda (arg) (car (assq arg params)))
(list :header :echo :bail :column
@ -85,7 +86,7 @@ This function is called by `org-babel-execute-src-block'."
(insert
(org-babel-eval
(org-fill-template
"%cmd %header %separator %nullvalue %others %csv %db "
"%cmd %header %separator %nullvalue %others %csv %readonly %db "
(list
(cons "cmd" org-babel-sqlite3-command)
(cons "header" (if headers-p "-header" "-noheader"))
@ -103,6 +104,8 @@ This function is called by `org-babel-execute-src-block'."
(member :html others) separator)
""
"-csv"))
(cons "readonly"
(if readonly-p "-readonly" ""))
(cons "db" (or db ""))))
;; body of the code block
(org-babel-expand-body:sqlite body params)))

View file

@ -41,14 +41,12 @@
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-before-first-heading-p "org" ())
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-begin "org-element" (node))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-heading-components "org" ())
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance))
(declare-function org-in-archived-heading-p "org" (&optional no-inheritance))
(declare-function outline-previous-heading "outline" ())
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance element))
(declare-function org-in-archived-heading-p "org" (&optional no-inheritance element))
(defvar org-id-link-to-org-use-id) ; Dynamically scoped
(defgroup org-babel-tangle nil
@ -58,18 +56,20 @@
(defcustom org-babel-tangle-lang-exts
'(("emacs-lisp" . "el")
("elisp" . "el"))
("elisp" . "el")
("bibtex" . "bib"))
"Alist mapping languages to their file extensions.
The key is the language name, the value is the string that should
be inserted as the extension commonly used to identify files
written in this language. If no entry is found in this list,
then the name of the language is used."
:group 'org-babel-tangle
:version "24.1"
:package-version '(Org . "9.8")
:type '(repeat
(cons
(string "Language name")
(string "File Extension"))))
(string "File Extension")))
:safe #'listp)
(defcustom org-babel-tangle-use-relative-file-links t
"Use relative path names in links from tangled source back the Org file."
@ -159,9 +159,10 @@ result. The default value is `org-remove-indentation'."
:type 'function)
(defcustom org-babel-tangle-default-file-mode #o644
"The default mode used for tangled files, as an integer.
The default value 420 correspands to the octal #o644, which is
read-write permissions for the user, read-only for everyone else."
"The default mode, an integer value, only used when the :tangle-mode
header argument specifies chmod-style symbolic notation. The default
value 420 corresponds to the octal #o644, which is read-write
permissions for the user, read-only for everyone else."
:group 'org-babel-tangle
:package-version '(Org . "9.6")
:type 'integer)
@ -181,7 +182,7 @@ replace contents otherwise."
(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)
:safe #'symbolp)
(defun org-babel-find-file-noselect-refresh (file)
"Find file ensuring that the latest changes on disk are represented in the file."
@ -227,13 +228,11 @@ Return list of the tangled file names."
(org-babel-tangle nil target-file lang-re)))))
(defun org-babel-tangle-publish (_ filename pub-dir)
"Tangle FILENAME and place the results in PUB-DIR."
(unless (file-exists-p pub-dir)
(make-directory pub-dir t))
(setq pub-dir (file-name-as-directory pub-dir))
;; Rename files to avoid copying to same file when publishing to ./
;; `copy-file' would throw an error when copying file to self.
(mapc (lambda (el) (rename-file el pub-dir t))
"Tangle FILENAME and copy the tangled file to PUB-DIR."
(require 'ox-publish)
(declare-function org-publish-attachment "ox-publish"
(plist filename pub-dir))
(mapc (lambda (el) (org-publish-attachment nil el pub-dir))
(org-babel-tangle-file filename)))
;;;###autoload
@ -269,7 +268,7 @@ matching a regular expression."
(or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info 'no-eval))))
(user-error "Point is not in a source code block"))))
path-collector
(source-file buffer-file-name))
(source-file (org-base-buffer-file-name)))
(mapc ;; map over file-names
(lambda (by-fn)
(let ((file-name (car by-fn)))
@ -290,8 +289,8 @@ matching a regular expression."
(tangle-mode (funcall get-spec :tangle-mode)))
(unless (string-equal block-lang lang)
(setq lang block-lang)
(let ((lang-f (org-src-get-lang-mode lang)))
(when (fboundp lang-f) (ignore-errors (funcall lang-f)))))
(when-let* ((lang-f (org-src-get-lang-mode-if-bound lang)))
(ignore-errors (funcall lang-f))))
;; if file contains she-bangs, then make it executable
(when she-bang
(unless tangle-mode (setq tangle-mode #o755)))
@ -509,9 +508,13 @@ code blocks by target file."
(src-lang (nth 0 info))
(src-tfile (cdr (assq :tangle (nth 2 info)))))
(unless (or (string= src-tfile "no")
(not src-lang) ;; src block without lang
;; src block without lang
(and (not src-lang) (string= src-tfile "yes"))
(and tangle-file (not (equal tangle-file src-tfile)))
(and lang-re (not (string-match-p lang-re src-lang))))
;; lang-re but either no lang or lang doesn't match
(and lang-re
(or (not src-lang)
(not (string-match-p lang-re src-lang)))))
;; Add the spec for this block to blocks under its tangled
;; file name.
(let* ((block (org-babel-tangle-single-block counter))
@ -531,10 +534,7 @@ code blocks by target file."
The PARAMS are the 3rd element of the info for the same src block."
(unless (string= "no" (cdr (assq :comments params)))
(save-match-data
(let* ((l (org-no-properties
(cl-letf (((symbol-function 'org-store-link-functions)
(lambda () nil)))
(org-store-link nil))))
(let* ((l (org-no-properties (org-store-link nil)))
(bare (and l
(string-match org-link-bracket-re l)
(match-string 1 l))))
@ -580,7 +580,8 @@ non-nil, return the full association list to be used by
(let ((body (if (org-babel-noweb-p params :tangle)
(if (string= "strip-tangle" (cdr (assq :noweb (nth 2 info))))
(replace-regexp-in-string (org-babel-noweb-wrap) "" (nth 1 info))
(org-babel-expand-noweb-references info))
(org-babel-expand-noweb-references
info nil :tangle))
(nth 1 info))))
(with-temp-buffer
(insert

View file

@ -92,9 +92,9 @@
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-element-contents "org-element-ast" (node))
(declare-function org-export-data "org-export" (data info))
(declare-function org-export-derived-backend-p "org-export" (backend &rest backends))
(declare-function org-export-raw-string "org-export" (contents))
(declare-function org-export-data "ox" (data info))
(declare-function org-export-derived-backend-p "ox" (backend &rest backends))
(declare-function org-export-raw-string "ox" (contents))
;;; Customization
@ -140,6 +140,31 @@
:type 'face
:safe #'facep)
(defcustom org-cite-basic-complete-key-crm-separator nil
"When non-nil, use `completing-read-multiple' with this as the separator.
When nil, use multiple `completing-read' prompts. When set to a string,
it should be a regexp to be used as `crm-separator' (which see). The
regexp string can carry the text properties `separator', which if
present `completing-read-multiple' will show as part of the prompt.
When set to symbol `dynamic', use \";;...\" as a separator with the
number of \";\" sufficient so that none of the completion candidates
contain the separator."
:group 'org-cite
:package-version '(Org . "9.8")
:type
'(choice
(const
:tag
"Use \";;\" as the separator."
(propertize "[ \t]*;;[ \t]*" 'separator ";;"))
(const
:tag
"Dynamically compute \";\"+ with the needed length."
dynamic)
(string :tag "Custom regexp for the separator.")
(const :tag "Prompt multiple times." nil))
:safe (lambda (obj) (or (string-or-null-p obj) (member obj '(dynamic)))))
;;; Internal variables
(defvar org-cite-basic--bibliography-cache nil
@ -884,6 +909,39 @@ Return nil if there are no bibliography files or no entries."
(puthash entries t org-cite-basic--completion-cache)
org-cite-basic--completion-cache)))))
(defun org-cite-basic--complete-key-dynamic-crm-separator
(completion-candidates separator)
"Return a repeated version of SEPARATOR as needed.
The number of appeared SEPARATORs in the returned string is sufficient
so that none of COMPLETION-CANDIDATES contains it. SEPARATOR should be
a literal string."
(let* ((dyn-sep separator)
(consecutive-sep-regexp
(format "%s+" (regexp-opt (list separator)))))
(with-temp-buffer
(dolist (cand completion-candidates)
(when (stringp cand)
(insert cand "\n")))
(goto-char (point-min))
(while (re-search-forward consecutive-sep-regexp nil t)
(while (<= (length dyn-sep) (length (match-string 0)))
(setq dyn-sep (concat dyn-sep separator)))))
dyn-sep))
(defvar crm-separator) ; defined in crm.el
(defun org-cite-basic--crm-indicate-prompt (orig-prompt)
"Return annotated ORIG-PROMPT with `crm-separator'.
For Emacs version 31 and above, just return ORIG-PROMPT."
(cond
((boundp 'crm-prompt)
orig-prompt)
(t
(let ((sep
(or (get-text-property 0 'separator crm-separator)
(string-replace "[ \t]*" "" crm-separator))))
(format "[list separated by %s] %s" sep orig-prompt)))))
(defun org-cite-basic--complete-key (&optional multiple)
"Prompt for a reference key and return a citation reference string.
@ -895,12 +953,39 @@ Raise an error when no bibliography is set in the buffer."
(let* ((table
(or (org-cite-basic--key-completion-table)
(user-error "No bibliography set")))
(prompt
(lambda (text)
(completing-read text table nil t))))
(if (null multiple)
(let ((key (gethash (funcall prompt "Key: ") table)))
(org-string-nw-p key))
(prompt-single
(lambda (text) (completing-read text table nil t)))
(choice-to-citation
(lambda (choice)
(let ((key (gethash choice table)))
(org-string-nw-p key)))))
(cond
((null multiple)
(funcall choice-to-citation
(completing-read "Key: " table nil t)))
(org-cite-basic-complete-key-crm-separator
(let*
((crm-separator
(pcase org-cite-basic-complete-key-crm-separator
((pred stringp)
org-cite-basic-complete-key-crm-separator)
(`dynamic
(let
((repeated-sep
(regexp-quote
(org-cite-basic--complete-key-dynamic-crm-separator
(hash-table-keys table) ";"))))
(propertize (format "[ \t]*%s[ \t]*" repeated-sep)
'separator
repeated-sep)))))
(prompt (org-cite-basic--crm-indicate-prompt "Keys: ")))
;; FIXME: Use `seq-keep' after we drop Emacs 28 support.
(delq
nil
(seq-map
choice-to-citation
(completing-read-multiple prompt table nil t)))))
(t
(let* ((keys nil)
(build-prompt
(lambda ()
@ -908,11 +993,12 @@ Raise an error when no bibliography is set in the buffer."
(format "Key (empty input exits) %s: "
(mapconcat #'identity (reverse keys) ";"))
"Key (empty input exits): "))))
(let ((key (funcall prompt (funcall build-prompt))))
(let ((key (funcall prompt-single (funcall build-prompt))))
(while (org-string-nw-p key)
(push (gethash key table) keys)
(setq key (funcall prompt (funcall build-prompt)))))
keys))))
(setq key
(funcall prompt-single (funcall build-prompt)))))
keys)))))
;;; Register processor

View file

@ -72,7 +72,7 @@
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-parent "org-element-ast" (node))
(declare-function org-export-data "org-export" (data info))
(declare-function org-export-data "ox" (data info))
;;; Customization
@ -179,7 +179,7 @@ a style in `org-cite-biblatex-styles'."
(cons :tag "Shortcut"
(string :tag "Name")
(string :tag "Full name")))
:safe t)
:safe #'listp)
;;; Internal functions

View file

@ -43,7 +43,35 @@
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-export-data "org-export" (data info))
(declare-function org-export-data "ox" (data info))
(defcustom org-cite-bibtex-bibliography-style "plain"
"Default BibTeX bibliography style.
BibTeX provides the following default styles:
\"plain\" Sorted numbered entries
\"abbrv\" Sorted numbered entries with abbreviated author name
\"unsrt\" Unsorted numbered entries
\"alpha\" Alphabetized entries with unique short id
\"acm\" ACM Transactions
\"apalike\" APA-like
\"ieeetr\" IEEE Transactions
\"siam\" SIAM"
:group 'org-cite
:package-version '(Org . "9.8")
:type
'(choice
(const :tag "Plain" "plain")
(const :tag "Abbreviated" "abbrv")
(const :tag "Unsorted" "unsrt")
(const :tag "Alphabetic" "alpha")
(const :tag "ACM" "acm")
(const :tag "APA-like" "apalike")
(const :tag "IEEE" "ieeetr")
(const :tag "SIAM" "siam")
(string :tag "Other"))
:safe #'stringp)
;;; Export capability
@ -51,7 +79,8 @@
"Print references from bibliography FILES.
FILES is a list of absolute file names. STYLE is the bibliography style, as
a string or nil."
(concat (and style (format "\\bibliographystyle{%s}\n" style))
(concat (format "\\bibliographystyle{%s}\n"
(or style org-cite-bibtex-bibliography-style))
(format "\\bibliography{%s}"
(mapconcat #'file-name-sans-extension
files

View file

@ -124,7 +124,6 @@
(require 'citeproc nil t)
(declare-function citeproc-style-cite-note "ext:citeproc")
(declare-function citeproc-proc-style "ext:citeproc")
(declare-function citeproc-bt-entry-to-csl "ext:citeproc")
(declare-function citeproc-locale-getter-from-dir "ext:citeproc")
(declare-function citeproc-create "ext:citeproc")
(declare-function citeproc-citation-create "ext:citeproc")
@ -137,13 +136,12 @@
(declare-function citeproc-style-cite-superscript-p "ext:citeproc")
(declare-function org-element-interpret-data "org-element" (data))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
(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-put-property "org-element-ast" (node property value))
(declare-function org-export-data "org-export" (data info))
(declare-function org-export-derived-backend-p "org-export" (backend &rest backends))
(declare-function org-export-get-footnote-number "org-export" (footnote info &optional data body-first))
(declare-function org-export-data "ox" (data info))
(declare-function org-export-derived-backend-p "ox" (backend &rest backends))
(declare-function org-export-get-footnote-number "ox" (footnote info &optional data body-first))
;;; Customization
@ -321,6 +319,24 @@ in the bibliography measured in characters."
:type 'string
:package-version '(Org . "9.7"))
(defcustom org-cite-csl-bibtex-titles-to-sentence-case t
"Convert bibtex title fields to sentence-case by default.
When non-nil, title fields in bibtex bibliography entries are
converted to sentence-case before being formatted according to a
CSL style, except for entries with a `langid' field specifying a
non-English language. When nil, title conversion is limited to
entries having a `langid' field specifying a variant of English.
Conversion of titles to sentence-case by default is in most cases
useful because the CSL standard assumes that English titles are
specified in sentence-case but the bibtex bibliography format
requires them to be written in title-case."
:group 'org-cite
:package-version '(Org . "9.8")
:type 'boolean
:safe #'booleanp)
;;; Internal variables
(defconst org-cite-csl--etc-dir
@ -579,7 +595,8 @@ property in INFO."
(processor
(citeproc-create
(org-cite-csl--style-file info)
(citeproc-hash-itemgetter-from-any bibliography)
(citeproc-hash-itemgetter-from-any
bibliography (not org-cite-csl-bibtex-titles-to-sentence-case))
(org-cite-csl--locale-getter)
locale)))
(plist-put info :cite-citeproc-processor processor)
@ -803,28 +820,12 @@ INFO is the export state, as a property list."
"\\[CSL-MAXLABEL-CHARS\\]" ,(number-to-string max-offset)))
result))
;;; Export capability
(defun org-cite-csl-render-citation (citation _style _backend info)
"Export CITATION object.
INFO is the export state, as a property list."
(org-require-package 'citeproc)
(let ((output (cdr (assq citation (org-cite-csl--rendered-citations info)))))
(if (not (eq 'org (org-cite-csl--output-format info)))
output
;; Parse Org output to re-export it during the regular export
;; process.
(org-cite-parse-objects output))))
(defun org-cite-csl-render-bibliography (_keys _files _style props _backend info)
"Export bibliography.
INFO is the export state, as a property list."
(org-require-package 'citeproc)
(pcase-let* ((format (org-cite-csl--output-format info))
(`(,outputs ,parameters) (org-cite-csl--rendered-bibliographies info))
(output (cdr (assoc props outputs))))
(pcase format
('html
(defun org-cite-csl--generate-html-head (info)
"Generate the CSL-related part of the HTML head.
INFO is the export state, as a property list. Return the generated head
fragment or nil if no fragment should be inserted."
(let* ((parameters (cadr (org-cite-csl--rendered-bibliographies info)))
(head-part
(concat
(and (cdr (assq 'second-field-align parameters))
(let* ((max-offset (cdr (assq 'max-offset parameters)))
@ -845,12 +846,40 @@ INFO is the export state, as a property list."
(format
"<style>.csl-entry{text-indent: -%s; margin-left: %s;}</style>"
org-cite-csl-html-hanging-indent
org-cite-csl-html-hanging-indent))
output))
('org-latex output)
(_
org-cite-csl-html-hanging-indent)))))
(and (not (string= "" head-part)) head-part)))
;;; Export capability
(defun org-cite-csl-render-citation (citation _style _backend info)
"Export CITATION object.
INFO is the export state, as a property list."
(org-require-package 'citeproc)
(let ((output (cdr (assq citation (org-cite-csl--rendered-citations info)))))
(if (not (eq 'org (org-cite-csl--output-format info)))
output
;; Parse Org output to re-export it during the regular export
;; process.
(org-cite-parse-objects output))))
(defun org-cite-csl-render-bibliography (_keys _files _style props _backend info)
"Export bibliography.
INFO is the export state, as a property list."
(org-require-package 'citeproc)
(let* ((format (org-cite-csl--output-format info))
(outputs (car (org-cite-csl--rendered-bibliographies info)))
(output (cdr (assoc props outputs))))
(pcase format
('html
(unless (plist-get info :html-head-csl-styles-added)
(if-let* ((head-part (org-cite-csl--generate-html-head info)))
(plist-put info :html-head
(concat (plist-get info :html-head) head-part)))
(plist-put info :html-head-csl-styles-added t))
output)
('org-latex output)
(_
;; Parse Org output to re-export it during the regular export process.
(org-cite-parse-elements output)))))
(defun org-cite-csl-finalizer (output _keys _files _style _backend info)

View file

@ -48,9 +48,7 @@
(require 'oc)
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-export-data "org-export" (data info))
(declare-function org-export-data "ox" (data info))
;;; Customization

View file

@ -80,8 +80,8 @@
(declare-function org-element-extract "org-element-ast" (node))
(declare-function org-element-insert-before "org-element-ast" (node location))
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
(declare-function org-element-normalize-string "org-element" (s))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated no-undefer))
(declare-function org-element-normalize-string "org-element" (s &optional keep-newlines))
(declare-function org-element-parse-buffer "org-element" (&optional granularity visible-only keep-deferred))
(declare-function org-element-parse-secondary-string "org-element" (string restriction &optional parent))
(declare-function org-element-context "org-element" (&optional element))
@ -95,14 +95,14 @@
(declare-function org-element-parent "org-element-ast" (node))
(declare-function org-element-put-property "org-element-ast" (node property value))
(declare-function org-element-restriction "org-element" (element))
(declare-function org-element-set "org-element-ast" (old new))
(declare-function org-element-set "org-element-ast" (old new &optional keep-props))
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-export-derived-backend-p "org-export" (backend &rest backends))
(declare-function org-export-get-next-element "org-export" (blob info &optional n))
(declare-function org-export-get-previous-element "org-export" (blob info &optional n))
(declare-function org-export-raw-string "org-export" (s))
(declare-function org-export-derived-backend-p "ox" (backend &rest backends))
(declare-function org-export-get-next-element "ox" (blob info &optional n))
(declare-function org-export-get-previous-element "ox" (blob info &optional n))
(declare-function org-export-raw-string "ox" (s))
(defvar org-complex-heading-regexp)
(defvar org-element-all-objects)

View file

@ -114,7 +114,6 @@
(declare-function bbdb-records "ext:bbdb" (&optional dont-check-disk already-in-db-buffer))
(declare-function bbdb-split "ext:bbdb" (string separators))
(declare-function bbdb-string-trim "ext:bbdb" (string))
(declare-function bbdb-record-get-field "ext:bbdb" (record field))
(declare-function bbdb-search-name "ext:bbdb-com" (regexp &optional layout))
(declare-function bbdb-search-organization "ext:bbdb-com" (regexp &optional layout))
@ -222,6 +221,7 @@ date year)."
:follow #'org-bbdb-open
:export #'org-bbdb-export
:complete #'org-bbdb-complete-link
:insert-description #'org-bbdb-describe-link
:store #'org-bbdb-store-link)
;;; Implementation
@ -499,7 +499,7 @@ must be positive"))
dates)))))
(defun org-bbdb-complete-link ()
"Read a bbdb link with name completion."
"Read a BBDB link with name completion."
(org-require-package 'bbdb-com "bbdb")
(let ((rec (bbdb-completing-read-record "Name: ")))
(concat "bbdb:"
@ -507,6 +507,12 @@ must be positive"))
(car rec)
rec)))))
(defun org-bbdb-describe-link (link desc)
"Return a description for a BBDB link."
(or (org-string-nw-p desc)
(if (string-prefix-p "bbdb:" link)
(string-remove-prefix "bbdb:" link))))
(defun org-bbdb-anniv-export-ical ()
"Extract anniversaries from BBDB and convert them to icalendar format."
(org-require-package 'bbdb)

View file

@ -123,11 +123,7 @@
(defvar org-property-end-re)
(defvar org-special-properties)
(defvar org-window-config-before-follow-link)
(declare-function bibtex-beginning-of-entry "bibtex" ())
(declare-function bibtex-generate-autokey "bibtex" ())
(declare-function bibtex-parse-entry "bibtex" (&optional content))
(declare-function bibtex-url "bibtex" (&optional pos no-browse))
(defvar org-tag--invalid-char-re)
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
@ -754,7 +750,7 @@ entry at point."
(funcall
togtag
(replace-regexp-in-string
"[^[:alnum:]_@#%]" ""
org-tag--invalid-char-re ""
(replace-regexp-in-string "[ \t]+" "_" kw))))
(org-bibtex-put (car pair) (cdr pair) insert-raw)))
(_ (org-bibtex-put (car pair) (cdr pair) insert-raw))))

View file

@ -48,7 +48,6 @@
(require 'doc-view)
(require 'ol)
(declare-function doc-view-goto-page "doc-view" (page))
(declare-function image-mode-window-get "image-mode" (prop &optional winprops))
(declare-function org-open-file "org" (path &optional in-emacs line search))

View file

@ -30,7 +30,7 @@
(require 'esh-mode)
(require 'ol)
(declare-function eshell/pwd "em-dirs.el" (&rest args))
(declare-function eshell/pwd "em-dirs" ())
(org-link-set-parameters "eshell"
:follow #'org-eshell-open

View file

@ -46,7 +46,6 @@
(declare-function gnus-activate-group "gnus-start" (group &optional scan dont-check method dont-sub-check))
(declare-function gnus-find-method-for-group "gnus" (group &optional info))
(declare-function gnus-article-show-summary "gnus-art" ())
(declare-function gnus-group-group-name "gnus-group")
(declare-function gnus-group-jump-to-group "gnus-group" (group &optional prompt))
(declare-function gnus-group-read-group "gnus-group" (&optional all no-article group select-articles))
@ -227,8 +226,13 @@ If `org-store-link' was called with a prefix arg the meaning of
(defun org-gnus-follow-link (&optional group article)
"Follow a Gnus link to GROUP and ARTICLE."
(require 'gnus)
(funcall (cdr (assq 'gnus org-link-frame-setup)))
(when gnus-other-frame-object (select-frame gnus-other-frame-object))
(funcall (org-link-frame-setup-function 'gnus))
(when gnus-other-frame-object
(if (not (frame-live-p gnus-other-frame-object))
;; Error out in case org-link-frame-setup did not take care of setting up
;; the gnus frame if was activate previously.
(error "Couldn't select \'gnus-other-frame-object\', make sure it is active"))
(select-frame gnus-other-frame-object))
(let ((group (org-no-properties group))
(article (org-no-properties article)))
(cond
@ -260,11 +264,24 @@ If `org-store-link' was called with a prefix arg the meaning of
(message "Couldn't follow Gnus link. The linked group is empty."))))
(group (gnus-group-jump-to-group group)))))
(defun org-gnus-no-new-news ()
"Like `\\[gnus]' but doesn't check for new news."
(cond ((gnus-alive-p) nil)
(org-gnus-no-server (gnus-no-server))
(t (gnus))))
(defun org-gnus-no-new-news (&optional other-frame)
"Like `\\[gnus]' but doesn't check for new news.
In case of OTHER-FRAME or `gnus-other-frame-object' call `gnus-other-frame'.
Ensures that `gnus-other-frame' is activated correctly if dead."
(let ((action (cond (org-gnus-no-server #'gnus-no-server)
(t #'gnus))))
(cond ((or other-frame gnus-other-frame-object)
(let ((gnus-other-frame-function action)
(gnus-other-frame-resume-function action))
(gnus-other-frame)))
(t (if (not (gnus-alive-p))
(funcall action))))))
(defun org-gnus-no-new-news-other-frame ()
"Like `org-gnus-no-new-news' but always in another frame."
(org-gnus-no-new-news t))
(provide 'ol-gnus)

View file

@ -38,7 +38,7 @@
;; Declare external functions and variables
(declare-function Info-find-node "info"
(filename nodename &optional no-going-back strict-case))
(filename nodename &optional no-going-back strict-case noerror))
(defvar Info-current-file)
(defvar Info-current-node)
@ -68,11 +68,12 @@
(org-info-follow-link path))
(defun org-info--link-file-node (path)
"Extract file name and node from info link PATH.
"Extract the file name and Info node from the Info link PATH.
Return cons consisting of file name and node name or \"Top\" if node
part is not specified. Components may be separated by \":\" or by \"#\".
File may be a virtual one, see `Info-virtual-files'."
Return a cons consisting of the file name and node name or \"Top\" if
the node part is not specified. Components may be separated by \":\"
or by \"#\". The file name may be a virtual one, see
`Info-virtual-files'."
(if (not path)
'("dir" . "Top")
(string-match "\\`\\([^#:]*\\)\\(?:[#:]:?\\(.*\\)\\)?\\'" path)
@ -84,7 +85,7 @@ File may be a virtual one, see `Info-virtual-files'."
(if (org-string-nw-p node) (org-trim node) "Top")))))
(defun org-info-description-as-command (link desc)
"Info link description that can be pasted as command.
"Return an Info link description that can be evaluated as a command.
For the following LINK
@ -99,8 +100,8 @@ that may be executed as shell command or evaluated by
in Emacs.
Calling convention is similar to `org-link-make-description-function'.
DESC has higher priority and returned when it is not nil or empty string.
If LINK is not an info link then DESC is returned."
DESC has higher priority and returned when it is both non-nil and
non-empty. If LINK is not an Info link, DESC is returned."
(let* ((prefix "info:")
(need-file-node (and (not (org-string-nw-p desc))
(string-prefix-p prefix link))))
@ -144,19 +145,20 @@ Taken from <https://www.gnu.org/software/emacs/manual/html_mono/.>")
("libc" . "https://www.gnu.org/software/libc/manual/html_mono/libc.html")
("make" . "https://www.gnu.org/software/make/manual/make.html"))
"Alist of documents generated from Texinfo source.
When converting info links to HTML, links to any one of these manuals are
When converting Info links to HTML, links to any one of these manuals are
converted to use these URL."
:group 'org-link
:type '(alist :key-type string :value-type string)
:package-version '(Org . "9.7")
:safe t)
:safe #'listp)
(defun org-info-map-html-url (filename)
"Return URL or HTML file associated to Info FILENAME.
If FILENAME refers to an official GNU document, return a URL pointing to
the official page for that document, e.g., use \"gnu.org\" for all Emacs
related documents. Otherwise, append \".html\" extension to FILENAME.
See `org-info-emacs-documents' and `org-info-other-documents' for details."
"Return the URL or HTML file associated with the Info FILENAME.
If FILENAME refers to an official GNU document, return the URL of the
official page for that document, e.g., use \"gnu.org\" for all Emacs
related documents. Otherwise, append \".html\" to the FILENAME. See
`org-info-emacs-documents' and `org-info-other-documents' for
details."
(cond ((cdr (assoc filename org-info-other-documents)))
((member filename org-info-emacs-documents)
(format "https://www.gnu.org/software/emacs/manual/html_mono/%s.html"
@ -164,7 +166,7 @@ See `org-info-emacs-documents' and `org-info-other-documents' for details."
(t (concat filename ".html"))))
(defun org-info--expand-node-name (node)
"Expand Info NODE to HTML cross reference."
"Expand the Info NODE to an HTML cross reference."
;; See (info "(texinfo) HTML Xref Node Name Expansion") for the
;; expansion rule.
(let ((node (replace-regexp-in-string
@ -177,7 +179,7 @@ See `org-info-emacs-documents' and `org-info-other-documents' for details."
(t node))))
(defun org-info-export (path desc format)
"Export an info link.
"Export an Info link.
See `org-link-parameters' for details about PATH, DESC and FORMAT."
(pcase-let ((`(,manual . ,node) (org-info--link-file-node path)))
(pcase format

View file

@ -2,7 +2,6 @@
;;
;; Copyright (C) 2020-2026 Free Software Foundation, Inc.
;; Author: Carsten Dominik <carsten.dominik@gmail.com>
;; Maintainer: Bastien Guerry <bzg@gnu.org>
;; Keywords: outlines, hypermedia, calendar, text
;; URL: https://orgmode.org
;;
@ -52,7 +51,8 @@ If PATH contains extra ::STRING which will use `occur' to search
matched strings in man buffer."
(require 'man) ; For `Man-translate-references'
(string-match "\\(.*?\\)\\(?:::\\(.*\\)\\)?$" path)
(let* ((command (match-string 1 path))
(let* ((search (match-string 2 path))
(command (match-string 1 path))
;; FIXME: Remove after we drop Emacs 29 support.
;; Working around security bug #66390.
(command (if (not (equal (Man-translate-references ";id") ";id"))
@ -65,7 +65,6 @@ matched strings in man buffer."
(mapcar #'shell-quote-argument
(split-string command "\\s-+"))
" ")))
(search (match-string 2 path))
(buffer (funcall org-man-command command)))
(when search
(with-current-buffer buffer

File diff suppressed because it is too large Load diff

View file

@ -75,11 +75,9 @@
(declare-function calendar-persian-date-string "cal-persia" (&optional date))
(declare-function calendar-check-holidays "holidays" (date))
(declare-function org-columns-remove-overlays "org-colview" ())
(declare-function org-datetree-find-date-create "org-datetree"
(date &optional keep-restriction))
(declare-function org-columns-quit "org-colview" ())
(declare-function diary-date-display-form "diary-lib" (&optional type))
(declare-function org-mobile-write-agenda-for-mobile "org-mobile" (file))
(declare-function org-habit-insert-consistency-graphs
"org-habit" (&optional line))
@ -1346,7 +1344,7 @@ When set to the symbol `next' only the first future repeat is shown."
:group 'org-agenda-daily/weekly
:type 'boolean
:package-version '(Org . "9.7")
:safe t)
:safe #'booleanp)
(defcustom org-agenda-prefer-last-repeat nil
"Non-nil sets date for repeated entries to their last repeat.
@ -1637,16 +1635,16 @@ symbols are recognized:
time-up Put entries with time-of-day indications first, early first.
time-down Put entries with time-of-day indications first, late first.
timestamp-up Sort by any timestamp, early first.
timestamp-down Sort by any timestamp, late first.
scheduled-up Sort by scheduled timestamp, early first.
scheduled-down Sort by scheduled timestamp, late first.
deadline-up Sort by deadline timestamp, early first.
deadline-down Sort by deadline timestamp, late first.
ts-up Sort by active timestamp, early first.
ts-down Sort by active timestamp, late first.
tsia-up Sort by inactive timestamp, early first.
tsia-down Sort by inactive timestamp, late first.
timestamp-up Sort by any timestamp date, early first.
timestamp-down Sort by any timestamp date, late first.
scheduled-up Sort by scheduled timestamp date, early first.
scheduled-down Sort by scheduled timestamp date, late first.
deadline-up Sort by deadline timestamp date, early first.
deadline-down Sort by deadline timestamp date, late first.
ts-up Sort by active timestamp date, early first.
ts-down Sort by active timestamp date, late first.
tsia-up Sort by inactive timestamp date, early first.
tsia-down Sort by inactive timestamp date, late first.
category-keep Keep the default order of categories, corresponding to the
sequence in `org-agenda-files'.
category-up Sort alphabetically by category, A-Z.
@ -1750,7 +1748,8 @@ An alist with one entry per agenda type. The keys of the
sublists are `agenda', `todo', `search' and `tags'. The values
are format strings.
This format works similar to a printf format, with the following meaning:
This format works similar to a `format' string, with the following
meaning:
%c the category of the item, \"Diary\" for entries from the diary,
or as given by the CATEGORY keyword or derived from the file name
@ -1761,12 +1760,20 @@ This format works similar to a printf format, with the following meaning:
%t the HH:MM time-of-day specification if one applies to the entry
%s Scheduling/Deadline information, a short string
%b show breadcrumbs, i.e., the names of the higher levels
%(expression) Eval EXPRESSION and replace the control string
by the result
%(expression) Eval `(EXPRESSION)' and replace the control string by
the result.
All specifiers work basically like the standard `%s' of printf, but may
contain two additional characters: a question mark just after the `%'
and a whitespace/punctuation character just before the final letter.
`(EXPRESSION)' is evaluated with point and buffer associated with
agenda entry/line being rendered. For example, heading lines will be
evaluated with point in the Org buffer at that corresponding heading.
However, diary records will not be evaluated in an Org buffer, but
inside diary buffer. Auxiliary agenda lines like time grids will be
evaluated with the point inside agenda buffer itself.
All specifiers work basically like the standard `%s' of `format', but
may contain two additional characters: a question mark just after the
`%' and a whitespace/punctuation character just before the final letter.
If the first character after `%' is a question mark, the entire field
will only be included if the corresponding value applies to the current
@ -2018,23 +2025,24 @@ When t, the highest priority entries are bold, lowest priority italic.
However, settings in `org-priority-faces' will overrule these faces.
When this variable is the symbol `cookies', only fontify the
cookies, not the entire task.
This may also be an association list of priority faces, whose
keys are the character values of `org-priority-highest',
`org-priority-default', and `org-priority-lowest' (the default values
are ?A, ?B, and ?C, respectively). The face may be a named face, a
color as a string, or a list like `(:background \"Red\")'.
If it is a color, the variable `org-faces-easy-properties'
determines if it is a foreground or a background color."
This may also be an association list of priority faces, whose keys are
priorities and values are faces. The face may be a named face, a color
as a string, or a list like `(:background \"Red\")'. If it is a color,
the variable `org-faces-easy-properties' determines if it is a
foreground or a background color."
:group 'org-agenda-line-format
:type '(choice
(const :tag "Never" nil)
(const :tag "Defaults" t)
(const :tag "Cookies only" cookies)
(repeat :tag "Specify"
(list (character :tag "Priority" :value ?A)
(choice :tag "Face "
(alist :tag "Association list"
:key-type (choice :tag "Priority"
(character :tag "Character" :value ?A)
(natnum :tag "Number" :value 1))
:value-type (choice :tag "Face "
(string :tag "Color")
(sexp :tag "Face"))))))
(sexp :tag "Face")))))
(defcustom org-agenda-day-face-function nil
"Function called to determine what face should be used to display a day.
@ -2391,7 +2399,7 @@ The following commands are available:
(list
(vector
(if (get 'org-agenda-files 'org-restrict)
"Restricted to single file"
"Restricted to Single File"
"Edit File List")
'(org-edit-agenda-file-list)
(not (get 'org-agenda-files 'org-restrict)))
@ -2829,7 +2837,7 @@ type."
:version "24.4"
:package-version '(Org . "8.0")
:group 'org-agenda-custom-commands
:type '(choice (symbol :tag "No limit" nil)
:type '(choice (const :tag "No limit" nil)
(integer :tag "Max number of entries")
(repeat
(cons (choice :tag "Agenda type"
@ -2847,7 +2855,7 @@ type."
:version "24.4"
:package-version '(Org . "8.0")
:group 'org-agenda-custom-commands
:type '(choice (symbol :tag "No limit" nil)
:type '(choice (const :tag "No limit" nil)
(integer :tag "Max number of TODOs")
(repeat
(cons (choice :tag "Agenda type"
@ -2865,7 +2873,7 @@ type."
:version "24.4"
:package-version '(Org . "8.0")
:group 'org-agenda-custom-commands
:type '(choice (symbol :tag "No limit" nil)
:type '(choice (const :tag "No limit" nil)
(integer :tag "Max number of tagged entries")
(repeat
(cons (choice :tag "Agenda type"
@ -2883,7 +2891,7 @@ to limit entries to in this type."
:version "24.4"
:package-version '(Org . "8.0")
:group 'org-agenda-custom-commands
:type '(choice (symbol :tag "No limit" nil)
:type '(choice (const :tag "No limit" nil)
(integer :tag "Max number of minutes")
(repeat
(cons (choice :tag "Agenda type"
@ -3571,8 +3579,7 @@ This ensures the export commands can easily use it."
(set-buffer bufname)
(while files
(cl-progv vars vals
(org-agenda-write (expand-file-name (pop files) dir)
nil t bufname))))
(org-agenda-write (expand-file-name (pop files) dir) nil t))))
(and (get-buffer bufname)
(kill-buffer bufname)))))))
@ -3590,7 +3597,7 @@ This ensures the export commands can easily use it."
(defvar org-mobile-creating-agendas) ; defined in org-mobile.el
(defvar org-agenda-write-buffer-name "Agenda View")
(defun org-agenda-write (file &optional open nosettings agenda-bufname)
(defun org-agenda-write (file &optional open nosettings _)
"Write the current buffer (an agenda view) as a file.
Depending on the extension of the file name, plain text (.txt),
@ -3603,9 +3610,7 @@ With prefix argument OPEN, open the new file immediately. If
NOSETTINGS is given, do not scope the settings of
`org-agenda-exporter-settings' into the export commands. This is
used when the settings have already been scoped and we do not
wish to overrule other, higher priority settings. If
AGENDA-BUFFER-NAME is provided, use this as the buffer name for
the agenda to write."
wish to overrule other, higher priority settings."
(interactive "FWrite agenda to file: \nP")
(if (or (not (file-writable-p file))
(and (file-exists-p file)
@ -3652,7 +3657,7 @@ the agenda to write."
(message "Org file written to %s" file)))
((member extension '("html" "htm"))
(org-require-package 'htmlize)
(declare-function htmlize-buffer "htmlize" (&optional buffer))
(declare-function htmlize-buffer "ext:htmlize" (&optional buffer))
(set-buffer (htmlize-buffer (current-buffer)))
(when org-agenda-export-html-style
;; replace <style> section with org-agenda-export-html-style
@ -3684,12 +3689,7 @@ the agenda to write."
(org-icalendar-export-current-agenda (expand-file-name file)))
(t
(write-region nil nil file)
(message "Plain text written to %s" file)))))))
(set-buffer (or agenda-bufname
;; FIXME: I'm pretty sure called-interactively-p
;; doesn't do what we want here!
(and (called-interactively-p 'any) (buffer-name))
org-agenda-buffer-name)))
(message "Plain text written to %s" file))))))))
(when open (org-open-file file)))
(defun org-agenda-remove-marked-text (property &optional value)
@ -4130,7 +4130,7 @@ agenda display, configure `org-agenda-finalize-hook'."
org-priority-highest)
l (or (get-char-property (point) 'org-priority-lowest)
org-priority-lowest)
p (string-to-char (match-string 2))
p (org-priority-to-value (match-string 2))
b (match-beginning 1)
e (if (eq org-agenda-fontify-priorities 'cookies)
(1+ (match-end 2))
@ -4819,7 +4819,7 @@ is active."
(setq rtn (list (format "ORG-AGENDA-ERROR: No such org-file %s"
file))))
(with-current-buffer buffer
(with-syntax-table (org-search-syntax-table)
(org-with-syntax-table (org-search-syntax-table)
(unless (derived-mode-p 'org-mode)
(error "Agenda file %s is not in Org mode" file))
(let ((case-fold-search t))
@ -6779,10 +6779,13 @@ scheduled items with an hour specification like [h]h:mm."
'help-echo
(format "mouse-2 or RET jump to org file %s"
(abbreviate-file-name buffer-file-name))))
;; Group 1: starting date timestamp without braces
;; Group 2: ending date timestamp without braces
(regexp (if org-agenda-include-inactive-timestamps
org-tr-regexp-both org-tr-regexp))
(d0 (calendar-absolute-from-gregorian date))
face marker hdmarker ee txt d1 d2 s1 s2 category level
(agenda-today (calendar-absolute-from-gregorian date))
face marker hdmarker block-list txt start-day end-day
category level
todo-state tags pos head donep inherited-tags effort
effort-minutes inactive?)
(goto-char (point-min))
@ -6793,29 +6796,28 @@ scheduled items with an hour specification like [h]h:mm."
(setq inactive? (eq ?\[ (char-after (match-beginning 0))))
(let ((start-time (match-string 1))
(end-time (match-string 2)))
(setq s1 (match-string 1)
s2 (match-string 2)
d1 (time-to-days
(setq start-day (time-to-days
(condition-case err
(org-time-string-to-time s1)
(org-time-string-to-time start-time)
(error
(error
"Bad timestamp %S at %d in buffer %S\nError was: %s"
s1
start-time
pos
(current-buffer)
(error-message-string err)))))
d2 (time-to-days
end-day (time-to-days
(condition-case err
(org-time-string-to-time s2)
(org-time-string-to-time end-time)
(error
(error
"Bad timestamp %S at %d in buffer %S\nError was: %s"
s2
end-time
pos
(current-buffer)
(error-message-string err))))))
(when (and (> (- d0 d1) -1) (> (- d2 d0) -1))
(when (and (> (- agenda-today start-day) -1)
(> (- end-day agenda-today) -1))
;; Only allow days between the limits, because the normal
;; date stamps will catch the limits.
(save-excursion
@ -6823,7 +6825,7 @@ scheduled items with an hour specification like [h]h:mm."
(setq donep (member todo-state org-done-keywords))
(when (and donep org-agenda-skip-timestamp-if-done)
(throw :skip t))
(setq face (if (= d1 d2)
(setq face (if (= start-day end-day)
'org-agenda-calendar-event
'org-agenda-calendar-daterange))
(setq marker (org-agenda-new-marker (point))
@ -6849,29 +6851,36 @@ scheduled items with an hour specification like [h]h:mm."
(let ((remove-re
(if org-agenda-remove-timeranges-from-blocks
(concat
"<" (regexp-quote s1) ".*?>"
"<" (regexp-quote start-time) ".*?>"
"--"
"<" (regexp-quote s2) ".*?>")
"<" (regexp-quote end-time) ".*?>")
nil)))
;; `org-agenda-format-item' automatically creates a
;; time range when
;; `org-agenda-default-appointment-duration' is
;; non-nil and only start/end time is given.
;; We do not want it here, when the range spans
;; multiple days.
(let ((org-agenda-default-appointment-duration nil))
(setq txt (org-agenda-format-item
(concat
(when inactive? org-agenda-inactive-leader)
(format
(nth (if (= d1 d2) 0 1)
(nth (if (= start-day end-day) 0 1)
org-agenda-timerange-leaders)
(1+ (- d0 d1)) (1+ (- d2 d1))))
(1+ (- agenda-today start-day)) (1+ (- end-day start-day))))
(org-add-props head nil
'effort effort
'effort-minutes effort-minutes)
level category tags
(cond
((and (= d1 d0) (= d2 d0))
((and (= start-day agenda-today) (= end-day agenda-today))
(concat "<" start-time ">--<" end-time ">"))
((= d1 d0)
((= start-day agenda-today)
(concat "<" start-time ">"))
((= d2 d0)
((= end-day agenda-today)
(concat "<" end-time ">")))
remove-re))))
remove-re)))))
(org-add-props txt props
'face face
'org-marker marker 'org-hd-marker hdmarker
@ -6881,10 +6890,10 @@ scheduled items with an hour specification like [h]h:mm."
'todo-state todo-state
'urgency (org-get-priority txt)
'priority (org-get-priority txt))
(push txt ee))))
(push txt block-list))))
(goto-char pos)))
;; Sort the entries by expiration date.
(nreverse ee)))
(nreverse block-list)))
;;; Agenda presentation and sorting
@ -6973,7 +6982,8 @@ Any match of REMOVE-RE will be removed from TXT."
(time-grid-trailing-characters (nth 2 org-agenda-time-grid))
(extra (or (and (not habitp) extra) ""))
time
(ts (when dotime (concat
(string-containing-time
(when dotime (concat
(if (stringp dotime) dotime "")
(and org-agenda-search-headline-for-time
;; Do not search inside
@ -6982,49 +6992,61 @@ Any match of REMOVE-RE will be removed from TXT."
(replace-regexp-in-string
org-ts-regexp-both ""
txt)))))
(time-of-day (and dotime (org-get-time-of-day ts)))
stamp plain s0 s1 s2 rtn srp l
(time-of-day (and dotime (org-get-time-of-day string-containing-time)))
timestamp-range? plain-time? date-range-same-day?
time-string start-time end-time rtn
duration breadcrumbs)
(and (derived-mode-p 'org-mode) buffer-file-name
(add-to-list 'org-agenda-contributing-files buffer-file-name))
(when (and dotime time-of-day)
;; Extract starting and ending time and move them to prefix
(when (or (setq stamp (string-match org-stamp-time-of-day-regexp ts))
(setq plain (string-match org-plain-time-of-day-regexp ts)))
(setq s0 (match-string 0 ts)
srp (and stamp (match-end 3))
s1 (match-string (if plain 1 2) ts)
s2 (match-string (if plain 8 (if srp 4 6)) ts))
(when (or (setq timestamp-range?
(string-match org-stamp-time-of-day-regexp
string-containing-time))
(setq plain-time?
(string-match org-plain-time-of-day-regexp
string-containing-time)))
(setq time-string (match-string 0 string-containing-time)
date-range-same-day? (and timestamp-range? (match-end 3))
start-time (match-string (if plain-time? 1 2)
string-containing-time)
end-time (match-string (if plain-time? 8
(if date-range-same-day? 4 6))
string-containing-time))
;; If the times are in TXT (not in DOTIMES), and the prefix will list
;; them, we might want to remove them there to avoid duplication.
;; The user can turn this off with a variable.
(when (and org-prefix-has-time
org-agenda-remove-times-when-in-prefix (or stamp plain)
(string-match (concat (regexp-quote s0) " *") txt)
org-agenda-remove-times-when-in-prefix
(or timestamp-range? plain-time?)
(string-match (concat (regexp-quote time-string) " *") txt)
(not (equal ?\] (string-to-char (substring txt (match-end 0)))))
(if (eq org-agenda-remove-times-when-in-prefix 'beg)
(= (match-beginning 0) 0)
t))
(setq txt (replace-match "" nil nil txt))))
;; Normalize the time(s) to 24 hour.
(when s1 (setq s1 (org-get-time-of-day s1 t)))
(when s2 (setq s2 (org-get-time-of-day s2 t)))
(when start-time (setq start-time (org-get-time-of-day start-time t)))
(when end-time (setq end-time (org-get-time-of-day end-time t)))
;; Try to set s2 if s1 and
;; `org-agenda-default-appointment-duration' are set
(when (and s1 (not s2) org-agenda-default-appointment-duration)
(setq s2
(when (and start-time (not end-time)
org-agenda-default-appointment-duration)
(setq end-time
(org-duration-from-minutes
(+ (org-duration-to-minutes s1 t)
(+ (org-duration-to-minutes start-time t)
org-agenda-default-appointment-duration)
nil t)))
;; Compute the duration
(when s2
(setq duration (- (org-duration-to-minutes s2)
(org-duration-to-minutes s1))))
(when end-time
(setq duration (- (org-duration-to-minutes end-time)
(org-duration-to-minutes start-time))))
;; Format S1 and S2 for display.
(when s1 (setq s1 (format "%5s" (org-get-time-of-day s1 'overtime))))
(when s2 (setq s2 (org-get-time-of-day s2 'overtime))))
(when start-time
(setq start-time (format "%5s" (org-get-time-of-day start-time 'overtime))))
(when end-time
(setq end-time (org-get-time-of-day end-time 'overtime))))
(when (string-match org-tag-group-re txt)
;; Tags are in the string
(if (or (eq org-agenda-remove-tags t)
@ -7061,12 +7083,14 @@ Any match of REMOVE-RE will be removed from TXT."
;; particular heading.
"")))
(if (equal "" s) "" (concat s org-agenda-breadcrumbs-separator))))))
(setq time (cond (s2 (concat
(org-agenda-time-of-day-to-ampm-maybe s1)
"-" (org-agenda-time-of-day-to-ampm-maybe s2)
(setq time (cond (end-time
(concat
(org-agenda-time-of-day-to-ampm-maybe start-time)
"-" (org-agenda-time-of-day-to-ampm-maybe end-time)
(when org-agenda-timegrid-use-ampm " ")))
(s1 (concat
(org-agenda-time-of-day-to-ampm-maybe s1)
(start-time
(concat
(org-agenda-time-of-day-to-ampm-maybe start-time)
(if org-agenda-timegrid-use-ampm
(concat time-grid-trailing-characters " ")
time-grid-trailing-characters)))
@ -7074,13 +7098,12 @@ Any match of REMOVE-RE will be removed from TXT."
category (if (symbolp category) (symbol-name category) category)
level (or with-level ""))
(if (string-match org-link-bracket-re category)
(progn
(setq l (string-width (or (match-string 2) (match-string 1))))
(when (< l (or org-prefix-category-length 0))
(let ((link-width (string-width (or (match-string 2) (match-string 1)))))
(when (< link-width (or org-prefix-category-length 0))
(setq category (copy-sequence category))
(org-add-props category nil
'extra-space (make-string
(- org-prefix-category-length l 1) ?\ ))))
(- org-prefix-category-length link-width 1) ?\ ))))
(when (and org-prefix-category-max-length
(>= (length category) org-prefix-category-max-length))
(setq category (substring category 0 (1- org-prefix-category-max-length)))))
@ -7345,6 +7368,15 @@ The optional argument TYPE tells the agenda type."
(max-entries (cond ((listp org-agenda-max-entries)
(cdr (assoc type org-agenda-max-entries)))
(t org-agenda-max-entries))))
;; Make sure that read-only is not set on entries. Agenda expects
;; all the inserted text to be editable, while e.g. column view
;; may apply read-only text properties in org buffers.
(mapc (lambda (entry)
(remove-text-properties
0 (length entry)
'(read-only nil)
entry))
list)
(when org-agenda-before-sorting-filter-function
(setq list
(delq nil
@ -7356,7 +7388,7 @@ The optional argument TYPE tells the agenda type."
(setq list (org-agenda-limit-entries
list 'effort-minutes max-effort
(lambda (e) (or e (if org-agenda-sort-noeffort-is-high
32767 -1))))))
most-positive-fixnum -1))))))
(when max-todo
(setq list (org-agenda-limit-entries list 'todo-state max-todo)))
(when max-tags
@ -7473,7 +7505,7 @@ The optional argument TYPE tells the agenda type."
(defsubst org-cmp-effort (a b)
"Compare the effort values of string A and B."
(let* ((def (if org-agenda-sort-noeffort-is-high 32767 -1))
(let* ((def (if org-agenda-sort-noeffort-is-high most-positive-fixnum -1))
;; `effort-minutes' property is not directly accessible from
;; the strings, but is stored as a property in `txt'.
(ea (or (get-text-property
@ -7546,8 +7578,8 @@ The optional argument TYPE tells the agenda type."
(cond ((not (or ta tb)) nil)
((not ta) +1)
((not tb) -1)
((funcall (or org-tags-sort-function #'org-string<) ta tb) -1)
((funcall (or org-tags-sort-function #'org-string<) tb ta) +1))))
((org-tags-sort ta tb) -1)
((org-tags-sort tb ta) +1))))
(defsubst org-cmp-time (a b)
"Compare the time-of-day values of strings A and B."
@ -7563,15 +7595,14 @@ When TYPE is \"scheduled\", \"deadline\", \"timestamp\" or
\"timestamp_ia\", compare within each of these type. When TYPE
is the empty string, compare all timestamps without respect of
their type."
(let* ((def (if org-agenda-sort-notime-is-late 99999999 -1))
(ta (or (and (string-match type (or (get-text-property 1 'type a) ""))
(get-text-property 1 'ts-date a))
def))
(tb (or (and (string-match type (or (get-text-property 1 'type b) ""))
(get-text-property 1 'ts-date b))
def)))
(cond ((if ta (and tb (< ta tb)) tb) -1)
((if tb (and ta (< tb ta)) ta) +1))))
(cl-flet ((get-timestamp (entry)
(or (and (string-match type (or (get-text-property 1 'type entry) ""))
(get-text-property 1 'ts-date entry))
(if org-agenda-sort-notime-is-late most-positive-fixnum -1))))
(let ((ta (get-timestamp a))
(tb (get-timestamp b)))
(cond ((< ta tb) -1)
((< tb ta) +1)))))
(defsubst org-cmp-habit-p (a b)
"Compare the todo states of strings A and B."
@ -7581,68 +7612,54 @@ their type."
((and (not ha) hb) +1))))
(defun org-entries-lessp (a b)
"Predicate for sorting agenda entries."
;; The following variables will be used when the form is evaluated.
;; So even though the compiler complains, keep them.
(let ((ss org-agenda-sorting-strategy-selected))
(org-dlet
((timestamp-up (and (org-em 'timestamp-up 'timestamp-down ss)
(org-cmp-ts a b "")))
(timestamp-down (if timestamp-up (- timestamp-up) nil))
(scheduled-up (and (org-em 'scheduled-up 'scheduled-down ss)
(org-cmp-ts a b "scheduled")))
(scheduled-down (if scheduled-up (- scheduled-up) nil))
(deadline-up (and (org-em 'deadline-up 'deadline-down ss)
(org-cmp-ts a b "deadline")))
(deadline-down (if deadline-up (- deadline-up) nil))
(tsia-up (and (org-em 'tsia-up 'tsia-down ss)
(org-cmp-ts a b "timestamp_ia")))
(tsia-down (if tsia-up (- tsia-up) nil))
(ts-up (and (org-em 'ts-up 'ts-down ss)
(org-cmp-ts a b "timestamp")))
(ts-down (if ts-up (- ts-up) nil))
(time-up (and (org-em 'time-up 'time-down ss)
(org-cmp-time a b)))
(time-down (if time-up (- time-up) nil))
(stats-up (and (org-em 'stats-up 'stats-down ss)
(org-cmp-values a b 'org-stats)))
(stats-down (if stats-up (- stats-up) nil))
(priority-up (and (org-em 'priority-up 'priority-down ss)
(org-cmp-values a b 'priority)))
(priority-down (if priority-up (- priority-up) nil))
(urgency-up (and (org-em 'urgency-up 'urgency-down ss)
(org-cmp-values a b 'urgency)))
(urgency-down (if urgency-up (- urgency-up) nil))
(effort-up (and (org-em 'effort-up 'effort-down ss)
(org-cmp-effort a b)))
(effort-down (if effort-up (- effort-up) nil))
(category-up (and (or (org-em 'category-up 'category-down ss)
(memq 'category-keep ss))
(org-cmp-category a b)))
(category-down (if category-up (- category-up) nil))
(category-keep (if category-up +1 nil))
(tag-up (and (org-em 'tag-up 'tag-down ss)
(org-cmp-tag a b)))
(tag-down (if tag-up (- tag-up) nil))
(todo-state-up (and (org-em 'todo-state-up 'todo-state-down ss)
(org-cmp-todo-state a b)))
(todo-state-down (if todo-state-up (- todo-state-up) nil))
(habit-up (and (org-em 'habit-up 'habit-down ss)
(org-cmp-habit-p a b)))
(habit-down (if habit-up (- habit-up) nil))
(alpha-up (and (org-em 'alpha-up 'alpha-down ss)
(org-cmp-alpha a b)))
(alpha-down (if alpha-up (- alpha-up) nil))
(need-user-cmp (org-em 'user-defined-up 'user-defined-down ss))
user-defined-up user-defined-down)
(when (and need-user-cmp org-agenda-cmp-user-defined
(functionp org-agenda-cmp-user-defined))
(setq user-defined-up
(funcall org-agenda-cmp-user-defined a b)
user-defined-down (if user-defined-up (- user-defined-up) nil)))
(cdr (assoc
(eval (cons 'or org-agenda-sorting-strategy-selected) t)
'((-1 . t) (1 . nil) (nil . nil)))))))
"Predicate for sorting agenda entries A and B."
(catch :org-entries-lessp-return
(dolist (strategy org-agenda-sorting-strategy-selected)
(when-let*
((result
(cl-case strategy
(timestamp-up (org-cmp-ts a b ""))
(timestamp-down (org-cmp-ts b a ""))
(scheduled-up (org-cmp-ts a b "scheduled"))
(scheduled-down (org-cmp-ts b a "scheduled"))
(deadline-up (org-cmp-ts a b "deadline"))
(deadline-down (org-cmp-ts b a "deadline"))
(tsia-up (org-cmp-ts a b "timestamp_ia"))
(tsia-down (org-cmp-ts b a "timestamp_ia"))
(ts-up (org-cmp-ts a b "timestamp"))
(ts-down (org-cmp-ts b a "timestamp"))
(time-up (org-cmp-time a b))
(time-down (org-cmp-time b a))
(stats-up (org-cmp-values a b 'org-stats))
(stats-down (org-cmp-values b a 'org-stats))
(priority-up (org-cmp-values a b 'priority))
(priority-down (org-cmp-values b a 'priority))
(urgency-up (org-cmp-values a b 'urgency))
(urgency-down (org-cmp-values b a 'urgency))
(effort-up (org-cmp-effort a b))
(effort-down (org-cmp-effort b a))
(category-up (org-cmp-category a b))
(category-down (org-cmp-category b a))
(category-keep (and (org-cmp-category a b) +1))
(tag-up (org-cmp-tag a b))
(tag-down (org-cmp-tag b a))
(todo-state-up (org-cmp-todo-state a b))
(todo-state-down (org-cmp-todo-state b a))
(habit-up (org-cmp-habit-p a b))
(habit-down (org-cmp-habit-p b a))
(alpha-up (org-cmp-alpha a b))
(alpha-down (org-cmp-alpha b a))
(user-defined-up (unless (functionp org-agenda-cmp-user-defined)
(error "Please set `org-agenda-cmp-user-defined' to a function or remove `user-defined-up' from `org-agenda-sorting-strategy'"))
(funcall org-agenda-cmp-user-defined a b))
(user-defined-down (unless (functionp org-agenda-cmp-user-defined)
(error "Please set `org-agenda-cmp-user-defined' to a function or remove `user-defined-down' from `org-agenda-sorting-strategy'"))
(funcall org-agenda-cmp-user-defined b a))
(t (error "Invalid value %S in `org-agenda-sorting-strategy'" strategy)))))
(cond
((eq -1 result) (throw :org-entries-lessp-return t))
((eq 1 result) (throw :org-entries-lessp-return nil)))))
nil))
;;; Agenda restriction lock
@ -8182,6 +8199,9 @@ the variable `org-agenda-auto-exclude-function'."
(defun org-agenda-filter-completion-function (string _predicate &optional flag)
"Complete a complex filter string.
See the Info Node `(org) Filtering/limiting agenda items'.
FLAG specifies the type of completion operation to perform. This
function is passed as a collection function to `completing-read',
which see."
@ -8213,7 +8233,7 @@ which see."
(`lambda (assoc string table)) ;exact match?
(`(boundaries . ,suffix)
(let ((end (if (string-match "[-+<>=]" suffix)
(match-string 0 suffix)
(match-beginning 0)
(length suffix))))
`(boundaries ,(or begin 0) . ,end)))
(`nil
@ -8460,7 +8480,7 @@ If the line does not have an effort defined, return nil."
;; current line but is stored as a property in `txt'.
(let ((effort (get-text-property 0 'effort-minutes (org-get-at-bol 'txt))))
(funcall op
(or effort (if org-agenda-sort-noeffort-is-high 32767 -1))
(or effort (if org-agenda-sort-noeffort-is-high most-positive-fixnum -1))
value)))
(defun org-agenda-filter-expand-tags (filter &optional no-operator)
@ -8774,7 +8794,7 @@ See also:
(list (car args) sd span)))
(org-agenda-redo)
(org-agenda-find-same-or-today-or-agenda cnt))
(set-window-start nil wstart)))
(set-window-start nil wstart 'noforce)))
(defun org-agenda-earlier (arg)
"Go backward in time by the current span in the agenda buffer.
@ -9243,8 +9263,12 @@ When called with a prefix argument, include all archive files as well."
(org-agenda-error)))
(buffer (marker-buffer marker))
(pos (marker-position marker)))
(switch-to-buffer-other-window buffer)
(widen)
(pop-to-buffer
buffer
`((display-buffer-reuse-window display-buffer-pop-up-window)
(reuse-indirect . buffer)))
(unless (<= (point-min) pos (point-max))
(widen))
(push-mark)
(goto-char pos)
(when (derived-mode-p 'org-mode)
@ -9637,7 +9661,7 @@ if it was hidden in the outline."
(defun org-agenda-cycle-show (&optional n)
"Show the current entry in another window, with default settings.
Default settings are taken from `org-show-context-detail'. When
Default settings are taken from `org-fold-show-context-detail'. When
use repeatedly in immediate succession, the remote entry will
cycle through visibility
@ -10106,15 +10130,15 @@ When called programmatically, FORCE-DIRECTION can be `set', `up',
(org-agenda-date-later (- arg) what))
(defun org-agenda-date-later-minutes (arg)
"Change the time of this item, in units of `org-timestamp-rounding-minutes'."
"Change the time of this item, in units of `org-time-stamp-rounding-minutes'."
(interactive "p")
(setq arg (* arg (cadr org-timestamp-rounding-minutes)))
(setq arg (* arg (cadr org-time-stamp-rounding-minutes)))
(org-agenda-date-later arg 'minute))
(defun org-agenda-date-earlier-minutes (arg)
"Change the time of this item, in units of `org-timestamp-rounding-minutes'."
"Change the time of this item, in units of `org-time-stamp-rounding-minutes'."
(interactive "p")
(setq arg (* arg (cadr org-timestamp-rounding-minutes)))
(setq arg (* arg (cadr org-time-stamp-rounding-minutes)))
(org-agenda-date-earlier arg 'minute))
(defun org-agenda-date-later-hours (arg)

View file

@ -36,7 +36,6 @@
(declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction))
(declare-function org-inlinetask-remove-END-maybe "org-inlinetask" ())
(declare-function org-timestamp-to-now "org" (timestamp-string &optional seconds))
;; From org-element.el
(defvar org-element--cache-avoid-synchronous-headline-re-parsing)
@ -136,6 +135,7 @@ For each symbol present in the list, a property will be created in
the archived entry, with a prefix \"ARCHIVE_\", to remember this
information."
:group 'org-archive
:package-version '(Org . "9.8")
:type '(set :greedy t
(const :tag "Time" time)
(const :tag "File" file)
@ -144,13 +144,30 @@ information."
(const :tag "Priority" priority)
(const :tag "Inherited tags" itags)
(const :tag "Outline path" olpath)
(const :tag "Local tags" ltags)))
(const :tag "Outline parent id" olid)
(const :tag "Local tags" ltags))
:safe #'listp)
(defvar org-archive-hook nil
(defcustom org-archive-hook nil
"Hook run after successfully archiving a subtree.
Hook functions are called with point on the subtree in the
original file. At this stage, the subtree has been added to the
archive location, but not yet deleted from the original file.")
archive location, but not yet deleted from the original file."
:group 'org-archive
:type 'hook
:risky t)
(defcustom org-archive-finalize-hook nil
"Hook run after successfully archiving a subtree in final location.
Hook functions are called with point on the subtree in the
destination file. Compare this with `org-archive-hook', which
runs in the original file. At this stage, the subtree has been
added to the archive location, but not yet deleted from the
original file."
:group 'org-archive
:package-version '(Org . "9.8")
:type 'hook
:risky t)
;;;###autoload
(defun org-add-archive-files (files)
@ -207,8 +224,9 @@ an error if LOCATION is not a valid archive location."
"Move the current subtree to the archive.
The archive can be a certain top-level heading in the current
file, or in a different file. The tree will be moved to that
location, the subtree heading be marked DONE, and the current
time will be added.
location. If `org-archive-mark-done' is non-nil and the subtree
has a TODO keyword, the subtree heading will be marked DONE.
And the current time will be added.
When called with a single prefix argument FIND-DONE, find whole
trees without any open TODO items and archive them (after getting
@ -299,6 +317,9 @@ direct children of this heading."
(olpath . ,(mapconcat #'identity
(org-get-outline-path)
"/"))
(olid . ,(org-with-wide-buffer
(and (org-up-heading-safe)
(org-entry-get (point) "ID"))))
(time . ,time)
(todo . ,(org-entry-get (point) "TODO")))))
;; We first only copy, in case something goes wrong
@ -306,6 +327,7 @@ direct children of this heading."
;; which would lead to duplication of subtrees
(let (this-command) (org-copy-subtree 1 nil t))
(set-buffer buffer)
(org-with-wide-buffer
;; Enforce Org mode for the archive buffer
(if (not (derived-mode-p 'org-mode))
;; Force the mode for future visits.
@ -328,7 +350,6 @@ direct children of this heading."
(org-todo-regexp tr-org-todo-regexp)
(org-todo-line-regexp tr-org-todo-line-regexp))
(goto-char (point-min))
(org-fold-show-all '(headings blocks))
(if (and heading (not (and datetree-date (not datetree-subheading-p))))
(progn
(if (re-search-forward
@ -393,6 +414,7 @@ direct children of this heading."
(point)
(concat "ARCHIVE_" (upcase (symbol-name item)))
value))))
(run-hooks 'org-archive-finalize-hook)
;; Save the buffer, if it is not the same buffer and
;; depending on `org-archive-subtree-save-file-p'.
(unless (eq this-buffer buffer)
@ -401,8 +423,7 @@ direct children of this heading."
(if (boundp 'org-archive-from-agenda)
'from-agenda
'from-org)))
(save-buffer)))
(widen))))
(save-buffer)))))))
;; Here we are back in the original buffer. Everything seems
;; to have worked. So now run hooks, cut the tree and finish
;; up.

View file

@ -437,18 +437,38 @@ adjust `org-attach-id-to-path-function-list'"
"Return a folder path based on `org-attach-id-dir' and ID.
Try id-to-path functions in `org-attach-id-to-path-function-list'
ignoring nils. If EXISTING is non-nil, then return the first path
found in the filesystem. Otherwise return the first non-nil value."
found in the filesystem. Otherwise return the first non-nil value.
The existing paths are searched in
1. `org-attach-id-dir';
2. in \"data/\" dir - the default value of `org-attach-id-dir';
3. if current buffer is a symlink, (1) and (2) searches are repeated
in the `default-directory' of symlink target."
(let ((fun-list org-attach-id-to-path-function-list)
(base-dir (expand-file-name org-attach-id-dir))
(default-base-dir (expand-file-name "data/"))
(fallback-dirs (list (expand-file-name "data/")))
preferred first)
(when (and (buffer-file-name)
(file-symlink-p (buffer-file-name)))
(let ((default-directory
(file-name-directory
(file-truename (buffer-file-name)))))
(cl-pushnew (expand-file-name org-attach-id-dir) fallback-dirs)
(cl-pushnew (expand-file-name "data/") fallback-dirs)))
(setq fallback-dirs (delete base-dir fallback-dirs))
(setq fallback-dirs (seq-filter #'file-directory-p fallback-dirs))
(while (and fun-list
(not preferred))
(let* ((name (funcall (car fun-list) id))
(candidate (and name (expand-file-name name base-dir)))
;; Try the default value `org-attach-id-dir' as a fallback.
(candidate2 (and name (not (equal base-dir default-base-dir))
(expand-file-name name default-base-dir))))
;; Try the default value `org-attach-id-dir', and linked
;; dirs if buffer is a symlink as a fallback.
(fallback-candidates
(and name (mapcar
(lambda (dir) (expand-file-name name dir))
fallback-dirs)))
(fallback-candidates
(seq-filter #'file-directory-p fallback-candidates)))
(setq fun-list (cdr fun-list))
(when candidate
(if (or (not existing) (file-directory-p candidate))
@ -456,10 +476,9 @@ found in the filesystem. Otherwise return the first non-nil value."
(unless first
(setq first candidate)))
(when (and existing
candidate2
(not (file-directory-p candidate))
(file-directory-p candidate2))
(setq preferred candidate2)))))
fallback-candidates
(not (file-directory-p candidate)))
(setq preferred (car fallback-candidates))))))
(or preferred first)))
(defun org-attach-check-absolute-path (dir)
@ -567,7 +586,13 @@ if it would overwrite an existing filename."
"Move/copy/link FILE into the attachment directory of the current outline node.
If VISIT-DIR is non-nil, visit the directory with `dired'.
METHOD may be `cp', `mv', `ln', `lns' or `url' default taken from
`org-attach-method'."
`org-attach-method'.
Return a list (LINK DESCRIPTION), representing the file stored.
When `org-attach-store-link-p' is non-nil, LINK and DESCRIPTION will
be the same as in the link stored.
When `org-attach-store-link-p' is nil, LINK will be an attachment: link
and DESCRIPTION be the file name."
(interactive
(list
(read-file-name "File to keep as an attachment: "
@ -580,7 +605,8 @@ METHOD may be `cp', `mv', `ln', `lns' or `url' default taken from
(setq method (or method org-attach-method))
(when (file-directory-p file)
(setq file (directory-file-name file)))
(let ((basename (file-name-nondirectory file)))
(let ((basename (file-name-nondirectory file))
link description)
(let* ((attach-dir (org-attach-dir 'get-create))
(attach-file (expand-file-name basename attach-dir)))
(cond
@ -599,20 +625,25 @@ METHOD may be `cp', `mv', `ln', `lns' or `url' default taken from
(run-hook-with-args 'org-attach-after-change-hook attach-dir)
(org-attach-tag)
(cond ((eq org-attach-store-link-p 'attached)
(push (list (concat "attachment:" (file-name-nondirectory attach-file))
(file-name-nondirectory attach-file))
org-stored-links))
(setq link (concat "attachment:" (file-name-nondirectory attach-file))
description (file-name-nondirectory attach-file))
(push (list link description) org-stored-links))
((eq org-attach-store-link-p t)
(push (list (concat "file:" file)
(file-name-nondirectory file))
org-stored-links))
(setq link (concat "file:" file)
description (file-name-nondirectory file))
(push (list link description) org-stored-links))
((eq org-attach-store-link-p 'file)
(push (list (concat "file:" attach-file)
(file-name-nondirectory attach-file))
org-stored-links)))
(setq link (concat "file:" attach-file)
description (file-name-nondirectory attach-file))
(push (list link description) org-stored-links))
(t
;; Do not save link, just return.
(setq link (concat "attachment:" (file-name-nondirectory attach-file))
description (file-name-nondirectory attach-file))))
(if visit-dir
(dired attach-dir)
(message "File %S is now an attachment" basename)))))
(message "File %S is now an attachment" basename))
(list link description))))
(defun org-attach-attach-cp ()
"Attach a file by copying it."
@ -753,7 +784,7 @@ Basically, this adds the path to the attachment directory."
(defun org-attach-expand-links (_)
"Expand links in current buffer.
It is meant to be added to `org-export-before-parsing-hook'."
It is meant to be added to `org-export-before-parsing-functions'."
(save-excursion
(while (re-search-forward "attachment:" nil t)
(let ((link (org-element-context)))
@ -778,9 +809,18 @@ It is meant to be added to `org-export-before-parsing-hook'."
See `org-open-file' for details about ARG."
(org-link-open-as-file (org-attach-expand file) arg))
(defun org-attach-preview-file (ov path link)
"Preview attachment with PATH in overlay OV.
LINK is the Org link element being previewed."
(org-with-point-at (org-element-begin link)
(org-link-preview-file
ov (org-attach-expand path) link)))
(org-link-set-parameters "attachment"
:follow #'org-attach-follow
:complete #'org-attach-complete-link)
:complete #'org-attach-complete-link
:preview #'org-attach-preview-file)
(defun org-attach-complete-link ()
"Advise the user with the available files in the attachment directory."
@ -833,7 +873,7 @@ Idea taken from `gnus-dired-attach'."
(get-window-with-predicate
(lambda (window)
(with-current-buffer (window-buffer window)
(eq major-mode 'org-mode))))))
(derived-mode-p 'org-mode))))))
(unless other-win
(user-error
"Can't attach to subtree. No window displaying an Org buffer"))

View file

@ -59,6 +59,7 @@
(declare-function org-clock-update-mode-line "org-clock" (&optional refresh))
(declare-function org-datetree-find-date-create "org-datetree" (date &optional keep-restriction))
(declare-function org-datetree-find-month-create "org-datetree" (d &optional keep-restriction))
(declare-function org-datetree-find-create-hierarchy "org-datetree" (hier-pairs &optional keep-restriction legacy-prop))
(declare-function org-decrypt-entry "org-crypt" ())
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
@ -67,7 +68,6 @@
(declare-function org-element-post-affiliated "org-element" (node))
(declare-function org-encrypt-entry "org-crypt" ())
(declare-function org-insert-link "ol" (&optional complete-file link-location default-description))
(declare-function org-link-make-string "ol" (link &optional description))
(declare-function org-table-analyze "org-table" ())
(declare-function org-table-current-dline "org-table" ())
(declare-function org-table-fix-formulas "org-table" (key replace &optional limit delta remove))
@ -182,10 +182,12 @@ type The type of entry. Valid types are:
plain text to be inserted as it is.
target Specification of where the captured item should be placed.
In Org files, targets usually define a node. Entries will
become children of this node, other types will be added to the
table or list in the body of this node.
In Org files, targets usually define a node. Entries
(type `entry') will become children of this node, other
types will be added to the table or list in the body of
this node.
<file-spec>
Most target specifications contain a file name. If that file
name is the empty string, it defaults to `org-default-notes-file'.
A file can also be given as a variable or as a function called
@ -194,41 +196,73 @@ target Specification of where the captured item should be placed.
Valid values are:
(file \"path/to/file\")
(file <file-spec>)
Text will be placed at the beginning or end of that file
(id \"id of existing Org entry\")
File as child of this entry, or in the body of the entry
(file+headline \"path/to/file\" \"node headline\")
(file+headline <file-spec> \"node headline\")
(file+headline <file-spec> function-returning-string)
(file+headline <file-spec> symbol-containing-string)
Fast configuration if the target heading is unique in the file
(file+olp \"path/to/file\" \"Level 1 heading\" \"Level 2\" ...)
(file+olp <file-spec> \"Level 1 heading\" \"Level 2\" ...)
(file+olp <file-spec> function-returning-list-of-strings)
(file+olp <file-spec> symbol-containing-list-of-strings)
For non-unique headings, the full outline path is safer
(file+regexp \"path/to/file\" \"regexp to find location\")
File to the entry matching regexp
(file+regexp <file-spec> \"regexp to find location\")
File to the entry containing matching regexp
(file+olp+datetree \"path/to/file\" \"Level 1 heading\" ...)
(file+olp+datetree <file-spec> \"Level 1 heading\" ...)
(file+olp+datetree <file-spec> function-returning-list-of-strings)
(file+olp+datetree <file-spec> symbol-containing-list-of-strings)
Will create a heading in a date tree for today's date.
If no heading is given, the tree will be on top level.
To prompt for date instead of using TODAY, use the
:time-prompt property. To create a week-tree, use the
:tree-type property.
(file+function \"path/to/file\" function-finding-location)
(file+function <file-spec> function-finding-location)
A function to find the right location in the file
(clock)
File to the entry that is currently being clocked
(here)
The position of point
The exact position to insert the template
(function function-finding-location)
Most general way: write your own function which both visits
the file and moves point to the right location
For (here) target, the template will be always inserted
in place.
When the target points to headline, the template will
be inserted into the headline body (for non-`entry' types)
or as an immediate child.
When the target points to text inside heading body, the
exact place where the template will be inserted depends
on its type:
entry will be inserted as a child of the Org
heading the point is in.
item, will be inserted in the nearest existing Org
checkitem list, if there is one. The list will be
searched from the point to the end of current
heading body.
table-line will be inserted into the nearest table, if any
searching from point to the end of current
heading body.
plain plain text will be inserted in place.
template The template for creating the capture item.
If it is an empty string or nil, a default template based on
the entry type will be used (see the \"type\" section above).
@ -285,7 +319,13 @@ properties are:
:tree-type When `week', make a week tree instead of the month-day
tree. When `month', make a month tree instead of the
month-day tree.
month-day tree. When any subset of
`(year quarter month week day)', create a
datetree hierarchy with the specified
levels. Can also be a function, in which
case it should take the date as an argument
and generate a list of pairs to pass to
`org-datetree-find-create-hierarchy'.
:unnarrowed Do not narrow the target buffer, simply show the
full buffer. Default is to narrow it so that you
@ -334,6 +374,7 @@ be replaced with content and expanded:
%-escapes, those can be used to fill the expression.
The evaluation happens with Org mode set as major mode
in a temporary buffer.
Examples: %(org-id-new), %(eval default-directory)
%<...> The result of `format-time-string' on the ... format
specification.
%t Time stamp, date only. The time stamp is the current
@ -376,8 +417,10 @@ be replaced with content and expanded:
prompt/completions. Default value and completions as in
%^{prompt|default|...}X are allowed.
%? After completing the template, position cursor here.
%\\1 ... %\\N Insert the text entered at the nth %^{prompt}, where N
is a number, starting from 1.
%\\1 ... %\\N Insert the text entered at the nth %^{prompt} (but not
%^{prompt}X), where N is a number, starting from 1.
%\\*1...%\\*N Same as \\N, but for all the prompts, including
%^{prompt} and %^{prompt}X.
Apart from these general escapes, you can access information specific to
the link type that is created. For example, calling `org-capture' in emails
@ -402,12 +445,17 @@ calendar | %:type %:date
When you need to insert a literal percent sign in the template,
you can escape ambiguous cases with a backward slash, e.g., \\%i."
:group 'org-capture
:package-version '(Org . "9.7")
:package-version '(Org . "9.8")
:set (lambda (s v) (set-default-toplevel-value s (org-capture-upgrade-templates v)))
:type
(let ((file-variants '(choice :tag "Filename "
(file :tag "Literal")
(function :tag "Function")
(variable :tag "Variable")))
(olp-variants '(choice :tag "Outline path"
(repeat :tag "Outline path" :inline t
(string :tag "Headline"))
(function :tag "Function")
(variable :tag "Variable"))))
`(repeat
(choice :value ("" "" entry (file "~/org/notes.org") "")
@ -433,12 +481,14 @@ you can escape ambiguous cases with a backward slash, e.g., \\%i."
(list :tag "File & Headline"
(const :format "" file+headline)
,file-variants
(string :tag " Headline"))
(choice :tag "Headline"
(string :tag "Headline")
(function :tag "Function")
(variable :tag "Variable")))
(list :tag "File & Outline path"
(const :format "" file+olp)
,file-variants
(repeat :tag "Outline path" :inline t
(string :tag "Headline")))
,olp-variants)
(list :tag "File & Regexp"
(const :format "" file+regexp)
,file-variants
@ -446,19 +496,23 @@ you can escape ambiguous cases with a backward slash, e.g., \\%i."
(list :tag "File [ & Outline path ] & Date tree"
(const :format "" file+olp+datetree)
,file-variants
(option (repeat :tag "Outline path" :inline t
(string :tag "Headline"))))
,(append
olp-variants
'((const :tag "Date tree at top level" nil))))
(list :tag "File & function"
(const :format "" file+function)
,file-variants
(function :tag " Function"))
(list :tag "Current clocking task"
(const :format "" clock))
(list :tag "The position at point"
(const :format "" here))
(list :tag "Function"
(const :format "" function)
(function :tag " Function")))
(choice :tag "Template "
(string)
(const :tag "Empty" nil)
(list :tag "File"
(const :format "" file)
(file :tag "Template file"))
@ -480,7 +534,8 @@ you can escape ambiguous cases with a backward slash, e.g., \\%i."
((const :format "%v " :tree-type) (const week))
((const :format "%v " :unnarrowed) (const t))
((const :format "%v " :table-line-pos) (string))
((const :format "%v " :kill-buffer) (const t)))))))))
((const :format "%v " :kill-buffer) (const t))))))))
:risky t)
(defcustom org-capture-before-finalize-hook nil
"Hook that is run right before a capture process is finalized.
@ -610,10 +665,9 @@ key for the capture template otherwise associated with \"d\".
to avoid duplicates.)"
:version "24.3"
:group 'org-capture
:type '(repeat (list :tag "Rule"
(string :tag " Capture key")
(string :tag "Replace by template")
(repeat :tag "Available when"
:type
(let ((available-when
'(repeat :tag "Available when"
(choice
(cons :tag "Condition"
(choice
@ -624,7 +678,16 @@ to avoid duplicates.)"
(const :tag "In mode" in-mode)
(const :tag "Not in mode" not-in-mode))
(regexp))
(function :tag "Custom function"))))))
(function :tag "Custom function")))))
`(repeat
(choice
(list :tag "Short rule"
(string :tag " Capture key")
,available-when)
(list :tag "Full rule"
(string :tag " Capture key")
(string :tag "Replace by template")
,available-when)))))
(defcustom org-capture-use-agenda-date nil
"Non-nil means use the date at point when capturing from agendas.
@ -716,7 +779,6 @@ of the day at point (if any) or the current HH:MM time."
(condition-case error
(org-capture-put :template (org-capture-fill-template))
((error quit)
(if (get-buffer "*Capture*") (kill-buffer "*Capture*"))
(error "Capture abort: %s" (error-message-string error))))
(setq org-capture-clock-keep (org-capture-get :clock-keep))
@ -794,7 +856,7 @@ captured item after finalizing."
(when (and org-capture-clock-was-started
(equal org-clock-marker org-capture-clock-was-started))
;; Looks like the clock we started is still running.
(if org-capture-clock-keep
(if (and org-capture-clock-keep (not org-note-abort))
;; User may have completed clocked heading from the template.
;; Refresh clock mode line.
(org-clock-update-mode-line t)
@ -983,7 +1045,7 @@ for `entry'-type templates"))
(org-capture-put
:initial-target-region
;; Check if the buffer is currently narrowed
(when (org-buffer-narrowed-p)
(when (buffer-narrowed-p)
(cons (point-min) (point-max))))
;; store the current point
(org-capture-put :initial-target-position (point)))
@ -1011,7 +1073,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 ,path ,(and headline (pred stringp)))
(`(file+headline ,path ,headline)
(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
@ -1025,6 +1087,7 @@ Store them in the capture property list."
(org-capture-put-target-region-and-position)
(widen)
(goto-char (point-min))
(setq headline (org-capture-expand-headline headline))
(if (re-search-forward (format org-complex-heading-regexp-format
(regexp-quote headline))
nil t)
@ -1034,8 +1097,9 @@ Store them in the capture property list."
(insert "* " headline "\n")
(forward-line -1)))
(`(file+olp ,path . ,(and outline-path (guard outline-path)))
(let ((m (org-find-olp (cons (org-capture-expand-file path)
outline-path))))
(let* ((expanded-file-path (org-capture-expand-file path))
(m (org-find-olp (cons expanded-file-path
(apply #'org-capture-expand-olp expanded-file-path outline-path)))))
(set-buffer (marker-buffer m))
(org-capture-put-target-region-and-position)
(widen)
@ -1056,8 +1120,9 @@ Store them in the capture property list."
(and (derived-mode-p 'org-mode) (org-at-heading-p)))))
(`(file+olp+datetree ,path . ,outline-path)
(let ((m (if outline-path
(org-find-olp (cons (org-capture-expand-file path)
outline-path))
(let ((expanded-file-path (org-capture-expand-file path)))
(org-find-olp (cons expanded-file-path
(apply #'org-capture-expand-olp expanded-file-path outline-path))))
(set-buffer (org-capture-target-buffer path))
(point-marker))))
(set-buffer (marker-buffer m))
@ -1075,7 +1140,19 @@ Store them in the capture property list."
(pcase (org-capture-get :tree-type)
(`week #'org-datetree-find-iso-week-create)
(`month #'org-datetree-find-month-create)
(_ #'org-datetree-find-date-create))
(`day #'org-datetree-find-date-create)
((pred not) #'org-datetree-find-date-create)
;; NOTE function case needs to be before list case to
;; handle lambda forms correctly
((and (pred functionp) fun)
(lambda (d keep-restriction)
(org-datetree-find-create-hierarchy
(funcall fun d) keep-restriction)))
((and (pred listp) grouping)
(lambda (d keep-restriction)
(funcall #'org-datetree-find-create-entry grouping
d keep-restriction)))
(_ (error "Unrecognized :tree-type")))
(calendar-gregorian-from-absolute
(cond
(org-overriding-default-time
@ -1142,6 +1219,36 @@ Store them in the capture property list."
(org-decrypt-entry)
(and (org-back-to-heading t) (point))))))))
(defun org-capture-expand-headline (headline)
"Expand functions, symbols and headline names for HEADLINE.
When HEADLINE is a function, call it. When it is a variable, return
its value. When it is a string, return it. In any other case, signal
an error."
(let* ((final-headline (cond ((stringp headline) headline)
((functionp headline) (funcall headline))
((and (symbolp headline) (boundp headline))
(symbol-value headline))
(t nil))))
(or final-headline
(error "org-capture: Invalid headline target: %S" headline))))
(defun org-capture-expand-olp (file &rest olp)
"Expand functions, symbols and outline paths in FILE for OLP.
When OLP is a function, call it with no arguments while the current
buffer is the FILE-visiting buffer. When it is a variable, return its
value. When it is a list of string, return it. In any other case,
signal an error."
(let* ((first (car olp))
(final-olp (cond ((not (memq nil (mapcar #'stringp olp))) olp)
((and (not (cdr olp)) (functionp first))
(with-current-buffer (find-file-noselect file)
(funcall first)))
((and (not (cdr olp)) (symbolp first) (boundp first))
(symbol-value first))
(t nil))))
(or final-olp
(error "org-capture: Invalid outline path target: %S" olp))))
(defun org-capture-expand-file (file)
"Expand functions, symbols and file names for FILE.
When FILE is a function, call it. When it is a form, evaluate
@ -1197,6 +1304,8 @@ may have been stored before."
(exact-position (org-capture-get :exact-position))
(insert-here? (org-capture-get :insert-here))
(level 1))
(unless (string-match org-outline-regexp-bol template)
(setq template (concat "* " template)))
(org-capture-verify-tree template)
(when exact-position (goto-char exact-position))
(cond
@ -1226,7 +1335,12 @@ may have been stored before."
(org-fold-region (max 1 (1- (point-max))) (point-max) nil))))
(let ((origin (point-marker)))
(unless (bolp) (insert "\n"))
(org-capture-empty-lines-before)
(org-capture-empty-lines-before
(or (org-capture-get :empty-lines-before)
(org-capture-get :empty-lines)
(when (and (org--blank-before-heading-p)
(not (org-previous-line-empty-p)))
1)))
(let ((beg (point)))
(save-restriction
(when insert-here? (narrow-to-region beg beg))
@ -1305,6 +1419,8 @@ may have been stored before."
(org-capture-empty-lines-before
(and item
(not prepend?)
;; FIXME: We should obey `org-blank-before-new-entry'
;; when :empty-lines* is not given.
(min 1 (or (org-capture-get :empty-lines-before)
(org-capture-get :empty-lines)
0)))))
@ -1353,10 +1469,11 @@ may have been stored before."
(defun org-capture-place-table-line ()
"Place the template as a table line."
(require 'org-table)
(let ((text
(pcase (org-trim (org-capture-get :template))
(let* ((template (org-trim (org-capture-get :template)))
(text
(pcase template
((pred (string-match-p org-table-border-regexp))
"| %?Bad template |")
(concat "| " template))
(text (concat text "\n"))))
(table-line-pos (org-capture-get :table-line-pos))
beg end)
@ -1439,7 +1556,7 @@ the text of the entry, before the first child. If not, place the
template at the beginning or end of the file.
Of course, if exact position has been required, just put it there."
(cond
((org-capture-get :exact-position)
((org-capture-get :insert-here)
(goto-char (org-capture-get :exact-position)))
((org-capture-get :target-entry-p)
;; Place the text into this entry.
@ -1448,6 +1565,8 @@ Of course, if exact position has been required, just put it there."
(org-end-of-meta-data t)
;; Go to end of the entry text, before the next headline.
(outline-next-heading)))
((org-capture-get :exact-position)
(goto-char (org-capture-get :exact-position)))
(t
;; Beginning or end of file.
(goto-char (if (org-capture-get :prepend) (point-min) (point-max)))))
@ -1628,7 +1747,10 @@ The template may still contain \"%?\" for cursor positioning.
INITIAL content and/or ANNOTATION may be specified, but will be overridden
by their respective `org-store-link-plist' properties if present.
Expansion occurs in a temporary Org mode buffer."
Expansion occurs in a temporary Org mode buffer that will be displayed
if the template expansion triggers user prompt. Beware that displaying
the temporary buffer may alter point position in the already displayed
buffers."
(let* ((template (or template (org-capture-get :template)))
(buffer (org-capture-get :buffer))
(file (buffer-file-name (or (buffer-base-buffer buffer) buffer)))
@ -1695,8 +1817,10 @@ Expansion occurs in a temporary Org mode buffer."
(setq template "")
(message "no template") (ding)
(sit-for 1))
(let ((capture-tmp-buffer (generate-new-buffer "*Capture*")))
(unwind-protect
(save-window-excursion
(switch-to-buffer-other-window (get-buffer-create "*Capture*"))
(switch-to-buffer-other-window capture-tmp-buffer)
(erase-buffer)
(setq buffer-file-name nil)
(setq mark-active nil)
@ -1779,7 +1903,9 @@ Expansion occurs in a temporary Org mode buffer."
;; completion in interactive prompts.
(let ((org-inhibit-startup t)) (org-mode))
(org-clone-local-variables buffer "\\`org-")
(let (strings) ; Stores interactive answers.
(let (strings ; Stores interactive answers.
strings-all ; ... include %^{prompt}X answers
)
(save-excursion
(let ((regexp "%\\^\\(?:{\\([^}]*\\)}\\)?\\([CgGLptTuU]\\)?"))
(while (re-search-forward regexp nil t)
@ -1814,6 +1940,7 @@ Expansion occurs in a temporary Org mode buffer."
'org-tags-history))
":")))
(when (org-string-nw-p ins)
(push (concat ":" ins ":") strings-all)
(unless (eq (char-before) ?:) (insert ":"))
(insert ins)
(unless (eq (char-after) ?:) (insert ":"))
@ -1823,13 +1950,18 @@ Expansion occurs in a temporary Org mode buffer."
(lambda (s) (org-insert-link 0 s)))))
(pcase org-capture--clipboards
(`nil nil)
(`(,value) (funcall insert-fun value))
(`(,value)
(funcall insert-fun value)
(push value strings-all))
(`(,first-value . ,_)
(funcall insert-fun
(let ((val
(read-string "Clipboard/kill value: "
first-value
'org-capture--clipboards
first-value)))
(push val strings-all)
val)))
(_ (error "Invalid `org-capture--clipboards' value: %S"
org-capture--clipboards)))))
("p"
@ -1862,16 +1994,19 @@ Expansion occurs in a temporary Org mode buffer."
(point-min-marker)))))))
(value
(org-read-property-value prompt pom default)))
(org-set-property prompt value)))
(org-set-property prompt value)
(push value strings-all)))
((or "t" "T" "u" "U")
;; These are the date/time related ones.
(let* ((upcase? (equal (upcase key) key))
(org-end-time-was-given nil)
(time (org-read-date upcase? t nil prompt)))
(push
(org-insert-timestamp
time (or org-time-was-given upcase?)
(member key '("u" "U"))
nil nil (list org-end-time-was-given))))
nil nil (list org-end-time-was-given))
strings-all)))
(`nil
;; Load history list for current prompt.
(setq org-capture--prompt-history
@ -1881,6 +2016,7 @@ Expansion occurs in a temporary Org mode buffer."
completions
nil nil nil 'org-capture--prompt-history default)
strings)
(push (car strings) strings-all)
(insert (car strings))
;; Save updated history list for current prompt.
(puthash prompt org-capture--prompt-history
@ -1895,6 +2031,14 @@ Expansion occurs in a temporary Org mode buffer."
(unless (org-capture-escaped-%)
(replace-match
(nth (1- (string-to-number (match-string 1))) strings)
nil t))))
;; Replace %*n escapes with nth %^{...} string.
(setq strings-all (nreverse strings-all))
(save-excursion
(while (re-search-forward "%\\\\\\(\\*\\([1-9][0-9]*\\)\\)" nil t)
(unless (org-capture-escaped-%)
(replace-match
(nth (1- (string-to-number (match-string 2))) strings-all)
nil t)))))
;; Make sure there are no empty lines before the text, and that
;; it ends with a newline character or it is empty.
@ -1908,9 +2052,11 @@ Expansion occurs in a temporary Org mode buffer."
(insert "\n"))
;; Return the expanded template and kill the capture buffer.
(untabify (point-min) (point-max))
(buffer-substring-no-properties (point-min) (point-max)))
(when (buffer-live-p capture-tmp-buffer)
(with-current-buffer capture-tmp-buffer
(set-buffer-modified-p nil)
(prog1 (buffer-substring-no-properties (point-min) (point-max))
(kill-buffer (current-buffer))))))
(kill-buffer)))))))
(defun org-capture-escaped-% ()
"Non-nil if % was escaped.
@ -1950,7 +2096,7 @@ marked Sexp are evaluated when this argument is nil."
;; Only mark valid and non-escaped sexp.
((org-capture-escaped-%) nil)
(t
(let ((end (with-syntax-table emacs-lisp-mode-syntax-table
(let ((end (org-with-syntax-table emacs-lisp-mode-syntax-table
(ignore-errors (scan-sexps (1- (point)) 1)))))
(when end
(put-text-property (- (point) 2) end 'org-embedded-elisp t))))))))

View file

@ -50,7 +50,6 @@
(declare-function org-link-heading-search-string "ol" (&optional string))
(declare-function org-link-make-string "ol" (link &optional description))
(declare-function org-table-goto-line "org-table" (n))
(declare-function org-dynamic-block-define "org" (type func))
(declare-function w32-notification-notify "w32fns.c" (&rest params))
(declare-function w32-notification-close "w32fns.c" (&rest params))
(declare-function dbus-list-activatable-names "dbus" (&optional bus))
@ -132,7 +131,7 @@ clocking out."
"Rounding minutes when clocking in or out.
The default value is 0 so that no rounding is done.
When set to a non-integer value, use the car of
`org-timestamp-rounding-minutes', like for setting a timestamp.
`org-time-stamp-rounding-minutes', like for setting a timestamp.
E.g. if `org-clock-rounding-minutes' is set to 5, time is 14:47
and you clock in: then the clock starts at 14:45. If you clock
@ -144,7 +143,7 @@ out time will be 14:50."
:package-version '(Org . "8.0")
:type '(choice
(integer :tag "Minutes (0 for no rounding)")
(symbol :tag "Use `org-time-stamp-rounding-minutes'" 'same-as-time-stamp)))
(const :tag "Use `org-time-stamp-rounding-minutes'" same-as-time-stamp)))
(defcustom org-clock-out-remove-zero-time-clocks nil
"Non-nil means remove the clock line when the resulting time is zero."
@ -741,27 +740,80 @@ pointing to it."
(defvar org-clock-update-period 60
"Number of seconds between mode line clock string updates.")
(defun org-clock-get-clock-string ()
(defun org-clock-get-clock-string (&optional max-length)
"Form a clock-string, that will be shown in the mode line.
If an effort estimate was defined for the current item, use
01:30/01:50 format (clocked/estimated).
If not, show simply the clocked time like 01:50."
(let ((clocked-time (org-clock-get-clocked-time)))
(if org-clock-effort
(let* ((effort-in-minutes (org-duration-to-minutes org-clock-effort))
(work-done-str
(propertize (org-duration-from-minutes clocked-time)
01:30/01:50 format (clocked/estimated). If not, show simply
the clocked time like 01:50.
When the optional MAX-LENGTH argument is given, this function
will preferentially truncate the headline in order to ensure
that the entire clock string's length remains under the
limit."
(let* ((max-string-length (or max-length 0))
(clocked-time (org-clock-get-clocked-time))
(clock-str (org-duration-from-minutes clocked-time))
(clock-format-str (propertize "[%s]" 'face 'org-mode-line-clock))
(clock-format-effort-str (propertize "[%s/%s]"
'face
'org-mode-line-clock))
(mode-line-str-with-headline (propertize "%s (%s) "
'face
'org-mode-line-clock))
(mode-line-str-without-headline (propertize "%s "
'face
'org-mode-line-clock))
(effort-estimate-str (if org-clock-effort
(org-duration-from-minutes
(org-duration-to-minutes
org-clock-effort))
nil))
(time-str (if (not org-clock-effort)
(format clock-format-str clock-str)
(format clock-format-effort-str
(propertize clock-str
'face
(if (and org-clock-task-overrun
(not org-clock-task-overrun-text))
(not
org-clock-task-overrun-text))
'org-mode-line-clock-overrun
'org-mode-line-clock)))
(effort-str (org-duration-from-minutes effort-in-minutes)))
(format (propertize "[%s/%s] (%s) " 'face 'org-mode-line-clock)
work-done-str effort-str org-clock-heading))
(format (propertize "[%s] (%s) " 'face 'org-mode-line-clock)
(org-duration-from-minutes clocked-time)
org-clock-heading))))
'org-mode-line-clock))
effort-estimate-str)))
(spaces-and-parens-length (1+ (length
(format
mode-line-str-with-headline "" ""))))
(untruncated-length (+ spaces-and-parens-length (length time-str)
(length org-clock-heading))))
;; There are three cases for displaying the mode-line clock string.
;; 1. MAX-STRING-LENGTH is zero or greater than UNTRUNCATED-LENGTH
;; - We can display the clock and the headline without truncation
;; 2. MAX-STRING-LENGTH is above zero and less than or equal to
;; (+ SPACES-AND-PARENS-LENGTH (LENGTH TIME-STR))
;; - There isn't enough room to display any of the headline so just
;; display a (truncated) time string
;; 3. ORG-CLOCK-STRING-LIMIT is greater than
;; (+ SPACES-AND-PARENS-LENGTH (LENGTH TIME-STR)) but less than
;; UNTRUNCATED-LENGTH
;; - Intelligently truncate the headline such that the total length of
;; the mode line string is less than ORG-CLOCK-STRING-LIMIT
(cond ((or (<= max-string-length 0)
(>= max-string-length untruncated-length))
(format mode-line-str-with-headline time-str org-clock-heading))
((or (<= max-string-length 0)
(<= max-string-length (+ spaces-and-parens-length
(length time-str))))
(format mode-line-str-without-headline
(substring time-str 0 (min (length time-str)
max-string-length))))
(t
(let ((heading-length (- max-string-length
(+ spaces-and-parens-length
(length time-str)))))
(format mode-line-str-with-headline
time-str
(string-join `(,(substring org-clock-heading
0 heading-length)
""))))))))
(defun org-clock-get-last-clock-out-time ()
"Get the last clock-out time for the current subtree."
@ -781,15 +833,10 @@ When optional argument is non-nil, refresh cached heading."
(when refresh (setq org-clock-heading (org-clock--mode-line-heading)))
(setq org-mode-line-string
(propertize
(let ((clock-string (org-clock-get-clock-string))
(let ((clock-string (org-clock-get-clock-string org-clock-string-limit))
(help-text "Org mode clock is running.\nmouse-1 shows a \
menu\nmouse-2 will jump to task"))
(if (and (> org-clock-string-limit 0)
(> (length clock-string) org-clock-string-limit))
(propertize
(substring clock-string 0 org-clock-string-limit)
'help-echo (concat help-text ": " org-clock-heading))
(propertize clock-string 'help-echo help-text)))
(propertize clock-string 'help-echo help-text))
'local-map org-clock-mode-line-map
'mouse-face 'mode-line-highlight))
(if (and org-clock-task-overrun org-clock-task-overrun-text)
@ -1245,14 +1292,11 @@ If `only-dangling-p' is non-nil, only ask to resolve dangling
(defvar org-x11idle-exists-p
;; Check that x11idle exists. But don't do that on DOS/Windows,
;; since the command definitely does NOT exist there, and invoking
;; COMMAND.COM on MS-Windows is a bad idea -- it hangs.
;; since the command definitely does NOT exist there.
(and (null (memq system-type '(windows-nt ms-dos)))
(eq 0 (call-process-shell-command
(format "command -v %s" org-clock-x11idle-program-name)))
(executable-find org-clock-x11idle-program-name)
;; Check that x11idle can retrieve the idle time
;; FIXME: Why "..-shell-command" rather than just `call-process'?
(eq 0 (call-process-shell-command org-clock-x11idle-program-name))))
(eq 0 (call-process org-clock-x11idle-program-name))))
(defun org-x11-idle-seconds ()
"Return the current X11 idle time in seconds."
@ -1287,6 +1331,8 @@ This routine returns a floating point number."
(org-mac-idle-seconds))
((and (eq window-system 'x) org-x11idle-exists-p)
(org-x11-idle-seconds))
((fboundp 'w32-system-idle-time)
(/ (w32-system-idle-time) 1000.0))
((and
org-logind-dbus-session-path
(dbus-get-property
@ -3251,7 +3297,7 @@ The details of what will be saved are regulated by the variable
org-clock-has-been-used
(not (file-exists-p org-clock-persist-file))))
(with-temp-file org-clock-persist-file
(insert (format ";; %s - %s at %s\n"
(insert (format ";; %s - %s at %s -*- lexical-binding: t; -*-\n"
(file-name-nondirectory org-clock-persist-file)
(system-name)
(format-time-string (org-time-stamp-format t))))
@ -3316,7 +3362,7 @@ The details of what will be saved are regulated by the variable
"Query user when killing Emacs.
This function is added to `kill-emacs-query-functions'."
(let ((buf (org-clocking-buffer)))
(when (and buf (yes-or-no-p "Clock out and save? "))
(when (and buf (yes-or-no-p "Clock out before exiting? "))
(with-current-buffer buf
(org-clock-out)
(save-buffer))))

View file

@ -39,12 +39,11 @@
(declare-function org-clock-sum-today "org-clock" (&optional headline-filter))
(declare-function org-element-extract "org-element-ast" (node))
(declare-function org-element-interpret-data "org-element" (data))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated no-undefer))
(declare-function org-element-parse-secondary-string "org-element" (string restriction &optional parent))
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-restriction "org-element" (element))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-dynamic-block-define "org" (type func))
(declare-function org-link-display-format "ol" (s))
(declare-function org-link-open-from-string "ol" (s &optional arg))
(declare-function face-remap-remove-relative "face-remap" (cookie))
@ -74,7 +73,7 @@ node `(org)Column attributes')."
(defcustom org-columns-modify-value-for-display-function nil
"Function that modifies values for display in column view.
For example, it can be used to cut out a certain part from a time stamp.
For example, it can be used to cut out a certain part from a timestamp.
The function must take 2 arguments:
column-title The title of the column (*not* the property name)
@ -107,9 +106,8 @@ or (LABEL SUMMARIZE COLLECT) where
properties is set, e.g., to return VACATION_DAYS only if
CONFIRMED is true.
Note that the return value can become one value for an higher
order summary, so the function is expected to handle its own
output.
Note that the return value can become one value for a higher-order
summary, so the function is expected to handle its own output.
Types defined in this variable take precedence over those defined
in `org-columns-summary-types-default', which see."
@ -151,10 +149,10 @@ This is the compiled version of the format.")
"Currently active maximum column widths, as a vector.")
(defvar-local org-columns-begin-marker nil
"Points to the position where last a column creation command was called.")
"Points to the position where a column creation command was last called.")
(defvar-local org-columns-top-level-marker nil
"Points to the position where current columns region starts.")
"Points to the position where the current columns region starts.")
(defvar org-columns--time 0.0
"Number of seconds since the epoch, as a floating point number.")
@ -179,7 +177,7 @@ This is the compiled version of the format.")
("@mean" . org-columns--summary-mean-age)
("@min" . org-columns--summary-min-age)
("est+" . org-columns--summary-estimate))
"Map operators to summarize functions.
"Map operators to summary functions.
See `org-columns-summary-types' for details.")
(defun org-columns-content ()
@ -274,12 +272,12 @@ value for ITEM property."
(`(,(or "DEADLINE" "SCHEDULED" "TIMESTAMP") . ,_)
(replace-regexp-in-string org-ts-regexp "[\\1]" value))
(`(,_ ,_ ,_ ,_ nil) value)
;; If PRINTF is set, assume we are displaying a number and
;; If FMT is set, assume we are displaying a number and
;; obey to the format string.
(`(,_ ,_ ,_ ,_ ,printf) (format printf (string-to-number value)))
(`(,_ ,_ ,_ ,_ ,fmt) (format fmt (string-to-number value)))
(_ (error "Invalid column specification format: %S" spec)))))
(defun org-columns--collect-values (&optional compiled-fmt)
(defun org-columns--collect-values (&optional compiled-fmt agenda-marker)
"Collect values for columns on the current line.
Return a list of triplets (SPEC VALUE DISPLAYED) suitable for
@ -287,7 +285,11 @@ Return a list of triplets (SPEC VALUE DISPLAYED) suitable for
This function assumes `org-columns-current-fmt-compiled' is
initialized is set in the current buffer. However, it is
possible to override it with optional argument COMPILED-FMT."
possible to override it with optional argument COMPILED-FMT.
The optional argument AGENDA-MARKER is used when called from the
agenda to pass a marker to the agenda line.
"
(let ((summaries (get-text-property (point) 'org-summaries)))
(mapcar
(lambda (spec)
@ -299,10 +301,18 @@ possible to override it with optional argument COMPILED-FMT."
;; Effort property is not defined. Try
;; to use appointment duration.
org-agenda-columns-add-appointments-to-effort-sum
agenda-marker
(string= p (upcase org-effort-property))
(get-text-property (point) 'duration)
(propertize (org-duration-from-minutes
(get-text-property (point) 'duration))
(get-text-property
(marker-position agenda-marker)
'duration
(marker-buffer agenda-marker))
(propertize
(org-duration-from-minutes
(get-text-property
(marker-position agenda-marker)
'duration
(marker-buffer agenda-marker)))
'face 'org-warning))
"")))
;; A non-nil COMPILED-FMT means we're calling from Org
@ -498,7 +508,11 @@ substring whose `string-width' does not exceed WIDTH."
"Inhibit recomputing of columns on column view startup.")
(defvar org-columns-flyspell-was-active nil
"Remember the state of `flyspell-mode' before column view.
Flyspell-mode can cause problems in columns view, so it is turned off
Flyspell mode can cause problems in columns view, so it is turned off
for the duration of the command.")
(defvar org-columns-org-num-was-active nil
"Remember the state of `org-num-mode' before column view.
Org-num mode can cause problems in columns view, so it is turned off
for the duration of the command.")
(defvar header-line-format)
@ -564,6 +578,8 @@ for the duration of the command.")
(remove-text-properties (point-min) (point-max) '(read-only t))))
(when org-columns-flyspell-was-active
(flyspell-mode 1))
(when org-columns-org-num-was-active
(org-num-mode 1))
(when (local-variable-p 'org-colview-initial-truncate-line-value)
(setq truncate-lines org-colview-initial-truncate-line-value))))
@ -685,7 +701,7 @@ Where possible, use the standard interface for changing this line."
((eq major-mode 'org-agenda-mode)
(org-columns--call action)
;; The following let preserves the current format, and makes
;; sure that in only a single file things need to be updated.
;; sure that only a single file needs to be updated.
(let* ((org-overriding-columns-format org-columns-current-fmt)
(buffer (marker-buffer pom))
(org-agenda-contributing-files
@ -801,8 +817,8 @@ an integer, select that value."
(defun org-colview-construct-allowed-dates (s)
"Construct a list of three dates around the date in S.
This respects the format of the time stamp in S, active or non-active,
and also including time or not. S must be just a time stamp, no text
This respects the format of the timestamp in S, active or non-active,
and also including time or not. S must be just a timestamp, no text
around it."
(when (and s (string-match (concat "^" org-ts-regexp3 "$") s))
(let* ((time (org-parse-time-string s 'nodefaults))
@ -873,7 +889,7 @@ Also sets `org-columns-top-level-marker' to the new position."
Column view applies to the whole buffer if point is before the first
headline. Otherwise, it applies to the first ancestor setting
\"COLUMNS\" property. If there is none, it defaults to the current
headline. With a `\\[universal-argument]' prefix \ argument, GLOBAL,
headline. With a `\\[universal-argument]' prefix argument, GLOBAL,
turn on column view for the whole buffer unconditionally.
When COLUMNS-FMT-STRING is non-nil, use it as the column format."
@ -909,6 +925,9 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format."
(when (setq-local org-columns-flyspell-was-active
(bound-and-true-p flyspell-mode))
(flyspell-mode 0))
(when (setq-local org-columns-org-num-was-active
(bound-and-true-p org-num-mode))
(org-num-mode 0))
(unless (local-variable-p 'org-colview-initial-truncate-line-value)
(setq-local org-colview-initial-truncate-line-value
truncate-lines))
@ -918,15 +937,37 @@ When COLUMNS-FMT-STRING is non-nil, use it as the column format."
(goto-char (car entry))
(org-columns--display-here (cdr entry)))))))))
(defun org-columns--summary-types-completion-function (string pred flag)
(let ((completion-table
(org-completion-table-with-metadata
(lambda (str pred comp)
(complete-with-action comp
(delete-dups
(cons '("" "")
(mapcar #'car
(append org-columns-summary-types
org-columns-summary-types-default))))
str pred))
`(metadata
. ((annotation-function
. ,(lambda (string)
(let* ((doc (ignore-errors
(documentation
(cdr (assoc string
(append org-columns-summary-types
org-columns-summary-types-default))))))
(doc (and doc (substring doc 0 (string-search "\n" doc)))))
(if doc (format " -- %s" doc) "")))))))))
(complete-with-action flag completion-table string pred)))
(defun org-columns-new (&optional spec &rest attributes)
"Insert a new column, to the left of the current column.
Interactively fill attributes for new column. When column format
specification SPEC is provided, edit it instead.
When optional argument attributes can be a list of columns
specifications attributes to create the new column
non-interactively. See `org-columns-compile-format' for
details."
When optional argument ATTRIBUTES is provided, it should be a list of
column specification attributes to create the new column
non-interactively. See `org-columns-compile-format' for details."
(interactive)
(let ((new (or attributes
(let ((prop
@ -935,8 +976,10 @@ details."
(mapcar #'list (org-buffer-property-keys t nil t))
nil nil (nth 0 spec))))
(list prop
;; Discard useless whitespace-only titles.
(org-string-nw-p
(read-string (format "Column title [%s]: " prop)
(nth 1 spec))
(nth 1 spec)))
;; Use `read-string' instead of `read-number'
;; to allow empty width.
(let ((w (read-string
@ -947,12 +990,7 @@ details."
(org-string-nw-p
(completing-read
"Summary: "
(delete-dups
(cons '("") ;Allow empty operator.
(mapcar (lambda (x) (list (car x)))
(append
org-columns-summary-types
org-columns-summary-types-default))))
'org-columns--summary-types-completion-function
nil t (nth 3 spec)))
(org-string-nw-p
(read-string "Format: " (nth 4 spec))))))))
@ -964,7 +1002,7 @@ details."
(org-columns-redo)))
(defun org-columns-delete ()
"Delete the column at point from columns view."
"Delete the column at point from column view."
(interactive)
(let ((spec (nth (org-current-text-column) org-columns-current-fmt-compiled)))
(when (y-or-n-p (format "Are you sure you want to remove column %S? "
@ -1158,13 +1196,13 @@ COMPILED is an alist, as returned by `org-columns-compile-format'."
(mapconcat
(lambda (spec)
(pcase spec
(`(,prop ,title ,width ,op ,printf)
(`(,prop ,title ,width ,op ,fmt)
(concat "%"
(and width (number-to-string width))
prop
(and title (not (equal prop title)) (format "(%s)" title))
(cond ((not op) nil)
(printf (format "{%s;%s}" op printf))
(fmt (format "{%s;%s}" op fmt))
(t (format "{%s}" op)))))))
compiled " "))
@ -1177,26 +1215,30 @@ property the property name, as an upper-case string
title the title field for the columns, as a string
width the column width in characters, can be nil for automatic width
operator the summary operator, as a string, or nil
printf a printf format for computed values, as a string, or nil
format a `format' string for computed values, or nil
This function updates `org-columns-current-fmt-compiled'."
(setq org-columns-current-fmt-compiled nil)
(let ((start 0))
(while (string-match
"%\\([0-9]+\\)?\\([[:alnum:]_-]+\\)\\(?:(\\([^)]+\\))\\)?\
\\(?:{\\([^}]+\\)}\\)?\\s-*"
(rx "%"
(optional (group (+ digit)))
(group (one-or-more (in alnum "_-")))
(optional "(" (group (zero-or-more (not (any ")")))) ")")
(optional "{" (group (zero-or-more (not (any "}")))) "}")
(zero-or-more space))
fmt start)
(setq start (match-end 0))
(let* ((width (and (match-end 1) (string-to-number (match-string 1 fmt))))
(prop (match-string-no-properties 2 fmt))
(title (or (match-string-no-properties 3 fmt) prop))
(operator (match-string-no-properties 4 fmt)))
(title (or (org-string-nw-p (match-string-no-properties 3 fmt)) prop))
(operator (org-string-nw-p (match-string-no-properties 4 fmt))))
(push (if (not operator) (list (upcase prop) title width nil nil)
(let (printf)
(let (fmt)
(when (string-match ";" operator)
(setq printf (substring operator (match-end 0)))
(setq fmt (substring operator (match-end 0)))
(setq operator (substring operator 0 (match-beginning 0))))
(list (upcase prop) title width operator printf)))
(list (upcase prop) title width operator fmt)))
org-columns-current-fmt-compiled)))
(setq org-columns-current-fmt-compiled
(nreverse org-columns-current-fmt-compiled))))
@ -1243,7 +1285,7 @@ properties drawers."
(inminlevel lmax)
(last-level lmax)
(property (car spec))
(printf (nth 4 spec))
(fmt (nth 4 spec))
;; Special properties cannot be collected nor summarized, as
;; they have their own way to be computed. Therefore, ignore
;; any operator attached to them.
@ -1275,7 +1317,7 @@ properties drawers."
(let ((values
(cl-loop for l from (1+ level) to lmax
append (aref lvals l))))
(and values (funcall summarize values printf))))))
(and values (funcall summarize values fmt))))))
;; Leaf values are not summaries: do not mark them.
(when summary
(let* ((summaries-alist (get-text-property pos 'org-summaries))
@ -1331,10 +1373,10 @@ column specification."
(org-columns--compute-spec spec (not (member property seen)))
(push property seen)))))
(defun org-columns--summary-sum (values printf)
(defun org-columns--summary-sum (values fmt)
"Compute the sum of VALUES.
When PRINTF is non-nil, use it to format the result."
(format (or printf "%s") (apply #'+ (mapcar #'string-to-number values))))
When FMT is non-nil, use it to format the result."
(format (or fmt "%s") (apply #'+ (mapcar #'string-to-number values))))
(defun org-columns--summary-currencies (values _)
"Compute the sum of VALUES, with two decimals."
@ -1363,22 +1405,22 @@ When PRINTF is non-nil, use it to format the result."
check-boxes))
(length check-boxes))))
(defun org-columns--summary-min (values printf)
(defun org-columns--summary-min (values fmt)
"Compute the minimum of VALUES.
When PRINTF is non-nil, use it to format the result."
(format (or printf "%s")
When FMT is non-nil, use it to format the result."
(format (or fmt "%s")
(apply #'min (mapcar #'string-to-number values))))
(defun org-columns--summary-max (values printf)
(defun org-columns--summary-max (values fmt)
"Compute the maximum of VALUES.
When PRINTF is non-nil, use it to format the result."
(format (or printf "%s")
When FMT is non-nil, use it to format the result."
(format (or fmt "%s")
(apply #'max (mapcar #'string-to-number values))))
(defun org-columns--summary-mean (values printf)
(defun org-columns--summary-mean (values fmt)
"Compute the mean of VALUES.
When PRINTF is non-nil, use it to format the result."
(format (or printf "%s")
When FMT is non-nil, use it to format the result."
(format (or fmt "%s")
(/ (apply #'+ (mapcar #'string-to-number values))
(float (length values)))))
@ -1600,7 +1642,7 @@ PARAMS is a property list of parameters:
(funcall formatter (point) table params)))
(defun org-columns-dblock-write-default (ipos table params)
"Write out a columnview table at position IPOS in the current buffer.
"Write out a column view table at position IPOS in the current buffer.
TABLE is a table with data as produced by `org-columns--capture-view'.
PARAMS is the parameter property list obtained from the dynamic block
definition."
@ -1752,8 +1794,9 @@ definition."
;; agenda buffer. Since current buffer is
;; changing, we need to force the original
;; compiled-fmt there.
(let ((agenda-marker (point-marker)))
(org-with-point-at m
(org-columns--collect-values compiled-fmt)))
(org-columns--collect-values compiled-fmt agenda-marker))))
cache)))
(forward-line))
(when cache
@ -1762,6 +1805,9 @@ definition."
(when (setq-local org-columns-flyspell-was-active
(bound-and-true-p flyspell-mode))
(flyspell-mode 0))
(when (setq-local org-columns-org-num-was-active
(bound-and-true-p org-num-mode))
(org-num-mode 0))
(dolist (entry cache)
(goto-char (car entry))
(org-columns--display-here (cdr entry)))
@ -1812,7 +1858,7 @@ This will add overlays to the date lines, to show the summary for each day."
(line-end-position))))
(list spec date date)))
(`(,_ ,_ ,_ nil ,_) (list spec "" ""))
(`(,_ ,_ ,_ ,operator ,printf)
(`(,_ ,_ ,_ ,operator ,fmt)
(let* ((summarize (org-columns--summarize operator))
(values
;; Use real values for summary, not
@ -1823,7 +1869,7 @@ This will add overlays to the date lines, to show the summary for each day."
(nth 1 (assoc spec e))))
entries)))
(final (if values
(funcall summarize values printf)
(funcall summarize values fmt)
"")))
(unless (equal final "")
(put-text-property 0 (length final)

View file

@ -61,13 +61,12 @@
(declare-function org-element-contents-begin "org-element" (node))
(declare-function org-element-contents-end "org-element" (node))
(declare-function org-element-post-affiliated "org-element" (node))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading element))
(declare-function org-get-heading "org" (&optional no-tags no-todo no-priority no-comment))
(declare-function org-get-tags "org" (&optional pos local))
(declare-function org-fold-hide-block-toggle "org-fold" (&optional force no-error element))
(declare-function org-link-display-format "ol" (s))
(declare-function org-link-set-parameters "ol" (type &rest rest))
(declare-function org-log-into-drawer "org" ())
(declare-function org-make-tag-string "org" (tags))
(declare-function org-next-visible-heading "org" (arg))
(declare-function org-reduced-level "org" (l))
@ -78,7 +77,6 @@
(declare-function speedbar-line-directory "speedbar" (&optional depth))
(declare-function table--at-cell-p "table" (position &optional object at-column))
(declare-function ob-clojure-eval-with-cmd "ob-clojure" (cmd expanded))
(declare-function org-fold-folded-p "org-fold" (&optional pos spec-or-alias))
(declare-function org-fold-hide-sublevels "org-fold" (levels))
(declare-function org-fold-hide-subtree "org-fold" ())
(declare-function org-fold-region "org-fold" (from to flag &optional spec))
@ -100,6 +98,19 @@
(defvar org-table1-hline-regexp)
(defvar org-fold-core-style)
;;; Emacs < 31 compatibility
(if (fboundp 'completion-table-with-metadata)
(defalias 'org-completion-table-with-metadata #'completion-table-with-metadata)
(defun org-completion-table-with-metadata (table metadata)
"Return new completion TABLE with METADATA.
METADATA should be an alist of completion metadata. See
`completion-metadata' for a list of supported metadata."
(lambda (string pred action)
(if (eq action 'metadata)
`(metadata . ,metadata)
(complete-with-action action table string pred)))))
;;; Emacs < 29 compatibility
@ -180,6 +191,33 @@ back to `window-text-pixel-size' otherwise."
(set-window-buffer nil oldbuffer)
(set-window-dedicated-p nil dedicatedp))))))
(if (fboundp 'with-undo-amalgamate)
(defalias 'org-with-undo-amalgamate 'with-undo-amalgamate)
;; Copied from Emacs source.
(defmacro org-with-undo-amalgamate (&rest body)
"Like `progn' but perform BODY with amalgamated undo barriers.
This allows multiple operations to be undone in a single step.
When undo is disabled this behaves like `progn'."
(declare (indent 0) (debug t))
(let ((handle (make-symbol "--change-group-handle--")))
`(let ((,handle (prepare-change-group))
;; Don't truncate any undo data in the middle of this,
;; otherwise Emacs might truncate part of the resulting
;; undo step: we want to mimic the behavior we'd get if the
;; undo-boundaries were never added in the first place.
(undo-outer-limit nil)
(undo-limit most-positive-fixnum)
(undo-strong-limit most-positive-fixnum))
(unwind-protect
(progn
(activate-change-group ,handle)
,@body)
(progn
(accept-change-group ,handle)
(undo-amalgamate-change-group ,handle)))))))
;;; Emacs < 28.1 compatibility
@ -207,7 +245,7 @@ inserted before concatenating."
(mapcar
(lambda (str)
(when (and str (not (seq-empty-p str))
(string-match "\\(.+\\)/?" str))
(string-match "\\(.+?\\)/?$" str))
(match-string 1 str)))
(cons directory components)))
"/"))))
@ -292,20 +330,16 @@ older than 27.1"
(if tree (push tree elems))
(nreverse elems))))
(defalias 'org-replace-region-contents
(if (> emacs-major-version 30)
#'replace-region-contents
;; The `replace-region-contents' in Emacs<31 does not accept a buffer
;; as SOURCE argument and does not preserve the position well enough.
(lambda (beg end source &optional max-secs max-costs)
(save-restriction
(narrow-to-region beg end)
(let ((eobp (eobp)))
(with-no-warnings
(if (< emacs-major-version 27)
(replace-buffer-contents source)
(replace-buffer-contents source max-secs max-costs)))
(if eobp (goto-char (point-max))))))))
(with-no-warnings ; `replace-buffer-contents' is obsolete in Emacs 31
(cond
((version< emacs-version "27.1")
(defsubst org-replace-buffer-contents (source &optional _max-secs _max-costs)
(replace-buffer-contents source)))
((version< emacs-version "31")
(defalias 'org-replace-buffer-contents #'replace-buffer-contents))
(t
(defsubst org-replace-buffer-contents (source &optional max-secs max-costs)
(replace-region-contents (point-min) (point-max) source max-secs max-costs)))))
(unless (fboundp 'proper-list-p)
;; `proper-list-p' was added in Emacs 27.1. The function below is
@ -502,6 +536,8 @@ Counting starts at 1."
(define-obsolete-function-alias 'org-string-match-p 'string-match-p "9.0")
;;;; Functions and variables from previous releases now obsolete.
(define-obsolete-variable-alias 'org-edit-src-content-indentation
'org-src-content-indentation "Org 9.8")
(define-obsolete-variable-alias 'org-export-ignored-local-variables
'org-element-ignored-local-variables "Org 9.7")
(define-obsolete-function-alias 'org-habit-get-priority
@ -718,19 +754,6 @@ This constant, for example, makes the below code not err:
"use `org-element' library"
"9.0")
;; 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"))
(eval (cons 'let (cons list body))))
;; FIXME: Unused; obsoleted; to be removed.
(defun org-let2 (list1 list2 &rest body) ;FIXME: Where did our karma go?
(declare (indent 2) (obsolete cl-progv "2021"))
(eval (cons 'let (cons list1 (list (cons 'let (cons list2 body)))))))
(make-obsolete 'org-let "to be removed" "9.6")
(make-obsolete 'org-let2 "to be removed" "9.6")
(define-obsolete-function-alias 'org--math-always-on
'org--math-p "9.7")
@ -1020,6 +1043,191 @@ use of this function is for the stuck project list."
(define-obsolete-function-alias 'org-add-angle-brackets
'org-link-add-angle-brackets "9.3")
(declare-function org-link-preview--remove-overlay "ol"
(ov after beg end &optional len))
(declare-function org-link-preview--get-overlays "ol" (&optional beg end))
(declare-function org-link-preview-clear "ol" (&optional beg end))
(declare-function org-link-preview--remove-overlay "ol"
(ov after beg end &optional len))
(declare-function org-attach-expand "org-attach" (file))
(declare-function org-display-inline-image--width "ol" (link))
(declare-function org-image--align "ol" (link))
(declare-function org--create-inline-image "ol" (file width))
(define-obsolete-function-alias 'org-display-inline-remove-overlay
'org-link-preview--remove-overlay "9.8")
(define-obsolete-function-alias 'org--inline-image-overlays
'org-link-preview--get-overlays "9.8")
(define-obsolete-function-alias 'org-remove-inline-images
'org-link-preview-clear "9.8")
(define-obsolete-function-alias 'org-redisplay-inline-images
'org-link-preview-refresh "9.8")
(define-obsolete-variable-alias 'org-inline-image-overlays
'org-link-preview-overlays "9.8")
(defvar org-link-preview-overlays)
(defvar org-link-abbrev-alist-local)
(defvar org-link-abbrev-alist)
(defvar org-link-angle-re)
(defvar org-link-plain-re)
(make-obsolete 'org-display-inline-images
'org-link-preview-region "9.8")
;; FIXME: Unused; obsoleted; to be removed
(defun org-display-inline-images (&optional include-linked refresh beg end)
"Display inline images.
An inline image is a link which follows either of these
conventions:
1. Its path is a file with an extension matching return value
from `image-file-name-regexp' and it has no contents.
2. Its description consists in a single link of the previous
type. In this case, that link must be a well-formed plain
or angle link, i.e., it must have an explicit \"file\" or
\"attachment\" type.
Equip each image with the key-map `image-map'.
When optional argument INCLUDE-LINKED is non-nil, also links with
a text description part will be inlined. This can be nice for
a quick look at those images, but it does not reflect what
exported files will look like.
When optional argument REFRESH is non-nil, refresh existing
images between BEG and END. This will create new image displays
only if necessary.
BEG and END define the considered part. They default to the
buffer boundaries with possible narrowing."
(interactive "P")
(when (display-graphic-p)
(when refresh
(org-link-preview-clear beg end)
(when (fboundp 'clear-image-cache) (clear-image-cache)))
(let ((end (or end (point-max))))
(org-with-point-at (or beg (point-min))
(let* ((case-fold-search t)
(file-extension-re (image-file-name-regexp))
(link-abbrevs (mapcar #'car
(append org-link-abbrev-alist-local
org-link-abbrev-alist)))
;; Check absolute, relative file names and explicit
;; "file:" links. Also check link abbreviations since
;; some might expand to "file" links.
(file-types-re
(format "\\[\\[\\(?:file%s:\\|attachment:\\|[./~]\\)\\|\\]\\[\\(<?\\(?:file\\|attachment\\):\\)"
(if (not link-abbrevs) ""
(concat "\\|" (regexp-opt link-abbrevs))))))
(while (re-search-forward file-types-re end t)
(let* ((link (org-element-lineage
(save-match-data (org-element-context))
'link t))
(linktype (org-element-property :type link))
(inner-start (match-beginning 1))
(path
(cond
;; No link at point; no inline image.
((not link) nil)
;; File link without a description. Also handle
;; INCLUDE-LINKED here since it should have
;; precedence over the next case. I.e., if link
;; contains filenames in both the path and the
;; description, prioritize the path only when
;; INCLUDE-LINKED is non-nil.
((or (not (org-element-contents-begin link))
include-linked)
(and (or (equal "file" linktype)
(equal "attachment" linktype))
(org-element-property :path link)))
;; Link with a description. Check if description
;; is a filename. Even if Org doesn't have syntax
;; for those -- clickable image -- constructs, fake
;; them, as in `org-export-insert-image-links'.
((not inner-start) nil)
(t
(org-with-point-at inner-start
(and (looking-at
(if (char-equal ?< (char-after inner-start))
org-link-angle-re
org-link-plain-re))
;; File name must fill the whole
;; description.
(= (org-element-contents-end link)
(match-end 0))
(progn
(setq linktype (match-string 1))
(match-string 2))))))))
(when (and path (string-match-p file-extension-re path))
(let ((file (if (equal "attachment" linktype)
(progn
(require 'org-attach)
(ignore-errors (org-attach-expand path)))
(expand-file-name path))))
;; Expand environment variables.
(when file (setq file (substitute-in-file-name file)))
(when (and file (file-exists-p file))
(let ((width (org-display-inline-image--width link))
(align (org-image--align link))
(old (get-char-property-and-overlay
(org-element-begin link)
'org-image-overlay)))
(if (and (car-safe old) refresh)
(image-flush (overlay-get (cdr old) 'display))
(let ((image (org--create-inline-image file width)))
(when image
(let ((ov (make-overlay
(org-element-begin link)
(progn
(goto-char
(org-element-end link))
(unless (eolp) (skip-chars-backward " \t"))
(point)))))
;; See bug#59902. We cannot rely
;; on Emacs to update image if the file
;; has changed.
(image-flush image)
(overlay-put ov 'display image)
(overlay-put ov 'face 'default)
(overlay-put ov 'org-image-overlay t)
(overlay-put
ov 'modification-hooks
(list 'org-link-preview--remove-overlay))
(when (boundp 'image-map)
(overlay-put ov 'keymap image-map))
(when align
(overlay-put
ov 'before-string
(propertize
" " 'face 'default
'display
(pcase align
("center" `(space :align-to (- center (0.5 . ,image))))
("right" `(space :align-to (- right ,image)))))))
(push ov org-inline-image-overlays))))))))))))))))
(make-obsolete 'org-toggle-inline-images
'org-link-preview "9.8")
(declare-function org-link-preview-region "ol")
;; FIXME: Unused; obsoleted; to be removed
(defun org-toggle-inline-images (&optional include-linked beg end)
"Toggle the display of inline images.
INCLUDE-LINKED is passed to `org-display-inline-images'."
(interactive "P")
(if (org-link-preview--get-overlays beg end)
(progn
(org-link-preview-clear beg end)
(when (called-interactively-p 'interactive)
(message "Inline image display turned off")))
(org-link-preview-region include-linked nil beg end)
(when (called-interactively-p 'interactive)
(let ((new (org-link-preview--get-overlays beg end)))
(message (if new
(format "%d images displayed inline"
(length new))
"No images to display inline"))))))
;; The function was made obsolete by commit 65399674d5 of 2013-02-22.
;; This make-obsolete call was added 2016-09-01.
(make-obsolete 'org-capture-import-remember-templates
@ -1032,7 +1240,7 @@ use of this function is for the stuck project list."
(org-fold-show-all '(blocks)))
(make-obsolete 'org-show-block-all
"use `org-show-all' instead."
"use `org-fold-show-all' instead."
"9.2")
(define-obsolete-function-alias 'org-get-tags-at 'org-get-tags "9.2")
@ -1071,7 +1279,7 @@ When optional argument ELEMENT is a parsed drawer, as returned by
When buffer positions BEG and END are provided, hide or show that
region as a drawer without further ado."
(declare (obsolete "use `org-hide-drawer-toggle' instead." "9.4"))
(declare (obsolete "use `org-fold-hide-drawer-toggle' instead." "9.4"))
(if (and beg end) (org-fold-region beg end flag 'drawer)
(let ((drawer
(or element
@ -1094,9 +1302,9 @@ region as a drawer without further ado."
(defun org-hide-block-toggle-maybe ()
"Toggle visibility of block at point.
Unlike to `org-hide-block-toggle', this function does not throw
Unlike to `org-fold-hide-block-toggle', this function does not throw
an error. Return a non-nil value when toggling is successful."
(declare (obsolete "use `org-hide-block-toggle' instead." "9.4"))
(declare (obsolete "use `org-fold-hide-block-toggle' instead." "9.4"))
(interactive)
(org-fold-hide-block-toggle nil t))
@ -1468,8 +1676,8 @@ This also applied for speedbar access."
(let* ((m (point-marker))
(item (propertize headline 'org-imenu-marker m 'org-imenu t)))
(push m org-imenu-markers)
(if (>= level last-level)
(push (cons item m) (aref subs level))
(unless (>= level last-level)
(push (cons item
(cl-mapcan #'identity (cl-subseq subs (1+ level))))
(aref subs level))
@ -1567,19 +1775,21 @@ ELEMENT is the element at point."
(when (looking-at-p "\\>") (backward-char))
(org-element-context element))))
(cl-case (org-element-type object)
;; Prevent checks in links due to keybinding conflict with
;; Flyspell.
((citation citation-reference code entity export-snippet inline-babel-call
inline-src-block line-break latex-fragment link macro
inline-src-block line-break latex-fragment macro
statistics-cookie target timestamp verbatim)
nil)
(link
;; Only check link description
(when-let* ((cbeg (org-element-contents-begin object))
(cend (org-element-contents-end object)))
(<= cbeg (point) cend)))
(footnote-reference
;; Only in inline footnotes, within the definition.
(and (eq (org-element-property :type object) 'inline)
(< (save-excursion
(goto-char (org-element-begin object))
(search-forward ":" nil t 2))
(point))))
(<= (org-element-contents-begin object)
(point)
(org-element-contents-end object))))
(otherwise t))))
(defun org-mode-flyspell-verify ()
@ -1607,14 +1817,6 @@ ELEMENT is the element at point."
(let ((case-fold-search t)) (looking-at "[ \t]*#\\+CAPTION:")))
(> (point) (match-end 0))
(org--flyspell-object-check-p element)))
;; Ignore checks in LOGBOOK (or equivalent) drawer.
((let ((log (org-log-into-drawer)))
(and log
(let ((drawer (org-element-lineage element 'drawer)))
(and drawer
(org-string-equal-ignore-case
log (org-element-property :drawer-name drawer))))))
nil)
(t
(cl-case (org-element-type element)
((comment quote-section) t)
@ -1730,7 +1932,7 @@ key."
;;;; Simple
(defun org-mark-jump-unhide (&rest _)
"Make the point visible with `org-show-context' after jumping to the mark."
"Make the point visible with `org-fold-show-context' after jumping to the mark."
(when (and (derived-mode-p 'org-mode)
(org-invisible-p))
(org-fold-show-context 'mark-goto)))
@ -1780,7 +1982,7 @@ key."
(or (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
nil t)
(signal 'outline-before-first-heading nil))
(setq found (and (or invisible-ok (not (org-fold-folded-p)))
(setq found (and (or invisible-ok (not (org-invisible-p)))
(point)))))
(goto-char found)
found)))
@ -1809,7 +2011,7 @@ key."
(if (derived-mode-p 'org-mode)
(save-excursion
(org-back-to-heading)
(if (not (org-fold-folded-p (line-end-position)))
(if (not (org-invisible-p (line-end-position)))
(org-fold-hide-subtree)
(org-fold-show-children)
(org-fold-show-entry 'hide-drawers)))
@ -1825,6 +2027,26 @@ key."
"configure `org-speed-commands' instead." "9.5")
(provide 'org-compat)
;;;; yank-media
;; Emacs 29's pgtk port has a bug where it might fail to return the
;; right TARGET. Install a workaround for Emacs <=29 since the fix
;; went to Emacs 30. See bug#72254.
;; Org bug report link: https://list.orgmode.org/orgmode/87ed7kttoa.fsf@k-7.ch
;; This should be removed once we drop Emacs 29 support.
(when (and (fboundp 'pgtk-get-selection-internal)
(<= emacs-major-version 29))
;; Only define the method if it hasn't been previously defined.
(unless (cl-find-method 'gui-backend-get-selection nil
'((eql 'CLIPBOARD) (eql 'TARGETS)
((&context . window-system) eql 'pgtk)))
(cl-defmethod gui-backend-get-selection ((selection-symbol (eql 'CLIPBOARD))
(target-type (eql 'TARGETS))
&context (window-system pgtk))
(let ((sel (pgtk-get-selection-internal selection-symbol target-type)))
(if (vectorp sel)
sel
(vector sel))))))
;; Local variables:
;; generated-autoload-file: "org-loaddefs.el"
;; End:

View file

@ -81,7 +81,6 @@
(declare-function org-make-tags-matcher "org" (match &optional only-local-tags))
(declare-function org-previous-visible-heading "org" (arg))
(declare-function org-scan-tags "org" (action matcher todo-only &optional start-level))
(declare-function org-set-property "org" (property value))
(declare-function org-cycle-set-startup-visibility "org-cycle" ())
(defgroup org-crypt nil

View file

@ -40,27 +40,27 @@
(declare-function org-element-post-affiliated "org-element" (node))
(declare-function org-element-lineage "org-element-ast" (datum &optional types with-self))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-display-inline-images "org" (&optional include-linked refresh beg end))
(declare-function org-get-tags "org" (&optional pos local fontify))
(declare-function org-link-preview-region "ol" (&optional include-linked refresh beg end))
(declare-function org-get-tags "org" (&optional epom local))
(declare-function org-subtree-end-visible-p "org" ())
(declare-function org-narrow-to-subtree "org" (&optional element))
(declare-function org-next-visible-heading "org" (arg))
(declare-function org-at-property-p "org" ())
(declare-function org-re-property "org" (property &optional literal allow-null value))
(declare-function org-remove-inline-images "org" (&optional beg end))
(declare-function org-item-beginning-re "org" ())
(declare-function org-link-preview-clear "ol" (&optional beg end))
(declare-function org-item-beginning-re "org-list" ())
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
(declare-function org-at-item-p "org" ())
(declare-function org-at-item-p "org-list" ())
(declare-function org-before-first-heading-p "org" ())
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading element))
(declare-function org-entry-end-position "org" ())
(declare-function org-try-cdlatex-tab "org" ())
(declare-function org-cycle-level "org" ())
(declare-function org-table-next-field "org-table" ())
(declare-function org-table-justify-field-maybe "org-table" (&optional new))
(declare-function org-inlinetask-at-task-p "org-inlinetask" ())
(declare-function org-inlinetask-toggle-visibility "org-inlinetask" ())
(declare-function org-inlinetask-toggle-visibility "org-inlinetask" (&optional state))
(declare-function org-list-get-all-items "org-list" (item struct prevs))
(declare-function org-list-get-bottom-point "org-list" (struct))
(declare-function org-list-prevs-alist "org-list" (struct))
@ -71,7 +71,6 @@
(declare-function org-list-struct "org-list" ())
(declare-function org-cycle-item-indentation "org-list" ())
(declare-function outline-previous-heading "outline" ())
(declare-function outline-next-heading "outline" ())
(declare-function outline-end-of-heading "outline" ())
(declare-function outline-up-heading "outline" (arg &optional invisible-ok))
@ -217,7 +216,7 @@ the values `folded', `children', or `subtree'."
(defcustom org-cycle-hook '(org-cycle-hide-archived-subtrees
org-cycle-show-empty-lines
org-cycle-optimize-window-after-visibility-change
org-cycle-display-inline-images)
org-cycle-display-link-previews)
"Hook that is run after `org-cycle' has changed the buffer visibility.
The function(s) in this hook must accept a single argument which indicates
the new state that was set by the most recent `org-cycle' command. The
@ -237,12 +236,17 @@ normal outline commands like `show-all', but not with the cycling commands."
:group 'org-cycle
:type 'boolean)
(defcustom org-cycle-inline-images-display nil
"Non-nil means auto display inline images under subtree when cycling."
(defvaralias 'org-cycle-inline-images-display
'org-cycle-link-previews-display
"Non-nil means auto display inline images under subtree when cycling.")
(defcustom org-cycle-link-previews-display nil
"Non-nil means auto display link previews under subtree when cycling."
:group 'org-startup
:group 'org-cycle
:package-version '(Org . "9.6")
:type 'boolean)
:package-version '(Org . "9.8")
:type 'boolean
:safe #'booleanp)
(defvaralias 'org-tab-first-hook 'org-cycle-tab-first-hook)
(defvar org-cycle-tab-first-hook nil
@ -514,7 +518,7 @@ Use `\\[org-edit-special]' to edit table.el tables"))
(forward-line 1)
(if (eq org-fold-core-style 'text-properties)
(while (and (not (eobp)) ;this is like `next-line'
(org-fold-folded-p (1- (point))))
(org-invisible-p (1- (point))))
(goto-char (org-fold-next-visibility-change nil nil t))
(and (eolp) (forward-line 1)))
(while (and (not (eobp)) ;this is like `next-line'
@ -745,7 +749,9 @@ symbols `content', `all', `folded', `children', or `subtree'."
The region to be covered depends on STATE when called through
`org-cycle-hook'. Lisp program can use t for STATE to get the
entire buffer covered. Note that an empty line is only shown if there
are at least `org-cycle-separator-lines' empty lines before the headline."
are at least `org-cycle-separator-lines' empty lines before the headline.
Always show empty lines at the end of file."
(when (/= org-cycle-separator-lines 0)
(save-excursion
(let* ((n (abs org-cycle-separator-lines))
@ -778,11 +784,8 @@ are at least `org-cycle-separator-lines' empty lines before the headline."
;; Never hide empty lines at the end of the file.
(save-excursion
(goto-char (point-max))
(outline-previous-heading)
(outline-end-of-heading)
(when (and (looking-at "[ \t\n]+")
(= (match-end 0) (point-max)))
(org-fold-region (point) (match-end 0) nil 'outline))))
(skip-chars-backward " \t\n")
(org-fold-region (point) (point-max) nil 'outline)))
(defun org-cycle-hide-archived-subtrees (state)
"Re-hide all archived subtrees after a visibility state change.
@ -804,12 +807,15 @@ STATE should be one of the symbols listed in the docstring of
"Subtree is archived and stays closed. Use \
`\\[org-cycle-force-archived]' to cycle it anyway."))))))
(defun org-cycle-display-inline-images (state)
(defalias 'org-cycle-inline-images-display
'org-cycle-display-link-previews)
(defun org-cycle-display-link-previews (state)
"Auto display inline images under subtree when cycling.
It works when `org-cycle-inline-images-display' is non-nil.
It works when `org-cycle-link-previews-display' is non-nil.
STATE is the current outline visibility state. It should be one of
symbols `content', `all', `folded', `children', or `subtree'."
(when org-cycle-inline-images-display
(when org-cycle-link-previews-display
(pcase state
('children
(org-with-wide-buffer
@ -817,19 +823,19 @@ symbols `content', `all', `folded', `children', or `subtree'."
;; If has nested headlines, beg,end only from parent headline
;; to first child headline which reference to upper
;; let-binding `org-next-visible-heading'.
(org-display-inline-images
(org-link-preview-region
nil nil
(point-min) (progn (org-next-visible-heading 1) (point)))))
('subtree
(org-with-wide-buffer
(org-narrow-to-subtree)
;; If has nested headlines, also inline display images under all sub-headlines.
(org-display-inline-images nil nil (point-min) (point-max))))
(org-link-preview-region nil nil (point-min) (point-max))))
('folded
(org-with-wide-buffer
(org-narrow-to-subtree)
(if (numberp (point-max))
(org-remove-inline-images (point-min) (point-max))
(org-link-preview-clear (point-min) (point-max))
(ignore)))))))
(provide 'org-cycle)

View file

@ -24,23 +24,20 @@
;;
;;; Commentary:
;; This file contains code to create entries in a tree where the top-level
;; nodes represent years, the level 2 nodes represent the months, and the
;; level 1 entries days.
;; This file contains code to create entries in a tree where the
;; top-level nodes represent years, the level 2 nodes represent the
;; months, and the level 1 entries days. It also implements
;; extensions to the datetree that allow for other levels such as
;; quarters and weeks.
;;; Code:
(require 'org-macs)
(org-assert-version)
(require 'cal-iso)
(require 'org)
(defvar org-datetree-base-level 1
"The level at which years should be placed in the date tree.
This is normally one, but if the buffer has an entry with a
DATE_TREE (or WEEK_TREE for ISO week entries) property (any
value), the date tree will become a subtree under that entry, so
the base level will be properly adjusted.")
(require 'org-element)
(defcustom org-datetree-add-timestamp nil
"When non-nil, add a time stamp matching date of entry.
@ -59,174 +56,264 @@ If KEEP-RESTRICTION is non-nil, do not widen the buffer.
When it is nil, the buffer will be widened to make sure an existing date
tree can be found. If it is the symbol `subtree-at-point', then the tree
will be built under the headline at point."
(org-datetree--find-create-group d 'day keep-restriction))
(org-datetree-find-create-entry '(year month day) d keep-restriction))
;;;###autoload
(defun org-datetree-find-month-create (d &optional keep-restriction)
"Find or create a month entry for date D.
Compared to `org-datetree-find-date-create' this function creates
entries grouped by month instead of days.
entries grouped by year-month instead of year-month-day.
If KEEP-RESTRICTION is non-nil, do not widen the buffer.
When it is nil, the buffer will be widened to make sure an existing date
tree can be found. If it is the symbol `subtree-at-point', then the tree
will be built under the headline at point."
(org-datetree--find-create-group d 'month keep-restriction))
(defun org-datetree--find-create-group
(d time-grouping &optional keep-restriction)
"Find or create an entry for date D.
If time-period is day, group entries by day.
If time-period is month, then group entries by month."
(setq-local org-datetree-base-level 1)
(save-restriction
(if (eq keep-restriction 'subtree-at-point)
(progn
(unless (org-at-heading-p) (error "Not at heading"))
(widen)
(org-narrow-to-subtree)
(setq-local org-datetree-base-level
(org-get-valid-level (org-current-level) 1)))
(unless keep-restriction (widen))
;; Support the old way of tree placement, using a property
(let ((prop (org-find-property "DATE_TREE")))
(when prop
(goto-char prop)
(setq-local org-datetree-base-level
(org-get-valid-level (org-current-level) 1))
(org-narrow-to-subtree))))
(goto-char (point-min))
(let ((year (calendar-extract-year d))
(month (calendar-extract-month d))
(day (calendar-extract-day d)))
(org-datetree--find-create
"\\([12][0-9]\\{3\\}\\)"
year nil nil nil t)
(org-datetree--find-create
"%d-\\([01][0-9]\\) \\w+"
year month nil nil t)
(when (eq time-grouping 'day)
(org-datetree--find-create
"%d-%02d-\\([0123][0-9]\\) \\w+"
year month day nil t)))))
(org-datetree-find-create-entry '(year month) d keep-restriction))
;;;###autoload
(defun org-datetree-find-iso-week-create (d &optional keep-restriction)
"Find or create an ISO week entry for date D.
Compared to `org-datetree-find-date-create' this function creates
entries ordered by week instead of months.
When it is nil, the buffer will be widened to make sure an existing date
tree can be found. If it is the symbol `subtree-at-point', then the tree
will be built under the headline at point."
(setq-local org-datetree-base-level 1)
(save-restriction
(if (eq keep-restriction 'subtree-at-point)
(progn
(unless (org-at-heading-p) (error "Not at heading"))
(widen)
(org-narrow-to-subtree)
(setq-local org-datetree-base-level
(org-get-valid-level (org-current-level) 1)))
(unless keep-restriction (widen))
;; Support the old way of tree placement, using a property
(let ((prop (org-find-property "WEEK_TREE")))
(when prop
(goto-char prop)
(setq-local org-datetree-base-level
(org-get-valid-level (org-current-level) 1))
(org-narrow-to-subtree))))
(goto-char (point-min))
(require 'cal-iso)
entries grouped by year-week-day instead of year-month-day. If
KEEP-RESTRICTION is non-nil, do not widen the buffer. When it is
nil, the buffer will be widened to make sure an existing date
tree can be found. If it is the symbol `subtree-at-point', then
the tree will be built under the headline at point."
(org-datetree-find-create-entry '(year week day) d keep-restriction))
;;;###autoload
(defun org-datetree-find-create-entry
(time-grouping d &optional keep-restriction)
"Find or create an entry for date D.
Moves point to the beginning of the entry.
TIME-GROUPING specifies the grouping levels of the datetree, and
should be a subset of `(year quarter month week day)'. Weeks are
assigned to years according to ISO-8601. If TIME-GROUPING
contains both `month' and `week', then weeks are assigned to the
month containing Thursday, for consistency with the ISO-8601
year-week rule. If TIME-GROUPING contains `quarter' and `week'
but not `month', quarters are defined as 13-week periods;
otherwise they are defined as 3-month periods.
If KEEP-RESTRICTION is non-nil, do not widen the buffer. When it
is nil, the buffer will be widened to make sure an existing date
tree can be found. If it is the symbol `subtree-at-point', then
the tree will be built under the headline at point.
If `org-datetree-add-timestamp' is non-nil and TIME-GROUPING
includes `day' and a new entry is created, adds a time stamp
after the new headline."
(when-let* ((setdiff (seq-difference time-grouping
'(year quarter month week day))))
(error (format "Unrecognized datetree grouping elements %s" setdiff)))
(let* ((year (calendar-extract-year d))
(month (calendar-extract-month d))
(day (calendar-extract-day d))
(time (org-encode-time 0 0 0 day month year))
(iso-date (calendar-iso-from-absolute
(calendar-absolute-from-gregorian d)))
(weekyear (nth 2 iso-date))
(week (nth 0 iso-date)))
;; ISO 8601 week format is %G-W%V(-%u)
(org-datetree--find-create
"\\([12][0-9]\\{3\\}\\)"
weekyear nil nil (format-time-string "%G" time) t)
(org-datetree--find-create
"%d-W\\([0-5][0-9]\\)"
weekyear week nil (format-time-string "%G-W%V" time) t)
;; For the actual day we use the regular date instead of ISO week.
(org-datetree--find-create
"%d-%02d-\\([0123][0-9]\\) \\w+" year month day nil t))))
(defun org-datetree--find-create
(regex-template year &optional month day insert match-title)
"Find the datetree matched by REGEX-TEMPLATE for YEAR, MONTH, or DAY.
REGEX-TEMPLATE is passed to `format' with YEAR, MONTH, and DAY as
arguments.
If MATCH-TITLE is non-nil, REGEX-TEMPLATE is matched against
heading title and the exact regexp matched against heading line is:
(format org-complex-heading-regexp-format
(format regex-template year month day))
If MATCH-TITLE is nil, the regexp matched against heading line is
REGEX-TEMPLATE:
(format regex-template year month day)
Match group 1 in REGEX-TEMPLATE is compared against the specified date
component. If INSERT is non-nil and there is no match then it is
inserted into the buffer."
(when (or month day)
(org-narrow-to-subtree))
;; ensure that the first match group in REGEX-TEMPLATE
;; is the first inside `org-complex-heading-regexp-format'
(when (and match-title
(not (string-match-p "\\\\(\\?1:" regex-template))
(string-match "\\\\(" regex-template))
(setq regex-template (replace-match "\\(?1:" nil t regex-template)))
(let ((re (if match-title
(format org-complex-heading-regexp-format
(format regex-template year month day))
(format regex-template year month day)))
match)
(goto-char (point-min))
(while (and (setq match (re-search-forward re nil t))
(goto-char (match-beginning 1))
(< (string-to-number (match-string 1)) (or day month year))))
(week (nth 0 iso-date))
(nominal-year
(if (memq 'week time-grouping)
(nth 2 iso-date)
year))
(nominal-month
(if (memq 'week time-grouping)
(calendar-extract-month
;; anchor on Thurs, to be consistent with weekyear
(calendar-gregorian-from-absolute
(calendar-iso-to-absolute
`(,week 4 ,nominal-year))))
month))
(quarter (if (and (memq 'week time-grouping)
(not (memq 'month time-grouping)))
(min 4 (1+ (/ (1- week) 13)))
(1+ (/ (1- nominal-month) 3))))
(found-p
(org-datetree-find-create-hierarchy
(append
(when (memq 'year time-grouping)
(list (list (number-to-string nominal-year)
(org-datetree-comparefun-from-regex
"\\([12][0-9]\\{3\\}\\)"))))
(when (memq 'quarter time-grouping)
(list (list (format "%d-Q%d" nominal-year quarter)
(org-datetree-comparefun-from-regex
"\\([12][0-9]\\{3\\}-Q[1-4]\\)"))))
(when (memq 'month time-grouping)
(list (list (format-time-string
"%Y-%m %B" (org-encode-time 0 0 0 1 nominal-month
nominal-year))
(org-datetree-comparefun-from-regex
"\\([12][0-9]\\{3\\}-[01][0-9]\\) \\w+"))))
(when (memq 'week time-grouping)
(list (list (format-time-string "%G-W%V" time)
(org-datetree-comparefun-from-regex
"\\([12][0-9]\\{3\\}-W[0-5][0-9]\\)"))))
(when (memq 'day time-grouping)
;; Use regular date instead of ISO-week year/month
(list (list (format-time-string
"%Y-%m-%d %A" (org-encode-time 0 0 0 day month year))
(org-datetree-comparefun-from-regex
"\\([12][0-9]\\{3\\}-[01][0-9]-[0123][0-9]\\) \\w+")))))
keep-restriction
;; Support the old way of tree placement, using a property
(cond
((not match)
(goto-char (point-max))
(unless (bolp) (insert "\n"))
(org-datetree-insert-line year month day insert))
((= (string-to-number (match-string 1)) (or day month year))
(forward-line 0))
(t
(forward-line 0)
(org-datetree-insert-line year month day insert)))))
(defun org-datetree-insert-line (year &optional month day text)
(delete-region (save-excursion (skip-chars-backward " \t\n") (point)) (point))
(when (org--blank-before-heading-p) (insert "\n"))
(insert "\n" (make-string org-datetree-base-level ?*) " \n")
(backward-char)
(when month (org-do-demote))
(when day (org-do-demote))
(if text
(insert text)
(insert (format "%d" year))
(when month
(insert
(if day
(format-time-string "-%m-%d %A" (org-encode-time 0 0 0 day month year))
(format-time-string "-%m %B" (org-encode-time 0 0 0 1 month year))))))
(when (and day org-datetree-add-timestamp)
((seq-set-equal-p time-grouping '(year month day))
"DATE_TREE")
((seq-set-equal-p time-grouping '(year month))
"DATE_TREE")
((seq-set-equal-p time-grouping '(year week day))
"WEEK_TREE")))))
(when (memq 'day time-grouping)
(when (and (not found-p) org-datetree-add-timestamp)
(save-excursion
(end-of-line)
(insert "\n")
(org-indent-line)
(org-insert-timestamp
(org-encode-time 0 0 0 day month year)
nil
(eq org-datetree-add-timestamp 'inactive))))
(forward-line 0))
(eq org-datetree-add-timestamp 'inactive)))))))
(defun org-datetree-comparefun-from-regex (sibling-regex)
"Construct comparison function based on regular expression.
The generated comparison function can be used with
`org-datetree-find-create-hierarchy'. SIBLING-REGEX should be a
regex that matches the headline and its siblings, with 1 match
group. Headlines are compared by the lexicographic ordering of
match group 1. The generated function returns -1 if the first
argument is earlier, 1 if later, 0 if equal, or nil if either
argument doesn't match."
(lambda (sibling-title new-title)
(let ((target-match (and (string-match sibling-regex new-title)
(match-string 1 new-title)))
(sibling-match (and (string-match sibling-regex sibling-title)
(match-string 1 sibling-title))))
(cond
((not (and target-match sibling-match)) nil)
((string< sibling-match target-match) -1)
((string> sibling-match target-match) 1)
(t 0)))))
(defun org-datetree-find-create-hierarchy
(hier-pairs &optional keep-restriction legacy-prop)
"Find or create entry in datetree using the full date hierarchy.
Moves point to the beginning of the entry. Returns non-nil if an
existing entry was found, or nil if a new entry was created.
HIER-PAIRS is a list whose first entry corresponds to the outermost element
(e.g. year) and last entry corresponds to the innermost (e.g. day).
Each entry of the list is a pair, the car is the headline for that level
(e.g. \"2024\" or \"2024-12-28 Saturday\"), and the cadr is a
string comparison function for sorting each headline among its
siblings. The comparison function should take 2 arguments,
corresponding to the titles of 2 headlines, and return a negative
number if the first headline is earlier, a positive number if the
second headline is earlier, 0 or t if the headlines are at the
same time, or `nil' if a headline isn't a valid datetree
subheading. For example, HIER-PAIRS could look like
((\"2024\" compare-year-fun)
(\"2024-12 December\" compare-month-fun)
(\"2024-12-28 Saturday\" compare-day-fun))
where compare-month-fun would be some function where
(compare-month-fun \"2024-11 November\" \"2024-12 December\") is
negative, and (compare-month-fun \"2024-12-December\" \"Potato\")
is nil. One way to construct such a comparison function is with
`org-datetree-comparefun-from-regex'.
If KEEP-RESTRICTION is non-nil, do not widen the buffer.
When it is nil, the buffer will be widened to make sure an existing date
tree can be found. If it is the symbol `subtree-at-point', then the tree
will be built under the headline at point.
If LEGACY-PROP is non-nil, the tree is located by searching for a
headline with property LEGACY-PROP, supporting the old way of
tree placement via a property."
(let ((level 1)
found-p)
(save-restriction
;; get the datetree base and narrow to it
(if (eq keep-restriction 'subtree-at-point)
(progn
(unless (org-at-heading-p) (error "Not at heading"))
(widen)
(org-narrow-to-subtree)
(setq level (org-get-valid-level (org-current-level) 1)))
(unless keep-restriction (widen))
;; Support the old way of tree placement, using a property
(let ((prop (and legacy-prop (org-find-property legacy-prop))))
(when prop
(progn
(goto-char prop)
(org-narrow-to-subtree)
(setq level (org-get-valid-level (org-current-level) 1))))))
(cl-loop
for pair in hier-pairs
do
(setq found-p (org-datetree--find-create-subheading
(cadr pair) (car pair) level))
(setq level (1+ level))))
found-p))
(defun org-datetree--find-create-subheading
(compare-fun new-title level)
"Find datetree subheading, or create it if it doesn't exist.
After insertion, move point to beginning of the subheading, and
narrow to its subtree. Returns non-nil if the heading was found,
or nil if a new heading was created.
NEW-TITLE is the title of the subheading to be found or created.
LEVEL is the level of the headline to be found or created.
COMPARE-FUN is a function of 2 arguments for comparing headline
titles; it should return a negative number if the first headline
precedes the second, a positive number if the second number has
precedence, 0 or t if the headlines are at the same time, and nil
if a headline isn't a valid datetree subheading at this level."
(let* ((nstars (if org-odd-levels-only (1- (* 2 level)) level))
(heading-re (format "^\\*\\{%d\\}" nstars))
(sibling (car (org-element-cache-map
(lambda (d)
(when (= (org-element-property :level d) level)
(let ((compare-result
(funcall compare-fun
(org-element-property :raw-value d)
new-title)))
(and compare-result
(or (eq compare-result t) (>= compare-result 0))
d))))
:granularity 'headline
:restrict-elements '(headline)
:next-re heading-re
:fail-re heading-re
:narrow t
:limit-count 1))))
;; go to headline, or first successor sibling, or end of buffer
(if sibling
(goto-char (org-element-property :begin sibling))
(goto-char (point-max))
(unless (bolp) (insert "\n")))
(if (and sibling
(memq (funcall compare-fun
(org-element-property :raw-value sibling)
new-title)
'(0 t)))
;; narrow and return the matched headline
(progn
(org-narrow-to-subtree)
t)
;; insert new headline, narrow, and return it
(delete-region (save-excursion (skip-chars-backward " \t\n") (point)) (point))
(when (org--blank-before-heading-p) (insert "\n"))
(insert
(format "\n%s %s\n"
(make-string nstars ?*)
new-title))
(forward-line -1)
(org-narrow-to-subtree)
nil)))
(defun org-datetree-file-entry-under (txt d)
"Insert a node TXT into the date tree under date D."

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2023-2026 Free Software Foundation, Inc.
;; Author: Ihor Radchenko <yantar92 at posteo dot net>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: data, lisp
;; This file is part of GNU Emacs.
@ -136,6 +137,9 @@
;; properties. This is useful to generate pure (in functional sense)
;; AST.
;;
;; To force resolving deferred properties, you can use
;; `org-element-properties-resolve'.
;;
;; The properties listed in `org-element--standard-properties', except
;; `:deferred' and `:parent' are never considered to have deferred value.
;; This constraint makes org-element API significantly faster.
@ -645,7 +649,7 @@ Return the modified NODE."
(if force-undefer
#'org-element--deferred-resolve-force-rec
#'org-element--deferred-resolve-rec)
node 'set 'no-standard)
node 'set)
node)
(defsubst org-element-properties-mapc (fun node &optional undefer)
@ -728,6 +732,11 @@ nodes. This way,
will yield expected results with contents of another node adopted into
a newly created one.
nil elements in CHILDREN are ignored. This way,
(let ((children nil))
(org-element-create \\='section nil children))
will yield expected results.
When TYPE is `plain-text', CHILDREN must contain a single node -
string. Alternatively, TYPE can be a string. When TYPE is nil or
`anonymous', PROPS must be nil."
@ -735,6 +744,12 @@ string. Alternatively, TYPE can be a string. When TYPE is nil or
(plistp props)
(let ((len (proper-list-p props)))
(and len (cl-evenp len)))))
;; Special case: CHILDREN is a single anonymous node
(when (and (= 1 (length children))
(org-element-type-p (car children) 'anonymous))
(setq children (car children)))
;; Filter out nil values from CHILDREN
(setq children (delq nil children))
;; Assign parray.
(when (and props (not (stringp type)) (not (eq type 'plain-text)))
(let ((node (list 'dummy props)))
@ -750,7 +765,7 @@ string. Alternatively, TYPE can be a string. When TYPE is nil or
(setq props (nbutlast props 2)
ptail nil)
(setcar ptail (nth 2 ptail))
(setcdr ptail (seq-drop ptail 3))))))))
(setcdr ptail (cdddr ptail))))))))
(pcase type
((or `nil `anonymous)
(cl-assert (null props))
@ -761,10 +776,7 @@ string. Alternatively, TYPE can be a string. When TYPE is nil or
((pred stringp)
(if props (org-add-props type props) type))
(_
(if (and (= 1 (length children))
(org-element-type-p (car children) 'anonymous))
(apply #'org-element-adopt (list type props) (car children))
(apply #'org-element-adopt (list type props) children)))))
(apply #'org-element-adopt (list type props) children))))
(defun org-element-copy (datum &optional keep-contents)
"Return a copy of DATUM.
@ -822,6 +834,7 @@ When DATUM is `plain-text', all the properties are removed."
(while contents
(setcar contents (org-element-copy (car contents) t))
(setq contents (cdr contents)))))
(org-element-resolve-deferred node-copy 'force)
node-copy))))
;;;; AST queries

View file

@ -76,20 +76,17 @@
(require 'org-table)
(require 'org-fold-core)
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-escape-code-in-string "org-src" (s))
(declare-function org-src-preserve-indentation-p "org-src" (&optional node))
(declare-function org-macro-escape-arguments "org-macro" (&rest args))
(declare-function org-macro-extract-arguments "org-macro" (s))
(declare-function org-reduced-level "org" (l))
(declare-function org-unescape-code-in-string "org-src" (s))
(declare-function org-inlinetask-outline-regexp "org-inlinetask" ())
(declare-function outline-next-heading "outline" ())
(declare-function outline-previous-heading "outline" ())
(defvar org-complex-heading-regexp)
(defvar org-done-keywords)
(defvar org-edit-src-content-indentation)
(defvar org-src-content-indentation)
(defvar org-match-substring-regexp)
(defvar org-odd-levels-only)
(defvar org-property-drawer-re)
@ -335,6 +332,12 @@ specially in `org-element--object-lex'.")
(append org-element-recursive-objects '(paragraph table-row verse-block))
"List of object or element types that can directly contain objects.")
(defconst org-element-elements-no-affiliated
'(org-data comment clock headline inlinetask item
node-property planning property-drawer
section table-row)
"List of paragraph-level node types that cannot have affiliated keywords.")
(defconst org-element-affiliated-keywords
'("CAPTION" "DATA" "HEADER" "HEADERS" "LABEL" "NAME" "PLOT" "RESNAME" "RESULT"
"RESULTS" "SOURCE" "SRCNAME" "TBLNAME")
@ -504,7 +507,7 @@ past the brackets."
(_ nil)))
(pos (point)))
(when syntax-table
(with-syntax-table syntax-table
(org-with-syntax-table syntax-table
(let ((end (ignore-errors (scan-lists pos 1 0))))
(when end
(goto-char end)
@ -533,13 +536,11 @@ created, all Org related local variables not in this list are copied
to the new buffer. Variables with an unreadable value are also
ignored.")
(cl-defun org-element--generate-copy-script (buffer
(cl-defun org-element--generate-copy-script
(buffer
&key
copy-unreadable
drop-visibility
drop-narrowing
drop-contents
drop-locals)
copy-unreadable drop-visibility drop-narrowing
drop-contents drop-text-properties drop-locals)
"Generate a function duplicating BUFFER.
The copy will preserve local variables, visibility, contents and
@ -549,18 +550,21 @@ BUFFER, contents will be narrowed to that region instead.
When optional key COPY-UNREADABLE is non-nil, do not ensure that all
the copied local variables will be readable in another Emacs session.
When optional keys DROP-VISIBILITY, DROP-NARROWING, DROP-CONTENTS, or
DROP-LOCALS are non-nil, do not preserve visibility, narrowing,
contents, or local variables correspondingly.
When optional keys DROP-VISIBILITY, DROP-NARROWING, DROP-CONTENTS,
DROP-TEXT-PROPERTIES, or DROP-LOCALS are non-nil, do not preserve
visibility, narrowing, contents, text properties of contents, or local
variables correspondingly.
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))))
(let ((str (unless drop-contents
(org-with-wide-buffer
(if drop-text-properties
(substring-no-properties (buffer-string))
(buffer-string)))))
(narrowing
(unless drop-narrowing
(if (org-region-active-p)
@ -849,6 +853,48 @@ and END-OFFSET."
(org-unescape-code-in-string
(org-element--substring element beg-offset end-offset)))
(defvar org-element--cache-diagnostics-level 2
"Detail level of the diagnostics.")
(defvar-local org-element--cache-diagnostics-ring nil
"Ring containing cache process log entries.
The ring size is `org-element--cache-diagnostics-ring-size'.")
(defvar org-element--cache-diagnostics-ring-size 5000
"Size of `org-element--cache-diagnostics-ring'.")
(defvar org-element--cache-self-verify nil
"Activate extra consistency checks for the cache.
This may cause serious performance degradation depending on the value
of `org-element--cache-self-verify-frequency'.
When set to symbol `backtrace', record and display backtrace log if
any inconsistency is detected.")
(defmacro org-element--cache-warn (format-string &rest args)
"Raise warning for org-element-cache.
FORMAT-STRING and ARGS are the same arguments as in `format'."
`(let* ((format-string (funcall #'format ,format-string ,@args))
(format-string
(if (or (not org-element--cache-diagnostics-ring)
(not (eq 'backtrace org-element--cache-self-verify)))
format-string
(prog1
(concat (format "Warning(%s): "
(buffer-name (current-buffer)))
format-string
"\nBacktrace:\n "
(mapconcat #'identity
(ring-elements org-element--cache-diagnostics-ring)
"\n "))
(setq org-element--cache-diagnostics-ring nil)))))
(if (and (boundp 'org-batch-test) org-batch-test)
(error "%s" (concat "org-element--cache: " format-string))
(push (concat "org-element--cache: " format-string) org--warnings)
(display-warning '(org-element org-element-cache)
(concat "org-element--cache: " format-string)))))
;;; Greater elements
;;
@ -944,6 +990,8 @@ Assume point is at the beginning of the block."
:end end
:contents-begin contents-begin
:contents-end contents-end
:robust-begin contents-begin
:robust-end contents-end
:post-blank (count-lines pos-before-blank end)
:post-affiliated post-affiliated)
(cdr affiliated))))))))
@ -985,8 +1033,13 @@ Assume point is at beginning of drawer."
(post-affiliated (point))
;; Empty drawers have no contents.
(contents-begin (progn (forward-line)
(org-skip-whitespace)
(forward-line 0)
(and (< (point) drawer-end-line)
(point))))
(pre-blank (1- (count-lines
post-affiliated
(or contents-begin drawer-end-line))))
(contents-end (and contents-begin drawer-end-line))
(pos-before-blank (progn (goto-char drawer-end-line)
(forward-line)
@ -998,9 +1051,20 @@ Assume point is at beginning of drawer."
(nconc
(list :begin begin
:end end
:pre-blank pre-blank
:drawer-name name
:contents-begin contents-begin
:contents-end contents-end
:robust-begin
(and contents-begin
;; We might be dealing with broken properties
;; drawer. Every change inside is sensitive.
(not (org-string-equal-ignore-case name "PROPERTIES"))
;; Inserting blank line at contents-begin
;; will trigger :pre-blank change and may not
;; be robust.
(min (1+ contents-begin) contents-end))
:robust-end contents-end
:post-blank (count-lines pos-before-blank end)
:post-affiliated post-affiliated)
(cdr affiliated))))))))
@ -1008,9 +1072,10 @@ Assume point is at beginning of drawer."
(defun org-element-drawer-interpreter (drawer contents)
"Interpret DRAWER element as Org syntax.
CONTENTS is the contents of the element."
(format ":%s:\n%s:END:"
(format ":%s:\n%s%s:END:"
(org-element-property :drawer-name drawer)
contents))
(make-string (org-element-property :pre-blank drawer) ?\n)
(or contents "")))
;;;; Dynamic Block
@ -1060,6 +1125,8 @@ Assume point is at beginning of dynamic block."
:arguments arguments
:contents-begin contents-begin
:contents-end contents-end
:robust-begin contents-begin
:robust-end contents-end
:post-blank (count-lines pos-before-blank end)
:post-affiliated post-affiliated)
(cdr affiliated)))))))))
@ -1334,9 +1401,9 @@ Throw `:org-element-deferred-retry' signal at the end."
(org-element--get-cached-string (match-string-no-properties 1)))))
(todo-type
(and todo (if (member todo org-done-keywords) 'done 'todo)))
(priority (and (looking-at "\\[#.\\][ \t]*")
(priority (and (looking-at org-priority-regexp)
(progn (goto-char (match-end 0))
(aref (match-string 0) 2))))
(org-priority-to-value (match-string 2)))))
(commentedp
(and (let ((case-fold-search nil))
(looking-at org-element--headline-comment-re))
@ -1344,10 +1411,13 @@ Throw `:org-element-deferred-retry' signal at the end."
(goto-char (match-end 0))
(skip-chars-forward " \t"))))
(title-start (point))
(tags (when (re-search-forward
"\\(:[[:alnum:]_@#%:]+:\\)[ \t]*$"
(line-end-position)
'move)
(tags (when (progn
;; `org-tag-group-re' includes spaces before tags.
;; Start search before preceding spaces, if any.
;; If there are no spaces before point here,
;; We are not looking at the tags.
(skip-chars-backward " \t")
(re-search-forward org-tag-group-re (line-end-position) 'move))
(goto-char (match-beginning 0))
(mapcar #'org-element--get-cached-string
(org-split-string (match-string-no-properties 1) ":"))))
@ -1494,8 +1564,8 @@ CONTENTS is the contents of the element."
(concat (make-string (if org-odd-levels-only (1- (* level 2)) level)
?*)
(and todo (concat " " todo))
(and priority (format " [#%s]" (org-priority-to-string priority)))
(and commentedp (concat " " org-element-comment-string))
(and priority (format " [#%c]" priority))
" "
(if (and org-footnote-section
(org-element-property :footnote-section-p headline))
@ -1580,8 +1650,8 @@ Alter DATA by side effect."
"Parse org-data.
Return a new syntax node of `org-data' type containing `:begin',
`:contents-begin', `:contents-end', `:end', `:post-blank',
`:post-affiliated', and `:path' properties."
`:pre-blank', `:contents-begin', `:contents-end', `:end',
`:post-blank', `:post-affiliated', and `:path' properties."
(org-with-wide-buffer
(let* ((begin 1)
(contents-begin (progn
@ -1612,6 +1682,7 @@ Return a new syntax node of `org-data' type containing `:begin',
:end end
:robust-begin robust-begin
:robust-end robust-end
:pre-blank (count-lines begin contents-begin)
;; Trailing blank lines in org-data, headlines, and
;; sections belong to the containing elements.
:post-blank 0
@ -1621,10 +1692,12 @@ Return a new syntax node of `org-data' type containing `:begin',
:buffer (current-buffer)
:deferred org-element--get-global-node-properties)))))
(defun org-element-org-data-interpreter (_ contents)
(defun org-element-org-data-interpreter (org-data contents)
"Interpret ORG-DATA element as Org syntax.
CONTENTS is the contents of the element."
contents)
(concat
(make-string (or (org-element-property :pre-blank org-data) 0) ?\n)
contents))
;;;; Inlinetask
@ -1711,7 +1784,7 @@ CONTENTS is the contents of inlinetask."
(format ":%s:" (mapconcat 'identity tag-list ":")))))
(task (concat (make-string level ?*)
(and todo (concat " " todo))
(and priority (format " [#%c]" priority))
(and priority (format " [#%s]" (org-priority-to-string priority)))
(and title (concat " " title)))))
(concat task
;; Align tags.
@ -1969,6 +2042,14 @@ Return a new syntax node of `plain-list' type containing `:type',
`:post-blank' and `:post-affiliated' properties.
Assume point is at the beginning of the list."
(when (and structure (not (assq (point) structure)))
;; STRUCT is corrupted - cannot find list inside.
(org-element--cache-warn
"Invalid :struct passed to plain-list parser at %S: %S
If this warning appears regularly, please report the warning text to Org mode mailing list (M-x org-submit-bug-report)."
(point) structure)
;; Try to recover
(setq structure nil))
(save-excursion
(let* ((struct (or structure (org-element--list-struct limit)))
(type (cond ((looking-at-p "[ \t]*[A-Za-z0-9]") 'ordered)
@ -2050,6 +2131,10 @@ Assume point is at the beginning of the property drawer."
:end end
:contents-begin (and contents-end contents-begin)
:contents-end contents-end
;; Changing anything inside property drawer may
;; change property drawer to ordinary drawer.
:robust-begin nil
:robust-end nil
:post-blank (count-lines before-blank end)
:post-affiliated begin))))))
@ -2100,6 +2185,8 @@ Assume point is at the beginning of the block."
:end end
:contents-begin contents-begin
:contents-end contents-end
:robust-begin contents-begin
:robust-end contents-end
:post-blank (count-lines pos-before-blank end)
:post-affiliated post-affiliated)
(cdr affiliated)))))))))
@ -2198,6 +2285,8 @@ Assume point is at the beginning of the block."
:end end
:contents-begin contents-begin
:contents-end contents-end
:robust-begin contents-begin
:robust-end contents-end
:post-blank (count-lines pos-before-blank end)
:post-affiliated post-affiliated)
(cdr affiliated)))))))))
@ -2562,10 +2651,10 @@ Return a new syntax node of `example-block' type containing `:begin',
(let ((val (org-element-property :value example-block)))
(cond
((org-src-preserve-indentation-p example-block) val)
((= 0 org-edit-src-content-indentation)
((= 0 org-src-content-indentation)
(org-remove-indentation val))
(t
(let ((ind (make-string org-edit-src-content-indentation ?\s)))
(let ((ind (make-string org-src-content-indentation ?\s)))
(replace-regexp-in-string "^[ \t]*\\S-"
(concat ind "\\&")
(org-remove-indentation val))))))))
@ -2649,11 +2738,13 @@ Assume point is at the beginning of the fixed-width area."
(save-excursion
(let* ((begin (car affiliated))
(post-affiliated (point))
pos-before-blank
(end-area
(progn
(while (and (< (point) limit)
(looking-at-p "[ \t]*:\\( \\|$\\)"))
(forward-line))
(setq pos-before-blank (point))
(if (bolp) (line-end-position 0) (point))))
(end (progn (skip-chars-forward " \r\t\n" limit)
(if (eobp) (point) (line-beginning-position)))))
@ -2666,7 +2757,7 @@ Assume point is at the beginning of the fixed-width area."
"^[ \t]*: ?" ""
(buffer-substring-no-properties post-affiliated
end-area))
:post-blank (count-lines end-area end)
:post-blank (count-lines pos-before-blank end)
:post-affiliated post-affiliated)
(cdr affiliated))))))
@ -3096,10 +3187,10 @@ Assume point is at the beginning of the block."
(let ((val (org-element-property :value src-block)))
(cond
((org-src-preserve-indentation-p src-block) val)
((zerop org-edit-src-content-indentation)
((zerop org-src-content-indentation)
(org-remove-indentation val))
(t
(let ((ind (make-string org-edit-src-content-indentation ?\s)))
(let ((ind (make-string org-src-content-indentation ?\s)))
(replace-regexp-in-string "^[ \t]*\\S-"
(concat ind "\\&")
(org-remove-indentation val))))))))
@ -3361,7 +3452,7 @@ Assume point is at the beginning of the citation."
(match-string-no-properties 1))))
;; Ignore blanks between cite type and prefix or key.
(start (match-end 0))
(closing (with-syntax-table org-element--pair-square-table
(closing (org-with-syntax-table org-element--pair-square-table
(ignore-errors (scan-lists begin 1 0)))))
(save-excursion
(when (and closing
@ -3602,7 +3693,7 @@ When at a footnote reference, return a new syntax node of
`:end', `:contents-begin', `:contents-end' and `:post-blank' as
properties. Otherwise, return nil."
(when (looking-at org-footnote-re)
(let ((closing (with-syntax-table org-element--pair-square-table
(let ((closing (org-with-syntax-table org-element--pair-square-table
(ignore-errors (scan-lists (point) 1 0)))))
(when closing
(save-excursion
@ -3694,6 +3785,9 @@ Assume point is at the beginning of the babel call."
;;;; Inline Src Block
(defconst org-element-inline-src-block-regexp "\\<src_\\([^ \t\n[{]+\\)[{[]"
"Regexp matching inline source blocks.")
(defun org-element-inline-src-block-parser ()
"Parse inline source block at point, if any.
@ -3706,7 +3800,7 @@ Assume point is at the beginning of the inline source block."
(save-excursion
(catch :no-object
(when (let ((case-fold-search nil))
(looking-at "\\<src_\\([^ \t\n[{]+\\)[{[]"))
(looking-at org-element-inline-src-block-regexp))
(goto-char (match-end 1))
(let ((begin (match-beginning 0))
(language (org-element--get-cached-string
@ -4278,8 +4372,6 @@ Assume point is at the target."
(defconst org-element--timestamp-regexp
(concat org-ts-regexp-both
"\\|"
"\\(?:<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)"
"\\|"
"\\(?:<%%\\(?:([^>\n]+)\\)\\([^\n>]*\\)>\\)")
"Regexp matching any timestamp type object.")
@ -4709,14 +4801,7 @@ element it has to parse."
;; Property drawer.
((and (pcase mode
(`planning (eq ?* (char-after (line-beginning-position 0))))
((or `property-drawer `top-comment)
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=63225#80
(save-excursion
(forward-line -1) ; faster than beginning-of-line
(skip-chars-forward "[:blank:]") ; faster than looking-at-p
(or (not (eolp)) ; very cheap
;; Document-wide property drawer may be preceded by blank lines.
(progn (skip-chars-backward " \t\n\r") (bobp)))))
((or `property-drawer `top-comment) t)
(_ nil))
(looking-at-p org-property-drawer-re))
(org-element-property-drawer-parser limit))
@ -4937,10 +5022,13 @@ When PARSE is non-nil, values from keywords belonging to
;; parts of the parse tree.
(defun org-element-parse-buffer (&optional granularity visible-only keep-deferred)
"Recursively parse the buffer and return structure.
"Recursively parse the current Org mode buffer and return structure.
If narrowing is in effect, only parse the visible part of the
buffer.
This function assumes that current major mode is `org-mode'. When the
major mode is different, the behaviour is undefined.
Optional argument GRANULARITY determines the depth of the
recursion. It can be set to the following symbols:
@ -4979,9 +5067,7 @@ pattern (TYPE PROPERTIES CONTENTS), where :
The Org buffer has `org-data' as type and nil as properties.
`org-element-map' function can be used to find specific elements
or objects within the parse tree.
This function assumes that current major mode is `org-mode'."
or objects within the parse tree."
(save-excursion
(goto-char (point-min))
(let ((org-data (org-element-org-data-parser))
@ -5470,11 +5556,6 @@ to interpret. Return Org syntax as a string."
(mapconcat (lambda (obj) (funcall fun obj parent))
data
""))
;; Full Org document.
((eq type 'org-data)
(mapconcat (lambda (obj) (funcall fun obj parent))
(org-element-contents data)
""))
;; Plain text: return it.
((stringp data) data)
;; Element or object without contents.
@ -5514,15 +5595,17 @@ to interpret. Return Org syntax as a string."
(if (eq (org-element-class data parent) 'object)
(concat results (make-string blank ?\s))
(concat (org-element--interpret-affiliated-keywords data)
(org-element-normalize-string results)
;; Make sure that we have at least a
;; single newline after (if non-empty),
;; but keep any existing newlines (they
;; come from :post-blank inside
;; contents)
(org-element-normalize-string results 'keep-newlines)
(make-string blank ?\n)))))))))
(funcall fun data nil)))
(defun org-element--interpret-affiliated-keywords (element)
"Return ELEMENT's affiliated keywords as Org syntax.
If there is no affiliated keyword, return the empty string."
(let ((keyword-to-org
(lambda (key value)
(defun org-element--interpret-affiliated-keyword (key value)
"Interpret affiliated keyword with KEY and VALUE."
(let (dual)
(when (member key org-element-dual-keywords)
(setq dual (cdr value) value (car value)))
@ -5533,34 +5616,38 @@ If there is no affiliated keyword, return the empty string."
(if (member key org-element-parsed-keywords)
(org-element-interpret-data value)
value)
"\n")))))
(mapconcat
(lambda (prop)
(let ((value (org-element-property prop element))
(keyword (upcase (substring (symbol-name prop) 1))))
(when value
(if (or (member keyword org-element-multiple-keywords)
;; All attribute keywords can have multiple lines.
(string-match-p "^ATTR_" keyword))
(mapconcat (lambda (line) (funcall keyword-to-org keyword line))
value "")
(funcall keyword-to-org keyword value)))))
"\n")))
(defun org-element--interpret-affiliated-keywords (element)
"Return ELEMENT's affiliated keywords as Org syntax.
If there is no affiliated keyword, return the empty string."
;; there are some elements that will never have affiliated keywords,
;; so do nothing for these
(if (member (org-element-type element)
org-element-elements-no-affiliated)
""
(let (acc)
;; List all ELEMENT's properties matching an attribute line or an
;; affiliated keyword, but ignore translated keywords since they
;; cannot belong to the property list.
(let (acc)
(org-element-properties-mapc
(lambda (prop _ __)
(let ((keyword (upcase (substring (symbol-name prop) 1))))
(when (or (string-match-p "^ATTR_" keyword)
(lambda (prop value)
(when value
(let* ((keyword (upcase (substring (symbol-name prop) 1)))
(attrp (string-prefix-p "ATTR_" keyword)))
(when (or attrp
(and
(member keyword org-element-affiliated-keywords)
(not (assoc keyword
org-element-keyword-translation-alist))))
(push prop acc))))
(push (if (or attrp ; All attribute keywords can have multiple lines.
(member keyword org-element-multiple-keywords))
(mapconcat (lambda (line) (org-element--interpret-affiliated-keyword keyword line))
value "")
(org-element--interpret-affiliated-keyword keyword value))
acc)))))
element t)
(nreverse acc))
"")))
(apply #'concat (nreverse acc)))))
;; Because interpretation of the parse tree must return the same
;; number of blank lines between elements and the same number of white
@ -5574,15 +5661,20 @@ If there is no affiliated keyword, return the empty string."
;; The second function, `org-element-normalize-contents', removes
;; global indentation from the contents of the current element.
(defun org-element-normalize-string (s)
(defun org-element-normalize-string (s &optional keep-newlines)
"Ensure string S ends with a single newline character.
If S isn't a string return it unchanged. If S is the empty
string, return it. Otherwise, return a new string with a single
newline character at its end."
newline character at its end.
When optional argument KEEP-NEWLINES is non-nil, keep any newlines that
are present, even when there are more than one."
(cond
((not (stringp s)) s)
((string= "" s) "")
(keep-newlines
(if (string-suffix-p "\n" s) s (concat s "\n")))
(t (and (string-match "\\(\n[ \t]*\\)*\\'" s)
(replace-match "\n" nil nil s)))))
@ -5766,15 +5858,6 @@ seconds.")
"Duration, as a time value, of the pause between synchronizations.
See `org-element-cache-sync-duration' for more information.")
(defvar org-element--cache-self-verify nil
"Activate extra consistency checks for the cache.
This may cause serious performance degradation depending on the value
of `org-element--cache-self-verify-frequency'.
When set to symbol `backtrace', record and display backtrace log if
any inconsistency is detected.")
(defvar org-element--cache-self-verify-before-persisting nil
"Perform consistency checks for the cache before writing to disk.
@ -5797,16 +5880,6 @@ to be correct. Setting this to a value less than 0.0001 is useless.")
(defvar org-element--cache-map-statistics-threshold 0.1
"Time threshold in seconds to log statistics for `org-element-cache-map'.")
(defvar org-element--cache-diagnostics-level 2
"Detail level of the diagnostics.")
(defvar-local org-element--cache-diagnostics-ring nil
"Ring containing cache process log entries.
The ring size is `org-element--cache-diagnostics-ring-size'.")
(defvar org-element--cache-diagnostics-ring-size 5000
"Size of `org-element--cache-diagnostics-ring'.")
;;;; Data Structure
(defvar-local org-element--cache nil
@ -5969,29 +6042,6 @@ FORMAT-STRING and ARGS are the same arguments as in `format'."
(make-ring org-element--cache-diagnostics-ring-size)))
(ring-insert org-element--cache-diagnostics-ring format-string)))))
(defmacro org-element--cache-warn (format-string &rest args)
"Raise warning for org-element-cache.
FORMAT-STRING and ARGS are the same arguments as in `format'."
`(let* ((format-string (funcall #'format ,format-string ,@args))
(format-string
(if (or (not org-element--cache-diagnostics-ring)
(not (eq 'backtrace org-element--cache-self-verify)))
format-string
(prog1
(concat (format "Warning(%s): "
(buffer-name (current-buffer)))
format-string
"\nBacktrace:\n "
(mapconcat #'identity
(ring-elements org-element--cache-diagnostics-ring)
"\n "))
(setq org-element--cache-diagnostics-ring nil)))))
(if (and (boundp 'org-batch-test) org-batch-test)
(error "%s" (concat "org-element--cache: " format-string))
(push (concat "org-element--cache: " format-string) org--warnings)
(display-warning '(org-element org-element-cache)
(concat "org-element--cache: " format-string)))))
(defsubst org-element--cache-key (element)
"Return a unique key for ELEMENT in cache tree.
@ -7036,6 +7086,14 @@ If you observe Emacs hangs frequently, please report this to Org mode mailing li
(unless (save-excursion
(org-skip-whitespace)
(eobp))
(unless (>= end (point))
(org-element--cache-warn
"Invalid LIMIT boundary during parsing. Please report it to Org mode mailing list (M-x org-submit-bug-report).\n Backtrace:\n%S"
(when (and (fboundp 'backtrace-get-frames)
(fboundp 'backtrace-to-string))
(backtrace-to-string (backtrace-get-frames 'backtrace))
(org-element-cache-reset)
(error "org-element--cache: Emergency exit"))))
(setq element (org-element--current-element
end 'element mode
(org-element-property :structure parent))))
@ -7307,18 +7365,12 @@ known element in cache (it may start after END)."
(or (and (memq type '( center-block dynamic-block
quote-block special-block
drawer))
(or (not (eq type 'drawer))
(not (string= "PROPERTIES" (org-element-property :drawer-name up))))
;; Sensitive change. This is
;; unconditionally non-robust change.
(not org-element--cache-change-warning)
(let ((cbeg (org-element-contents-begin up))
(cend (org-element-contents-end up)))
(and cbeg
(<= cbeg beg)
(or (> cend end)
(and (= cend end)
(= (+ end offset) (point-max)))))))
(let ((rbeg (org-element-property :robust-begin up))
(rend (org-element-property :robust-end up)))
(and rbeg rend (<= rbeg beg) (>= rend end))))
(and (memq type '(headline section org-data))
(let ((rbeg (org-element-property :robust-begin up))
(rend (org-element-property :robust-end up)))
@ -7991,7 +8043,8 @@ the cache."
;; Bind variables used inside loop to avoid memory
;; re-allocation on every iteration.
;; See https://emacsconf.org/2021/talks/faster/
tmpnext-start tmpparent tmpelement)
tmpnext-start tmpparent tmpelement
func-match-data)
(save-excursion
(save-restriction
(unless narrow (widen))
@ -8062,8 +8115,7 @@ the cache."
;; point.
(move-start-to-next-match
;; Preserve match data that might be set by FUNC.
(re) `(save-match-data
(if (or (not ,re)
(re) `(if (or (not ,re)
(if org-element--cache-map-statistics
(progn
(setq before-time (float-time))
@ -8085,7 +8137,7 @@ the cache."
(or (org-element-begin (element-match-at-point)) -1))))
(when (>= start to-pos) (cache-walk-abort))
(when (eq start -1) (setq start nil)))
(cache-walk-abort))))
(cache-walk-abort)))
;; Find expected begin position of an element after
;; DATA.
(next-element-start
@ -8277,6 +8329,8 @@ the cache."
;;
;; Call FUNC. FUNC may move point.
(setq org-element-cache-map-continue-from nil)
(when func-match-data
(set-match-data func-match-data t))
(if (org-with-base-buffer nil org-element--cache-map-statistics)
(progn
(setq before-time (float-time))
@ -8289,6 +8343,7 @@ the cache."
(cl-incf count-predicate-calls-fail)))
(push (funcall func data) result)
(when (car result) (cl-incf count-predicate-calls-match)))
(setq func-match-data (match-data))
;; Set `last-match'.
(setq last-match (car result))
;; If FUNC moved point forward, update
@ -8511,7 +8566,7 @@ This function may modify the match data."
(org-element-at-point (1+ epom) cached-only))))))))
;;;###autoload
(defsubst org-element-at-point-no-context (&optional pom)
(defun org-element-at-point-no-context (&optional pom)
"Quickly find element at point or POM.
It is a faster version of `org-element-at-point' that is not
@ -8586,7 +8641,7 @@ This function may modify match data."
(goto-char (org-element-begin element))
(looking-at org-complex-heading-regexp)
(let ((end (match-end 4)))
(if (not end) (throw 'objects-forbidden element)
(if (or (not end) (> pos end)) (throw 'objects-forbidden element)
(goto-char (match-beginning 4))
(when (looking-at org-element-comment-string)
(goto-char (match-end 0)))

View file

@ -4,6 +4,7 @@
;; Author: Carsten Dominik <carsten.dominik@gmail.com>,
;; Ulf Stegemann <ulf at zeitform dot de>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: outlines, calendar, text
;; URL: https://orgmode.org
;;

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2004-2026 Free Software Foundation, Inc.
;; Author: Carsten Dominik <carsten.dominik@gmail.com>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: outlines, hypermedia, calendar, text
;; URL: https://orgmode.org
;;
@ -324,18 +325,21 @@ determines if it is a foreground or a background color."
(defcustom org-priority-faces nil
"Faces for specific Priorities.
This is a list of cons cells, with priority character in the car
and faces in the cdr. The face can be a symbol, a color
as a string, or a property list of attributes, like
This is an association list, whose keys are priorities and values are
faces. The face can be a symbol, a color as a string, or a property
list of attributes, like
(:foreground \"blue\" :weight bold :underline t).
If it is a color string, the variable `org-faces-easy-properties'
determines if it is a foreground or a background color."
:group 'org-faces
:group 'org-todo
:type '(repeat
(cons
(character :tag "Priority")
(choice :tag "Face "
:type '(choice (const :tag "Defaults" nil)
(alist :tag "Association list"
:key-type
(choice :tag "Priority"
(character :tag "Character" :value ?A)
(natnum :tag "Number" :value 1))
:value-type (choice :tag "Face "
(string :tag "Color")
(sexp :tag "Face")))))

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2020-2026 Free Software Foundation, Inc.
;;
;; Author: Ihor Radchenko <yantar92 at posteo dot net>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: folding, invisible text
;; URL: https://orgmode.org
;;
@ -27,6 +28,11 @@
;; This file contains library to control temporary invisibility
;; (folding and unfolding) of text in buffers.
;; Do note that something being folded does not necessarily mean
;; that it is invisible. Folded regions might be permanently visible
;; by the means of `:visible' folding spec or because isearch
;; temporarily revealed the folds.
;; The file implements the following functionality:
;;
;; - Folding/unfolding regions of text
@ -245,7 +251,7 @@
;; with `grab-invisible' option, folded regions copied to other
;; buffers (including buffers that do not use this library) will
;; remain invisible. org-fold-core provides functions to work around
;; this issue: `org-fold-core-remove-optimisation' and `org-fold-core-update-optimisation', but
;; this issue: `org-fold-core-remove-optimization' and `org-fold-core-update-optimization', but
;; it is unlikely that a random external package will use them.
;; Another possible bottleneck is the fragility check after the change
@ -276,8 +282,6 @@
(require 'org-macs)
(require 'org-compat)
(declare-function isearch-filter-visible "isearch" (beg end))
;;; Customization
(defcustom org-fold-core-style (if (version< emacs-version "29")
@ -1070,8 +1074,9 @@ If SPEC-OR-ALIAS is omitted and FLAG is nil, unfold everything in the region."
(overlay-get ov 'invisible)
(org-fold-core-get-folding-spec-property
(overlay-get ov 'invisible) :isearch-open))
(when (overlay-get ov 'invisible)
(overlay-put ov 'org-invisible (overlay-get ov 'invisible)))
(when-let* ((spec (overlay-get ov 'invisible)))
(overlay-put ov 'org-invisible spec)
(overlay-put ov (org-fold-core--property-symbol-get-create spec) nil))
(overlay-put ov 'invisible nil)
(when org-fold-core--isearch-active
(cl-pushnew ov org-fold-core--isearch-overlays)))))
@ -1244,11 +1249,11 @@ This function is intended to be used as `isearch-filter-predicate'."
(setq beg (car overlay-or-region)
end (cdr overlay-or-region)))
;; FIXME: Reveal the match (usually point, but may sometimes go beyond the region).
(when (< beg (point) end)
(funcall org-fold-core-isearch-open-function (point)))
(if (<= beg (point) end)
(funcall org-fold-core-isearch-open-function (point))
(if (overlayp overlay-or-region)
(delete-overlay overlay-or-region)
(org-fold-core-region beg end nil))))
(org-fold-core-region beg end nil)))))
(defun org-fold-core--isearch-show-temporary (region hide-p)
"Temporarily reveal text in REGION.
@ -1259,8 +1264,9 @@ REGION can also be an overlay in current buffer."
(if hide-p
(if (not (overlayp region))
nil ;; FIXME: after isearch supports text properties.
(when (overlay-get region 'org-invisible)
(overlay-put region 'invisible (overlay-get region 'org-invisible))))
(when-let* ((spec (overlay-get region 'org-invisible)))
(overlay-put region 'invisible spec)
(overlay-put region (org-fold-core--property-symbol-get-create spec) spec)))
;; isearch expects all the temporarily opened overlays to exist.
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=60399
(org-fold-core--keep-overlays

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2020-2026 Free Software Foundation, Inc.
;;
;; Author: Ihor Radchenko <yantar92 at posteo dot net>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: folding, invisible text
;; URL: https://orgmode.org
;;
@ -58,29 +59,25 @@
(defvar org-custom-properties-overlays)
(defvar org-element-headline-re)
(declare-function isearch-filter-visible "isearch" (beg end))
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-end "org-element" (node))
(declare-function org-element-post-affiliated "org-element" (node))
(declare-function org-element--current-element "org-element" (limit &optional granularity mode structure))
(declare-function org-toggle-custom-properties-visibility "org" ())
(declare-function org-item-re "org-list" ())
(declare-function org-up-heading-safe "org" ())
(declare-function org-get-tags "org" (&optional pos local fontify))
(declare-function org-get-tags "org" (&optional epom local))
(declare-function org-get-valid-level "org" (level &optional change))
(declare-function org-before-first-heading-p "org" ())
(declare-function org-goto-sibling "org" (&optional previous))
(declare-function org-block-map "org" (function &optional start end))
(declare-function org-map-region "org" (fun beg end))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading element))
(declare-function org-back-to-heading-or-point-min "org" (&optional invisible-ok))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
(declare-function org-cycle-hide-drawers "org-cycle" (state))
(declare-function outline-show-branches "outline" ())
(declare-function outline-hide-sublevels "outline" (levels))
(declare-function outline-get-next-sibling "outline" ())
(declare-function outline-invisible-p "outline" (&optional pos))
@ -249,7 +246,7 @@ Also, see `org-fold-catch-invisible-edits'."
;; overlays for isearch.
(setq-local org-fold-core--isearch-special-specs '(org-link))
(org-fold-core-initialize
`((,(if (eq org-fold-core-style 'text-properties) 'org-fold-outline 'outline)
`((org-fold-outline
(:ellipsis . ,ellipsis)
(:fragile . ,#'org-fold--reveal-outline-maybe)
(:isearch-open . t)
@ -650,7 +647,7 @@ be shown."
"Set visibility around point according to DETAIL.
DETAIL is either nil, `minimal', `local', `ancestors',
`ancestors-full', `lineage', `tree', `canonical' or t. See
`org-show-context-detail' for more information."
`org-fold-show-context-detail' for more information."
;; Show current heading and possibly its entry, following headline
;; or all children.
(if (and (org-at-heading-p) (not (eq detail 'local)))
@ -745,15 +742,18 @@ This function is intended to be used as a member of
(setq from (save-excursion (goto-char from) (line-beginning-position 0)))
(cons from to))
(defun org-fold--reveal-headline-at-point ()
(defun org-fold--reveal-headline-at-point-delayed ()
"Reveal header line and empty contents inside.
Reveal the header line and, if present, also reveal its contents, when
the contents consists of blank lines.
Only perform actual unfolding in `post-command-hook' by calling
`org-fold-core--region-delayed'.
Assume that point is located at the header line."
(org-with-wide-buffer
(forward-line 0)
(org-fold-region
(org-fold-core--region-delayed
(max (point-min) (1- (point)))
(let ((endl (line-end-position)))
(save-excursion
@ -783,19 +783,19 @@ This function is intended to be used as :fragile property of
(forward-line 0)
;; Make sure that headline is not partially hidden.
(unless (org-fold-folded-p nil 'headline)
(org-fold--reveal-headline-at-point))
(org-fold--reveal-headline-at-point-delayed))
;; Never hide level 1 headlines
(save-excursion
(goto-char (line-end-position))
(unless (>= (point) (cdr region))
(when (re-search-forward (rx bol "* ") (cdr region) t)
(org-fold--reveal-headline-at-point))))
(org-fold--reveal-headline-at-point-delayed))))
;; Make sure that headline after is not partially hidden.
(goto-char (cdr region))
(forward-line 0)
(unless (org-fold-folded-p nil 'headline)
(when (looking-at-p org-element-headline-re)
(org-fold--reveal-headline-at-point)))
(org-fold--reveal-headline-at-point-delayed)))
;; Check the validity of headline
(goto-char (car region))
(backward-char)

View file

@ -49,11 +49,11 @@
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading))
(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading element))
(declare-function org-fill-paragraph "org" (&optional justify region))
(declare-function org-in-block-p "org" (names))
(declare-function org-in-verbatim-emphasis "org" ())
(declare-function org-inside-LaTeX-fragment-p "org" ())
(declare-function org-inside-LaTeX-fragment-p "org" (&optional element))
(declare-function org-inside-latex-macro-p "org" ())
(declare-function org-mark-ring-push "org" (&optional pos buffer))
(declare-function org-fold-show-context "org-fold" (&optional key))
@ -248,13 +248,16 @@ otherwise."
;;;; Internal functions
(defun org-footnote--allow-reference-p ()
"Non-nil when a footnote reference can be inserted at point."
(defun org-footnote--allow-reference-p (&optional inline)
"Non-nil when a footnote reference can be inserted at point.
When optional argument INLINE is non-nil, assume that the footnote
reference is an inline or anonymous footnote (and can be placed at the
beginning of the line)."
;; XXX: This is similar to `org-footnote-in-valid-context-p' but
;; more accurate and usually faster, except in some corner cases.
;; It may replace it after doing proper benchmarks as it would be
;; used in fontification.
(unless (bolp)
(unless (and (not inline) (bolp))
(let* ((context (org-element-context))
(type (org-element-type context)))
(cond
@ -401,7 +404,8 @@ References are sorted according to a deep-reading order."
(dolist (r (mapcar (lambda (l) (assoc l references))
(reverse
(cdr (assoc (nth 0 ref) nested)))))
(funcall add-reference r t))))))
(unless (member r ordered) ; avoid infinite recursion when references link to each other
(funcall add-reference r t)))))))
(dolist (r (reverse references) (nreverse ordered))
(funcall add-reference r nil))))))
@ -665,8 +669,6 @@ This command prompts for a label. If this is a label referencing an
existing label, only insert the label. If the footnote label is empty
or new, let the user edit the definition of the footnote."
(interactive)
(unless (org-footnote--allow-reference-p)
(user-error "Cannot insert a footnote here"))
(let* ((all (org-footnote-all-labels))
(label
(unless (eq org-footnote-auto-label 'anonymous)
@ -680,16 +682,24 @@ or new, let the user edit the definition of the footnote."
(mapcar #'list all) nil nil
(and (eq org-footnote-auto-label 'confirm) propose)))))))))
(cond ((not label)
(unless (org-footnote--allow-reference-p 'anonymous)
(user-error "Cannot insert a footnote here"))
(insert "[fn::]")
(backward-char 1))
((member label all)
(unless (org-footnote--allow-reference-p)
(user-error "Cannot insert a footnote here"))
(insert "[fn:" label "]")
(message "New reference to existing note"))
(org-footnote-define-inline
(unless (org-footnote--allow-reference-p 'inline)
(user-error "Cannot insert a footnote here"))
(insert "[fn:" label ":]")
(backward-char 1)
(org-footnote-auto-adjust-maybe))
(t
(unless (org-footnote--allow-reference-p)
(user-error "Cannot insert a footnote here"))
(insert "[fn:" label "]")
(let ((p (org-footnote-create-definition label)))
;; `org-footnote-goto-definition' needs to be called

View file

@ -134,14 +134,9 @@ When nil, you can use these keybindings to navigate the buffer:
(org-defkey map "\C-c\C-u" 'outline-up-heading)
map)))
;; `isearch-other-control-char' was removed in Emacs 24.4.
(if (fboundp 'isearch-other-control-char)
(progn
(define-key org-goto-local-auto-isearch-map "\C-i" 'isearch-other-control-char)
(define-key org-goto-local-auto-isearch-map "\C-m" 'isearch-other-control-char))
(define-key org-goto-local-auto-isearch-map "\C-i" nil)
(define-key org-goto-local-auto-isearch-map "\C-m" nil)
(define-key org-goto-local-auto-isearch-map [return] nil))
(define-key org-goto-local-auto-isearch-map "\C-i" nil)
(define-key org-goto-local-auto-isearch-map "\C-m" nil)
(define-key org-goto-local-auto-isearch-map [return] nil)
(defun org-goto--local-search-headings (string bound noerror)
"Search and make sure that any matches are in headlines."

View file

@ -80,8 +80,6 @@
(declare-function message-make-fqdn "message" ())
(declare-function org-goto-location "org-goto" (&optional _buf help))
;; Declared inside `org-element-with-disabled-cache' macro.
(declare-function org-element--cache-active-p "org-element.el" (&optional called-from-cache-change-func-p))
;;; Customization
@ -295,6 +293,16 @@ This variable is only relevant when `org-id-track-globally' is set."
:group 'org-id
:type 'boolean)
(defcustom org-id-completion-targets
'((nil . t)
(org-id-files . t))
"Candidate headings for completing \"id:\" links in `\\[org-insert-link]'.
This variable has the same form as `org-refile-targets', which see."
:group 'org-id
:type (get 'org-refile-targets 'custom-type)
:package-version '(Org . "9.8")
:risky t)
;;; The API functions
;;;###autoload
@ -354,7 +362,7 @@ TARGETS may be a setting for `org-refile-targets' to define
eligible headlines. When omitted, all headlines in the current
file are eligible. This function returns the ID of the entry.
If necessary, the ID is created."
(let* ((org-refile-targets (or targets '((nil . (:maxlevel . 10)))))
(let* ((org-refile-targets (or targets '((nil . t))))
(org-refile-use-outline-path
(if (caar org-refile-targets) 'file t))
(org-refile-target-verify-function nil)
@ -631,7 +639,10 @@ If SILENT is non-nil, messages are suppressed."
(let ((loc (file-name-directory org-id-locations-file)))
(mapc (lambda (item)
(unless (file-name-absolute-p (car item))
(setf (car item) (expand-file-name (car item) loc))))
(setf (car item)
;; Abbreviate as `org-id-add-location' does.
(abbreviate-file-name
(expand-file-name (car item) loc)))))
org-id-locations)))
(error
(message "Could not read `org-id-locations' from %s, setting it to nil"
@ -705,8 +716,9 @@ This is to be able to write it to a file."
(hash-table-p org-id-locations)
(gethash id org-id-locations))
;; Fall back on current buffer
(when (derived-mode-p 'org-mode)
(buffer-file-name (or (buffer-base-buffer (current-buffer))
(current-buffer)))))
(current-buffer))))))
(defun org-id-find-id-in-file (id file &optional markerp)
"Return the position of the entry ID in FILE.
@ -884,9 +896,47 @@ will be tried as an ID."
(org-element-lineage (org-element-at-point) 'headline t))))
(org-fold-show-context)))
(defun org-id-complete (&optional _arg)
"Complete IDs for `org-insert-link'.
If a headline without an ID is selected, one will automatically be
created."
(unless org-id-locations (org-id-locations-load))
(or (ignore-errors ; Catch the error if we have no refile targets.
(when-let* ((id (org-id-get-with-outline-path-completion
(if (buffer-file-name) org-id-completion-targets
;; If the current buffer isn't associated
;; with a file, we can't include it so we
;; exclude all "targets" where the car is
;; nil.
(seq-filter #'car org-id-completion-targets)))))
(concat "id:" id)))
(read-string "Link: " "id:")))
(defun org-id-description (link desc)
"Return a description for an ID link, derived from the linked headline.
Calling convention is similar to `org-link-make-description-function'.
DESC has higher priority and is returned if it is both non-nil and
non-empty. Otherwise, if the passed LINK is an ID link and can be
resolved to an existing headline, the target headline is returned. If
all else fails, DESC is returned as-is.
TODO keywords, tags, and priorities are stripped from the description."
(or (org-string-nw-p desc)
(when-let* ((loc (org-id-find (string-remove-prefix "id:" link))))
(org-with-file-buffer (car loc)
(org-with-wide-buffer
(goto-char (cdr loc))
(org-link-display-format
(org-get-heading t t t t)))))
desc))
(org-link-set-parameters "id"
:follow #'org-id-open
:store #'org-id-store-link-maybe)
:store #'org-id-store-link-maybe
:complete #'org-id-complete
:insert-description #'org-id-description)
(provide 'org-id)

View file

@ -45,8 +45,6 @@
(require 'cl-lib)
(declare-function org-inlinetask-get-task-level "org-inlinetask" ())
(declare-function org-inlinetask-in-task-p "org-inlinetask" ())
(declare-function org-list-item-body-column "org-list" (item))
(defvar org-inlinetask-show-first-star)

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2018-2026 Free Software Foundation, Inc.
;; Author: Nicolas Goaziou <mail@nicolasgoaziou.fr>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; This file is part of GNU Emacs.
@ -37,29 +38,29 @@
(require 'oc)
(declare-function org-add-note "org" ())
(declare-function org-agenda "org" (&optional arg org-keys restriction))
(declare-function org-agenda "org-agenda" (&optional arg org-keys restriction))
(declare-function org-agenda-file-to-front "org" (&optional to-end))
(declare-function org-agenda-remove-restriction-lock "org" (&optional noupdate))
(declare-function org-agenda-set-restriction-lock "org" (&optional type))
(declare-function org-archive-subtree "org" (&optional find-done))
(declare-function org-archive-subtree-default "org" ())
(declare-function org-archive-subtree-default-with-confirmation "org" ())
(declare-function org-archive-to-archive-sibling "org" ())
(declare-function org-agenda-remove-restriction-lock "org-agenda" (&optional noupdate))
(declare-function org-agenda-set-restriction-lock "org-agenda" (&optional type))
(declare-function org-archive-subtree "org-archive" (&optional find-done))
(declare-function org-archive-subtree-default "org-archive" ())
(declare-function org-archive-subtree-default-with-confirmation "org-archive" ())
(declare-function org-archive-to-archive-sibling "org-archive" ())
(declare-function org-at-heading-p "org" (&optional ignored))
(declare-function org-attach "org" ())
(declare-function org-attach "org-attach" ())
(declare-function org-backward-element "org" ())
(declare-function org-backward-heading-same-level "org" (arg &optional invisible-ok))
(declare-function org-backward-paragraph "org" ())
(declare-function org-backward-paragraph "org" (&optional arg))
(declare-function org-backward-sentence "org" (&optional arg))
(declare-function org-beginning-of-line "org" (&optional n))
(declare-function org-clock-cancel "org" ())
(declare-function org-clock-display "org" (&optional arg))
(declare-function org-clock-goto "org" (&optional select))
(declare-function org-clock-in "org" (&optional select start-time))
(declare-function org-clock-in-last "org" (&optional arg))
(declare-function org-clock-out "org" (&optional switch-to-state fail-quietly at-time))
(declare-function org-clock-cancel "org-clock" ())
(declare-function org-clock-display "org-clock" (&optional arg))
(declare-function org-clock-goto "org-clock" (&optional select))
(declare-function org-clock-in "org-clock" (&optional select start-time))
(declare-function org-clock-in-last "org-clock" (&optional arg))
(declare-function org-clock-out "org-clock" (&optional switch-to-state fail-quietly at-time))
(declare-function org-clone-subtree-with-time-shift "org" (n &optional shift))
(declare-function org-columns "org" (&optional global columns-fmt-string))
(declare-function org-columns "org-colview" (&optional global columns-fmt-string))
(declare-function org-comment-dwim "org" (arg))
(declare-function org-copy-special "org" ())
(declare-function org-copy-visible "org" (beg end))
@ -71,15 +72,15 @@
(declare-function org-cut-special "org" ())
(declare-function org-cut-subtree "org" (&optional n))
(declare-function org-cycle "org-cycle" (&optional arg))
(declare-function org-cycle-agenda-files "org-cycle" ())
(declare-function org-cycle-agenda-files "org" ())
(declare-function org-date-from-calendar "org" ())
(declare-function org-dynamic-block-insert-dblock "org" (&optional arg))
(declare-function org-dynamic-block-insert-dblock "org" (type &optional interactive-p))
(declare-function org-dblock-update "org" (&optional arg))
(declare-function org-deadline "org" (arg1 &optional time))
(declare-function org-decrease-number-at-point "org" (&optional inc))
(declare-function org-delete-backward-char "org" (n))
(declare-function org-delete-char "org" (n))
(declare-function org-delete-indentation "org" (&optional arg))
(declare-function org-delete-indentation "org" (&optional arg beg end))
(declare-function org-demote-subtree "org" ())
(declare-function org-display-outline-path "org" (&optional file current separator just-return-string))
(declare-function org-down-element "org" ())
@ -89,7 +90,6 @@
(declare-function org-emphasize "org" (&optional char))
(declare-function org-end-of-line "org" (&optional n))
(declare-function org-entry-put "org" (pom property value))
(declare-function org-eval-in-calendar "org" (form &optional keepdate))
(declare-function org-calendar-goto-today-or-insert-dot "org" ())
(declare-function org-calendar-goto-today "org" ())
(declare-function org-calendar-backward-month "org" ())
@ -106,34 +106,34 @@
(declare-function org-calendar-scroll-three-months-left "org" ())
(declare-function org-calendar-scroll-three-months-right "org" ())
(declare-function org-evaluate-time-range "org" (&optional to-buffer))
(declare-function org-export-dispatch "org" (&optional arg))
(declare-function org-feed-goto-inbox "org" (feed))
(declare-function org-feed-update-all "org" ())
(declare-function org-export-dispatch "ox" (&optional arg))
(declare-function org-feed-goto-inbox "org-feed" (feed))
(declare-function org-feed-update-all "org-feed" ())
(declare-function org-fill-paragraph "org" (&optional justify region))
(declare-function org-find-file-at-mouse "org" (ev))
(declare-function org-footnote-action "org" (&optional special))
(declare-function org-footnote-action "org-footnote" (&optional special))
(declare-function org-cycle-force-archived "org-cycle" ())
(declare-function org-force-self-insert "org" (n))
(declare-function org-forward-element "org" ())
(declare-function org-forward-heading-same-level "org" (arg &optional invisible-ok))
(declare-function org-forward-paragraph "org" ())
(declare-function org-forward-paragraph "org" (&optional arg))
(declare-function org-forward-sentence "org" (&optional arg))
(declare-function org-goto "org" (&optional alternative-interface))
(declare-function org-goto "org-goto" (&optional alternative-interface))
(declare-function org-goto-calendar "org" (&optional arg))
(declare-function org-inc-effort "org" ())
(declare-function org-increase-number-at-point "org" (&optional inc))
(declare-function org-info-find-node "org" (&optional nodename))
(declare-function org-insert-all-links "org" (arg &optional pre post))
(declare-function org-insert-all-links "ol" (arg &optional pre post))
(declare-function org-insert-drawer "org" (&optional arg drawer))
(declare-function org-insert-heading-respect-content "org" (&optional invisible-ok))
(declare-function org-insert-last-stored-link "org" (arg))
(declare-function org-insert-link "org" (&optional complete-file link-location default-description))
(declare-function org-insert-last-stored-link "ol" (arg))
(declare-function org-insert-link "ol" (&optional complete-file link-location default-description))
(declare-function org-insert-structure-template "org" (type))
(declare-function org-insert-todo-heading "org" (arg &optional force-heading))
(declare-function org-insert-todo-heading-respect-content "org" (&optional force-state))
(declare-function org-kill-line "org" (&optional arg))
(declare-function org-kill-note-or-show-branches "org" ())
(declare-function org-list-make-subtree "org" ())
(declare-function org-list-make-subtree "org-list" ())
(declare-function org-mark-element "org" ())
(declare-function org-mark-ring-goto "org" (&optional n))
(declare-function org-mark-ring-push "org" (&optional pos buffer))
@ -148,27 +148,29 @@
(declare-function org-narrow-to-element "org" ())
(declare-function org-narrow-to-subtree "org" (&optional element))
(declare-function org-next-block "org" (arg &optional backward block-regexp))
(declare-function org-next-link "org" (&optional search-backward))
(declare-function org-next-link "ol" (&optional search-backward))
(declare-function org-next-visible-heading "org" (arg))
(declare-function org-open-at-mouse "org" (ev))
(declare-function org-open-at-point "org" (&optional arg reference-buffer))
(declare-function org-open-at-point "org" (&optional arg))
(declare-function org-open-line "org" (n))
(declare-function org-paste-special "org" (arg))
(declare-function org-plot/gnuplot "org-plot" (&optional params))
(declare-function org-previous-block "org" (arg &optional block-regexp))
(declare-function org-previous-link "org" ())
(declare-function org-previous-link "ol" ())
(declare-function org-previous-visible-heading "org" (arg))
(declare-function org-priority "org" (&optional action show))
(defvar org-priority-highest)
(defvar org-priority-default)
(defvar org-priority-lowest)
(declare-function org-promote-subtree "org" ())
(declare-function org-redisplay-inline-images "org" ())
(declare-function org-refile "org-refile" (&optional arg1 default-buffer rfloc msg))
(declare-function org-refile-copy "org-refile" ())
(declare-function org-refile-reverse "org-refile" (&optional arg default-buffer rfloc msg))
(declare-function org-reftex-citation "org" ())
(declare-function org-reload "org" (&optional arg1))
(declare-function org-remove-file "org" (&optional file))
(declare-function org-resolve-clocks "org" (&optional only-dangling-p prompt-fn last-valid))
(declare-function org-return "org" (&optional indent))
(declare-function org-resolve-clocks "org-clock" (&optional only-dangling-p prompt-fn last-valid))
(declare-function org-return "org" (&optional indent arg interactive))
(declare-function org-return-and-maybe-indent "org" ())
(declare-function org-fold-reveal "org-fold" (&optional siblings))
(declare-function org-schedule "org" (arg &optional time))
@ -190,36 +192,36 @@
(declare-function org-shiftright "org" (&optional arg))
(declare-function org-shifttab "org" (&optional arg))
(declare-function org-shiftup "org" (&optional arg))
(declare-function org-fold-show-all "org-fold" (&optional types))
(declare-function org-fold-show-children "org-fold" (&optional level))
(declare-function org-fold-show-subtree "org-fold" ())
(declare-function org-sort "org" (&optional with-case))
(declare-function org-sparse-tree "org" (&optional arg type))
(declare-function org-table-copy-down "org" (n))
(declare-function org-table-create-or-convert-from-region "org" (arg))
(declare-function org-table-copy-down "org-table" (n))
(declare-function org-table-create-or-convert-from-region "org-table" (arg))
(declare-function org-table-create-with-table\.el "org-table" ())
(declare-function org-table-edit-field "org" (arg))
(declare-function org-table-eval-formula "org" (&optional arg equation suppress-align suppress-const suppress-store suppress-analysis))
(declare-function org-table-field-info "org" (arg))
(declare-function org-table-rotate-recalc-marks "org" (&optional newchar))
(declare-function org-table-sum "org" (&optional beg end nlast))
(declare-function org-table-toggle-coordinate-overlays "org" ())
(declare-function org-table-toggle-formula-debugger "org" ())
(declare-function org-table-edit-field "org-table" (arg))
(declare-function org-table-eval-formula "org-table" (&optional arg equation suppress-align suppress-const suppress-store suppress-analysis))
(declare-function org-table-field-info "org-table" (arg))
(declare-function org-table-rotate-recalc-marks "org-table" (&optional newchar))
(declare-function org-table-sum "org-table" (&optional beg end nlast))
(declare-function org-table-toggle-coordinate-overlays "org-table" ())
(declare-function org-table-toggle-formula-debugger "org-table" ())
(declare-function org-timestamp "org" (arg &optional inactive))
(declare-function org-timestamp-inactive "org" (&optional arg))
(declare-function org-timer "org" (&optional restart no-insert))
(declare-function org-timer-item "org" (&optional arg))
(declare-function org-timer-pause-or-continue "org" (&optional stop))
(declare-function org-timer-set-timer "org" (&optional opt))
(declare-function org-timer-start "org" (&optional offset))
(declare-function org-timer-stop "org" ())
(declare-function org-timer "org-timer" (&optional restart no-insert))
(declare-function org-timer-item "org-timer" (&optional arg))
(declare-function org-timer-pause-or-continue "org-timer" (&optional stop))
(declare-function org-timer-set-timer "org-timer" (&optional opt))
(declare-function org-timer-start "org-timer" (&optional offset))
(declare-function org-timer-stop "org-timer" ())
(declare-function org-todo "org" (&optional arg1))
(declare-function org-toggle-archive-tag "org" (&optional find-done))
(declare-function org-toggle-checkbox "org" (&optional toggle-presence))
(declare-function org-toggle-radio-button "org" (&optional arg))
(declare-function org-toggle-archive-tag "org-archive" (&optional find-done))
(declare-function org-toggle-checkbox "org-list" (&optional toggle-presence))
(declare-function org-toggle-radio-button "org-list" (&optional arg))
(declare-function org-toggle-comment "org" ())
(declare-function org-toggle-fixed-width "org" ())
(declare-function org-toggle-inline-images "org" (&optional include-linked beg end))
(declare-function org-link-preview "ol" (&optional arg beg end))
(declare-function org-link-preview-refresh "ol" ())
(declare-function org-latex-preview "org" (&optional arg))
(declare-function org-toggle-narrow-to-subtree "org" ())
(declare-function org-toggle-ordered-property "org" ())
@ -233,6 +235,7 @@
(declare-function org-update-statistics-cookies "org" (all))
(declare-function org-yank "org" (&optional arg))
(declare-function orgtbl-ascii-plot "org-table" (&optional ask))
(declare-function outline-up-heading "outline" (arg &optional invisible-ok))
@ -390,9 +393,9 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
;;; Global bindings
;;;; Outline functions
(define-key org-mode-map [menu-bar headings] 'undefined)
(define-key org-mode-map [menu-bar hide] 'undefined)
(define-key org-mode-map [menu-bar show] 'undefined)
(define-key org-mode-map [menu-bar headings] #'undefined)
(define-key org-mode-map [menu-bar hide] #'undefined)
(define-key org-mode-map [menu-bar show] #'undefined)
(define-key org-mode-map [remap outline-mark-subtree] #'org-mark-subtree)
(define-key org-mode-map [remap outline-show-subtree] #'org-fold-show-subtree)
@ -410,6 +413,8 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
(define-key org-mode-map [remap outline-previous-visible-heading]
#'org-previous-visible-heading)
(define-key org-mode-map [remap outline-show-children] #'org-fold-show-children)
(defalias 'org-up-heading #'outline-up-heading)
(define-key org-mode-map [remap outline-up-heading] #'org-up-heading)
;;;; Make `C-c C-x' a prefix key
(org-defkey org-mode-map (kbd "C-c C-x") (make-sparse-keymap))
@ -463,6 +468,37 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
(org-defkey org-mode-map (kbd "C-S-<up>") #'org-shiftcontrolup)
(org-defkey org-mode-map (kbd "C-S-<down>") #'org-shiftcontroldown)
;;; Repeat-mode map.
(defvar org-navigation-repeat-map (make-sparse-keymap)
"Repeat keymap for navigation commands.")
(org-defkey org-navigation-repeat-map (kbd "b") #'org-backward-heading-same-level)
(org-defkey org-navigation-repeat-map (kbd "f") #'org-forward-heading-same-level)
(org-defkey org-navigation-repeat-map (kbd "n") #'org-next-visible-heading)
(org-defkey org-navigation-repeat-map (kbd "p") #'org-previous-visible-heading)
(org-defkey org-navigation-repeat-map (kbd "u") #'org-up-heading)
(map-keymap
(lambda (_key cmd)
(put cmd 'repeat-map 'org-navigation-repeat-map))
org-navigation-repeat-map)
(defvar org-link-navigation-repeat-map (make-sparse-keymap)
"Repeat keymap for link navigation commands.")
(org-defkey org-link-navigation-repeat-map (kbd "n") #'org-next-link)
(org-defkey org-link-navigation-repeat-map (kbd "p") #'org-previous-link)
(map-keymap
(lambda (_ cmd)
(put cmd 'repeat-map 'org-link-navigation-repeat-map))
org-link-navigation-repeat-map)
(defvar org-block-navigation-repeat-map (make-sparse-keymap)
"Repeat keymap for block navigation commands.")
(org-defkey org-block-navigation-repeat-map (kbd "f") #'org-next-block)
(org-defkey org-block-navigation-repeat-map (kbd "b") #'org-previous-block)
(map-keymap
(lambda (_ cmd)
(put cmd 'repeat-map 'org-block-navigation-repeat-map))
org-block-navigation-repeat-map)
;;;; Extra keys for TTY access.
;; We only set them when really needed because otherwise the
@ -619,8 +655,8 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
(org-defkey org-mode-map (kbd "C-c C-x x") #'org-dynamic-block-insert-dblock)
(org-defkey org-mode-map (kbd "C-c C-x C-u") #'org-dblock-update)
(org-defkey org-mode-map (kbd "C-c C-x C-l") #'org-latex-preview)
(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-toggle-inline-images)
(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-redisplay-inline-images)
(org-defkey org-mode-map (kbd "C-c C-x C-v") #'org-link-preview)
(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-link-preview-refresh)
(org-defkey org-mode-map (kbd "C-c C-x \\") #'org-toggle-pretty-entities)
(org-defkey org-mode-map (kbd "C-c C-x C-b") #'org-toggle-checkbox)
(org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-toggle-radio-button)
@ -655,8 +691,13 @@ commands should be active.
For example, to activate speed commands when the point is on any
star at the beginning of the headline, you can do this:
(setq org-use-speed-commands
(lambda () (and (looking-at org-outline-regexp) (looking-back \"^\\**\"))))"
(setopt org-use-speed-commands
(lambda ()
(and (looking-at org-outline-regexp)
(looking-back \"^\\\\**\"))))
Note that prior to Emacs 29, `setopt' is unavailable, and
`custom-set-variables' or `setq' is used instead."
:group 'org-structure
:type '(choice
(const :tag "Never" nil)
@ -719,10 +760,10 @@ hook. The default setting is `org-speed-command-activate'."
("Meta Data Editing")
("t" . org-todo)
("," . (org-priority))
("0" . (org-priority ?\ ))
("1" . (org-priority ?A))
("2" . (org-priority ?B))
("3" . (org-priority ?C))
("0" . (org-priority 'remove))
("1" . (org-priority org-priority-highest))
("2" . (org-priority org-priority-default))
("3" . (org-priority org-priority-lowest))
(":" . org-set-tags-command)
("e" . org-set-effort)
("E" . org-inc-effort)
@ -748,7 +789,7 @@ interpreted as a descriptive headline that will be added when
listing the speed commands in the Help buffer using the `?' speed
command."
:group 'org-structure
:package-version '(Org . "9.5")
:package-version '(Org . "9.8")
:type '(repeat :value ("k" . ignore)
(choice :value ("k" . ignore)
(list :tag "Descriptive Headline" (string :tag "Headline"))
@ -756,7 +797,8 @@ command."
(string :tag "Command letter")
(choice
(function)
(sexp))))))
(sexp)))))
:risky t)
(defun org--print-speed-command (speed-command)
"Print information about SPEED-COMMAND in help buffer.

View file

@ -37,7 +37,7 @@
;; Checks currently implemented report the following:
;; - duplicates CUSTOM_ID properties,
;; - duplicate CUSTOM_ID properties,
;; - duplicate NAME values,
;; - duplicate targets,
;; - duplicate footnote definitions,
@ -159,6 +159,16 @@ checker. Currently, two properties are supported:
(seq-remove (lambda (c) (eq name (org-lint-checker-name c)))
org-lint--checkers))))
;;;###autoload
(defun org-lint-remove-checker (name &rest names)
"Remove checker(s) from linter.
NAME is the unique check identifier, as a non-nil symbol. NAMES
are additional check identifiers to be removed."
(let ((removelist (cons name names)))
(setq org-lint--checkers
(seq-remove (lambda (c) (memq (org-lint-checker-name c) removelist))
org-lint--checkers))))
;;; Reports UI
@ -470,7 +480,10 @@ Example:
(when (= (org-element-post-blank keyword) 0)
(let ((next-element (org-with-point-at (org-element-end keyword)
(org-element-at-point))))
(when (< (org-element-begin next-element) (org-element-post-affiliated next-element))
(when (and
;; KEYWORD being the last in the file is OK.
(not (equal (org-element-begin next-element) (org-element-begin keyword)))
(< (org-element-begin next-element) (org-element-post-affiliated next-element)))
;; A keyword followed without blank lines by an element with affiliated keywords.
;; The keyword may be confused with affiliated keywords.
(list (org-element-begin keyword)
@ -553,9 +566,8 @@ Use :header-args: instead"
(lambda (b)
(when-let* ((lang (org-element-property :language b)))
(unless (or (functionp (intern (format "org-babel-execute:%s" lang)))
;; No babel backend, but there is corresponding
;; major mode.
(fboundp (org-src-get-lang-mode lang)))
;; No Babel backend, but relevant major mode is bound.
(org-src-get-lang-mode-if-bound lang))
(list (org-element-property :post-affiliated b)
(format "Unknown source block language: '%s'" lang)))))))
@ -718,6 +730,10 @@ Use :header-args: instead"
(pcase type
((or "attachment" "file")
(let* ((path (org-element-property :path l))
(path (if (and (equal type "attachment")
(string-match "::\\(.*\\)\\'" path))
(substring path 0 (match-beginning 0))
path))
(file (if (string= type "file")
path
(org-with-point-at (org-element-begin l)
@ -837,7 +853,6 @@ Use \"export %s\" instead"
(defun org-lint-export-option-keywords (ast)
"Check for options keyword properties without EXPORT in AST."
(require 'ox)
(let (options reports common-options options-alist)
(dolist (opt org-export-options-alist)
(when (stringp (nth 1 opt))
@ -872,6 +887,7 @@ Use \"export %s\" instead"
reports))
(defun org-lint-invalid-macro-argument-and-template (ast)
"Check for invalid macro arguments in AST."
(let* ((reports nil)
(extract-placeholders
(lambda (template)
@ -934,7 +950,7 @@ Use \"export %s\" instead"
name))
reports))))))))
;; Check arguments for macros.
(org-macro-initialize-templates)
(org-macro-initialize-templates org-export-global-macros)
(let ((templates (append
(mapcar (lambda (m) (cons m "$1"))
'("author" "date" "email" "title" "results"))
@ -1236,7 +1252,7 @@ Use \"export %s\" instead"
(funcall verify
datum
nil
(cl-mapcan #'org-babel-parse-header-arguments
(cl-mapcan (lambda (s) (org-babel-parse-header-arguments s 'no-eval))
(list
(org-element-property :inside-header datum)
(org-element-property :end-header datum)))))
@ -1245,7 +1261,8 @@ Use \"export %s\" instead"
datum
(org-element-property :language datum)
(org-babel-parse-header-arguments
(org-element-property :parameters datum))))
(org-element-property :parameters datum)
'no-eval)))
(`keyword
(when (string= (org-element-property :key datum) "PROPERTY")
(let ((value (org-element-property :value datum)))
@ -1257,7 +1274,8 @@ Use \"export %s\" instead"
datum
(match-string 1 value)
(org-babel-parse-header-arguments
(substring value (match-end 0))))))))
(substring value (match-end 0))
'no-eval))))))
(`node-property
(let ((key (org-element-property :key datum)))
(when (let ((case-fold-search t))
@ -1269,12 +1287,13 @@ Use \"export %s\" instead"
datum
(match-string 1 key)
(org-babel-parse-header-arguments
(org-element-property :value datum))))))
(org-element-property :value datum)
'no-eval)))))
(`src-block
(funcall verify
datum
(org-element-property :language datum)
(cl-mapcan #'org-babel-parse-header-arguments
(cl-mapcan (lambda (s) (org-babel-parse-header-arguments s 'no-eval))
(cons (org-element-property :parameters datum)
(org-element-property :header datum))))))))
reports))
@ -1339,7 +1358,8 @@ Use \"export %s\" instead"
(concat
(org-element-property :inside-header datum)
" "
(org-element-property :end-header datum))))))))
(org-element-property :end-header datum)))))
'no-eval)))
(dolist (header datum-header-values)
(let ((allowed-values
(cdr (assoc-string (substring (symbol-name (car header)) 1)
@ -1395,7 +1415,10 @@ Use \"export %s\" instead"
(org-export-resolve-link (car result) `(:parse-tree ,ast))
(org-link-broken nil))
(org-export-get-previous-element el nil))))
(when (org-element-type-p origin-block 'src-block)
(when (and (org-element-type-p origin-block 'src-block)
(pcase-let ((`(,_ ,_ ,args . ,_)
(org-babel-get-src-block-info 'light origin-block)))
(not (member (alist-get :exports args) '("results" "both")))))
(list (org-element-begin el)
(format "Links to \"%s\" will not be valid during export unless the parent source block has :exports results or both" result-name)))))))
@ -1483,6 +1506,32 @@ Use \"export %s\" instead"
(format "Bullet counter \"%s\" is not the same with item position %d. Consider adding manual [@%d] counter."
bullet (car (last true-number)) bullet-number))))))))
(defun org-lint-priority (ast)
"Report out-of-bounds, invalid, and malformed priorities.
Raise warnings on headlines containing out-of-bounds, invalid (e.g.,
`[#-1]', `[#AA]'), or malformed (e.g., `[#1', `[#A') priorities."
(let ((bad-priority-rx (rx line-start ?\[ ?#
(group (zero-or-more (not (in ?\[ ?\]))))
(group (zero-or-more ?\])))))
(org-element-map ast 'headline
(lambda (headline)
(if-let* ((priority (org-element-property :priority headline)))
(when (and (not (org-priority-valid-value-p priority))
(org-priority-valid-value-p priority t))
(list (org-element-begin headline)
(format "Out-of-bounds priority '%s'"
(org-priority-to-string priority))))
(when-let* ((headline-value (org-element-property
:raw-value headline))
(matches (string-match bad-priority-rx
headline-value)))
(list (org-element-begin headline)
(if (string-empty-p (match-string 2 headline-value))
(format "Malformed priority '%s'"
(match-string 0 headline-value))
(format "Invalid priority '%s'"
(match-string 1 headline-value))))))))))
(defun org-lint-LaTeX-$ (ast)
"Report semi-obsolete $...$ LaTeX fragments.
AST is the buffer parse tree."
@ -1491,6 +1540,7 @@ AST is the buffer parse tree."
(and (string-match-p "^[$][^$]" (org-element-property :value fragment))
(list (org-element-begin fragment)
"Potentially confusing LaTeX fragment format. Prefer using more reliable \\(...\\)")))))
(defun org-lint-LaTeX-$-ambiguous (_)
"Report LaTeX fragment-like text.
AST is the buffer parse tree."
@ -1507,6 +1557,7 @@ AST is the buffer parse tree."
"$ symbol potentially matching LaTeX fragment boundary. Consider using \\dollar entity.")
report)))
report)))
(defun org-lint-timestamp-syntax (ast)
"Report malformed timestamps.
AST is the buffer parse tree."
@ -1519,6 +1570,23 @@ AST is the buffer parse tree."
(unless (equal expected actual)
(list (org-element-property :begin timestamp)
(format "Potentially malformed timestamp %s. Parsed as: %s" actual expected)))))))
(defun org-lint-clock-syntax (ast)
"Report malformed clocks.
AST is the buffer parse tree."
(org-element-map ast 'clock
(lambda (clock)
(let ((expected (string-trim-right (org-element-interpret-data clock)))
(actual (string-trim
(buffer-substring-no-properties
(org-element-property :begin clock)
(org-element-property :end clock)))))
(unless (equal expected actual)
(list (org-element-property :begin clock)
(format "Potentially malformed CLOCK: line
%s
Parsed as: %s" actual expected)))))))
(defun org-lint-inactive-planning (ast)
"Report inactive timestamp in SCHEDULED/DEADLINE.
AST is the buffer parse tree."
@ -1553,7 +1621,7 @@ AST is the buffer parse tree."
#'org-lint-misplaced-heading :trust 'low)
(org-lint-add-checker 'duplicate-custom-id
"Report duplicates CUSTOM_ID properties"
"Report duplicate CUSTOM_ID properties"
#'org-lint-duplicate-custom-id
:categories '(link))
@ -1822,6 +1890,11 @@ AST is the buffer parse tree."
#'org-lint-item-number
:categories '(plain-list))
(org-lint-add-checker 'priority
"Report out-of-bounds, invalid, and malformed priorities."
#'org-lint-priority
:categories '(markup))
(org-lint-add-checker 'LaTeX-$
"Report potentially confusing $...$ LaTeX markup."
#'org-lint-LaTeX-$
@ -1838,6 +1911,10 @@ AST is the buffer parse tree."
"Report malformed timestamps."
#'org-lint-timestamp-syntax
:categories '(timestamp) :trust 'low)
(org-lint-add-checker 'clock-syntax
"Report malformed clocks."
#'org-lint-clock-syntax
:categories '(timestamp) :trust 'low)
(org-lint-add-checker 'planning-inactive
"Report inactive timestamps in SCHEDULED/DEADLINE."
#'org-lint-inactive-planning

View file

@ -106,6 +106,7 @@
(declare-function org-at-heading-p "org" (&optional invisible-ok))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-back-to-heading-or-point-min "org" (&optional invisible-ok))
(declare-function org-before-first-heading-p "org" ())
(declare-function org-current-level "org" ())
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
@ -113,8 +114,8 @@
(declare-function org-element-interpret-data "org-element" (data))
(declare-function org-element-lineage "org-element-ast" (blob &optional types with-self))
(declare-function org-element-macro-interpreter "org-element" (macro ##))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
(declare-function org-element-normalize-string "org-element" (s))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated no-undefer))
(declare-function org-element-normalize-string "org-element" (s &optional keep-newlines))
(declare-function org-element-parse-buffer "org-element" (&optional granularity visible-only keep-deferred))
(declare-function org-element-property "org-element-ast" (property node))
(declare-function org-element-begin "org-element" (node))
@ -125,7 +126,7 @@
(declare-function org-element-post-blank "org-element" (node))
(declare-function org-element-parent "org-element-ast" (node))
(declare-function org-element-put-property "org-element-ast" (node property value))
(declare-function org-element-set "org-element-ast" (old new))
(declare-function org-element-set "org-element-ast" (old new &optional keep-props))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-element-update-syntax "org-element" ())
(declare-function org-end-of-meta-data "org" (&optional full))
@ -147,9 +148,9 @@
(declare-function org-mode "org" ())
(declare-function org-narrow-to-subtree "org" (&optional element))
(declare-function org-outline-level "org" ())
(declare-function org-previous-line-empty-p "org" ())
(declare-function org-reduced-level "org" (L))
(declare-function org-set-tags "org" (tags))
(declare-function org--deactivate-mark "org" ())
(declare-function org-fold-show-subtree "org-fold" ())
(declare-function org-fold-region "org-fold" (from to flag &optional spec))
(declare-function org-sort-remove-invisible "org" (S))
@ -1858,7 +1859,10 @@ Initial position of cursor is restored after the changes."
(shift-body-ind
;; Shift the indentation between END and BEG by DELTA.
;; Start from the line before END.
(lambda (end beg delta)
;; Take care not to shift to or before IND, which is the
;; containg list item indentation. (otherwise, we are going
;; to break the list structure)
(lambda (end beg delta ind)
(goto-char end)
(skip-chars-backward " \r\t\n")
(forward-line 0)
@ -1871,7 +1875,8 @@ Initial position of cursor is restored after the changes."
(org-inlinetask-goto-beginning))
;; Shift only non-empty lines.
((looking-at-p "^[ \t]*\\S-")
(indent-line-to (+ (org-current-text-indentation) delta))))
(indent-line-to (max (+ (org-current-text-indentation) delta)
(if ind (1+ ind) -1)))))
(forward-line -1))))
(modify-item
;; Replace ITEM first line elements with new elements from
@ -1933,7 +1938,7 @@ Initial position of cursor is restored after the changes."
(ind-shift (- (+ ind-pos (length bul-pos))
(+ ind-old (length bul-old))))
(end-pos (org-list-get-item-end pos old-struct)))
(push (cons pos ind-shift) itm-shift)
(push (list pos ind-shift ind-pos) itm-shift)
(unless (assq end-pos old-struct)
;; To determine real ind of an ending position that
;; is not at an item, we have to find the item it
@ -1955,7 +1960,7 @@ Initial position of cursor is restored after the changes."
(down (car all-ends))
(itemp (assq up struct))
(delta
(if itemp (cdr (assq up itm-shift))
(if itemp (nth 1 (assq up itm-shift))
;; If we're not at an item, there's a child of the
;; item point belongs to above. Make sure the less
;; indented line in this slice has the same column
@ -1981,7 +1986,7 @@ Initial position of cursor is restored after the changes."
down t)))))
(forward-line)))
(- ind min-ind)))))
(push (list down up delta) sliced-struct)))
(push (list down up delta (when itemp (nth 2 (assq up itm-shift)))) sliced-struct)))
;; 3. Shift each slice in buffer, provided delta isn't 0, from
;; end to beginning. Take a special action when beginning is
;; at item bullet.
@ -2358,7 +2363,7 @@ is an integer, 0 means `-', 1 means `+' etc. If WHICH is
(define-minor-mode org-list-checkbox-radio-mode
"When turned on, use list checkboxes as radio buttons."
:lighter " CheckBoxRadio"
(unless (eq major-mode 'org-mode)
(unless (derived-mode-p 'org-mode)
(user-error "Cannot turn this mode outside org-mode buffers")))
(defun org-toggle-radio-button (&optional arg)
@ -2541,10 +2546,6 @@ portion of the buffer."
(let* ((cookie-re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)")
(box-re "^[ \t]*\\([-+*]\\|\\([0-9]+\\|[A-Za-z]\\)[.)]\\)[ \t]+\
\\(?:\\[@\\(?:start:\\)?\\([0-9]+\\|[A-Za-z]\\)\\][ \t]*\\)?\\(\\[[- X]\\]\\)")
(cookie-data (or (org-entry-get nil "COOKIE_DATA") ""))
(recursivep
(or (not org-checkbox-hierarchical-statistics)
(string-match-p "\\<recursive\\>" cookie-data)))
(within-inlinetask (and (not all)
(featurep 'org-inlinetask)
(org-inlinetask-in-task-p)))
@ -2582,14 +2583,18 @@ portion of the buffer."
;; Move to start.
(cond (all (goto-char (point-min)))
(within-inlinetask (org-back-to-heading t))
(t (org-with-limited-levels (outline-previous-heading))))
(t (org-with-limited-levels (org-back-to-heading-or-point-min t))))
;; Build an alist for each cookie found. The key is the position
;; at beginning of cookie and values ending position, format of
;; cookie, number of checked boxes to report and total number of
;; boxes.
(while (re-search-forward cookie-re end t)
(let ((context (save-excursion (backward-char)
(save-match-data (org-element-context)))))
(let* ((context (save-excursion (backward-char)
(save-match-data (org-element-context))))
(cookie-data (save-match-data (or (org-entry-get nil "COOKIE_DATA") "")))
(recursivep
(or (not org-checkbox-hierarchical-statistics)
(string-match-p "\\<recursive\\>" cookie-data))))
(when (and (org-element-type-p context 'statistics-cookie)
(not (string-match-p "\\<todo\\>" cookie-data)))
(push
@ -2725,8 +2730,7 @@ Return t if successful."
(no-subtree (1+ (line-beginning-position)))
(t (org-list-get-item-end (line-beginning-position) struct))))))
(let* ((beg (marker-position org-last-indent-begin-marker))
(end (marker-position org-last-indent-end-marker))
(deactivate-mark nil))
(end (marker-position org-last-indent-end-marker)))
(cond
;; Special case: moving top-item with indent rule.
(specialp
@ -2773,6 +2777,7 @@ Return t if successful."
(org-list-struct-indent beg end struct parents prevs))))
(org-list-write-struct struct new-parents old-struct))
(org-update-checkbox-count-maybe))))))
(setq deactivate-mark (org--deactivate-mark))
t)
(defun org-outdent-item ()

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2013-2026 Free Software Foundation, Inc.
;; Author: Nicolas Goaziou <n.goaziou@gmail.com>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: outlines, hypermedia, calendar, text
;; This file is part of GNU Emacs.
@ -55,9 +56,8 @@
(require 'org-compat)
(declare-function org-collect-keywords "org" (keywords &optional unique directory))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-context "org-element" (&optional element))
(declare-function org-element-copy "org-element-ast" (datum))
(declare-function org-element-copy "org-element-ast" (datum &optional keep-contents))
(declare-function org-element-macro-parser "org-element" ())
(declare-function org-element-keyword-parser "org-element" (limit affiliated))
(declare-function org-element-put-property "org-element-ast" (node property value))
@ -69,10 +69,8 @@
(declare-function org-element-type "org-element-ast" (node &optional anonymous))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
(declare-function org-file-contents "org" (file &optional noerror nocache))
(declare-function org-in-commented-heading-p "org" (&optional no-inheritance element))
(declare-function org-link-search "ol" (s &optional avoid-pos stealth))
(declare-function org-mode "org" ())
(declare-function org-link-search "ol" (s &optional avoid-pos stealth new-heading-container))
(declare-function vc-backend "vc-hooks" (f))
(declare-function vc-call "vc-hooks" (fun file &rest args) t)
(declare-function vc-exec-after "vc-dispatcher" (code &optional success))

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2004-2026 Free Software Foundation, Inc.
;; Author: Carsten Dominik <carsten.dominik@gmail.com>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: outlines, hypermedia, calendar, text
;; URL: https://orgmode.org
;;
@ -33,6 +34,7 @@
(require 'cl-lib)
(require 'format-spec)
(eval-when-compile (require 'subr-x)) ; For `when-let*', Emacs < 29
;;; Org version verification.
@ -80,7 +82,16 @@ Version mismatch is commonly encountered in the following situations:
It is recommended to set `load-path' as early in the config as
possible.
3. New Org version is loaded using straight.el package manager and
3. New Org version is loaded while an old Org version is partially
loaded during compilation or M-x package-upgrade. This usually
should not happen (at least, a number of attemps have been made
to avoid this problem in package.el), but sometimes it does anyway.
You can manually delete Org installation from ~/.emacs.d/elpa/ and
try installing again, possibly from emacs -Q without any
configuration loaded.
4. New Org version is loaded using straight.el package manager and
other package depending on Org is loaded before straight triggers
loading of the newer Org version.
@ -93,7 +104,7 @@ Version mismatch is commonly encountered in the following situations:
sufficient if the corresponding `use-package' statement is
deferring the loading.
4. A new Org version is synchronized with Emacs git repository and
5. A new Org version is synchronized with Emacs git repository and
stale .elc files are still left from the previous build.
It is recommended to remove .elc files from lisp/org directory and
@ -108,10 +119,8 @@ Version mismatch is commonly encountered in the following situations:
(declare-function org-mode "org" ())
(declare-function org-agenda-files "org" (&optional unrestricted archives))
(declare-function org-time-string-to-seconds "org" (s))
(declare-function org-fold-show-context "org-fold" (&optional key))
(declare-function org-fold-save-outline-visibility "org-fold" (use-markers &rest body))
(declare-function org-fold-next-visibility-change "org-fold" (&optional pos limit ignore-hidden-p previous-p))
(declare-function org-fold-core-with-forced-fontification "org-fold" (&rest body))
(declare-function org-fold-folded-p "org-fold" (&optional pos limit ignore-hidden-p previous-p))
(declare-function org-time-convert-to-list "org-compat" (time))
(declare-function org-buffer-text-pixel-width "org-compat" ())
@ -246,26 +255,37 @@ This function is only useful when called from Agenda buffer."
(defmacro org-preserve-local-variables (&rest body)
"Execute BODY while preserving local variables."
(declare (debug (body)))
`(let ((local-variables
(org-with-gensyms (local-variables tick-counter-before)
`(org-with-undo-amalgamate
(let ((,local-variables
(org-with-wide-buffer
(goto-char (point-max))
(let ((case-fold-search t))
(and (re-search-backward "^[ \t]*# +Local Variables:"
(and (re-search-backward
,(rx-let ((prefix
(seq line-start (zero-or-more whitespace)
"#" (one-or-more whitespace))))
(rx prefix "Local Variables:"
(one-or-more anychar)
prefix "End:"
(zero-or-more whitespace) (optional "\n")))
(max (- (point) 3000) 1)
t)
(let ((buffer-undo-list t))
(delete-and-extract-region (point) (point-max)))))))
(tick-counter-before (buffer-modified-tick)))
(cons (match-beginning 0)
(delete-and-extract-region (match-beginning 0)
(match-end 0)))))))
(,tick-counter-before (buffer-modified-tick)))
(unwind-protect (progn ,@body)
(when local-variables
(when ,local-variables
(org-with-wide-buffer
(let ((modified (< ,tick-counter-before (buffer-modified-tick))))
(if (not modified)
(goto-char (car ,local-variables))
(goto-char (point-max))
(unless (bolp) (insert "\n"))
(let ((modified (< tick-counter-before (buffer-modified-tick)))
(buffer-undo-list t))
(insert local-variables)
(unless (bolp) (insert "\n")))
(insert (cdr ,local-variables))
(unless modified
(restore-buffer-modified-p nil))))))))
(restore-buffer-modified-p nil))))))))))
;;;###autoload
(defmacro org-element-with-disabled-cache (&rest body)
@ -274,6 +294,16 @@ This function is only useful when called from Agenda buffer."
`(cl-letf (((symbol-function #'org-element--cache-active-p) (lambda (&rest _) nil)))
,@body))
(defmacro org-with-syntax-table (table &rest body)
"Evaluate BODY with syntax table of current buffer set to TABLE.
This is the same as `with-syntax-table' except that it also binds
`parse-sexp-lookup-properties' to nil."
(declare (debug t) (indent 1))
`(with-syntax-table ,table
(let ((parse-sexp-lookup-properties nil))
,@body)))
;;; Buffer and windows
@ -672,7 +702,14 @@ ones and overrule settings in the other lists."
org-element--cache-diagnostics-ring-size
org-element--cache-sync-keys
org-element--cache-sync-requests
org-element--cache-sync-timer)
org-element--cache-sync-timer
;; FIXME: Avoid copying `buffer-file-name' - when closing a
;; temporary buffer, org-persist badly interacts with multiple
;; _different_ buffers with the same `buffer-file-name' and may
;; modify (via `org-element--cache-persist-before-write' by side
;; effect the cache in a _different_ buffer (whatever comes first
;; in `get-file-buffer').
buffer-file-name)
"List of local variables that cannot be transferred to another buffer.")
(defun org-get-local-variables ()
@ -1142,6 +1179,9 @@ delimiting S."
((= cursor end) 0)
(t (string-width (substring s cursor end)))))))
(defvar org-string-width--old-emacs (version< emacs-version "28")
"When non-nil, use fallback behavior, primarily for old Emacs versions.")
(defun org--string-width-1 (string)
"Return width of STRING when displayed in the current buffer.
Unlike `string-width', this function takes into consideration
@ -1151,29 +1191,10 @@ Results may be off sometimes if it cannot handle a given
`display' value."
(org--string-from-props string 'display 0 (length string)))
(defun org-string-width (string &optional pixels default-face)
"Return width of STRING when displayed in the current buffer.
Return width in pixels when PIXELS is non-nil.
When PIXELS is nil, DEFAULT-FACE is the face used to calculate relative
STRING width. When REFERENCE-FACE is nil, `default' face is used."
(if (and (version< emacs-version "28") (not pixels))
;; FIXME: Fallback to old limited version, because
;; `window-pixel-width' is buggy in older Emacs.
(org--string-width-1 string)
;; Wrap/line prefix will make `window-text-pixel-size' return too
;; large value including the prefix.
(remove-text-properties 0 (length string)
'(wrap-prefix t line-prefix t)
string)
;; Face should be removed to make sure that all the string symbols
;; are using default face with constant width. Constant char width
;; is critical to get right string width from pixel width (not needed
;; when PIXELS are requested though).
(unless pixels
(put-text-property 0 (length string) 'face (or default-face 'default) string))
(let (;; We need to remove the folds to make sure that folded table
(defun org-string-width-invisibility-spec ()
"Return the invisibility spec of this buffer without folds and ellipses."
;; We need to remove the folds to make sure that folded table
;; alignment is not messed up.
(current-invisibility-spec
(or (and (not (listp buffer-invisibility-spec))
buffer-invisibility-spec)
(let (result)
@ -1187,24 +1208,47 @@ STRING width. When REFERENCE-FACE is nil, `default' face is used."
'(org-fold-drawer
org-fold-block
org-fold-outline))))
(push el result)))
result)))
(current-char-property-alias-alist char-property-alias-alist))
(with-current-buffer (get-buffer-create " *Org string width*")
(setq-local display-line-numbers nil)
(setq-local line-prefix nil)
(setq-local wrap-prefix nil)
(setq-local buffer-invisibility-spec
(if (listp current-invisibility-spec)
(mapcar (lambda (el)
(push
;; Consider ellipsis to have 0 width.
;; It is what Emacs 28+ does, but we have
;; to force it in earlier Emacs versions.
(if (and (consp el) (cdr el))
(list (car el))
el))
el)
result)))
result)))
(defun org-string-width (string &optional pixels default-face invisibility-spec)
"Return width of STRING when displayed in the current buffer.
Return width in pixels when PIXELS is non-nil.
When PIXELS is nil, DEFAULT-FACE is the face used to calculate relative
STRING width. When REFERENCE-FACE is nil, `default' face is used.
Use INVISIBILITY-SPEC when non-nil, otherwise construct one without
folds and ellipses."
(if (and org-string-width--old-emacs (not pixels))
;; FIXME: Fallback to old limited version, because
;; `window-pixel-width' is buggy in older Emacs.
(org--string-width-1 string)
;; Wrap/line prefix will make `window-text-pixel-size' return too
;; large value including the prefix.
(setq string (copy-sequence string)) ; do not modify STRING object
(remove-text-properties 0 (length string)
'(wrap-prefix t line-prefix t)
string)
;; Face should be removed to make sure that all the string symbols
;; are using default face with constant width. Constant char width
;; is critical to get right string width from pixel width (not needed
;; when PIXELS are requested though).
(unless pixels
(put-text-property 0 (length string) 'face (or default-face 'default) string))
(let ((current-invisibility-spec (or invisibility-spec (org-string-width-invisibility-spec)))
(current-char-property-alias-alist char-property-alias-alist))
(with-current-buffer (get-buffer-create " *Org string width*" t)
(setq-local display-line-numbers nil)
(setq-local line-prefix nil)
(setq-local wrap-prefix nil)
(setq-local buffer-invisibility-spec
current-invisibility-spec)
current-invisibility-spec))
(setq-local char-property-alias-alist
current-char-property-alias-alist)
(let (pixel-width symbol-width)
@ -1218,7 +1262,7 @@ STRING width. When REFERENCE-FACE is nil, `default' face is used."
(setq symbol-width (org-buffer-text-pixel-width))))
(if pixels
pixel-width
(ceiling pixel-width symbol-width)))))))
(round pixel-width symbol-width)))))))
(defmacro org-current-text-column ()
"Like `current-column' but ignore display properties.
@ -1228,7 +1272,9 @@ This function forces `tab-width' value because it is used as a part of
the parser, to ensure parser consistency when calculating list
indentation."
`(progn
(unless (= 8 tab-width) (error "Tab width in Org files must be 8, not %d. Please adjust your `tab-width' settings for Org mode" tab-width))
(unless (= 8 tab-width)
(org--set-tab-width)
(warn "Tab width in Org files must be 8, not %d. Setting back to 8. Please adjust your `tab-width' settings for Org mode" tab-width))
(string-width (buffer-substring-no-properties
(line-beginning-position) (point)))))
@ -1701,18 +1747,33 @@ it for output."
(file-relative-name source pwd))
source))
(log-buf (and log-buf (get-buffer-create log-buf)))
(time (file-attribute-modification-time (file-attributes output))))
(time (file-attribute-modification-time (file-attributes output)))
exit-status (did-error nil))
(save-window-excursion
(dolist (command commands)
(cond
((functionp command)
;; We could treat return value of the function
;; as return code in shell command, but that would be
;; a breaking changed compared to historical behavior.
;; Functions might still take care to remove the target file
;; (if it already exists) to mark failure.
(funcall command (shell-quote-argument relname)))
((stringp command)
(let ((shell-command-dont-erase-buffer t))
(shell-command command log-buf))))))
(setq exit-status (shell-command command log-buf))
(when (and (numberp exit-status) (> exit-status 0))
(setq did-error t)))))))
;; Check for process failure. Output file is expected to be
;; located in the same directory as SOURCE.
(unless (org-file-newer-than-p output time)
;; Sometimes, the (LaTeX) process fails still producing output.
;; Then, assume compilation success. It is way too common for
;; LaTeX to throw non-0 exit code yet producing perfectly usable
;; pdfs.
(when (or (not (file-exists-p output))
;; non-0 exit code and output not updated.
(and did-error
(not (org-file-newer-than-p output time))))
(ignore (defvar org-batch-test))
;; Display logs when running tests.
(when (bound-and-true-p org-batch-test)
@ -1812,6 +1873,15 @@ indirectly called by the latter."
(eq (window-frame) (window-frame window))))
(window--display-buffer buffer window 'reuse alist))))
(defun org-base-buffer-file-name (&optional buffer)
"Resolve the base file name for the provided BUFFER.
If BUFFER is not provided, default to the current buffer. If
BUFFER does not have a file name associated with it (e.g. a
transient buffer) then return nil."
(if-let* ((base-buffer (buffer-base-buffer buffer)))
(buffer-file-name base-buffer)
(buffer-file-name buffer)))
(provide 'org-macs)
;; Local variables:

View file

@ -1035,13 +1035,13 @@ be returned that indicates what went wrong."
((eq what 'priority)
(let ((case-fold-search nil))
(when (looking-at org-complex-heading-regexp)
(let ((current (and (match-end 3) (substring (match-string 3) 2 3))))
(let ((current (and (match-end 3) (substring (match-string 3) 2 -1))))
(cond
((equal current new) t) ;no action required
((or (equal current old)
(eq org-mobile-force-mobile-change t)
(memq 'tags org-mobile-force-mobile-change))
(org-priority (and new (string-to-char new))))
(org-priority (and new (org-priority-to-value new))))
(t (error "Priority was expected to be %s, but is %s"
old current)))))))

View file

@ -427,13 +427,13 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
(let ((tags (org-get-tags nil t)))
(org-mouse-keyword-menu
(sort (mapcar #'car (org-get-buffer-tags))
(or org-tags-sort-function #'org-string<))
#'org-tags-sort)
(lambda (tag)
(org-mouse-set-tags
(sort (if (member tag tags)
(delete tag tags)
(cons tag tags))
(or org-tags-sort-function #'org-string<))))
#'org-tags-sort)))
(lambda (tag) (member tag tags))
))
'("--"
@ -491,7 +491,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
`("Main Menu"
["Show Overview" org-mouse-show-overview t]
["Show Headlines" org-mouse-show-headlines t]
["Show All" org-show-all t]
["Show All" org-fold-show-all t]
["Remove Highlights" org-remove-occur-highlights
:visible org-occur-highlights]
"--"
@ -504,7 +504,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
("Check Tags"
,@(org-mouse-keyword-menu
(sort (mapcar #'car (org-get-buffer-tags))
(or org-tags-sort-function #'org-string<))
#'org-tags-sort)
(lambda (tag) (org-tags-sparse-tree nil tag)))
"--"
["Custom Tag ..." org-tags-sparse-tree t])
@ -515,7 +515,7 @@ SCHEDULED: or DEADLINE: or ANYTHINGLIKETHIS:"
("Display Tags"
,@(org-mouse-keyword-menu
(sort (mapcar #'car (org-get-buffer-tags))
(or org-tags-sort-function #'org-string<))
#'org-tags-sort)
(lambda (tag) (org-tags-view nil tag)))
"--"
["Custom Tag ..." org-tags-view t])
@ -627,7 +627,7 @@ This means, between the beginning of line and the point."
["Sparse Tree" (org-occur ',region-string)]
["Find in Buffer" (occur ',region-string)]
["Grep in Current Dir"
(grep (format "grep -rnH -e '%s' *" ',region-string))]
(grep (format "grep -rnH -e '%s' ./*" ',region-string))]
["Grep in Parent Dir"
(grep (format "grep -rnH -e '%s' ../*" ',region-string))]
"--"
@ -887,7 +887,7 @@ This means, between the beginning of line and the point."
(when (memq 'activate-stars org-mouse-features)
(font-lock-add-keywords
nil
`((,org-outline-regexp
`((,org-outline-regexp-bol
0 `(face org-link mouse-face highlight keymap ,org-mouse-map)
'prepend))
t))

View file

@ -76,10 +76,6 @@
(defvar org-n-level-faces)
(defvar org-odd-levels-only)
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
(declare-function org-reduced-level "org" (l))
;;; Customization

View file

@ -37,7 +37,7 @@
(require 'org-compat)
(require 'pcomplete)
(declare-function org-at-heading-p "org" (&optional ignored))
(declare-function org-load-export-backends "org" ())
(declare-function org-babel-combine-header-arg-lists "ob-core" (original &rest others))
(declare-function org-babel-get-src-block-info "ob-core" (&optional no-eval datum))
(declare-function org-before-first-heading-p "org" ())
@ -51,15 +51,14 @@
(declare-function org-export-backend-options "ox" (cl-x) t)
(declare-function org-get-buffer-tags "org" ())
(declare-function org-get-export-keywords "org" ())
(declare-function org-get-heading "org" (&optional no-tags no-todo no-priority no-comment))
(declare-function org-get-tags "org" (&optional pos local))
(declare-function org-link-heading-search-string "ol" (&optional string))
(declare-function org-tag-alist-to-string "org" (alist &optional skip-key))
(declare-function org-time-stamp-format "org" (&optional with-time inactive custom))
(declare-function org-priority-to-string "org" (priority))
(defvar org-babel-common-header-args-w-values)
(defvar org-current-tag-alist)
(defvar org-priority-default)
(defvar org-drawer-regexp)
(defvar org-element-affiliated-keywords)
(defvar org-entities)
@ -68,9 +67,10 @@
(defvar org-export-select-tags)
(defvar org-file-tags)
(defvar org-priority-highest)
(defvar org-priority-default)
(defvar org-priority-lowest)
(defvar org-link-abbrev-alist)
(defvar org-link-abbrev-alist-local)
(defvar org-priority-lowest)
(defvar org-options-keywords)
(defvar org-outline-regexp)
(defvar org-property-re)
@ -157,6 +157,26 @@ The return value is a string naming the thing at point."
(while (setq e (pop list))
(setq res (cons (downcase e) (cons (upcase e) res))))
(nreverse res)))
;; Variables and constants
(defconst org-block-keywords
(let (block-names)
(dolist (name
'("CENTER" "COMMENT" "EXAMPLE" "EXPORT" "QUOTE" "SRC"
"VERSE")
block-names)
(push (format "END_%s" name) block-names)
(push (concat "BEGIN_"
name
;; Since language is compulsory in
;; export blocks source blocks, add
;; a space.
(and (member name '("EXPORT" "SRC")) " "))
block-names)
(push (concat "ATTR_" name ": ") block-names))
block-names)
"Keywords related to blocks.")
;;; Completion API
@ -219,20 +239,7 @@ When completing for #+STARTUP, for example, this function returns
org-options-keywords)
(mapcar (lambda (keyword) (concat keyword ": "))
org-element-affiliated-keywords)
(let (block-names)
(dolist (name
'("CENTER" "COMMENT" "EXAMPLE" "EXPORT" "QUOTE" "SRC"
"VERSE")
block-names)
(push (format "END_%s" name) block-names)
(push (concat "BEGIN_"
name
;; Since language is compulsory in
;; export blocks source blocks, add
;; a space.
(and (member name '("EXPORT" "SRC")) " "))
block-names)
(push (format "ATTR_%s: " name) block-names)))
org-block-keywords
(mapcar (lambda (keyword) (concat keyword ": "))
(org-get-export-keywords))))
(substring pcomplete-stub 2)))
@ -269,10 +276,11 @@ When completing for #+STARTUP, for example, this function returns
(defun pcomplete/org-mode/file-option/priorities ()
"Complete arguments for the #+PRIORITIES file option."
(pcomplete-here (list (format "%c %c %c"
org-priority-highest
org-priority-lowest
org-priority-default))))
(pcomplete-here
(list (format "%s %s %s"
(org-priority-to-string org-priority-highest)
(org-priority-to-string org-priority-lowest)
(org-priority-to-string org-priority-default)))))
(defun pcomplete/org-mode/file-option/select_tags ()
"Complete arguments for the #+SELECT_TAGS file option."
@ -311,6 +319,7 @@ When completing for #+STARTUP, for example, this function returns
(defun pcomplete/org-mode/file-option/options ()
"Complete arguments for the #+OPTIONS file option."
(org-load-export-backends)
(while (pcomplete-here
(pcomplete-uniquify-list
(append

View file

@ -3,6 +3,7 @@
;; Copyright (C) 2021-2026 Free Software Foundation, Inc.
;; Author: Ihor Radchenko <yantar92 at posteo dot net>
;; Maintainer: Ihor Radchenko <yantar92 at posteo dot net>
;; Keywords: cache, storage
;; This file is part of GNU Emacs.
@ -58,7 +59,8 @@
;; has been removed.
;;
;; 3. Temporarily cache a file, including TRAMP path to disk:
;; (org-persist-write 'file "/path/to/file")
;; (org-persist-write `(file "/path/to/file"))
;; (org-persist-read `(file "/path/to/file")) ; => get path to the cached file copy
;;
;; 4. Cache file or URL while some other file exists.
;; (org-persist-register '(url "https://static.fsf.org/common/img/logo-new.png") '(:file "/path to the other file") :expiry 'never :write-immediately t)
@ -262,10 +264,6 @@
(require 'org-id)
(require 'xdg nil t)
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-next-visible-heading "org" (arg))
(declare-function org-at-heading-p "org" (&optional invisible-not-ok))
;; Silence byte-compiler (used in `org-persist--write-elisp-file').
(defvar pp-use-max-width)
@ -396,6 +394,9 @@ message is displayed.
When the value is a non-nil non-number, always display the message.
When the value is nil, never display the message.")
(defvar org-persist--wrote-to-disk nil
"Whether we wrote to disk during current Emacs session.")
;;;; Common functions
(defun org-persist--display-time (duration format &rest args)
@ -448,7 +449,11 @@ FORMAT and ARGS are passed to `message'."
;; FIXME: `pp' is very slow when writing even moderately large datasets
;; We should probably drop it or find some fast formatter.
(defun org-persist--write-elisp-file (file data &optional no-circular pp)
"Write elisp DATA to FILE."
"Write to index and then write elisp DATA to FILE.
When optional argument NO-CIRCULAR is non-nil, do not bind
`print-circle' to t.
When optional argument PP is non-nil, pretty-print the data (slow on
moderately large data)."
;; Fsync slightly reduces the chance of an incomplete filesystem
;; write, however on modern hardware its effectiveness is
;; questionable and it is insufficient to guarantee complete writes.
@ -472,20 +477,22 @@ FORMAT and ARGS are passed to `message'."
(print-escape-nonascii t)
(print-continuous-numbering t)
print-number-table
(start-time (float-time)))
(start-time (float-time))
(tmp-file (make-temp-file "org-persist-")))
(unless (file-exists-p (file-name-directory file))
(make-directory (file-name-directory file) t))
;; Force writing even when the file happens to be opened by
;; another Emacs process.
(cl-letf (((symbol-function #'ask-user-about-lock)
;; FIXME: Emacs 27 does not yet have `always'.
(lambda (&rest _) t)))
(with-temp-file file
;; Do not write to FILE directly. Another Emacs instance may be
;; doing the same at the same time. Instead, write to new
;; temporary file and then rename it (renaming is atomic
;; operation that does not create data races).
;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=75209#35
(with-temp-file tmp-file
(insert ";; -*- mode: lisp-data; -*-\n")
(if pp
(let ((pp-use-max-width nil)) ; Emacs bug#58687
(pp data (current-buffer)))
(prin1 data (current-buffer)))))
(prin1 data (current-buffer))))
(rename-file tmp-file file 'overwrite)
(org-persist--display-time
(- (float-time) start-time)
"Writing to %S" file)))
@ -533,13 +540,26 @@ BODY is executed in a context with the following additional variables:
(org-persist-collection-let collection
(and org-persist--index-hash
(catch :found
(dolist (cont (cons container container))
(dolist (cont
(if (listp (car container)) ; container group
(cons container container)
(list container)))
(let ((r (or (gethash (cons cont associated) org-persist--index-hash)
(and path (gethash (cons cont (list :file path)) org-persist--index-hash))
(and inode (gethash (cons cont (list :inode inode)) org-persist--index-hash))
(and hash (gethash (cons cont (list :hash hash)) org-persist--index-hash))
(and key (gethash (cons cont (list :key key)) org-persist--index-hash)))))
(when r (throw :found r))))))))
(when (and r
;; Every element in container group of
;; COLLECTION matches returned CONTAINER.
(seq-every-p
(lambda (cont)
(org-persist-collection-let r
(member cont container)))
(if (listp (car container))
container
(list container))))
(throw :found r))))))))
(defun org-persist--add-to-index (collection &optional hash-only)
"Add or update COLLECTION in `org-persist--index'.
@ -557,7 +577,10 @@ Return PLIST."
existing)
(unless hash-only (push collection org-persist--index))
(unless org-persist--index-hash (setq org-persist--index-hash (make-hash-table :test 'equal)))
(dolist (cont (cons container container))
(dolist (cont
(if (listp (car container)) ; container group
(cons container container)
(list container)))
(puthash (cons cont associated) collection org-persist--index-hash)
(when path (puthash (cons cont (list :file path)) collection org-persist--index-hash))
(when inode (puthash (cons cont (list :inode inode)) collection org-persist--index-hash))
@ -579,7 +602,10 @@ Return PLIST."
(let ((existing (org-persist--find-index collection)))
(when existing
(org-persist-collection-let collection
(dolist (cont (cons container container))
(dolist (cont
(if (listp (car container)) ; container group
(cons container container)
(list container)))
(unless (listp (car container))
(org-persist-gc:generic cont collection)
(dolist (afile (org-persist-associated-files:generic cont collection))
@ -599,7 +625,6 @@ or file-path, (:inode inode), (:hash hash), or or (:key key).
MISC, if non-nil will be appended to the collection. It must be a plist."
(unless (and (listp container) (listp (car container)))
(setq container (list container)))
(setq associated (org-persist--normalize-associated associated))
(when (and misc (or (not (listp misc)) (cl-oddp (length misc))))
(error "org-persist: Not a plist: %S" misc))
(or (org-persist--find-index
@ -609,7 +634,8 @@ MISC, if non-nil will be appended to the collection. It must be a plist."
(nconc
(list :container (org-persist--normalize-container container)
:persist-file
(replace-regexp-in-string "^.." "\\&/" (org-id-uuid))
(let ((uuid (org-id-uuid)))
(concat (substring uuid 0 2) "/" (substring uuid 2)))
:associated associated)
misc))))
@ -635,9 +661,10 @@ When INNER is non-nil, do not try to match as list of containers."
(defvar org-persist--associated-buffer-cache (make-hash-table :weakness 'key)
"Buffer hash cache.")
(defun org-persist--normalize-associated (associated)
(defsubst org-persist--normalize-associated (associated)
"Normalize ASSOCIATED representation into (:type value)."
(pcase associated
(`nil nil)
((or (pred stringp) `(:file ,_))
(unless (stringp associated)
(setq associated (cadr associated)))
@ -729,7 +756,7 @@ COLLECTION is the plist holding data collection."
(defun org-persist-read:index (cont index-file _)
"Read index container CONT from INDEX-FILE."
(when (file-exists-p index-file)
(when (and (file-exists-p index-file) (file-readable-p index-file))
(let ((index (org-persist--read-elisp-file index-file)))
(when index
(catch :found
@ -785,9 +812,10 @@ COLLECTION is the plist holding data collection."
(when (file-exists-p org-persist-directory)
(dolist (file (directory-files org-persist-directory 'absolute
"\\`[^.][^.]"))
(when (file-writable-p file)
(if (file-directory-p file)
(delete-directory file t)
(delete-file file))))
(delete-file file)))))
(plist-put (org-persist--get-collection container) :expiry 'never))))
(defun org-persist--load-index ()
@ -827,6 +855,12 @@ COLLECTION is the plist holding data collection."
(when (boundp (cadr container))
(symbol-value (cadr container))))
(`nil
;; FIXME: Here and in other places, we use `get-file-buffer'
;; assuming that all the buffers with the same
;; `buffer-file-name' are same. However, this may not
;; necessarily be the case in general and we may initiate
;; writing cache in one buffer, but `get-file-buffer' may then
;; return _another_ buffer (with the same `buffer-file-name').
(if-let* ((buf (and (plist-get (plist-get collection :associated) :file)
(get-file-buffer (plist-get (plist-get collection :associated) :file)))))
;; FIXME: There is `buffer-local-boundp' introduced in Emacs 28.
@ -906,7 +940,7 @@ Otherwise, return t."
(let ((index-file
(org-file-name-concat org-persist-directory org-persist-index-file)))
(org-persist--merge-index-with-disk)
(org-persist--write-elisp-file index-file org-persist--index t)
(org-persist--write-elisp-file index-file org-persist--index t nil)
(setq org-persist--index-age
(file-attribute-modification-time (file-attributes index-file)))
index-file)))
@ -918,6 +952,7 @@ Otherwise, return t."
(defun org-persist--merge-index-with-disk ()
"Merge `org-persist--index' with the current index file on disk."
(org-persist--load-index)
(let* ((index-file
(org-file-name-concat org-persist-directory org-persist-index-file))
(disk-index
@ -929,13 +964,17 @@ Otherwise, return t."
(when disk-index
(setq org-persist--index combined-index
org-persist--index-age
(file-attribute-modification-time (file-attributes index-file))))))
(file-attribute-modification-time (file-attributes index-file)))
;; Store newly added entries in the index hash.
(mapc (lambda (collection) (org-persist--add-to-index collection 'hash))
org-persist--index))))
(defun org-persist--merge-index (base other)
"Attempt to merge new index items in OTHER into BASE.
Items with different details are considered too difficult, and skipped."
(if other
(let ((new (cl-set-difference other base :test #'equal))
(if (not base) other
(let ((new (cl-set-difference other base :key #'org-persist--find-index))
(base-files (mapcar (lambda (s) (plist-get s :persist-file)) base))
(combined (reverse base)))
(dolist (item (nreverse new))
@ -945,12 +984,13 @@ Items with different details are considered too difficult, and skipped."
(plist-get item :persist-file))))
(member (plist-get item :persist-file) base-files))
(push item combined)))
(nreverse combined))
(nreverse combined)))
base))
;;;; Public API
(cl-defun org-persist-register (container &optional associated &rest misc
(cl-defun org-persist-register
( container &optional associated &rest misc
&key inherit
&key (expiry org-persist-default-expiry)
&key (write-immediately nil)
@ -969,8 +1009,11 @@ MISC will be appended to the collection. It must be alternating :KEY
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))
;; Sync cache with disk, dropping conflicting items between multiple
;; Emacsen.
(org-persist--merge-index-with-disk)
(setq container (org-persist--normalize-container container))
(setq associated (org-persist--normalize-associated associated))
(when inherit
(setq inherit (org-persist--normalize-container inherit))
(let ((inherited-collection (org-persist--get-collection inherit associated))
@ -996,7 +1039,9 @@ with `org-persist-write'."
When ASSOCIATED is `all', unregister CONTAINER everywhere.
When REMOVE-RELATED is non-nil, remove all the containers stored with
the CONTAINER as well."
(unless org-persist--index (org-persist--load-index))
;; Sync cache with disk, dropping conflicting items between multiple
;; Emacsen.
(org-persist--merge-index-with-disk)
(setq container (org-persist--normalize-container container))
(if (eq associated 'all)
(mapc (lambda (collection)
@ -1012,10 +1057,6 @@ the CONTAINER as well."
(remove container (plist-get collection :container)))
(org-persist--add-to-index collection))))))
(defvar org-persist--write-cache (make-hash-table :test #'equal)
"Hash table storing as-written data objects.
This data is used to avoid reading the data multiple times.")
(cl-defun org-persist-read (container &optional associated hash-must-match load &key read-related)
"Restore CONTAINER data for ASSOCIATED.
When HASH-MUST-MATCH is non-nil, do not restore data if hash for
@ -1037,7 +1078,9 @@ CONTAINER as well. For example:
(org-persist-read \"My data\") ; => \"My data\"
(org-persist-read \"My data\" nil nil nil
:read-related t) ; => (\"My data\" \"test\")"
(unless org-persist--index (org-persist--load-index))
;; Sync cache with disk, dropping conflicting items between multiple
;; Emacsen.
(org-persist--merge-index-with-disk)
(setq associated (org-persist--normalize-associated associated))
(setq container (org-persist--normalize-container container))
(let* ((collection (org-persist--find-index `(:container ,container :associated ,associated)))
@ -1063,8 +1106,7 @@ CONTAINER as well. For example:
(unless (seq-find (lambda (v)
(run-hook-with-args-until-success 'org-persist-before-read-hook v associated))
(plist-get collection :container))
(setq data (or (gethash persist-file org-persist--write-cache)
(org-persist--read-elisp-file persist-file)))
(setq data (org-persist--read-elisp-file persist-file))
(when data
(cl-loop for c in (plist-get collection :container)
with result = nil
@ -1086,7 +1128,9 @@ have the same meaning as in `org-persist-read'."
(defun org-persist-load-all (&optional associated)
"Restore all the persistent data associated with ASSOCIATED."
(unless org-persist--index (org-persist--load-index))
;; Sync cache with disk, dropping conflicting items between multiple
;; Emacsen.
(org-persist--merge-index-with-disk)
(setq associated (org-persist--normalize-associated associated))
(let (all-containers)
(dolist (collection org-persist--index)
@ -1113,6 +1157,7 @@ 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 org-persist--wrote-to-disk t)
(setq associated (org-persist--normalize-associated associated))
;; Update hash
(when (and (plist-get associated :file)
@ -1131,18 +1176,18 @@ When IGNORE-RETURN is non-nil, just return t on success without calling
(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)))))))
(or ignore-return (org-persist-read container associated))))))
(defun org-persist-write-all (&optional associated)
"Save all the persistent data.
When ASSOCIATED is non-nil, only save the matching data."
(unless org-persist--index (org-persist--load-index))
;; Sync cache with disk, dropping conflicting items between multiple
;; Emacsen.
(org-persist--merge-index-with-disk)
(setq associated (org-persist--normalize-associated associated))
(if
(and (equal 1 (length org-persist--index))
@ -1220,6 +1265,7 @@ Do nothing in an indirect buffer."
(defun org-persist--refresh-gc-lock ()
"Refresh session timestamp in `org-persist-gc-lock-file'.
Remove expired sessions timestamps."
(when org-persist--wrote-to-disk
(let* ((file (org-file-name-concat org-persist-directory org-persist-gc-lock-file))
(alist (when (file-exists-p file) (org-persist--read-elisp-file file)))
new-alist)
@ -1229,7 +1275,7 @@ Remove expired sessions timestamps."
(when (< (- (float-time (cdr record)) (float-time (current-time)))
org-persist-gc-lock-expiry)
(push record new-alist)))
(org-persist--write-elisp-file file new-alist)))
(ignore-errors (org-persist--write-elisp-file file new-alist)))))
(defun org-persist--gc-orphan-p ()
"Return non-nil, when orphan files should be garbage-collected.
@ -1237,16 +1283,16 @@ Remove current sessions from `org-persist-gc-lock-file'."
(let* ((file (org-file-name-concat org-persist-directory org-persist-gc-lock-file))
(alist (when (file-exists-p file) (org-persist--read-elisp-file file))))
(setq alist (org-assoc-delete-all before-init-time alist))
(org-persist--write-elisp-file file alist)
(ignore-errors (org-persist--write-elisp-file file alist))
;; Only GC orphan files when there are no active sessions.
(not alist)))
(defun org-persist-gc ()
"Remove expired or unregistered containers and orphaned files.
Also, remove containers associated with non-existing files."
(if org-persist--index
;; Sync cache with disk, dropping conflicting items between multiple
;; Emacsen.
(org-persist--merge-index-with-disk)
(org-persist--load-index))
(let (new-index
(remote-files-num 0)
(orphan-files

View file

@ -38,7 +38,6 @@
(require 'org)
(require 'org-table)
(declare-function gnuplot-delchar-or-maybe-eof "ext:gnuplot" (arg))
(declare-function gnuplot-mode "ext:gnuplot" ())
(declare-function gnuplot-send-buffer-to-gnuplot "ext:gnuplot" ())
@ -48,7 +47,9 @@
(:ind . 0))
"Default options to gnuplot used by `org-plot/gnuplot'.")
(defvar org-plot-timestamp-fmt nil)
(defvar org-plot-timestamp-fmt "%Y-%m-%d-%H:%M:%S"
"Default time format to be passed to Gnuplot.
Can be changed via timefmt plot option.")
(defun org-plot/add-options-to-plist (p options)
"Parse an OPTIONS line and set values in the property list P.
@ -130,14 +131,17 @@ will be added. Returns the resulting property list."
Pass PARAMS through to `orgtbl-to-generic' when exporting TABLE."
(with-temp-file
data-file
(setq-local org-plot-timestamp-fmt (or
(let ((org-plot-timestamp-fmt
(or
(plist-get params :timefmt)
"%Y-%m-%d-%H:%M:%S"))
org-plot-timestamp-fmt
"%Y-%m-%d-%H:%M:%S")))
(insert (orgtbl-to-generic
table
(org-combine-plists
'(:sep "\t" :fmt org-plot-quote-tsv-field)
params))))
'( :sep "\t" :fmt org-plot-quote-tsv-field
:with-special-rows nil)
params)))))
nil)
(defun org-plot/gnuplot-to-grid-data (table data-file params)
@ -302,9 +306,10 @@ When NORMALIZE is non-nil, the count is divided by the number of values."
(defcustom org-plot/gnuplot-script-preamble ""
"String of function to be inserted before the gnuplot plot command is run.
Note that this is in addition to, not instead of other content generated in
`org-plot/gnuplot-script'. If a function, it is called with the plot type as
the argument, and must return a string to be used."
Note that this is in addition to, not instead of other content generated
in `org-plot/gnuplot-script'. If a function, it is called with the
parameters used by the current plot type (see
`org-plot/preset-plot-types'), and must return a string to be used."
:group 'org-plot
:type '(choice string function))
@ -349,7 +354,7 @@ the argument, and must return a string to be used."
(grid :plot-cmd "splot"
:plot-pre (lambda (_table _data-file _num-cols params _plot-str)
(if (plist-get params :map) "set pm3d map" "set map"))
:data-dump (lambda (table data-file params _num-cols)
:data-dump (lambda (table data-file _num-cols params)
(let ((y-labels (org-plot/gnuplot-to-grid-data
table data-file params)))
(when y-labels (plist-put params :ylabels y-labels))))
@ -391,8 +396,8 @@ be set.
- :data-dump - Function to dump the table to a datafile for ease of
use.
Accepts lambda function. Default lambda body:
(org-plot/gnuplot-to-data table data-file params)
Accepts function with arguments:
(table data-file num-cols params)
- :plot-pre - Gnuplot code to be inserted early into the script, just
after term and output have been set.
@ -400,7 +405,9 @@ be set.
Accepts string, nil, or lambda function which returns string
or nil. Defaults to nil."
:group 'org-plot
:type 'alist)
:package-version '(Org . "9.8")
:type 'alist
:risky t)
(defvar org--plot/radar-template
"### spider plot/chart with gnuplot
@ -541,7 +548,8 @@ EOD
"String or function which provides the extra term options.
E.g. a value of \"size 1050,650\" would cause
\"set term ... size 1050,650\" to be used.
If a function, it is called with the plot type as the argument."
If a function, it is called with the parameters used by the current plot
type, see `org-plot/preset-plot-types'."
:group 'org-plot
:type '(choice string function))
@ -627,7 +635,7 @@ manner suitable for prepending to a user-specified script."
(defun org-plot/redisplay-img-in-buffer (img-file)
"Find any overlays for IMG-FILE in the current Org buffer, and refresh them."
(dolist (img-overlay org-inline-image-overlays)
(dolist (img-overlay org-link-preview-overlays)
(when (string= img-file (plist-get (cdr (overlay-get img-overlay 'display)) :file))
(when (and (file-exists-p img-file)
(fboundp 'image-flush))
@ -662,8 +670,7 @@ line directly before or after the table."
(looking-at "[[:space:]]*#\\+"))
(setf params (org-plot/collect-options params))))
;; collect table and table information
(let* ((data-file (make-temp-file "org-plot"))
(table (let ((tbl (save-excursion
(let* ((table (let ((tbl (save-excursion
(org-plot/goto-nearest-table)
(org-table-to-lisp))))
(when (pcase (plist-get params :transpose)
@ -679,14 +686,13 @@ line directly before or after the table."
tbl))
(num-cols (length (if (eq (nth 0 table) 'hline) (nth 1 table)
(nth 0 table))))
(type (assoc (plist-get params :plot-type)
org-plot/preset-plot-types))
gnuplot-script)
(type (cdr (assoc (plist-get params :plot-type)
org-plot/preset-plot-types)))
gnuplot-script data-file)
(unless type
(user-error "Org-plot type `%s' is undefined" (plist-get params :plot-type)))
(run-with-idle-timer 0.1 nil #'delete-file data-file)
(when (eq (cadr table) 'hline)
(setf params
(plist-put params :labels (car table))) ; headers to labels
@ -695,8 +701,18 @@ line directly before or after the table."
(save-excursion (while (and (equal 0 (forward-line -1))
(looking-at "[[:space:]]*#\\+"))
(setf params (org-plot/collect-options params))))
;; Ensure that the user can override any plot parameter, and
;; that the parameters set by the plot type in
;; `org-plot/preset-plot-types' is respected.
(setq params (org-combine-plists type params))
;; Dump table to datafile
(let ((dump-func (plist-get type :data-dump)))
;; Use a stable temporary file to ensure that 'replot' upon
;; resizing a GUI gnuplot terminal window works.
(setq data-file (org-babel-temp-stable-file
(list (or dump-func 'org-plot/gnuplot-to-data)
table num-cols params)
"org-plot"))
(if dump-func
(funcall dump-func table data-file num-cols params)
(org-plot/gnuplot-to-data table data-file params)))

View file

@ -101,15 +101,15 @@
;; new URLSearchParams({
;; url: location.href,
;; title: document.title,
;; body: window.getSelection()})
;; body: window.getSelection()});void(0)
;;
;; Alternatively use the following expression that encodes space as \"%20\"
;; instead of \"+\", so it is compatible with Org versions from 9.0 to 9.4:
;;
;; location.href='org-protocol://sub-protocol?url='+
;; javascript:location.href='org-protocol://sub-protocol?url='+
;; encodeURIComponent(location.href)+'&title='+
;; encodeURIComponent(document.title)+'&body='+
;; encodeURIComponent(window.getSelection())
;; encodeURIComponent(window.getSelection());void(0)
;;
;; The handler for the sub-protocol \"capture\" detects an optional template
;; char that, if present, triggers the use of a special template.
@ -437,14 +437,14 @@ also recognized.
The location for a browser's bookmark may look like this:
javascript:location.href = \\='org-protocol://store-link?\\=' +
new URLSearchParams({url:location.href, title:document.title});
new URLSearchParams({url:location.href, title:document.title});void(0);
or to keep compatibility with Org versions from 9.0 to 9.4 it may be:
javascript:location.href = \\
\\='org-protocol://store-link?url=\\=' + \\
encodeURIComponent(location.href) + \\='&title=\\=' + \\
encodeURIComponent(document.title);
encodeURIComponent(document.title);void(0);
Don't use `escape()'! Use `encodeURIComponent()' instead. The
title of the page could contain slashes and the location
@ -482,20 +482,21 @@ by `/'. The location for a browser's bookmark looks like this:
new URLSearchParams({
url: location.href,
title: document.title,
body: window.getSelection()})
body: window.getSelection()});void(0)
or to keep compatibility with Org versions from 9.0 to 9.4:
javascript:location.href = \\='org-protocol://capture?url=\\='+ \\
encodeURIComponent(location.href) + \\='&title=\\=' + \\
encodeURIComponent(document.title) + \\='&body=\\=' + \\
encodeURIComponent(window.getSelection())
encodeURIComponent(window.getSelection());void(0)
By default, it uses the character `org-protocol-default-template-key',
which should be associated with a template in `org-capture-templates'.
You may specify the template with a template= query parameter, like this:
javascript:location.href = \\='org-protocol://capture?template=b\\='+ ...
javascript:location.href
= \\='org-protocol://capture?template=b\\='+ ...;void(0)
Now template ?b will be used."
(let* ((parts
@ -556,13 +557,13 @@ in `org-protocol-project-alist'.
The location for a browser's bookmark should look like this:
javascript:location.href = \\='org-protocol://open-source?\\=' +
new URLSearchParams({url: location.href})
new URLSearchParams({url: location.href});void(0)
or if you prefer to keep compatibility with older Org versions (9.0 to 9.4),
consider the following expression:
javascript:location.href = \\='org-protocol://open-source?url=\\=' + \\
encodeURIComponent(location.href)"
encodeURIComponent(location.href);void(0)"
;; As we enter this function for a match on our protocol, the return value
;; defaults to nil.
(let (;; (result nil)

View file

@ -72,9 +72,10 @@ This is a list of cons cells. Each cell contains:
or a symbol whose function or variable value will be used to retrieve
a file name or a list of file names. If you use `org-agenda-files' for
that, all agenda files will be scanned for targets. Nil means consider
headings in the current buffer.
headlines in the current buffer.
- A specification of how to find candidate refile targets. This may be
any of:
- t to indicate that all headlines should be considered.
- a cons cell (:tag . \"TAG\") to identify refile targets by a tag.
This tag has to be present in all target headlines, inheritance will
not be considered.
@ -104,13 +105,15 @@ are used, equivalent to the value `((nil . (:level . 1)))'."
(choice :value org-agenda-files
(const :tag "All agenda files" org-agenda-files)
(const :tag "Current buffer" nil)
(function) (variable) (file))
(choice :tag "Identify target headline by"
(cons :tag "Specific tag" (const :value :tag) (string))
(cons :tag "TODO keyword" (const :value :todo) (string))
(cons :tag "Regular expression" (const :value :regexp) (regexp))
(cons :tag "Level number" (const :value :level) (integer))
(cons :tag "Max Level number" (const :value :maxlevel) (integer))))))
(function) (variable) (file) (repeat (file)))
(choice :tag "Target headlines"
(const :tag "All" t)
(cons :tag "Tagged with" (const :value :tag) (string))
(cons :tag "With the TODO keyword" (const :value :todo) (string))
(cons :tag "Matching the regexp" (const :value :regexp) (regexp))
(cons :tag "At level" (const :value :level) (integer))
(cons :tag "Up through level" (const :value :maxlevel) (integer)))))
:package-version '(Org . "9.8"))
(defcustom org-refile-target-verify-function nil
"Function to verify if the headline at point should be a refile target.
@ -289,10 +292,12 @@ When `org-refile-use-cache' is nil, just return POS."
(setq files (symbol-value files))))
(when (stringp files) (setq files (list files)))
;; Allow commonly used (FILE :maxlevel N) and similar values.
(when (and (listp (cdr desc)) (null (cddr desc)))
(when (and (listp desc) (listp (cdr desc)) (null (cddr desc)))
(setq desc (cons (car desc) (cadr desc))))
(condition-case err
(cond
((eq desc t)
(setq descre (concat "^\\*+[ \t]")))
((eq (car desc) :tag)
(setq descre (concat "^\\*+[ \t]+.*?:" (regexp-quote (cdr desc)) ":")))
((eq (car desc) :todo)
@ -341,25 +346,7 @@ When `org-refile-use-cache' is nil, just return POS."
(org-with-wide-buffer
(goto-char (point-min))
(setq org-outline-path-cache nil)
(while (re-search-forward descre nil t)
(forward-line 0)
(let ((case-fold-search nil))
(looking-at org-complex-heading-regexp))
(let ((begin (point))
(heading (match-string-no-properties 4)))
(unless (or (and
org-refile-target-verify-function
(not
(funcall org-refile-target-verify-function)))
(not heading))
(let ((re (format org-complex-heading-regexp-format
(regexp-quote heading)))
(target
(if (not org-refile-use-outline-path) heading
(mapconcat
#'identity
(append
(pcase org-refile-use-outline-path
(let ((base (pcase org-refile-use-outline-path
(`file (list
(and (buffer-file-name (buffer-base-buffer))
(file-name-nondirectory
@ -375,7 +362,27 @@ When `org-refile-use-cache' is nil, just return POS."
(`buffer-name
(list (buffer-name
(buffer-base-buffer))))
(_ nil))
(_ nil))))
(while (re-search-forward descre nil t)
(forward-line 0)
(let ((case-fold-search nil))
(looking-at org-complex-heading-regexp))
(let ((begin (point))
(heading (match-string-no-properties 4)))
(unless (or (and
org-refile-target-verify-function
(not
(funcall org-refile-target-verify-function)))
(not heading))
(let ((re (format org-complex-heading-regexp-format
(regexp-quote heading)))
(target
(if (not org-refile-use-outline-path)
(org-link-display-format heading)
(mapconcat
#'identity
(append
base
(mapcar (lambda (s) (replace-regexp-in-string
"/" "\\/" s nil t))
(org-get-outline-path t t)))
@ -384,7 +391,7 @@ When `org-refile-use-cache' is nil, just return POS."
tgs)))
(when (= (point) begin)
;; Verification function has not moved point.
(end-of-line)))))))
(end-of-line))))))))
(when org-refile-use-cache
(org-refile-cache-put tgs (buffer-file-name) descre))
(setq targets (append tgs targets))))))
@ -482,7 +489,7 @@ prefix argument (\\`C-u C-u C-u C-c C-w')."
(region-start (and regionp (region-beginning)))
(region-end (and regionp (region-end)))
(org-refile-keep (if (equal arg 3) t org-refile-keep))
pos it nbuf file level reversed)
pos it nbuf file level reversed tree)
(setq last-command nil)
(when regionp
(goto-char region-start)
@ -544,6 +551,7 @@ prefix argument (\\`C-u C-u C-u C-c C-w')."
(setq nbuf (find-file-noselect file 'nowarn))
(if (and arg (not (equal arg 3)))
(progn
(org-mark-ring-push)
(pop-to-buffer-same-window nbuf)
(goto-char (cond (pos)
((org-notes-order-reversed-p) (point-min))
@ -551,9 +559,10 @@ prefix argument (\\`C-u C-u C-u C-c C-w')."
(org-fold-show-context 'org-goto))
(if regionp
(progn
(org-kill-new (buffer-substring region-start region-end))
(setq tree (buffer-substring region-start region-end))
(org-kill-new tree)
(org-save-markers-in-region region-start region-end))
(org-copy-subtree 1 nil t))
(setq tree (org-copy-subtree 1 nil t)))
(let ((origin (point-marker)))
;; Handle special case when we refile to exactly same
;; location with tree promotion/demotion. Point marker
@ -580,7 +589,7 @@ prefix argument (\\`C-u C-u C-u C-c C-w')."
(goto-char (point-min))
(or (outline-next-heading) (goto-char (point-max)))))
(unless (bolp) (newline))
(org-paste-subtree level nil nil t)
(org-paste-subtree level tree nil t)
;; Record information, according to `org-log-refile'.
;; Do not prompt for a note when refiling multiple
;; headlines, however. Simply add a time stamp.
@ -634,6 +643,7 @@ prefix argument (\\`C-u C-u C-u C-c C-w')."
(defun org-refile-goto-last-stored ()
"Go to the location where the last refile was stored."
(interactive)
(org-mark-ring-push)
(bookmark-jump (plist-get org-bookmark-names-plist :last-refile))
(message "This is the location of the last refile"))
@ -777,9 +787,7 @@ this function appends the default value from
(all-completions string thetable predicate))))
((eq (car-safe flag) 'boundaries)
;; See `completion-file-name-table'.
(let ((start (or (and (string-match "/" string)
(match-beginning 0))
(length string)))
(let ((start 0)
(end (and (string-match "/" (cdr flag))
(match-beginning 0))))
`(boundaries ,start . ,end)))

View file

@ -44,7 +44,6 @@
(declare-function org--get-expected-indentation "org" (element contentsp))
(declare-function org-mode "org" ())
(declare-function org--get-expected-indentation "org" (element contentsp))
(declare-function org-fold-region "org-fold" (from to flag &optional spec-or-alias))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-class "org-element" (datum &optional parent))
(declare-function org-element-context "org-element" (&optional element))
@ -128,17 +127,21 @@ When this variable is nil, while indenting with `\\[org-indent-block]'
or after editing with `\\[org-edit-src-code]', the minimum (across-lines)
number of leading whitespace characters are removed from all lines,
and the code block is uniformly indented according to the value of
`org-edit-src-content-indentation'."
`org-src-content-indentation'."
:group 'org-edit-structure
:type 'boolean)
(defcustom org-edit-src-content-indentation 2
(defcustom org-src-content-indentation 2
"Indentation for the content of a source code block.
This should be the number of spaces added to the indentation of the #+begin
line in order to compute the indentation of the block content after
editing it with `\\[org-edit-src-code]'.
This customization also affects how the source code and example blocks
are printed - when interpreting Org AST (during export), during
detangling, and indentation.
It has no effect if `org-src-preserve-indentation' is non-nil."
:group 'org-edit-structure
:type 'integer
@ -220,7 +223,7 @@ The shells are associated with `sh-mode'."
("cpp" . c++)
("ditaa" . artist)
("desktop" . conf-desktop)
("dot" . fundamental)
("dot" . graphviz-dot)
("elisp" . emacs-lisp)
("ocaml" . tuareg)
("screen" . shell-script)
@ -238,11 +241,12 @@ not the case, this variable provides a way to simplify things on
the user side. For example, there is no `ocaml-mode' in Emacs,
but the mode to use is `tuareg-mode'."
:group 'org-edit-structure
:package-version '(Org . "9.7")
:package-version '(Org . "9.8")
:type '(repeat
(cons
(string :tag "Language name")
(symbol :tag "Major mode"))))
(symbol :tag "Major mode")))
:safe #'listp)
(defcustom org-src-block-faces nil
"Alist of faces to be used for source-block.
@ -339,10 +343,14 @@ Return nil if there is no such buffer."
(dolist (b (buffer-list))
(with-current-buffer b
(and (org-src-edit-buffer-p)
(= beg org-src--beg-marker)
(eq (marker-buffer beg) (marker-buffer org-src--beg-marker))
(= end org-src--end-marker)
(eq (marker-buffer end) (marker-buffer org-src--end-marker))
;; Do it after comparing buffers. In some scenarios
;; (namely, when Org buffer is generated as a copy and
;; the source buffer gets killed), these markers may
;; point nowhere making `=' throw an error.
(= beg org-src--beg-marker)
(= end org-src--end-marker)
(throw 'exit b))))))
(defun org-src--coordinates (pos beg end)
@ -585,7 +593,7 @@ Leave point in edit buffer."
(org--get-expected-indentation
(org-element-parent datum) nil))
(t (org-current-text-indentation)))))
(content-ind org-edit-src-content-indentation)
(content-ind org-src-content-indentation)
(preserve-ind (org-src-preserve-indentation-p datum))
;; Store relative positions of mark (if any) and point
;; within the edited area.
@ -605,7 +613,10 @@ Leave point in edit buffer."
;; Insert contents.
(insert contents)
(remove-text-properties (point-min) (point-max)
'(display nil invisible nil intangible nil))
'( display nil
invisible nil
intangible nil
syntax-table nil ))
(let ((lf (eq type 'latex-fragment)))
(unless preserve-ind (org-do-remove-indentation (and lf block-ind) lf)))
(set-buffer-modified-p nil)
@ -665,11 +676,10 @@ Leave point in edit buffer."
"Fontify code block between START and END using LANG's syntax.
This function is called by Emacs's automatic fontification, as long
as `org-src-fontify-natively' is non-nil."
(let ((modified (buffer-modified-p))
(native-tab-width tab-width))
(let ((modified (buffer-modified-p)) native-tab-width)
(remove-text-properties start end '(face nil))
(let ((lang-mode (org-src-get-lang-mode lang)))
(when (fboundp lang-mode)
(when-let* ((lang-mode (org-src-get-lang-mode-if-bound lang)))
(condition-case nil
(let ((string (buffer-substring-no-properties start end))
(org-buffer (current-buffer)))
(with-current-buffer
@ -693,7 +703,7 @@ as `org-src-fontify-natively' is non-nil."
;; space and the remapping between 'font-lock-face and 'face
;; text properties may thus not be set. See commit
;; 453d634bc.
(dolist (prop (append '(font-lock-face face) font-lock-extra-managed-props))
(dolist (prop (append '(font-lock-face face syntax-table) font-lock-extra-managed-props))
(let ((new-prop (get-text-property pos prop)))
(when new-prop
(if (not (eq prop 'invisible))
@ -734,27 +744,39 @@ as `org-src-fontify-natively' is non-nil."
'org-src-invisible new-prop
org-buffer)))))))
(setq pos next)))
(set-buffer-modified-p nil)))))
(let ((new-table (syntax-table)))
(alter-text-property
start end 'syntax-table
(lambda (old-table) (or old-table new-table))
org-buffer))
(set-buffer-modified-p nil)))
(error
(message "Native code fontification error in %S at pos%d\n Error: %S"
(current-buffer) start
(when (and (fboundp 'backtrace-get-frames)
(fboundp 'backtrace-to-string))
(backtrace-to-string (backtrace-get-frames 'backtrace)))))))
;; Add Org faces.
(let ((src-face (nth 1 (assoc-string lang org-src-block-faces t))))
(when (or (facep src-face) (listp src-face))
(font-lock-append-text-property start end 'face src-face))
(font-lock-append-text-property start end 'face 'org-block))
;; Display native tab indentation characters as spaces
(when native-tab-width
(save-excursion
(goto-char start)
(let ((indent-offset
(if (org-src-preserve-indentation-p) 0
(+ (progn (backward-char)
(org-current-text-indentation))
org-edit-src-content-indentation))))
org-src-content-indentation))))
(while (re-search-forward "^[ ]*\t" end t)
(let* ((b (and (eq indent-offset (move-to-column indent-offset))
(point)))
(e (progn (skip-chars-forward "\t") (point)))
(s (and b (make-string (* (- e b) native-tab-width) ? ))))
(when (and b (< b e)) (add-text-properties b e `(display ,s)))
(forward-char)))))
(forward-char))))))
(add-text-properties
start end
'(font-lock-fontified t fontified t font-lock-multiline t))
@ -970,12 +992,31 @@ Org-babel commands."
(defun org-src-get-lang-mode (lang)
"Return major mode that should be used for LANG.
LANG is a string, and the returned major mode is a symbol."
(intern
LANG is a string, and the returned value is a symbol."
(let ((mode (intern
(concat
(let ((l (or (cdr (assoc lang org-src-lang-modes)) lang)))
(if (symbolp l) (symbol-name l) l))
"-mode")))
"-mode"))))
(cond
((fboundp 'major-mode-remap) (major-mode-remap mode))
((boundp 'major-mode-remap-alist)
(or (cdr (assq mode major-mode-remap-alist)) mode))
(t mode))))
(defun org-src-get-lang-mode-if-bound (lang &optional fallback fallback-message-p)
"Return major mode for LANG, if bound, and FALLBACK otherwise.
LANG is a string. FALLBACK and the returned value are both symbols. If
FALLBACK-MESSAGE-P and FALLBACK are both non-nil, display a message when
falling back to a major mode different from that for LANG."
(let ((mode (org-src-get-lang-mode lang)))
(if (functionp mode)
mode
(when (and fallback
fallback-message-p
(not (eq fallback mode)))
(message "%s not available, falling back to %s" mode fallback))
fallback)))
(defun org-src-edit-buffer-p (&optional buffer)
"Non-nil when current buffer is a source editing buffer.
@ -1236,16 +1277,21 @@ Throw an error when not at an export block."
(unless (and (org-element-type-p element 'export-block)
(org-src--on-datum-p element))
(user-error "Not in an export block"))
(let* ((type (downcase (or (org-element-property :type element)
;; Missing export-block type. Fallback
;; to default mode.
"fundamental")))
(mode (org-src-get-lang-mode type)))
(unless (functionp mode) (error "No such language mode: %s" mode))
(let* ((lang-f-fallback #'fundamental-mode)
(lang (or (if-let* ((lang
(org-element-property :type element)))
(downcase lang))
(replace-regexp-in-string
"-mode$" ""
(symbol-name lang-f-fallback))))
(lang-f (org-src-get-lang-mode-if-bound
lang
lang-f-fallback
t)))
(org-src--edit-element
element
(org-src--construct-edit-buffer-name (buffer-name) type)
mode
(org-src--construct-edit-buffer-name (buffer-name) lang)
lang-f
(lambda () (org-escape-code-in-region (point-min) (point-max)))))
t))
@ -1282,8 +1328,9 @@ original code in the Org buffer, and replace it with the edited
version. See `org-src-window-setup' to configure the display of
windows containing the Org buffer and the code buffer.
When optional argument CODE is a string, edit it in a dedicated
buffer instead.
When optional argument CODE is a string, edit it in a read-only buffer
instead. The contents of that buffer will *not* be written back to
the source of example block at point.
When optional argument EDIT-BUFFER-NAME is non-nil, use it as the
name of the sub-editing buffer."
@ -1296,12 +1343,12 @@ name of the sub-editing buffer."
(let* ((lang
(if (eq type 'src-block) (org-element-property :language element)
"example"))
(lang-f (and (eq type 'src-block) (org-src-get-lang-mode lang)))
(lang-f (and (eq type 'src-block)
(org-src-get-lang-mode-if-bound
lang #'fundamental-mode lang)))
(babel-info (and (eq type 'src-block)
(org-babel-get-src-block-info 'no-eval)))
deactivate-mark)
(when (and (eq type 'src-block) (not (functionp lang-f)))
(error "No such language mode: %s" lang-f))
(org-src--edit-element
element
(or edit-buffer-name
@ -1331,10 +1378,9 @@ name of the sub-editing buffer."
(org-src--on-datum-p context))
(user-error "Not on inline source code"))
(let* ((lang (org-element-property :language context))
(lang-f (org-src-get-lang-mode lang))
(lang-f (org-src-get-lang-mode-if-bound lang #'fundamental-mode t))
(babel-info (org-babel-get-src-block-info 'no-eval))
deactivate-mark)
(unless (functionp lang-f) (error "No such language mode: %s" lang-f))
(org-src--edit-element
context
(org-src--construct-edit-buffer-name (buffer-name) lang)
@ -1409,15 +1455,22 @@ EVENT is passed to `mouse-set-point'."
(overlay org-src--overlay))
(org-src--contents-for-write-back write-back-buf)
(with-current-buffer (org-src-source-buffer)
;; Note: be careful to not move point here to make sure that
;; point motion does not get recorded into the undo list,
;; leading to unexpected results.
;; https://orgmode.org/list/XF_7mLNCUN8XKtnd7G-NUoAF5Vq0DDafaDdF0v53eFlhQ35N-H3bPA0VkYyDrbEWE-0PEQg8iiyB7NatUtvPEQe6SQyJaTE5vW0CwoUKzqs=@proton.me
(undo-boundary)
(goto-char beg)
;; Temporarily disable read-only features of OVERLAY in order to
;; insert new contents.
(delete-overlay overlay)
(let ((expecting-bol (bolp)))
(goto-char end)
(org-replace-region-contents beg end write-back-buf 0.1 nil)
(cl-assert (= (point) (+ beg (buffer-size write-back-buf))))
(let ((expecting-bol (save-excursion (goto-char beg) (bolp))))
(if (version< emacs-version "27.1")
(progn (delete-region beg end)
(insert (with-current-buffer write-back-buf (buffer-string))))
(save-restriction
(narrow-to-region beg end)
(org-replace-buffer-contents write-back-buf 0.1 nil)
(goto-char (point-max))))
(when (and expecting-bol (not (bolp))) (insert "\n")))
(kill-buffer write-back-buf)
(save-buffer)
@ -1455,12 +1508,20 @@ EVENT is passed to `mouse-set-point'."
(not (equal (buffer-substring beg end)
(with-current-buffer write-back-buf
(buffer-string)))))
;; Note: be careful to not move point here to make sure that
;; point motion does not get recorded into the undo list,
;; leading to unexpected results.
;; https://orgmode.org/list/XF_7mLNCUN8XKtnd7G-NUoAF5Vq0DDafaDdF0v53eFlhQ35N-H3bPA0VkYyDrbEWE-0PEQg8iiyB7NatUtvPEQe6SQyJaTE5vW0CwoUKzqs=@proton.me
(undo-boundary)
(goto-char beg)
(let ((expecting-bol (bolp)))
(goto-char end)
(org-replace-region-contents beg end write-back-buf 0.1 nil)
(cl-assert (= (point) (+ beg (buffer-size write-back-buf))))
(let ((expecting-bol (save-excursion (goto-char beg) (bolp))))
(if (version< emacs-version "27.1")
(progn (delete-region beg end)
(insert (with-current-buffer write-back-buf
(buffer-string))))
(save-restriction
(narrow-to-region beg end)
(org-replace-buffer-contents write-back-buf 0.1 nil)
(goto-char (point-max))))
(when (and expecting-bol (not (bolp))) (insert "\n")))))
(when write-back-buf (kill-buffer write-back-buf))
;; If we are to return to source buffer, put point at an
@ -1470,7 +1531,7 @@ EVENT is passed to `mouse-set-point'."
(goto-char beg)
(cond
;; Block is hidden; move at start of block.
((org-fold-folded-p nil 'block) (forward-line -1))
((org-invisible-p) (forward-line -1))
(write-back (org-src--goto-coordinates coordinates beg end))))
;; Clean up left-over markers and restore window configuration.
(set-marker beg nil)

View file

@ -44,42 +44,32 @@
(require 'org-fold-core)
(declare-function calc-eval "calc" (str &optional separator &rest args))
(declare-function face-remap-remove-relative "face-remap" (cookie))
(declare-function face-remap-add-relative "face-remap" (face &rest specs))
(declare-function org-at-timestamp-p "org" (&optional extended))
(declare-function org-delete-backward-char "org" (N))
(declare-function org-mode "org" ())
(declare-function org-duration-p "org-duration" (duration &optional canonical))
(declare-function org-duration-p "org-duration" (duration))
(declare-function org-duration-to-minutes "org-duration" (duration &optional canonical))
(declare-function org-element-at-point "org-element" (&optional pom cached-only))
(declare-function org-element-contents "org-element-ast" (node))
(declare-function org-element-extract "org-element-ast" (node))
(declare-function org-element-interpret-data "org-element" (data))
(declare-function org-element-lineage "org-element-ast" (blob &optional types with-self))
(declare-function org-element-map "org-element" (data types fun &optional info first-match no-recursion with-affiliated))
(declare-function org-element-parse-buffer "org-element" (&optional granularity visible-only keep-deferred))
(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-end "org-element" (node))
(declare-function org-element-post-affiliated "org-element" (node))
(declare-function org-element-type-p "org-element-ast" (node types))
(declare-function org-element-cache-reset "org-element" (&optional all no-persistence))
(declare-function org-entry-get "org" (pom property &optional inherit literal-nil))
(declare-function org-export-create-backend "ox" (&rest rest) t)
(declare-function org-export-data-with-backend "ox" (data backend info))
(declare-function org-export-filter-apply-functions "ox" (filters value info))
(declare-function org-export-first-sibling-p "ox" (blob info))
(declare-function org-export-get-backend "ox" (name))
(declare-function org-export-get-environment "ox" (&optional backend subtreep ext-plist))
(declare-function org-export-install-filters "ox" (info))
(declare-function org-export-table-has-special-column-p "ox" (table))
(declare-function org-export-table-row-is-special-p "ox" (table-row info))
(declare-function org-forward-paragraph "org" (&optional arg))
(declare-function org-id-find "org-id" (id &optional markerp))
(declare-function org-indent-line "org" ())
(declare-function org-load-modules-maybe "org" (&optional force))
(declare-function org-restart-font-lock "org" ())
(declare-function org-sort-remove-invisible "org" (s))
(declare-function org-time-stamp-format "org" (&optional long inactive))
(declare-function org-time-stamp-format "org" (&optional long inactive custom))
(declare-function org-time-string-to-absolute "org" (s &optional daynr prefer buffer pos))
(declare-function org-time-string-to-time "org" (s))
(declare-function org-timestamp-up-day "org" (&optional arg))
@ -344,11 +334,11 @@ The `U' flag in a table formula will select this specific format for
a single formula."
:group 'org-table-calculation
:version "24.1"
:type '(choice (symbol :tag "Seconds" 'seconds)
(symbol :tag "Minutes" 'minutes)
(symbol :tag "Hours " 'hours)
(symbol :tag "Days " 'days)
(symbol :tag "HH:MM " 'hh:mm)))
:type '(choice (const :tag "Seconds" seconds)
(const :tag "Minutes" minutes)
(const :tag "Hours " hours)
(const :tag "Days " days)
(const :tag "HH:MM " hh:mm)))
(defcustom org-table-duration-hour-zero-padding t
"Non-nil means hours in table duration computations should be zero-padded.
@ -521,7 +511,7 @@ This may be useful when columns have been shrunk."
(define-minor-mode org-table-header-line-mode
"Display the first row of the table at point in the header line."
:lighter " TblHeader"
(unless (eq major-mode 'org-mode)
(unless (derived-mode-p 'org-mode)
(user-error "Cannot turn org table header mode outside org-mode buffers"))
(if org-table-header-line-mode
(add-hook 'post-command-hook #'org-table-header-set-header nil t)
@ -586,7 +576,7 @@ This works for both table types.")
"Match a reference that needs translation, for reference display.")
(defconst org-table--separator-space-pre
(propertize " " 'display '(space :relative-width 1))
(propertize " " 'display '(space :relative-width 1) 'rear-nonsticky t)
"Space used in front of fields when aligning the table.
This space serves as a segment separator for the purposes of the
bidirectional reordering.
@ -929,8 +919,8 @@ nil When nil, the command tries to be smart and figure out the
(cond
((looking-at "^") (insert "| "))
((looking-at "[ \t]*$") (replace-match " |") (forward-line 1))
((looking-at "[ \t]*\"\\([^\"\n]*\\)\"")
(replace-match "\\1")
((looking-at "[ \t]*\"\\([^\"]*\\)\"")
(replace-match (replace-regexp-in-string "\n" " " (match-string 1)) t t)
(if (looking-at "\"") (insert "\"")))
((looking-at "[^,\n]+") (goto-char (match-end 0)))
((looking-at "[ \t]*,") (replace-match " | "))
@ -1118,7 +1108,7 @@ Before doing so, re-align the table if necessary."
(when (looking-at "| ?")
(goto-char (match-end 0))))
(defun org-table-beginning-of-field (&optional n)
(defun org-table-beginning-of-field (n)
"Move to the beginning of the current table field.
If already at or before the beginning, move to the beginning of the
previous field.
@ -1134,7 +1124,7 @@ With numeric argument N, move N-1 fields backward first."
(and (looking-at " ") (forward-char 1)))
(when (>= (point) pos) (org-table-beginning-of-field 2))))
(defun org-table-end-of-field (&optional n)
(defun org-table-end-of-field (n)
"Move to the end of the current table field.
If already at or after the end, move to the end of the next table field.
With numeric argument N, move N-1 fields forward first."
@ -2966,6 +2956,7 @@ known that the table will be realigned a little later anyway."
beg end eqlcol eqlfield)
;; Insert constants in all formulas.
(when eqlist
(org-fold-core-ignore-modifications
(org-table-with-shrunk-columns
(org-table-save-field
;; Expand equations, then split the equation list between
@ -3014,6 +3005,7 @@ existing formula for column %s"
(t nil)))
(setq beg (line-beginning-position)
end (copy-marker (line-beginning-position 2))))
(org-combine-change-calls beg end
(goto-char beg)
;; Mark named fields untouchable. Also check if several
;; field/range formulas try to set the same field.
@ -3098,7 +3090,7 @@ existing formula for column %s"
"Missing columns in the table. Aborting"))))))
(org-table-eval-formula nil formula t t t t)))
;; Clean up marker.
(set-marker end nil)))
(set-marker end nil)))))
(unless noalign
(when org-table-may-need-update (org-table-align))
(when all
@ -3142,7 +3134,7 @@ with the prefix ARG."
;; the way.
(org-table-recalculate t t)
(org-table-align))
t)))
t 'org)))
;;;###autoload
(defun org-table-iterate-buffer-tables ()
@ -3156,7 +3148,9 @@ with the prefix ARG."
(catch 'exit
(while (> i 0)
(setq i (1- i))
(org-table-map-tables (lambda () (org-table-recalculate t t)) t)
(org-table-map-tables
(lambda () (org-table-recalculate t t))
t 'org)
(if (equal checksum (setq c1 (md5 (buffer-string))))
(progn
(org-table-map-tables #'org-table-align t)
@ -3323,8 +3317,8 @@ Parameters get priority."
(org-defkey map [(shift right)] 'org-table-fedit-ref-right)
(org-defkey map [(meta up)] 'org-table-fedit-scroll-down)
(org-defkey map [(meta down)] 'org-table-fedit-scroll)
(org-defkey map [(meta tab)] 'lisp-complete-symbol)
(org-defkey map "\M-\C-i" 'lisp-complete-symbol)
(org-defkey map [(meta tab)] 'completion-at-point)
(org-defkey map "\M-\C-i" 'completion-at-point)
(org-defkey map [(tab)] 'org-table-fedit-lisp-indent)
(org-defkey map "\C-i" 'org-table-fedit-lisp-indent)
(org-defkey map "\C-c\C-r" 'org-table-fedit-toggle-ref-type)
@ -3338,7 +3332,7 @@ Parameters get priority."
["Abort" org-table-fedit-abort t]
"--"
["Pretty-Print Lisp Formula" org-table-fedit-lisp-indent t]
["Complete Lisp Symbol" lisp-complete-symbol t]
["Complete Lisp Symbol" completion-at-point t]
"--"
"Shift Reference at Point"
["Up" org-table-fedit-ref-up t]
@ -3397,6 +3391,9 @@ Parameters get priority."
(setq-local org-table--fedit-source source)
(setq-local org-window-configuration wc)
(setq-local org-selected-window sel-win)
;; Use completion from `emacs-lisp-mode'
(add-hook 'completion-at-point-functions
#'elisp-completion-at-point nil 'local)
(use-local-map org-table-fedit-map)
(add-hook 'post-command-hook #'org-table-fedit-post-command t t)
(setq startline (org-current-line))
@ -3427,7 +3424,7 @@ Edit formulas, finish with `\\[org-ctrl-c-ctrl-c]' or `\\[org-edit-special]'. \
See menu for more commands.")))))
(defun org-table-fedit-post-command ()
(when (not (memq this-command '(lisp-complete-symbol)))
(when (not (memq this-command '(lisp-complete-symbol completion-at-point)))
(let ((win (selected-window)))
(save-excursion
(ignore-errors (org-table-show-reference))
@ -4292,12 +4289,16 @@ beginning and end position of the current table."
;;; Generic Tools
;;;###autoload
(defun org-table-map-tables (f &optional quietly)
"Apply function F to the start of all tables in the buffer."
(defun org-table-map-tables (f &optional quietly type)
"Apply function F to the start of all tables in the buffer.
When TYPE is non-nil, only consider Org tables of that type (symbol
`org' or symbol `table.el'."
(org-with-point-at 1
(while (re-search-forward org-table-line-regexp nil t)
(let ((table (org-element-lineage (org-element-at-point) 'table t)))
(when table
(when (and table
(or (not type)
(eq type (org-element-property :type table))))
(unless quietly
(message "Mapping tables: %d%%"
(floor (* 100.0 (point)) (buffer-size))))
@ -4375,11 +4376,12 @@ extension of the given file name, and finally on the variable
(user-error "TABLE_EXPORT_FORMAT invalid")))))
;;;###autoload
(defun org-table--align-field (field width align)
(defun org-table--align-field (field width align &optional field-width)
"Format FIELD according to column WIDTH and alignment ALIGN.
FIELD is a string. WIDTH is a number. ALIGN is either \"c\",
\"l\" or\"r\"."
(let* ((spaces (- width (org-string-width field nil 'org-table)))
\"l\" or\"r\". If FIELD-WIDTH is non-nil, then it's used as
FIELD's width. Otherwise, it's calculated."
(let* ((spaces (- width (or field-width (org-string-width field nil 'org-table))))
(prefix (pcase align
("l" "")
("r" (make-string spaces ?\s))
@ -4408,7 +4410,16 @@ FIELD is a string. WIDTH is a number. ALIGN is either \"c\",
(rows (remq 'hline table))
(widths nil)
(alignments nil)
(columns-number 1))
(columns-number 1)
(invisibility-spec (org-string-width-invisibility-spec))
(cell-width-cache (make-hash-table :test 'equal))
(get-or-compute-cell-width
(lambda (cell)
(or (gethash cell cell-width-cache)
(puthash
cell
(org-string-width cell nil 'org-table invisibility-spec)
cell-width-cache)))))
(if (null rows)
;; Table contains only horizontal rules. Compute the
;; number of columns anyway, and choose an arbitrary width
@ -4428,7 +4439,7 @@ FIELD is a string. WIDTH is a number. ALIGN is either \"c\",
(non-empty 0))
(dolist (row rows)
(let ((cell (or (nth i row) "")))
(setq max-width (max max-width (org-string-width cell nil 'org-table)))
(setq max-width (max max-width (funcall get-or-compute-cell-width cell)))
(cond (fixed-align? nil)
((equal cell "") nil)
((string-match "\\`<\\([lrc]\\)[0-9]*>\\'" cell)
@ -4451,6 +4462,8 @@ FIELD is a string. WIDTH is a number. ALIGN is either \"c\",
(setq org-table-last-column-widths widths)
;; Build new table rows. Only replace rows that actually
;; changed.
(org-fold-core-ignore-modifications
(org-combine-change-calls beg end
(let ((rule (and (memq 'hline table)
(mapconcat (lambda (w) (make-string (+ 2 w) ?-))
widths
@ -4467,7 +4480,13 @@ FIELD is a string. WIDTH is a number. ALIGN is either \"c\",
(append row
(make-list offset "")))))
(mapconcat #'identity
(cl-mapcar #'org-table--align-field
(cl-mapcar
(lambda (field width alignment)
(org-table--align-field
field
width
alignment
(funcall get-or-compute-cell-width field)))
fields
widths
alignments)
@ -4476,7 +4495,7 @@ FIELD is a string. WIDTH is a number. ALIGN is either \"c\",
(if (equal new previous)
(forward-line)
(insert new "\n")
(delete-region (point) (line-beginning-position 2))))))
(delete-region (point) (line-beginning-position 2))))))))
(set-marker end nil)
(when org-table-overlay-coordinates (org-table-overlay-coordinates))
(setq org-table-may-need-update nil))))))
@ -5653,6 +5672,42 @@ First element has index 0, or I0 if given."
(insert txt)
(goto-char pos)))
(defun orgtbl--skip (ast _ info)
"Extract first X table rows from AST.
X is taken from :skip property in INFO plist.
Return the modified AST."
(when-let* ((skip (plist-get info :skip)))
(unless (wholenump skip) (user-error "Wrong :skip value"))
(let ((n 0))
(org-element-map ast 'table-row
(lambda (row)
(if (>= n skip) t
(org-element-extract row)
(cl-incf n)
nil))
nil t)))
ast)
(defun orgtbl--skipcols (ast _ info)
"Extract first X table columns from AST.
X is taken from :skipcols property in INFO plist.
Special columns are always ignored.
Return the modified AST."
(when-let* ((skipcols (plist-get info :skipcols)))
(unless (consp skipcols) (user-error "Wrong :skipcols value"))
(org-element-map ast 'table
(lambda (table)
(let ((specialp (org-export-table-has-special-column-p table)))
(dolist (row (org-element-contents table))
(when (eq (org-element-property :type row) 'standard)
(let ((c 1))
(dolist (cell (nthcdr (if specialp 1 0)
(org-element-contents row)))
(when (memq c skipcols)
(org-element-extract cell))
(cl-incf c)))))))))
ast)
;;;###autoload
(defun orgtbl-to-generic (table params)
"Convert the `orgtbl-mode' TABLE to some other format.
@ -5664,7 +5719,8 @@ a horizontal separator line, or a list of fields for that
line. PARAMS is a property list of parameters that can
influence the conversion.
Valid parameters are:
Valid parameters are all the export options understood by the export
backend and also:
:backend, :raw
@ -5773,84 +5829,55 @@ This may be either a string or a function of two arguments:
;; regular backend has a transcoder for them. We
;; provide one so they are not ignored, but displayed
;; as-is instead.
(macro . (lambda (m c i) (org-element-macro-interpreter m nil))))))
data info)
(macro . (lambda (m c i) (org-element-macro-interpreter m nil)))
;; Only export the actual table. Do nothing with the
;; containing section regardless what backend think about
;; it. (It is somewhat like BODY-ONLY argument in
;; `org-export-as', but skips not only transcoding the
;; full document, but also section containing the table.
(section . (lambda (_ contents _) contents))))))
;; Store TABLE as Org syntax in DATA. Tolerate non-string cells.
;; Initialize communication channel in INFO.
(with-temp-buffer
(let ((org-inhibit-startup t)) (org-mode))
(org-fold-core-ignore-modifications
(let ((standard-output (current-buffer))
(org-element-use-cache nil))
(let ((standard-output (current-buffer)))
(dolist (e table)
(cond ((eq e 'hline) (princ "|--\n"))
((consp e)
(princ "| ") (dolist (c e) (princ c) (princ " |"))
(princ "\n")))))
(org-element-cache-reset)
;; Add backend specific filters, but not user-defined ones. In
;; particular, make sure to call parse-tree filters on the
;; table.
(setq info
(let ((org-export-filters-alist nil))
(org-export-install-filters
(org-combine-plists
(org-export-get-environment backend nil params)
`(:back-end ,(org-export-get-backend backend))))))
(setq data
(org-export-filter-apply-functions
(plist-get info :filter-parse-tree)
(org-element-map (org-element-parse-buffer) 'table
#'identity nil t)
info))
(let ((org-inhibit-startup t)) (org-mode))
(defvar org-export-before-processing-functions) ; ox.el
(defvar org-export-process-citations) ; ox.el
(defvar org-export-expand-links) ; ox.el
(defvar org-export-filter-parse-tree-functions) ; ox.el
(defvar org-export-filters-alist) ; ox.el
(defvar org-export-replace-macros) ; ox.el
(declare-function
org-export-as "ox"
(backend &optional subtreep visible-only body-only ext-plist))
;; We disable the usual pre-processing and post-processing,
;; i.e., hooks, Babel code evaluation, and macro expansion.
;; Only backend specific filters are retained.
;; We _do not_ disable `org-export-filter-parse-tree-functions'
;; (historically).
(let ((org-export-before-processing-functions nil)
(org-export-replace-macros nil)
(org-export-use-babel nil)
(org-export-before-parsing-functions nil)
(org-export-process-citations nil)
(org-export-expand-links nil)
(org-export-filter-parse-tree-functions
(append
'(orgtbl--skip orgtbl--skipcols)
org-export-filter-parse-tree-functions))
(org-export-filters-alist
'((:filter-parse-tree . org-export-filter-parse-tree-functions))))
(when (or (not backend) (plist-get params :raw)) (require 'ox-org))
(when (and backend (symbolp backend) (not (org-export-get-backend backend)))
(user-error "Unknown :backend value"))))
(when (or (not backend) (plist-get info :raw)) (require 'ox-org))
;; Handle :skip parameter.
(let ((skip (plist-get info :skip)))
(when skip
(unless (wholenump skip) (user-error "Wrong :skip value"))
(let ((n 0))
(org-element-map data 'table-row
(lambda (row)
(if (>= n skip) t
(org-element-extract row)
(cl-incf n)
nil))
nil t))))
;; Handle :skipcols parameter.
(let ((skipcols (plist-get info :skipcols)))
(when skipcols
(unless (consp skipcols) (user-error "Wrong :skipcols value"))
(org-element-map data 'table
(lambda (table)
(let ((specialp (org-export-table-has-special-column-p table)))
(dolist (row (org-element-contents table))
(when (eq (org-element-property :type row) 'standard)
(let ((c 1))
(dolist (cell (nthcdr (if specialp 1 0)
(org-element-contents row)))
(when (memq c skipcols)
(org-element-extract cell))
(cl-incf c))))))))))
;; Since we are going to export using a low-level mechanism,
;; ignore special column and special rows manually.
(let ((special? (org-export-table-has-special-column-p data))
ignore)
(org-element-map data (if special? '(table-cell table-row) 'table-row)
(lambda (datum)
(when (if (org-element-type-p datum 'table-row)
(org-export-table-row-is-special-p datum nil)
(org-export-first-sibling-p datum nil))
(push datum ignore))))
(setq info (plist-put info :ignore-list ignore)))
;; We use a low-level mechanism to export DATA so as to skip all
;; usual pre-processing and post-processing, i.e., hooks, Babel
;; code evaluation, include keywords and macro expansion. Only
;; backend specific filters are retained.
(let ((output (org-export-data-with-backend data custom-backend info)))
(user-error "Unknown :backend value: %S" backend))
(let ((output (org-export-as custom-backend nil nil 'body-only params)))
;; Remove final newline.
(if (org-string-nw-p output) (substring-no-properties output 0 -1) ""))))
(if (org-string-nw-p output) (substring-no-properties output 0 -1) ""))))))
(defun org-table--generic-apply (value name &optional with-cons &rest args)
(cond ((null value) nil)

View file

@ -41,8 +41,6 @@
(require 'cl-lib)
(require 'org-clock)
(declare-function org-agenda-error "org-agenda" ())
(defvar org-timer-start-time nil
"Start time for the running timer.")
@ -108,7 +106,8 @@ nil current timer is not displayed"
"Hook run after countdown timer is set.")
(defvar org-timer-done-hook nil
"Hook run after countdown timer reaches zero.")
"Hook run after countdown timer reaches zero.
The hook is run before the timer is actually stopped.")
;;;###autoload
(defun org-timer-start (&optional offset)
@ -426,7 +425,8 @@ using three \\[universal-argument] prefix arguments."
org-timer-default-timer))
(effort-minutes
(cond ((derived-mode-p 'org-agenda-mode)
(org-get-at-bol 'effort-minutes))
(when-let* ((effort (org-get-at-bol 'effort-minutes)))
(floor effort)))
((derived-mode-p 'org-mode)
(let ((effort (org-entry-get nil org-effort-property)))
(when (org-string-nw-p effort)
@ -470,11 +470,11 @@ time is up."
(sound org-clock-sound))
(run-with-timer
secs nil (lambda ()
(setq org-timer-countdown-timer nil
org-timer-start-time nil)
(org-notify msg sound)
(org-timer-set-mode-line 'off)
(run-hooks 'org-timer-done-hook)))))
(run-hooks 'org-timer-done-hook)
(setq org-timer-countdown-timer nil
org-timer-start-time nil)))))
(defun org-timer--get-timer-title ()
"Construct timer title.

View file

@ -5,13 +5,13 @@
(defun org-release ()
"The release version of Org.
Inserted by installing Org mode or when a release is made."
(let ((org-release "9.7.11"))
(let ((org-release "9.8.1"))
org-release))
;;;###autoload
(defun org-git-version ()
"The Git version of Org mode.
Inserted by installing Org or when a release is made."
(let ((org-git-version "release_9.7.11"))
(let ((org-git-version "release_9.8.1"))
org-git-version))
(provide 'org-version)

File diff suppressed because it is too large Load diff

View file

@ -37,9 +37,6 @@
;;; Function Declarations
(declare-function aa2u "ext:ascii-art-to-unicode" ())
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-next-visible-heading "org" (arg))
;;; Define Backend
;;
@ -688,8 +685,8 @@ possible. It doesn't apply to `inlinetask' elements."
(org-make-tag-string tag-list)))))
(priority
(and (plist-get info :with-priority)
(let ((char (org-element-property :priority element)))
(and char (format "(#%c) " char)))))
(let ((priority-value (org-element-property :priority element)))
(and priority-value (format "(#%s) " (org-priority-to-string priority-value))))))
(first-part (concat numbers todo priority text)))
(concat
first-part

View file

@ -198,6 +198,7 @@ through `org-beamer-environments-extra' variable.")
("example" "e" "\\begin{example}%a[%h]%l" "\\end{example}")
("exampleblock" "E" "\\begin{exampleblock}%a{%h}%l" "\\end{exampleblock}")
("proof" "p" "\\begin{proof}%a[%h]" "\\end{proof}")
("onlyenv" "O" "\\begin{onlyenv}%a" "\\end{onlyenv}")
("beamercolorbox" "o" "\\begin{beamercolorbox}%o{%h}" "\\end{beamercolorbox}"))
"Environments triggered by properties in Beamer export.
These are the defaults - for user definitions, see
@ -500,6 +501,15 @@ used as a communication channel."
(if (and env (equal (downcase env) "fullframe")) ""
(org-export-data
(org-element-property :title headline) info))))
;; Subtitle
(when-let* ((subtitle
(org-element-property :BEAMER_SUBTITLE headline)))
(format "{%s}"
(org-export-data
(org-element-parse-secondary-string
subtitle
(org-element-restriction 'keyword))
info)))
"\n"
;; The following workaround is required in fragile frames
;; as Beamer will append "\par" to the beginning of the

View file

@ -43,12 +43,7 @@
;;; Function Declarations
(declare-function org-id-find-id-file "org-id" (id))
(declare-function htmlize-region "ext:htmlize" (beg end))
(declare-function mm-url-decode-entities "mm-url" ())
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-next-visible-heading "org" (arg))
(defvar htmlize-css-name-prefix)
(defvar htmlize-output-type)
@ -161,7 +156,7 @@
(:html-postamble-format nil nil org-html-postamble-format)
(:html-preamble-format nil nil org-html-preamble-format)
(:html-prefer-user-labels nil nil org-html-prefer-user-labels)
(:html-self-link-headlines nil nil org-html-self-link-headlines)
(:html-self-link-headlines nil "html-self-link-headlines" org-html-self-link-headlines)
(:html-table-align-individual-fields
nil nil org-html-table-align-individual-fields)
(:html-table-caption-above nil nil org-html-table-caption-above)
@ -233,10 +228,10 @@ For blocks that should contain headlines, use the HTML_CONTAINER
property on the headline itself.")
(defconst org-html-special-string-regexps
'(("\\\\-" . "&#x00ad;") ; shy
("---\\([^-]\\)" . "&#x2014;\\1") ; mdash
("--\\([^-]\\)" . "&#x2013;\\1") ; ndash
("\\.\\.\\." . "&#x2026;")) ; hellip
'(("\\\\-" . "&shy;")
("---\\([^-]\\)" . "&mdash;\\1")
("--\\([^-]\\)" . "&ndash;\\1")
("\\.\\.\\." . "&hellip;"))
"Regular expressions for special string conversion.")
(defvar org-html--id-attr-prefix "ID-"
@ -318,6 +313,7 @@ This affects IDs that are determined from the ID property.")
pre.src-asymptote:before { content: 'Asymptote'; }
pre.src-awk:before { content: 'Awk'; }
pre.src-authinfo::before { content: 'Authinfo'; }
pre.src-c:before { content: 'C'; }
pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */
pre.src-clojure:before { content: 'Clojure'; }
@ -332,7 +328,7 @@ This affects IDs that are determined from the ID property.")
pre.src-haskell:before { content: 'Haskell'; }
pre.src-hledger:before { content: 'hledger'; }
pre.src-java:before { content: 'Java'; }
pre.src-js:before { content: 'Javascript'; }
pre.src-js:before { content: 'JavaScript'; }
pre.src-latex:before { content: 'LaTeX'; }
pre.src-ledger:before { content: 'Ledger'; }
pre.src-lisp:before { content: 'Lisp'; }
@ -459,8 +455,9 @@ You can use `org-html-head' and `org-html-head-extra' to add to
this style. If you don't want to include this default style,
customize `org-html-head-include-default-style'."
:group 'org-export-html
:package-version '(Org . "9.5")
:type 'string)
:package-version '(Org . "9.8")
:type 'string
:safe #'stringp)
;;; User Configuration Variables
@ -1130,7 +1127,7 @@ org-info.js for your website."
((on . "&#x2611;") (off . "&#x2610;") (trans . "&#x2610;")))
(ascii .
((on . "<code>[X]</code>")
(off . "<code>[&#xa0;]</code>")
(off . "<code>[&nbsp;]</code>")
(trans . "<code>[-]</code>")))
(html .
((on . "<input type='checkbox' checked='checked' />")
@ -1168,6 +1165,20 @@ See `format-time-string' for more information on its components."
:package-version '(Org . "8.0")
:type 'string)
(defcustom org-html-datetime-formats '("%F" . "%FT%T")
"Formats used for the timestamp added as metadata to the time HTML element.
This only has an effect when `org-html-html5-fancy' is enabled, but
does not affect how the timestamp is displayed. The format in CAR
represents the timestamp used for timestamps without a time component,
CDR the one for the full date and time. Note that the HTML standard
restricts what timestamp formats are considered valid for the datetime
attribute. See `format-time-string' for more information on its
components."
:type '(cons string string)
:group 'org-export-html
:package-version '(Org . "9.8")
:safe #'consp)
;;;; Template :: Mathjax
(defcustom org-html-mathjax-options
@ -1531,7 +1542,8 @@ style information."
This variable can contain the full HTML structure to provide a
style, including the surrounding HTML tags. You can consider
including definitions for the following classes: title, todo,
done, timestamp, timestamp-kwd, tag, target.
done, timestamp, timestamp-kwd, tag, target. Can be a string, or
a function that accepts the INFO plist and returns a string.
For example, a valid value would be:
@ -1554,21 +1566,25 @@ header.
You can set this on a per-file basis using #+HTML_HEAD:,
or for publication projects using the :html-head property."
:group 'org-export-html
:version "24.4"
:package-version '(Org . "8.0")
:type 'string)
:package-version '(Org . "9.8")
:type '(choice (string :tag "Literal text to insert")
(function :tag "Function evaluating to a string"))
:safe #'stringp)
;;;###autoload
(put 'org-html-head 'safe-local-variable 'stringp)
(defcustom org-html-head-extra ""
"More head information to add in the HTML output.
You can set this on a per-file basis using #+HTML_HEAD_EXTRA:,
or for publication projects using the :html-head-extra property."
You can set this on a per-file basis using #+HTML_HEAD_EXTRA:, or
for publication projects using the :html-head-extra property.
Can be a string, or a function that accepts the INFO plist and returns
a string."
:group 'org-export-html
:version "24.4"
:package-version '(Org . "8.0")
:type 'string)
:package-version '(Org . "9.8")
:type '(choice (string :tag "Literal text to insert")
(function :tag "Function evaluating to a string"))
:safe #'stringp)
;;;###autoload
(put 'org-html-head-extra 'safe-local-variable 'stringp)
@ -1648,7 +1664,7 @@ https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag"
selector_eval_scheme: '.src-scheme',
selector: '.src-clojure',
selector_eval_ruby: '.src-ruby'};"
"Javascript snippet to activate klipse."
"JavaScript snippet to activate klipse."
:group 'org-export-html
:package-version '(Org . "9.1")
:type 'string)
@ -1804,6 +1820,29 @@ is meant to be used as a predicate for `org-export-get-ordinal' or
a value to `org-html-standalone-image-predicate'."
(org-element-property :caption element))
(defun org-html--format-timestamp (timestamp info)
"Format given TIMESTAMP for inclusion in an HTML document.
INFO is a plist used as a communication channel. Formatted timestamp
will be wrapped in an element with class timestamp."
(let ((html-tag (if (org-html--html5-fancy-p info) "time" "span"))
(html-attrs (concat "class=\"timestamp\""
(when (org-html--html5-fancy-p info)
(format " datetime=\"%s\""
(org-format-timestamp
timestamp
(if (org-timestamp-has-time-p timestamp)
(cdr org-html-datetime-formats)
(car org-html-datetime-formats))))))))
(replace-regexp-in-string
"--"
"&ndash;"
(format "<%s %s>%s</%s>"
html-tag
html-attrs
(org-html-plain-text (org-timestamp-translate timestamp)
info)
html-tag))))
;;;; Table
(defun org-html-htmlize-region-for-paste (beg end)
@ -2001,6 +2040,15 @@ INFO is a plist used as a communication channel."
org-html-meta-tags))
""))))
(defun org-html-normalize-string-or-function (input &rest args)
"Normalize INPUT function or string.
If INPUT is a string, it is passed to
`org-element-normalize-string'. If INPUT is a function, it is
applied to arguments ARGS, and the result is passed to
`org-element-normalize-string'."
(let ((s (if (functionp input) (format "%s" (apply input args)) input)))
(org-element-normalize-string s)))
(defun org-html--build-head (info)
"Return information for the <head>..</head> of the HTML output.
INFO is a plist used as a communication channel."
@ -2008,8 +2056,9 @@ INFO is a plist used as a communication channel."
(concat
(when (plist-get info :html-head-include-default-style)
(org-element-normalize-string org-html-style-default))
(org-element-normalize-string (plist-get info :html-head))
(org-element-normalize-string (plist-get info :html-head-extra))
(org-html-normalize-string-or-function (plist-get info :html-head) info)
(org-html-normalize-string-or-function (plist-get info :html-head-extra)
info)
(when (and (plist-get info :html-htmlized-css-url)
(eq org-html-htmlize-output-type 'css))
(org-html-close-tag "link"
@ -2318,7 +2367,8 @@ INFO is the info plist."
"Format a priority into HTML.
PRIORITY is the character code of the priority or nil. INFO is
a plist containing export options."
(and priority (format "<span class=\"priority\">[%c]</span>" priority)))
(and priority (format "<span class=\"priority\">[%s]</span>"
(org-priority-to-string priority))))
;;;; Tags
@ -2333,7 +2383,7 @@ INFO is a plist containing export options."
(concat (plist-get info :html-tag-class-prefix)
(org-html-fix-class-name tag))
tag))
tags "&#xa0;"))))
tags "&nbsp;"))))
;;;; Src Code
@ -2355,8 +2405,7 @@ is the language used for CODE, as a string, or nil."
(org-html-encode-plain-text code))
(t
;; Map language
(setq lang (or (assoc-default lang org-src-lang-modes) lang))
(let* ((lang-mode (and lang (intern (format "%s-mode" lang)))))
(let* ((lang-mode (and lang (org-src-get-lang-mode lang))))
(cond
;; Case 1: Language is not associated with any Emacs mode
((not (functionp lang-mode))
@ -2471,7 +2520,7 @@ of contents as a string, or nil if it is empty."
(let* ((toc-id-counter (plist-get info :org-html--toc-counter))
(toc (concat (format "<div id=\"text-table-of-contents%s\" role=\"doc-toc\">"
(if toc-id-counter (format "-%d" toc-id-counter) ""))
(org-html--toc-text toc-entries)
(org-html--toc-text toc-entries scope)
"</div>\n")))
(plist-put info :org-html--toc-counter (1+ (or toc-id-counter 0)))
(if scope toc
@ -2489,11 +2538,14 @@ of contents as a string, or nil if it is empty."
toc
(format "</%s>\n" outer-tag))))))))
(defun org-html--toc-text (toc-entries)
(defun org-html--toc-text (toc-entries &optional scope)
"Return innards of a table of contents, as a string.
TOC-ENTRIES is an alist where key is an entry title, as a string,
and value is its relative level, as an integer."
(let* ((prev-level (1- (cdar toc-entries)))
and value is its relative level, as an integer. Optional SCOPE,
when non-nil, indicates a TOC for a subtree, which affects the
initial nesting level."
(let* ((prev-level (if scope (1- (cdar toc-entries)) 0))
(start-level prev-level))
(concat
(mapconcat
@ -2639,17 +2691,17 @@ holding contextual information."
;;;; Clock
(defun org-html-clock (clock _contents _info)
(defun org-html-clock (clock _contents info)
"Transcode a CLOCK element from Org to HTML.
CONTENTS is nil. INFO is a plist used as a communication
channel."
(format "<p>
<span class=\"timestamp-wrapper\">
<span class=\"timestamp-kwd\">%s</span> <span class=\"timestamp\">%s</span>%s
<span class=\"timestamp-kwd\">%s</span> %s%s
</span>
</p>"
org-clock-string
(org-timestamp-translate (org-element-property :value clock))
(org-html--format-timestamp (org-element-property :value clock) info)
(let ((time (org-element-property :duration clock)))
(and time (format " <span class=\"timestamp\">(%s)</span>" time)))))
@ -2860,7 +2912,7 @@ description of TODO, PRIORITY, TEXT, TAGS, and INFO arguments."
(concat todo (and todo " ")
priority (and priority " ")
text
(and tags "&#xa0;&#xa0;&#xa0;") tags)))
(and tags "&nbsp;&nbsp;&nbsp") tags)))
(defun org-html--container (headline info)
"Return HTML container name for HEADLINE as a string.
@ -3051,7 +3103,7 @@ INFO is a plist containing export properties."
(concat (file-name-as-directory org-preview-latex-image-directory)
(file-name-sans-extension
(file-name-nondirectory bfn)))
cache-dir (file-name-directory bfn))
cache-dir (file-name-directory (plist-get info :output-file)))
;; Re-create LaTeX environment from original buffer in
;; temporary buffer so that dvipng/imagemagick can properly
;; turn the fragment into an image.
@ -3085,6 +3137,8 @@ Math environments match the regular expression defined in
`org-latex-math-environments-re'. This function is meant to be
used as a predicate for `org-export-get-ordinal' or a value to
`org-html-standalone-image-predicate'."
(require 'ox-latex)
(defvar org-latex-math-environments-re) ; defined in ox-latex.el
(string-match-p org-latex-math-environments-re
(org-element-property :value element)))
@ -3328,7 +3382,7 @@ INFO is a plist holding contextual information. See
(let ((destination (org-export-resolve-radio-link link info)))
(if (not destination) desc
(format "<a href=\"#%s\"%s>%s</a>"
(org-export-get-reference destination info)
(org-html--reference destination info)
attributes
desc))))
;; Links pointing to a headline: Find destination and build
@ -3373,6 +3427,8 @@ INFO is a plist holding contextual information. See
(format "<a href=\"#%s\"%s>%s</a>" href attributes desc)))
;; Fuzzy link points to a target or an element.
(_
(require 'ox-latex)
(declare-function org-latex--environment-type "ox-latex" (latex-environment))
(if (and destination
(memq (plist-get info :with-latex) '(mathjax t))
(org-element-type-p destination 'latex-environment)
@ -3565,10 +3621,9 @@ channel."
(when timestamp
(let ((string (car pair)))
(format "<span class=\"timestamp-kwd\">%s</span> \
<span class=\"timestamp\">%s</span> "
%s "
string
(org-html-plain-text (org-timestamp-translate timestamp)
info))))))
(org-html--format-timestamp timestamp info))))))
`((,org-closed-string . ,(org-element-property :closed planning))
(,org-deadline-string . ,(org-element-property :deadline planning))
(,org-scheduled-string . ,(org-element-property :scheduled planning)))
@ -3697,7 +3752,7 @@ contextual information."
" data-editor-type=\"html\""
"")
code)
(format "<pre class=\"src src-%s\"%s>%s</pre>"
(format "<pre class=\"src src-%s\"%s><code>%s</code></pre>"
;; Lang being nil is OK.
lang label code))))))
@ -3751,7 +3806,7 @@ channel."
" align=\"%s\"" " class=\"org-%s\"")
(org-export-table-cell-alignment table-cell info)))))
(when (or (not contents) (string= "" (org-trim contents)))
(setq contents "&#xa0;"))
(setq contents "&nbsp;"))
(cond
((and (org-export-table-has-header-p table info)
(= 1 (org-export-table-row-group table-row info)))
@ -3837,7 +3892,6 @@ INFO is a plist used as a communication channel."
"Format table.el TABLE into HTML.
INFO is a plist used as a communication channel."
(when (eq (org-element-property :type table) 'table.el)
(require 'table)
(let ((outbuf (with-current-buffer
(get-buffer-create "*org-export-table*")
(erase-buffer) (current-buffer))))
@ -3920,9 +3974,16 @@ information."
"Transcode a TIMESTAMP object from Org to HTML.
CONTENTS is nil. INFO is a plist holding contextual
information."
(let ((value (org-html-plain-text (org-timestamp-translate timestamp) info)))
(format "<span class=\"timestamp-wrapper\"><span class=\"timestamp\">%s</span></span>"
(replace-regexp-in-string "--" "&#x2013;" value))))
(let (
;; Strip :post-blank
;; It will be handled as a part of generic transcoder code
;; so we should avoid double-counting post-blank.
(timestamp-no-blank
(org-element-put-property
(org-element-copy timestamp t)
:post-blank 0)))
(format "<span class=\"timestamp-wrapper\">%s</span>"
(org-html--format-timestamp timestamp-no-blank info))))
;;;; Underline
@ -3952,7 +4013,7 @@ contextual information."
(format "<p class=\"verse\">\n%s</p>"
;; Replace leading white spaces with non-breaking spaces.
(replace-regexp-in-string
"^[ \t]+" (lambda (m) (org-html--make-string (length m) "&#xa0;"))
"^[ \t]+" (lambda (m) (org-html--make-string (length m) "&nbsp;"))
;; Replace each newline character with line break. Also
;; remove any trailing "br" close-tag so as to avoid
;; duplicates.

View file

@ -39,9 +39,6 @@
(require 'org-agenda)
(require 'ox-ascii)
(declare-function org-bbdb-anniv-export-ical "ol-bbdb" nil)
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-next-visible-heading "org" (arg))
@ -202,18 +199,28 @@ This is a list of symbols, the following are valid:
(defcustom org-icalendar-with-timestamps 'active
"Non-nil means make an event from plain time stamps.
It can be set to `active', `inactive', t or nil, in order to make
an event from, respectively, only active timestamps, only
inactive ones, all of them or none.
It can be set to `active', `active-exclude-diary', `inactive', t
or nil, in order to make an event from, respectively, only active
timestamps (with/without diary timestamps), only inactive ones,
all of them or none.
This variable has the same options as
`org-export-with-timestamps', and takes precedence over it.
However, note that this variable applies to all timestamps within
an entry, whereas `org-export-with-timestamps' only applies to
timestamps isolated in a paragraph containing only timestamps.
This variable has precedence over `org-export-with-timestamps'.
It can also be set with the #+OPTIONS line, e.g. \"<:t\"."
:group 'org-export-icalendar
:package-version '(Org . "9.8")
:type '(choice
(const :tag "All timestamps" t)
(const :tag "Only active timestamps" active)
(const :tag "Active timestamps, including diary timestamps" active)
(const :tag "Active timestamps, excluding diary timestamps"
active-exclude-diary)
(const :tag "Only inactive timestamps" inactive)
(const :tag "No timestamp" nil)))
(const :tag "No timestamp" nil))
:safe (lambda (x) (memq x '(t nil active active-exclude-diary inactive))))
(defcustom org-icalendar-include-todo nil
"Non-nil means create VTODO components from TODO items.
@ -744,11 +751,9 @@ inlinetask within the section."
(org-element-contents inside))
'timestamp
(lambda (ts)
(when (let ((type (org-element-property :type ts)))
(cl-case (plist-get info :with-timestamps)
(active (memq type '(active active-range)))
(inactive (memq type '(inactive inactive-range)))
((t) t)))
(unless (org-export--skip-timestamp-p
(plist-get info :with-timestamps)
(org-element-property :type ts))
(let ((uid (format "TS%d-%s" (cl-incf counter) uid)))
(org-icalendar--vevent
entry ts uid summary loc desc cat tz class))))

View file

@ -124,6 +124,7 @@
(:latex-class-options "LATEX_CLASS_OPTIONS" nil nil t)
(:latex-header "LATEX_HEADER" nil nil newline)
(:latex-header-extra "LATEX_HEADER_EXTRA" nil nil newline)
(:latex-class-pre "LATEX_CLASS_PRE" nil nil newline)
(:description "DESCRIPTION" nil nil parse)
(:keywords "KEYWORDS" nil nil parse)
(:subtitle "SUBTITLE" nil nil parse)
@ -170,6 +171,7 @@
(:latex-title-command nil nil org-latex-title-command)
(:latex-toc-command nil nil org-latex-toc-command)
(:latex-compiler "LATEX_COMPILER" nil org-latex-compiler)
(:latex-use-sans nil "latex-use-sans" org-latex-use-sans)
;; Redefine regular options.
(:date "DATE" nil "\\today" parse)))
@ -588,6 +590,7 @@ like that: \"%%\".
Setting :latex-title-command in publishing projects will take
precedence over this variable."
:group 'org-export-latex
:safe #'stringp
:type '(string :tag "Format string"))
(defcustom org-latex-subtitle-format "\\\\\\medskip\n\\large %s"
@ -597,6 +600,7 @@ which is replaced with the subtitle."
:group 'org-export-latex
:version "26.1"
:package-version '(Org . "8.3")
:safe #'stringp
:type '(string :tag "Format string"))
(defcustom org-latex-subtitle-separate nil
@ -604,6 +608,7 @@ which is replaced with the subtitle."
:group 'org-export-latex
:version "26.1"
:package-version '(Org . "8.3")
:safe #'booleanp
:type 'boolean)
(defcustom org-latex-toc-command "\\tableofcontents\n\n"
@ -616,7 +621,7 @@ the #+TOC keyword."
(defcustom org-latex-hyperref-template
"\\hypersetup{\n pdfauthor={%a},\n pdftitle={%t},\n pdfkeywords={%k},
pdfsubject={%d},\n pdfcreator={%c}, \n pdflang={%L}}\n"
pdfsubject={%d},\n pdfcreator={%c},\n pdflang={%L}}\n"
"Template for hyperref package options.
This format string may contain these elements:
@ -643,7 +648,8 @@ precedence over this variable."
:version "26.1"
:package-version '(Org . "8.3")
:type '(choice (const :tag "No template" nil)
(string :tag "Format string")))
(string :tag "Format string"))
:safe #'string-or-null-p)
;;;; Headline
@ -675,6 +681,7 @@ command like \"\\sidenote{%s%s}\" that you want to use.
The value will be passed as an argument to `format' as the following
(format org-latex-default-footnote-command
footnote-description footnote-label)"
:group 'org-export-latex
:package-version '(Org . "9.7")
:type 'string)
@ -698,17 +705,17 @@ The value will be passed as an argument to `format' as the following
;;;; Timestamps
(defcustom org-latex-active-timestamp-format "\\textit{%s}"
"A printf format string to be applied to active timestamps."
"A `format' string to be applied to active timestamps."
:group 'org-export-latex
:type 'string)
(defcustom org-latex-inactive-timestamp-format "\\textit{%s}"
"A printf format string to be applied to inactive timestamps."
"A `format' string to be applied to inactive timestamps."
:group 'org-export-latex
:type 'string)
(defcustom org-latex-diary-timestamp-format "\\textit{%s}"
"A printf format string to be applied to diary timestamps."
"A `format' string to be applied to diary timestamps."
:group 'org-export-latex
:type 'string)
@ -956,13 +963,13 @@ The first two options provide basic syntax
highlighting (listings), or none at all (verbatim).
When using listings, you also need to make use of LaTeX package
\"listings\". The \"color\" LaTeX package is also needed if you
would like color too. These can simply be added to
\"listings\". The \"xcolor\" LaTeX package is also needed for
color management. These can simply be added to
`org-latex-packages-alist', using customize or something like:
(require \\='ox-latex)
(add-to-list \\='org-latex-packages-alist \\='(\"\" \"listings\"))
(add-to-list \\='org-latex-packages-alist \\='(\"\" \"color\"))
(add-to-list \\='org-latex-packages-alist \\='(\"\" \"xcolor\"))
There are two further options for more comprehensive
fontification. The first can be set with,
@ -983,7 +990,7 @@ In addition, it is necessary to install pygments
passed to pdflatex.
The minted choice has possible repercussions on the preview of
latex fragments (see `org-preview-latex-fragment'). If you run
latex fragments (see `org-latex-preview'). If you run
into previewing problems, please consult
URL `https://orgmode.org/worg/org-tutorials/org-latex-preview.html'.
@ -1409,6 +1416,10 @@ See also `org-latex-compiler'."
:version "26.1"
:package-version '(Org . "9.0"))
(defconst org-latex-compilers '("pdflatex" "xelatex" "lualatex")
"Known LaTeX compilers.
See also `org-latex-compiler'.")
(defcustom org-latex-compiler "pdflatex"
"LaTeX compiler to use.
@ -1422,11 +1433,12 @@ Can also be set in buffers via #+LATEX_COMPILER. See also
(const :tag "LuaLaTeX" "lualatex")
(const :tag "Unset" ""))
:version "26.1"
:package-version '(Org . "9.0"))
(defconst org-latex-compilers '("pdflatex" "xelatex" "lualatex")
"Known LaTeX compilers.
See also `org-latex-compiler'.")
:package-version '(Org . "9.0")
:safe (lambda (s)
(and (stringp s) ; must be a string
;; either an empty string or one of the supported compilers
(or (length= s 0)
(member s org-latex-compilers)))))
(defcustom org-latex-bib-compiler "bibtex"
"Command to process a LaTeX file's bibliography.
@ -1443,7 +1455,7 @@ A better approach is to use a compiler suit such as `latexmk'."
:package-version '(Org . "9.0"))
(defcustom org-latex-pdf-process
(if (executable-find "latexmk")
(if (and (executable-find "latexmk") (executable-find "perl"))
'("latexmk -f -pdf -%latex -interaction=nonstopmode -output-directory=%o %f")
'("%latex -interaction nonstopmode -output-directory %o %f"
"%latex -interaction nonstopmode -output-directory %o %f"
@ -1522,7 +1534,7 @@ logfiles to remove, set `org-latex-logfiles-extensions'."
("Underfull \\hbox" . "[underfull hbox]")
("Overfull \\hbox" . "[overfull hbox]")
("Citation.*?undefined" . "[undefined citation]")
("^!.+Unicode character" . "[unicode character(s) not set up for use with pdflatex. You can run lualatex or xelatex instead]")
("^!.+Unicode character" . "[unicode character(s) not supported by pdflatex. Set org-latex-compiler to lualatex or xelatex instead]")
("Missing character: There is no" . "[Missing character(s): please load an appropriate font with the fontspec package]")
("Undefined control sequence" . "[undefined control sequence]"))
"Alist of regular expressions and associated messages for the user.
@ -1537,6 +1549,18 @@ calling `org-latex-compile'."
(string :tag "Message"))))
(defcustom org-latex-toc-include-unnumbered nil
"Whether to include unnumbered headings in the table of contents.
The default behaviour is to include numbered headings only, as it is
usually the case in LaTeX (but different from other Org exporters).
To include an unnumbered heading, set the `:UNNUMBERED:'
property to `toc'"
:group 'org-export-latex
:package-version '(Org . "9.8")
:type 'boolean
:safe #'booleanp)
;;; Internal Functions
@ -1926,16 +1950,24 @@ INFO is a plist used as a communication channel."
;; Here the actual name of the LANGUAGE or LANG is used.
(or (plist-get plist :lang-name)
lang))))
`((?a . ,(org-export-data (plist-get info :author) info))
(?t . ,(org-export-data (plist-get info :title) info))
(?s . ,(org-export-data (plist-get info :subtitle) info))
`((?a . ,(if (plist-get info :with-author)
(org-export-data (plist-get info :author) info)
""))
(?t . ,(if (plist-get info :with-title)
(org-export-data (plist-get info :title) info)
""))
(?s . ,(if (plist-get info :with-title)
(org-export-data (plist-get info :subtitle) info)
""))
(?k . ,(org-export-data (org-latex--wrap-latex-math-block
(plist-get info :keywords) info)
info))
(?d . ,(org-export-data (org-latex--wrap-latex-math-block
(plist-get info :description) info)
info))
(?c . ,(plist-get info :creator))
(?c . ,(if (plist-get info :with-creator)
(plist-get info :creator)
""))
(?l . ,language)
(?L . ,(capitalize language))
(?D . ,(org-export-data (org-export-get-date info) info)))))
@ -1948,6 +1980,15 @@ INFO is a plist used as a communication channel."
(member (or compiler "") org-latex-compilers)
(format org-latex-compiler-file-string compiler))))
(defcustom org-latex-use-sans nil
"Whether to typeset the document with the Sans font family.
The default behaviour is to typeset with the Roman font family."
:group 'org-export-latex
:package-version '(Org . "9.8")
:type 'boolean
:safe #'booleanp)
;;; Filters
@ -1985,10 +2026,15 @@ specified in `org-latex-default-packages-alist' or
(let* ((class-options (plist-get info :latex-class-options))
(header (nth 1 (assoc class (plist-get info :latex-classes)))))
(and (stringp header)
(mapconcat #'org-element-normalize-string
(list
(and (not snippet?)
(plist-get info :latex-class-pre))
(if (not class-options) header
(replace-regexp-in-string
"^[ \t]*\\\\documentclass\\(\\(\\[[^]]*\\]\\)?\\)"
class-options header t nil 1))))
class-options header t nil 1)))
nil)))
(user-error "Unknown LaTeX class `%s'" class))))
(org-latex-guess-polyglossia-language
(org-latex-guess-babel-language
@ -2002,7 +2048,11 @@ specified in `org-latex-default-packages-alist' or
(mapconcat #'org-element-normalize-string
(list (plist-get info :latex-header)
(and (not snippet?)
(plist-get info :latex-header-extra)))
(plist-get info :latex-header-extra))
(and (not snippet?)
(plist-get info :latex-use-sans)
"\\renewcommand*\\familydefault{\\sfdefault}"))
""))))
info)
info)))
@ -2237,11 +2287,10 @@ CONTENTS is nil. INFO is a plist holding contextual information."
(org-export-get-footnote-definition footnote-reference info)
info t)))
;; Use \footnotemark if reference is within another footnote
;; reference, footnote definition, table cell, verse block, or
;; item's tag.
;; reference, footnote definition, table cell, or item's tag.
((or (org-element-lineage footnote-reference
'(footnote-reference footnote-definition
table-cell verse-block))
'( footnote-reference footnote-definition
table-cell))
(org-element-type-p
(org-element-parent-element footnote-reference) 'item))
"\\footnotemark")
@ -2269,18 +2318,13 @@ CONTENTS is nil. INFO is a plist holding contextual information."
;;;; Headline
(defun org-latex-headline (headline contents info)
"Transcode a HEADLINE element from Org to LaTeX.
CONTENTS holds the contents of the headline. INFO is a plist
holding contextual information."
(unless (org-element-property :footnote-section-p headline)
(defun org-latex--get-section-format (headline info)
"Get section format for HEADLINE.
INFO is the communication plist."
(let* ((class (plist-get info :latex-class))
(level (org-export-get-relative-level headline info))
(numberedp (org-export-numbered-headline-p headline info))
(class-sectioning (assoc class (plist-get info :latex-classes)))
;; Section formatting will set two placeholders: one for
;; the title and the other for the contents.
(section-fmt
(class-sectioning (assoc class (plist-get info :latex-classes))))
(let ((sec (if (functionp (nth 2 class-sectioning))
(funcall (nth 2 class-sectioning) level numberedp)
(nth (1+ level) class-sectioning))))
@ -2299,10 +2343,9 @@ holding contextual information."
;; (num-in num-out no-num-in no-num-out)
((= (length sec) 4)
(if numberedp (concat (car sec) "\n%s" (nth 1 sec))
(concat (nth 2 sec) "\n%s" (nth 3 sec)))))))
;; Create a temporary export backend that hard-codes
;; "\underline" within "\section" and alike.
(section-backend
(concat (nth 2 sec) "\n%s" (nth 3 sec))))))))
(defconst org-latex--section-backend
(org-export-create-backend
:parent 'latex
:transcoders
@ -2312,23 +2355,45 @@ holding contextual information."
;; So, within headings it's a good idea to replace any instances of \verb
;; with \texttt.
(code . (lambda (o _ _) (org-latex--protect-texttt (org-element-property :value o))))
(verbatim . (lambda (o _ _) (org-latex--protect-texttt (org-element-property :value o)))))))
;; Create a temporary export backend that strips footnotes from title.
;; Footnotes are not allowed in \section and similar
;; commands that contribute to TOC and footers.
;; See https://orgmode.org/list/691643eb-49d0-45c3-ab7f-a1edbd093bef@gmail.com
;; https://texfaq.org/FAQ-ftnsect
(section-no-footnote-backend
(verbatim . (lambda (o _ _) (org-latex--protect-texttt (org-element-property :value o))))))
"Export backend that hard-codes \\underline within \\section and alike.")
(defconst org-latex--section-no-footnote-backend
(org-export-create-backend
:parent section-backend
:parent org-latex--section-backend
:transcoders
`((footnote-reference . ignore))))
`((footnote-reference . ignore)))
"Export backend that strips footnotes from title.
Footnotes are not allowed in \\section and similar commands that
contribute to TOC and footers.
See https://orgmode.org/list/691643eb-49d0-45c3-ab7f-a1edbd093bef@gmail.com
https://texfaq.org/FAQ-ftnsect")
(defun org-latex-headline (headline contents info)
"Transcode a HEADLINE element from Org to LaTeX.
CONTENTS holds the contents of the headline. INFO is a plist
holding contextual information."
(unless (org-element-property :footnote-section-p headline)
(let* ((level (org-export-get-relative-level headline info))
;; "LaTeX TOC handling"
;; :unnumbered: toc will add the heading to the ToC
;; "Org TOC handling"
;; :unnumbered: notoc to suppress heading from the ToC
;; else include all headings (including unnumbered) like other modes
(unnumbered-type (org-export-get-node-property :UNNUMBERED headline t))
(numberedp (org-export-numbered-headline-p headline info))
;; Section formatting will set two placeholders: one for
;; the title and the other for the contents.
(section-fmt (org-latex--get-section-format headline info))
(text
(org-export-data-with-backend
(org-element-property :title headline) section-backend info))
(org-element-property :title headline)
org-latex--section-backend info))
(text-no-footnote
(org-export-data-with-backend
(org-element-property :title headline) section-no-footnote-backend info))
(org-element-property :title headline)
org-latex--section-no-footnote-backend info))
(todo
(and (plist-get info :with-todo-keywords)
(let ((todo (org-element-property :todo-keyword headline)))
@ -2382,8 +2447,10 @@ holding contextual information."
(funcall (plist-get info :latex-format-headline-function)
todo todo-type priority
(org-export-data-with-backend
;; Returns alternative title when provided or
;; title itself.
(org-export-get-alt-title headline info)
section-backend info)
org-latex--section-backend info)
(and (eq (plist-get info :with-tags) t) tags)
info))
;; Maybe end local TOC (see `org-latex-keyword').
@ -2402,25 +2469,80 @@ holding contextual information."
(string-match-p "\\<local\\>" v)
(format "\\stopcontents[level-%d]" level)))))
info t)))))
(if (and (or (and opt-title (not (equal opt-title full-text)))
;; Heading contains footnotes. Add optional title
;; version without footnotes to avoid footnotes in
;; TOC/footers.
(and (not (equal full-text-no-footnote full-text))
(setq opt-title full-text-no-footnote)))
(string-match "\\`\\\\\\(.+?\\){" section-fmt))
(format (replace-match "\\1[%s]" nil nil section-fmt 1)
;; When do we need to explicitly specify a heading for TOC?
;; 1. On numbered section with footnotes in title or alt_title
;; 2. On an unnumbered section if :UNNUMBERED: allows it regardless of footnotes
;; This applies to anything that may go into the ToC.
;; Specifically for paragraphs, see first answer of
;; https://tex.stackexchange.com/questions/288072/footnotes-within-paragraph
(let ((section-kw
(and (string-match "\\`\\\\\\(.+?\\){" section-fmt)
(match-string 1 section-fmt)))
need-alternative-toc-title)
(if (not section-kw)
;; We only know how to add \SECTION-KW{...} to TOC.
(setq need-alternative-toc-title nil)
(if (string-suffix-p "*" section-kw)
;; FIXME: In theory, user may customize section-fmt
;; to use, e.g. \section{...} for unnumbered headings
;; We do not handle such scenario.
(progn ;; unnumbered sections (ending with *)
;; Then we need to obey what the :UNNUMBERED: property says
(if org-latex-toc-include-unnumbered
;; Treat the ToC closer to what other exporters do
;; Include unnumbered section into TOC unless
;; explicitly requested not to.
(if (string= unnumbered-type "notoc")
(setq need-alternative-toc-title nil)
(setq need-alternative-toc-title t))
;; Ignore unnumbered headings in ToC - as in LaTeX
;; unless explicitly requested to include.
(if (string= unnumbered-type "toc")
(setq need-alternative-toc-title t)
(setq need-alternative-toc-title nil))))
;; Numbered sections
;; Specify special TOC title only when there is
;; opt-title or when title contains footnotes.
(if (and (string= full-text full-text-no-footnote) ;; no footnotes
;; opt-title is either ALT_TITLE or title itself
;; as returned by `org-export-get-alt-title'
(string= full-text opt-title)) ;; same alternative title
(setq need-alternative-toc-title nil)
(setq need-alternative-toc-title t))))
;; In all cases
;; Get rid of the footnotes in opt-title
(when (and (not (string= full-text-no-footnote full-text)) ;; when we have footnotess
(string= full-text opt-title)) ;; And we do not impose an alternative title
(setq opt-title full-text-no-footnote))
(if need-alternative-toc-title
(let ((new-format section-fmt)
(new-extra "")) ;; put the addcontentsline here
(if (string-suffix-p "*" section-kw)
;; Subsection that needs alternative title:
;; Keep section format, use \\addcontentsline
(setq new-extra
(format "\\addcontentsline{toc}{%s}{%s}\n"
(string-remove-suffix "*" section-kw)
opt-title))
;; section... we need the brackets
(let*
;; Replace square brackets with parenthesis
;; since square brackets are not supported in
;; optional arguments.
(replace-regexp-in-string
"\\[" "(" (replace-regexp-in-string "\\]" ")" opt-title))
((un-bracketed-alt (replace-regexp-in-string
"\\[" "(" (replace-regexp-in-string "\\]" ")" opt-title)))
(replacement-re (concat
"\\1["
(replace-regexp-in-string (rx "\\") "\\\\" un-bracketed-alt nil t)
"]")))
(setq new-format (replace-match replacement-re nil nil section-fmt 1))))
(format new-format
full-text
(concat headline-label pre-blanks contents))
;; Impossible to add an alternative heading. Fallback to
;; regular sectioning format string.
(concat headline-label new-extra pre-blanks contents)))
;; Don't need or cannot have alternative heading.
;; Use regular sectioning format string.
(format section-fmt full-text
(concat headline-label pre-blanks contents))))))))
(concat headline-label pre-blanks contents)))))))))
(defun org-latex-format-headline-default-function
(todo _todo-type priority text tags _info)
@ -2428,7 +2550,7 @@ holding contextual information."
See `org-latex-format-headline-function' for details."
(concat
(and todo (format "{\\bfseries\\sffamily %s} " todo))
(and priority (format "\\framebox{\\#%c} " priority))
(and priority (format "\\framebox{\\#%s} " (org-priority-to-string priority)))
text
(and tags
(format "\\hfill{}\\textsc{%s}"
@ -2534,7 +2656,7 @@ holding contextual information."
See `org-latex-format-inlinetask-function' for details."
(let ((full-title
(concat (when todo (format "\\textbf{\\textsf{\\textsc{%s}}} " todo))
(when priority (format "\\framebox{\\#%c} " priority))
(when priority (format "\\framebox{\\#%s} " (org-priority-to-string priority)))
title
(when tags
(format "\\hfill{}\\textsc{%s}"
@ -3119,6 +3241,8 @@ contextual information."
"{[}"
output
nil nil 1))
;; When inside verse block, use special rules.
(setq output (org-latex--plain-text-verse-block output text))
;; Return value.
output))
@ -4188,39 +4312,27 @@ channel."
;;;; Verse Block
(defun org-latex-verse-block (verse-block contents info)
"Transcode a VERSE-BLOCK element from Org to LaTeX.
CONTENTS is verse block contents. INFO is a plist holding
contextual information."
(defun org-latex--plain-text-verse-block (contents plain-text)
"Format CONTENTS if PLAIN-TEXT is inside verse environment.
INFO is the communication plist.
Return CONTENTS unchanged when TEXT is not inside verse environment or
when TEXT is a part of footnote reference.
In a verse environment, add a line break to each newline character and
change each white space at beginning of a line into a normal space,
calculated with `\\fontdimen2\\font'. One or more blank lines between
lines are exported as a single blank line. If the `:lines' attribute
is used, the last verse of each stanza ends with the string `\\!',
according to the syntax of the `verse' package. The separation between
stanzas can be controlled with the length `\\stanzaskip', of the
aforementioned package. If the `:literal' attribute is used, all
blank lines are preserved and exported as `\\vspace*{\\baselineskip}',
including the blank lines before or after CONTENTS."
(if-let* ((verse-block (org-element-lineage plain-text 'verse-block))
;; VALUEFORM
((not (org-element-lineage plain-text 'footnote-reference))))
(let* ((lin (org-export-read-attribute :attr_latex verse-block :lines))
(latcode (org-export-read-attribute :attr_latex verse-block :latexcode))
(cent (org-export-read-attribute :attr_latex verse-block :center))
(lit (org-export-read-attribute :attr_latex verse-block :literal))
(attr (concat
(if cent "[\\versewidth]" "")
(if lin (format "\n\\poemlines{%s}" lin) "")
(if latcode (format "\n%s" latcode) "")))
(versewidth (org-export-read-attribute :attr_latex verse-block :versewidth))
(vwidth (if versewidth (format "\\settowidth{\\versewidth}{%s}\n" versewidth) ""))
(linreset (if lin "\n\\poemlines{0}" "")))
(concat
(org-latex--wrap-label
verse-block
;; In a verse environment, add a line break to each newline
;; character and change each white space at beginning of a line
;; into a normal space, calculated with `\fontdimen2\font'. One
;; or more blank lines between lines are exported as a single
;; blank line. If the `:lines' attribute is used, the last
;; verse of each stanza ends with the string `\\!', according to
;; the syntax of the `verse' package. The separation between
;; stanzas can be controlled with the length `\stanzaskip', of
;; the aforementioned package. If the `:literal' attribute is
;; used, all blank lines are preserved and exported as
;; `\vspace*{\baselineskip}', including the blank lines before
;; or after CONTENTS.
(format "%s\\begin{verse}%s\n%s\\end{verse}%s"
vwidth
attr
(lit (org-export-read-attribute :attr_latex verse-block :literal)))
(replace-regexp-in-string
"^[ \t]+" (lambda (m) (format "\\hspace*{%d\\fontdimen2\\font}" (length m)))
(replace-regexp-in-string
@ -4235,17 +4347,41 @@ contextual information."
(replace-regexp-in-string
"\\([ \t]*\\\\\\\\\\)?[ \t]*\n"
"\\\\\n"
contents
nil t)
nil t)
nil t))
;; Not in verse block, return CONTENTS unchanged.
contents))
(defun org-latex-verse-block (verse-block contents info)
"Transcode a VERSE-BLOCK element from Org to LaTeX.
CONTENTS is verse block contents. INFO is a plist holding
contextual information."
(let* ((lin (org-export-read-attribute :attr_latex verse-block :lines))
(latcode (org-export-read-attribute :attr_latex verse-block :latexcode))
(cent (org-export-read-attribute :attr_latex verse-block :center))
(attr (concat
(if cent "[\\versewidth]" "")
(if lin (format "\n\\poemlines{%s}" lin) "")
(if latcode (format "\n%s" latcode) "")))
(lit (org-export-read-attribute :attr_latex verse-block :literal))
(versewidth (org-export-read-attribute :attr_latex verse-block :versewidth))
(vwidth (if versewidth (format "\\settowidth{\\versewidth}{%s}\n" versewidth) ""))
(linreset (if lin "\n\\poemlines{0}" "")))
(org-latex--wrap-label
verse-block
(format "%s\\begin{verse}%s\n%s\\end{verse}%s"
vwidth attr
;; If the `:literal' attribute is used, all blank lines
;; are preserved and exported as
;; `\\vspace*{\\baselineskip}', including the blank lines
;; before or after CONTENTS.
(if (not lit)
(concat (org-trim contents t) "\n")
contents)
nil t)
nil t)
nil t)
linreset)
info)
;; Insert footnote definitions, if any, after the environment, so
;; the special formatting above is not applied to them.
(org-latex--delayed-footnotes-definitions verse-block info))))
info)))
;;; End-user functions
@ -4282,11 +4418,16 @@ Export is done in a buffer named \"*Org LATEX Export*\", which
will be displayed when `org-export-show-temporary-export-buffer'
is non-nil."
(interactive)
(defvar TeX-parse-self) ;; defined in tex.el
(let (;; FIXME: Working around LaTeX-mode being broken in non-file buffers.
;; To be removed once we drop Emacs 30 and earlier, where the problem
;; is not yet fixed.
(TeX-parse-self nil))
(org-export-to-buffer 'latex "*Org LATEX Export*"
async subtreep visible-only body-only ext-plist
(if (fboundp 'major-mode-remap)
(major-mode-remap 'latex-mode)
#'LaTeX-mode)))
#'LaTeX-mode))))
;;;###autoload
(defun org-latex-convert-region-to-latex ()

View file

@ -33,7 +33,11 @@
;; See ox.el for more details on how this exporter works.
;;
;; It introduces one new buffer keywords:
;; "MAN_CLASS_OPTIONS".
;; "MAN_CLASS_OPTIONS" that accepts the following options:
;; - `:section-id', the section, a string (eg. ':section-id "2"');
;; - `:release', the footer middle, a string (eg. ':release "Emacs 13"');
;; - `:header', the header middle, a string (eg. ':header "GNU"').
;;; Code:
@ -101,6 +105,7 @@
(underline . org-man-underline)
(verbatim . org-man-verbatim)
(verse-block . org-man-verse-block))
:filters-alist '((:filter-parse-tree . org-man--remove-blank))
:menu-entry
'(?M "Export to MAN"
((?m "As MAN file" org-man-export-to-man)
@ -300,6 +305,17 @@ This function shouldn't be used for floats. See
;; backslash) cannot be used as per the same man page.
(replace-regexp-in-string "\\\\" "\\e" text nil t))
;;; Filters
(defun org-man--remove-blank (tree _backend info)
"Remove :post-blank from TREE elements.
INFO is the communication plist.
Avoiding blank lines is adviced by groff_man_style(7) man page."
(org-element-map tree org-element-all-elements
(lambda (el) (setf (org-element-post-blank el) 0))
info)
tree)
;;; Template
@ -315,19 +331,25 @@ holding export options."
#'identity
(list (plist-get info :man-class-options))
" "))))
(section-item (plist-get attr :section-id)))
(section-item (plist-get attr :section-id))
(release (plist-get attr :release))
(header (plist-get attr :header))
;; Note: groff linter suggests date to be the third argument
;; of .TH
(date (and (plist-get info :with-date)
(org-export-data (org-export-get-date info) info))))
(concat
(cond
((and title (stringp section-item))
(format ".TH \"%s\" \"%s\" \n" title section-item))
((and (string= "" title) (stringp section-item))
(format ".TH \"%s\" \"%s\" \n" " " section-item))
(title
(format ".TH \"%s\" \"1\" \n" title))
(t
".TH \" \" \"1\" "))
(format ".TH \"%s\" \"%s\"" ;; only two required by groff_man(7).
(if title title " ")
(if (stringp section-item) section-item "1"))
(if date (format " \"%s\"" date)
" \"\"") ;; in case later options are present.
(if release (format " \"%s\"" release)
" \"\"") ;; in case later options are present.
;; Do not write an empty footer-outside, otherwise man(1) will
;; no longer generate its content, see groff_man(7).
(if header (format " \"%s\"" header))
" \n"
contents)))
@ -535,7 +557,7 @@ contextual information."
(delete-file in-file)
(delete-file out-file)
code-block)
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE\n"
(format ".RS\n.nf\n\\fC%s\\m[]\\fP\n.fi\n.RE\n"
(org-man--protect-example code)))))
;; Do not use a special package: transcode it verbatim.
@ -755,7 +777,7 @@ holding contextual information."
CONTENTS holds the contents of the item. INFO is a plist holding
contextual information."
(if (not (plist-get info :man-source-highlight))
(format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n\n"
(format ".RS\n.nf\n\\fC%s\\fP\n.fi\n.RE\n"
(org-man--protect-example (org-export-format-code-default src-block info)))
(let* ((tmpdir temporary-file-directory)
(in-file (make-temp-name (expand-file-name "srchilite" tmpdir)))
@ -779,7 +801,7 @@ contextual information."
(delete-file in-file)
(delete-file out-file)
code-block)
(format ".RS\n.nf\n\\fC\\m[black]%s\\m[]\\fP\n.fi\n.RE" (org-man--protect-example code))))))
(format ".RS\n.nf\n\\fC%s\\m[]\\fP\n.fi\n.RE" (org-man--protect-example code))))))
;;; Statistics Cookie
@ -1125,6 +1147,8 @@ file-local settings.
Return PDF file's name."
(interactive)
(let ((outfile (org-export-output-file-name ".man" subtreep)))
(require 'ox-latex)
(declare-function org-latex-compile "ox-latex" (texfile &optional snippet))
(org-export-to-file 'man outfile
async subtreep visible-only body-only ext-plist
#'org-latex-compile)))

View file

@ -93,6 +93,22 @@ headings for its own use."
;; Avoid `natnum' because that's not available until Emacs 28.1.
:type 'integer)
(defcustom org-md-link-org-files-as-md t
"Non-nil means make file links to \"file.org\" point to \"file.md\".
When Org mode is exporting an Org file to markdown, links to
non-markdown files are directly put into a \"href\" tag in
markdown. However, links to other Org files \(recognized by the
extension \".org\") should become links to the corresponding
markdown file, assuming that the linked Org file will also be
converted to markdown.
When nil, the links still point to the plain \".org\" file."
:group 'org-export-md
:package-version '(Org . "9.8")
:type 'boolean
:safe #'booleanp)
;;; Define Backend
@ -144,7 +160,8 @@ headings for its own use."
'((:md-footnote-format nil nil org-md-footnote-format)
(:md-footnotes-section nil nil org-md-footnotes-section)
(:md-headline-style nil nil org-md-headline-style)
(:md-toplevel-hlevel nil nil org-md-toplevel-hlevel)))
(:md-toplevel-hlevel nil nil org-md-toplevel-hlevel)
(:md-link-org-files-as-md nil nil org-md-link-org-files-as-md)))
;;; Filters
@ -393,8 +410,8 @@ a communication channel."
(concat " " (org-make-tag-string tag-list))))))
(priority
(and (plist-get info :with-priority)
(let ((char (org-element-property :priority headline)))
(and char (format "[#%c] " char)))))
(let ((priority-value (org-element-property :priority headline)))
(and priority-value (format "[#%s] " (org-priority-to-string priority-value))))))
;; Headline text without tags.
(heading (concat todo priority title))
(style (plist-get info :md-headline-style)))
@ -540,17 +557,19 @@ channel."
DESC is the description part of the link, or the empty string.
INFO is a plist holding contextual information. See
`org-export-data'."
(let* ((link-org-files-as-md
(let* ((link-org-files-as-md-maybe
(lambda (raw-path)
;; Treat links to `file.org' as links to `file.md'.
(if (string= ".org" (downcase (file-name-extension raw-path ".")))
(if (and
(plist-get info :md-link-org-files-as-md)
(string= ".org" (downcase (file-name-extension raw-path "."))))
(concat (file-name-sans-extension raw-path) ".md")
raw-path)))
(type (org-element-property :type link))
(raw-path (org-element-property :path link))
(path (cond
((string-equal type "file")
(org-export-file-uri (funcall link-org-files-as-md raw-path)))
(org-export-file-uri (funcall link-org-files-as-md-maybe raw-path)))
(t (concat type ":" raw-path)))))
(cond
;; Link type is handled by a special function.
@ -561,7 +580,7 @@ INFO is a plist holding contextual information. See
(org-export-resolve-id-link link info))))
(pcase (org-element-type destination)
(`plain-text ; External file.
(let ((path (funcall link-org-files-as-md destination)))
(let ((path (funcall link-org-files-as-md-maybe destination)))
(if (not desc) (format "<%s>" path)
(format "[%s](%s)" desc path))))
(`headline

View file

@ -35,10 +35,6 @@
(require 'ox)
(require 'table nil 'noerror)
(declare-function org-at-heading-p "org" (&optional _))
(declare-function org-back-to-heading "org" (&optional invisible-ok))
(declare-function org-next-visible-heading "org" (arg))
;;; Define Backend
(org-export-define-backend 'odt
@ -91,10 +87,12 @@
(verbatim . org-odt-verbatim)
(verse-block . org-odt-verse-block))
:filters-alist '((:filter-parse-tree
. (org-odt--translate-latex-fragments
. (org-odt--strip-trailing-newlines
org-odt--translate-latex-fragments
org-odt--translate-description-lists
org-odt--translate-list-tables
org-odt--translate-image-links)))
org-odt--translate-image-links))
(:filter-final-output . org-odt--remove-forbidden))
:menu-entry
'(?o "Export to ODT"
((?o "As ODT file" org-odt-export-to-odt)
@ -108,6 +106,7 @@
(:keywords "KEYWORDS" nil nil space)
(:subtitle "SUBTITLE" nil nil parse)
;; Other variables.
(:odt-with-forbidden-chars nil nil org-odt-with-forbidden-chars)
(:odt-content-template-file nil nil org-odt-content-template-file)
(:odt-display-outline-level nil nil org-odt-display-outline-level)
(:odt-fontify-srcblocks nil nil org-odt-fontify-srcblocks)
@ -170,6 +169,14 @@ Use this to infer values of `org-odt-styles-dir' and
("\\.\\.\\." . "&#x2026;")) ; hellip
"Regular expressions for special string conversion.")
(defconst org-odt-forbidden-char-re
(rx (not (in ?\N{U+9} ?\N{U+A} ?\N{U+D}
(?\N{U+20} . ?\N{U+D7FF})
(?\N{U+E000} . ?\N{U+FFFD})
(?\N{U+10000} . ?\N{U+10FFFF}))))
"Regexp matching forbidden XML1.0 characters.
https://www.w3.org/TR/REC-xml/#charsets")
(defconst org-odt-schema-dir-list
(list (expand-file-name "./schema/" org-odt-data-dir))
"List of directories to search for OpenDocument schema files.
@ -364,6 +371,20 @@ the entity. See `org-odt--enumerate'.")
:tag "Org Export ODT"
:group 'org-export)
(defcustom org-odt-with-forbidden-chars ""
"String to replace forbidden XML characters.
When set to t, forbidden characters are left as-is.
When set to nil, an error is thrown.
See `org-odt-forbidden-char-re' for the list of forbidden characters
that cannot occur inside ODT documents.
You may also consider export filters to perform more fine-grained
replacements. See info node `(org)Advanced Export Configuration'."
:package-version '(Org . "9.8")
:type '(choice (const :tag "Leave forbidden characters as-is" t)
(const :tag "Err when forbidden characters encountered" nil)
(string :tag "Replacement string"))
:safe #'always)
;;;; Debugging
@ -699,26 +720,31 @@ When set, the exporter will process LaTeX environments and
fragments.
This option can also be set with the +OPTIONS line,
e.g. \"tex:mathjax\". Allowed values are:
e.g. \"tex:dvipng\". Allowed values are:
nil Ignore math snippets.
`verbatim' Keep everything in verbatim
`dvipng' Process the LaTeX fragments to images. This will also
include processing of non-math environments.
`imagemagick' Convert the LaTeX fragments to pdf files and use
imagemagick to convert pdf files to png files.
`mathjax' Do MathJax preprocessing and arrange for MathJax.js to
be loaded.
t, `mathml' Convert the LaTeX fragments to MathML if the
`org-latex-to-mathml-convert-command' is usable.
SYMBOL Convert the LaTeX fragments to images using any symbol
defined in `org-preview-latex-process-alist', e.g.,
`dvipng'.
`verbatim' Keep everything in verbatim.
Any other symbol is a synonym for `mathjax'."
:version "24.4"
:package-version '(Org . "8.0")
:type '(choice
If the desired converter is not available or any other symbol is
provided, process as `verbatim'."
:package-version '(Org . "9.8")
:type `(choice
(const :tag "Do not process math in any way" nil)
(const :tag "Leave math verbatim" verbatim)
(const :tag "Use dvipng to make images" dvipng)
(const :tag "Use imagemagick to make images" imagemagick)
(other :tag "Use MathJax to display math" mathjax)))
(choice :tag "Convert fragments to MathML" :value t
(const t)
(const mathml))
(restricted-sexp :tag "Convert fragments to images"
:value ,(caar org-preview-latex-process-alist)
:match-alternatives
(,(lambda (v)
(assq v org-preview-latex-process-alist))))
(const :tag "Leave math verbatim" verbatim))
:safe #'always)
;;;; Links
@ -1097,8 +1123,9 @@ specifying the depth of the table."
(format "<text:span text:style-name=\"%s\">%s</text:span> "
style todo)))
(when priority
(let* ((style (format "OrgPriority-%s" priority))
(priority (format "[#%c]" priority)))
(let* ((priority-string (org-priority-to-string priority))
(style (format "OrgPriority-%s" priority-string))
(priority (format "[#%s]" priority-string)))
(format "<text:span text:style-name=\"%s\">%s</text:span> "
style priority)))
;; Title.
@ -1369,7 +1396,11 @@ original parsed data. INFO is a plist holding export options."
;; Ensure we have write permissions to this file.
(set-file-modes (concat org-odt-zip-dir "styles.xml") #o600)
(let ((styles-xml (concat org-odt-zip-dir "styles.xml")))
(let ((styles-xml (concat org-odt-zip-dir "styles.xml"))
;; Capture the current (possibly buffer-local) values for priorities
;; because these get reset to global values when we use `with-temp-buffer'
(priority-high org-priority-highest)
(priority-low org-priority-lowest))
(with-temp-buffer
(when (file-exists-p styles-xml)
(insert-file-contents styles-xml))
@ -1401,6 +1432,16 @@ original parsed data. INFO is a plist holding export options."
(if (wholenump sec-num) (<= level sec-num) sec-num))
(replace-match replacement t nil))))
;; Update styles.xml with priority styles for the current valid priority range
(when (plist-get info :with-priority)
(goto-char (point-min))
(when (re-search-forward "<style:style style:name=\"OrgPriority\" style:family=\"text\"/>" nil t)
(goto-char (match-end 0))
(insert "\n <!-- Org Priority Styles -->\n")
(dolist (priority (number-sequence priority-high priority-low))
(insert (format " <style:style style:name=\"OrgPriority-%s\" style:family=\"text\" style:parent-style-name=\"OrgPriority\"/>\n"
(org-priority-to-string priority))))))
;; Write back the new contents.
(write-region nil nil styles-xml))))
;; Update content.xml.
@ -1838,8 +1879,9 @@ See `org-odt-format-headline-function' for details."
(let ((style (if (eq todo-type 'done) "OrgDone" "OrgTodo")))
(format "<text:span text:style-name=\"%s\">%s</text:span> " style todo)))
(when priority
(let* ((style (format "OrgPriority-%c" priority))
(priority (format "[#%c]" priority)))
(let* ((priority-string (org-priority-to-string priority))
(style (format "OrgPriority-%s" priority-string))
(priority (format "[#%s]" priority-string)))
(format "<text:span text:style-name=\"%s\">%s</text:span> "
style priority)))
;; Title.
@ -2209,9 +2251,10 @@ SHORT-CAPTION are strings."
;; Use Imagemagick.
(and (executable-find "identify")
(let ((size-in-pixels
(let ((dim (shell-command-to-string
(format "identify -format \"%%w:%%h\" \"%s\""
file))))
(let ((dim (with-temp-buffer
(call-process "identify" nil `(,(current-buffer) nil) nil
"-format" "%w:%h" (format "%s" file))
(buffer-string))))
(when (string-match "\\([0-9]+\\):\\([0-9]+\\)" dim)
(cons (string-to-number (match-string 1 dim))
(string-to-number (match-string 2 dim)))))))
@ -2892,6 +2935,32 @@ contextual information."
(format " <text:s text:c=\"%d\"/>" (1- (length s)))))
line))
(defun org-odt--remove-forbidden (text _backend info)
"Remove forbidden and discouraged characters from TEXT.
INFO is the communication plist"
(pcase-exhaustive (plist-get info :odt-with-forbidden-chars)
((and (pred stringp) rep)
(let ((replacements (make-hash-table :test 'equal)))
(with-temp-buffer
(insert text)
(goto-char (point-min))
(while (re-search-forward org-odt-forbidden-char-re nil t)
(cl-incf (gethash (match-string 0) replacements 0))
(replace-match rep))
(cl-loop for forbidden being the hash-keys of replacements
using (hash-values count)
do (display-warning
'(ox-odt ox-odt-with-forbidden-chars)
(format "Replaced forbidden character '%s' with '%s' %d times"
forbidden rep count)))
(buffer-string))))
(`nil
(if (string-match org-odt-forbidden-char-re text)
(error "Forbidden character '%s' found. See `org-odt-with-forbidden-chars'"
(match-string 0 text))
text))
('t text)))
(defun org-odt--encode-plain-text (text &optional no-whitespace-filling)
(dolist (pair '(("&" . "&amp;") ("<" . "&lt;") (">" . "&gt;")))
(setq text (replace-regexp-in-string (car pair) (cdr pair) text t t)))
@ -2925,7 +2994,11 @@ contextual information."
;; FIXME: The unnecessary spacing may still remain when a newline
;; is at a boundary between Org objects (e.g. italics markup
;; followed by newline).
(when (org-string-nw-p output) ; blank string needs not to be re-filled
(when (and (org-string-nw-p output) ; blank string needs not to be re-filled
;; Plain text inside verse blocks gotta preserve newlines
;; and spaces.
(not (org-element-lineage text '(verse-block)))
)
(setq output
(with-temp-buffer
(save-match-data
@ -3116,8 +3189,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 (if lang (intern (format "%s-mode" lang)) #'ignore))
(let* ((lang-mode (if lang (org-src-get-lang-mode lang) #'ignore))
(code-lines (org-split-string code "\n"))
(code-length (length code-lines))
(use-htmlfontify-p (and (functionp lang-mode)
@ -3711,14 +3783,38 @@ contextual information."
(replace-regexp-in-string
;; Replace leading tabs and spaces.
"^[ \t]+" #'org-odt--encode-tabs-and-spaces
;; Add line breaks to each line of verse.
(replace-regexp-in-string
"\\(<text:line-break/>\\)?[ \t]*$" "<text:line-break/>" contents))))
;; Remove newlines after line breaks.
"<text:line-break/>[\n]" "<text:line-break/>"
(replace-regexp-in-string
;; Add line breaks to each line of verse.
"\\(<text:line-break/>\\)?[ \t]*$" "<text:line-break/>" contents)))))
;;; Filters
;;; Plain text
;; Trailing newlines appear as spaces in ODT.
;; We do not want that in, for example, in paragraphs where
;; end of paragraph is already signaled by ODT tag.
(defun org-odt--strip-trailing-newlines (data _backend info)
"Strip trailing newlines in DATA at the end of contents.
INFO is the communication channel."
(org-element-map data org-element-all-elements
(lambda (el)
(when-let* ((last-child (car (last (org-element-contents el)))))
(when (and (org-element-type-p last-child 'plain-text)
(string-suffix-p "\n" last-child))
(when (length> last-child 1)
(org-element-insert-before
(substring last-child 0 (1- (length last-child)))
last-child))
(org-element-extract last-child))))
info nil nil t)
data)
;;; Images
(defun org-odt--translate-image-links (data _backend info)
@ -3728,26 +3824,35 @@ contextual information."
(defun org-odt--translate-latex-fragments (tree _backend info)
(let ((processing-type (plist-get info :with-latex))
(preview-symbols (mapcar #'car org-preview-latex-process-alist))
(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)
;; Normalize processing-type to one of mathml, verbatim, or a
;; symbol in org-preview-latex-process-alist. If the desired
;; converter is not available, force verbatim processing.
(pcase processing-type
((or 't 'mathml)
(if (and (fboundp 'org-format-latex-mathml-available-p)
(org-format-latex-mathml-available-p))
(setq processing-type 'mathml)
(setq warning "`org-odt-with-latex': LaTeX to MathML converter not available. Falling back to verbatim.")
(setq processing-type 'verbatim)))
((dvipng imagemagick)
(unless (and (org-check-external-command "latex" "" t)
(org-check-external-command
(if (eq processing-type 'dvipng) "dvipng" "convert") "" t))
(setq warning "`org-odt-with-latex': LaTeX to PNG converter not available. Falling back to verbatim.")
(setq processing-type 'verbatim)))
(verbatim) ;; nothing to do
(otherwise
((and s (guard (memq s preview-symbols)))
(let* ((ext-commands (plist-get
(cdr (assq s org-preview-latex-process-alist))
:programs))
(ext-commands-available
(seq-reduce (lambda (result cmd)
(and result
(not
(null
(org-check-external-command cmd "" t)))))
ext-commands t)))
(unless ext-commands-available
(setq warning "`org-odt-with-latex': LaTeX to image converter not available. Falling back to verbatim.")
(setq processing-type 'verbatim))))
('verbatim) ;; nothing to do
(_
(setq warning "`org-odt-with-latex': Unknown LaTeX option. Forcing verbatim.")
(setq processing-type 'verbatim)))
@ -3764,34 +3869,32 @@ contextual information."
(message "Formatting LaTeX using %s" processing-type)
;; Convert `latex-fragment's and `latex-environment's.
(when (memq processing-type '(mathml dvipng imagemagick))
(when (memq processing-type (append '(mathml) preview-symbols))
(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))
(is-image (memq processing-type preview-symbols))
(cache-dir (file-name-directory input-file))
(cache-subdir (concat
(cl-case processing-type
((dvipng imagemagick)
org-preview-latex-image-directory)
(mathml "ltxmathml/"))
(if is-image
org-preview-latex-image-directory
org-latex-mathml-directory)
(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.
(if is-image
(format "Creating LaTeX image %d..." count)
(format "Creating MathML snippet %d..." count)))
;; Get an Org-style link to 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.
;; When converting to an 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
@ -4033,7 +4136,11 @@ contextual information."
(kill-buffer buf)))))
;; Delete temporary directory and also other embedded
;; files that get copied there.
(delete-directory org-odt-zip-dir t))))
(delete-directory org-odt-zip-dir t)))
;; We specify UTF-8 in `org-odt-template'. Enforce it even
;; when the buffer text has different encoding.
(coding-system-for-write 'utf-8)
(save-buffer-coding-system 'utf-8))
(condition-case-unless-debug err
(progn
(unless (executable-find "zip")

View file

@ -245,7 +245,7 @@ a communication channel."
;; them are included in the result.
(let ((footnotes
(org-element-map
(list (org-element-lineage section 'headline) section)
(list (or (org-element-lineage section 'headline) "") section)
'footnote-reference
(lambda (fn)
(and (eq (org-element-property :type fn) 'standard)
@ -337,10 +337,13 @@ is the property list for the given project. PUB-DIR is the
publishing directory.
Return output file name."
(require 'ox-publish)
(declare-function org-publish-org-to "ox-publish" (backend filename extension plist &optional pub-dir))
(org-publish-org-to 'org filename ".org" plist pub-dir)
(when (plist-get plist :htmlized-source)
(org-require-package 'htmlize)
(require 'ox-html)
(defvar org-html-extension) ; defined in ox-html.el
(let* ((org-inhibit-startup t)
(htmlize-output-type 'css)
(html-ext (concat "." (or (plist-get plist :html-extension)

Some files were not shown because too many files have changed in this diff Show more