update packages

This commit is contained in:
2025-11-25 19:52:03 +01:00
parent 14ba373378
commit dbbae92267
280 changed files with 13451 additions and 11207 deletions

View File

@@ -86,6 +86,7 @@
(require 'org-element)
(require 'org-ref-utils)
(require 'ox)
(require 'org-ref-ref-links) ; For multi-file support utilities
;;; Code:
(defgroup org-ref-glossary nil
@@ -102,6 +103,28 @@ This is not always fast, so we provide a way to disable it."
:group 'org-ref-glossary)
(defcustom org-ref-glossary-show-tooltips t
"If non-nil, show tooltips when hovering over glossary and acronym links.
When nil, tooltips are disabled entirely for glossary links, which can
improve responsiveness if you find the tooltips distracting or slow.
This is separate from `org-ref-activate-glossary-links' which controls
whether links are fontified and clickable."
:type 'boolean
:group 'org-ref-glossary)
(defcustom org-ref-glossary-enable-multi-file t
"Enable scanning #+INCLUDE'd files for glossary/acronym definitions.
When non-nil, glossary and acronym lookups will search in files included
via #+INCLUDE directives, enabling multi-file document support.
Uses timestamp-based caching to maintain performance. Only files that have
changed since the last scan are re-parsed."
:type 'boolean
:group 'org-ref-glossary)
(defcustom org-ref-glsentries '()
"Variable to hold locations of glsentries load files.")
@@ -114,6 +137,18 @@ This is not always fast, so we provide a way to disable it."
"Buffer-local variable for acronym entry cache.")
(defvar org-ref-glossary-file-cache (make-hash-table :test 'equal)
"Global cache of glossary entries per file.
Maps file paths to lists of glossary entry plists.
Used when `org-ref-glossary-enable-multi-file' is non-nil.")
(defvar org-ref-acronym-file-cache (make-hash-table :test 'equal)
"Global cache of acronym entries per file.
Maps file paths to lists of acronym entry plists.
Used when `org-ref-glossary-enable-multi-file' is non-nil.")
(defun or-find-closing-curly-bracket (&optional limit)
"Find closing bracket for the bracket at point and move point to it.
Go up to LIMIT or `point-max'. This is a parsing function. I
@@ -160,12 +195,12 @@ changes."
(let* (end-of-entry
data
(external (when (re-search-forward
"\\loadglsentries\\(\\[.*\\]\\){\\(?1:.*\\)}" nil t)
"\\\\loadglsentries\\(\\[.*\\]\\)?{\\(?1:.*\\)}" nil t)
(match-string 1)))
(glsentries (and external
(or (cdr (assoc external org-ref-glsentries))
(progn
(cl-pushnew (cons external (s-trim
(cl-pushnew (cons external (string-trim
(shell-command-to-string
(format "kpsewhich tex %s"
external))))
@@ -268,6 +303,131 @@ changes."
data))))
;;** Multi-file glossary support
(defun or-scan-file-for-glossary-table (file)
"Scan FILE and return list of glossary entries from #+name: glossary table.
Returns list of plists with :label, :name, :description, :file.
Returns nil if no glossary table is found in FILE."
(when (file-exists-p file)
(with-temp-buffer
(insert-file-contents file)
(org-mode)
(let ((entries '()))
(or (catch 'found
(org-element-map
(org-element-parse-buffer)
'table
(lambda (el)
(when (string= "glossary" (org-element-property :name el))
(goto-char (org-element-property :contents-begin el))
(let ((table-data (nthcdr 2 (org-babel-read-table))))
;; Convert table rows to plists
(dolist (row table-data)
(when (and (listp row) (= 3 (length row)))
(push (list :label (nth 0 row)
:name (nth 1 row)
:description (nth 2 row)
:file file)
entries)))
(throw 'found (nreverse entries)))))))
entries)))))
(defun or-scan-file-for-acronym-table (file)
"Scan FILE and return list of acronym entries from #+name: acronyms table.
Returns list of plists with :label, :abbrv, :full, :file.
Returns nil if no acronym table is found in FILE."
(when (file-exists-p file)
(with-temp-buffer
(insert-file-contents file)
(org-mode)
(let ((entries '()))
(or (catch 'found
(org-element-map
(org-element-parse-buffer)
'table
(lambda (el)
(when (string= "acronyms" (org-element-property :name el))
(goto-char (org-element-property :contents-begin el))
(let ((table-data (nthcdr 2 (org-babel-read-table))))
;; Convert table rows to plists
(dolist (row table-data)
(when (and (listp row) (= 3 (length row)))
(push (list :label (nth 0 row)
:abbrv (nth 1 row)
:full (nth 2 row)
:file file)
entries)))
(throw 'found (nreverse entries)))))))
entries)))))
(defun or-parse-glossary-entry-multi-file (label)
"Find glossary LABEL in current file or included files.
Uses timestamp-based caching to avoid re-scanning unchanged files.
Returns plist with :label, :name, :description, :file."
(when (and (boundp 'org-ref-glossary-enable-multi-file)
org-ref-glossary-enable-multi-file
(boundp 'org-ref-glossary-file-cache)
(buffer-file-name))
(cl-block or-parse-glossary-entry-multi-file
(let* ((current-file (buffer-file-name))
(included-files (org-ref-get-included-files))
(all-files (cons current-file included-files)))
;; Scan each file (with caching)
(dolist (file all-files)
;; Scan if file changed OR if not in cache yet
(when (or (org-ref-file-changed-p file)
(not (gethash file org-ref-glossary-file-cache)))
;; File changed or not cached, scan it
(let ((file-entries (or-scan-file-for-glossary-table file)))
(puthash file file-entries org-ref-glossary-file-cache)
(org-ref-mark-file-scanned file)))
;; Look for label in this file's cache
(let ((entries (gethash file org-ref-glossary-file-cache)))
(when entries
(let ((entry (cl-find label entries
:key (lambda (e) (plist-get e :label))
:test 'string=)))
(when entry
(cl-return-from or-parse-glossary-entry-multi-file entry))))))))))
(defun or-parse-acronym-entry-multi-file (label)
"Find acronym LABEL in current file or included files.
Uses timestamp-based caching to avoid re-scanning unchanged files.
Returns plist with :label, :abbrv, :full, :file."
(when (and (boundp 'org-ref-glossary-enable-multi-file)
org-ref-glossary-enable-multi-file
(boundp 'org-ref-acronym-file-cache)
(buffer-file-name))
(cl-block or-parse-acronym-entry-multi-file
(let* ((current-file (buffer-file-name))
(included-files (org-ref-get-included-files))
(all-files (cons current-file included-files)))
;; Scan each file (with caching)
(dolist (file all-files)
;; Scan if file changed OR if not in cache yet
(when (or (org-ref-file-changed-p file)
(not (gethash file org-ref-acronym-file-cache)))
;; File changed or not cached, scan it
(let ((file-entries (or-scan-file-for-acronym-table file)))
(puthash file file-entries org-ref-acronym-file-cache)
(org-ref-mark-file-scanned file)))
;; Look for label in this file's cache
(let ((entries (gethash file org-ref-acronym-file-cache)))
(when entries
(let ((entry (cl-find label entries
:key (lambda (e) (plist-get e :label))
:test 'string=)))
(when entry
(cl-return-from or-parse-acronym-entry-multi-file entry))))))))))
;;** Glossary links
@@ -276,13 +436,22 @@ changes."
set data on text with properties
Set face property, and help-echo."
(let ((data (or (or-parse-glossary-entry path)
(or-parse-acronym-entry path))))
(or-parse-acronym-entry path)
;; Try multi-file lookup if enabled and not found in current buffer
(or-parse-glossary-entry-multi-file path)
(or-parse-acronym-entry-multi-file path))))
(add-text-properties
start end
(list 'or-glossary data
'face (if data
'org-ref-glossary-face
'font-lock-warning-face)))))
'font-lock-warning-face)
;; Suppress spell-checking with nospell property.
;; For jinx users: add 'nospell to jinx-exclude-properties:
;; (setq jinx-exclude-properties '((org-mode read-only nospell)))
;; Or exclude by face using jinx-exclude-faces:
;; (add-to-list 'jinx-exclude-faces 'org-ref-glossary-face)
'nospell t))))
(defface org-ref-glossary-face
`((t (:inherit org-link :foreground "Mediumpurple3")))
@@ -290,28 +459,53 @@ Set face property, and help-echo."
(defun or-follow-glossary (entry)
"Goto beginning of the glossary ENTRY."
"Goto beginning of the glossary ENTRY.
If entry is in an included file, opens that file and navigates to the glossary table."
(org-mark-ring-push)
(goto-char (plist-get (get-text-property (point) 'or-glossary) :position)))
(let* ((data (get-text-property (point) 'or-glossary))
(file (plist-get data :file))
(label (plist-get data :label))
(position (plist-get data :position)))
(cond
;; Entry in current buffer (has position)
(position
(goto-char position))
;; Entry in external file
(file
(find-file file)
(goto-char (point-min))
(when (re-search-forward "^[ \t]*#\\+name:[ \t]+\\(glossary\\|acronyms\\)" nil t)
(when (re-search-forward (regexp-quote label) nil t)
(goto-char (line-beginning-position)))))
;; Fallback: search in current buffer
(t
(goto-char (point-min))
(when (re-search-forward "^[ \t]*#\\+name:[ \t]+\\(glossary\\|acronyms\\)" nil t)
(when (re-search-forward (regexp-quote label) nil t)
(goto-char (line-beginning-position))))))))
(defun or-glossary-tooltip (_window buffer position)
"Return tooltip for the glossary entry.
The entry is in WINDOW and OBJECT at POSITION.
Used in fontification."
(with-current-buffer buffer
(let* ((data (get-text-property position 'or-glossary))
(name (or (plist-get data :name)
(plist-get data :abbrv)))
(description (or (plist-get data :description)
(plist-get data :full))))
(format
"%s: %s"
name
(with-temp-buffer
(insert (concat description "."))
(fill-paragraph)
(buffer-string))))))
Used in fontification."
(when org-ref-glossary-show-tooltips
(with-current-buffer buffer
(let ((data (get-text-property position 'or-glossary)))
(if data
(let ((name (or (plist-get data :name)
(plist-get data :abbrv)))
(description (or (plist-get data :description)
(plist-get data :full))))
(when (and name description)
(format "%s: %s"
name
(with-temp-buffer
(insert (concat description "."))
(fill-paragraph)
(buffer-string)))))
;; No data found - return helpful message or nil
"This is not defined in this file.")))))
(defvar org-ref-glossary-gls-commands
@@ -431,13 +625,13 @@ The plist maps to \newacronym{<label>}{<abbrv>}{<full>}"
(goto-char (point-min))
(let* (abbrv
full p1
(external (when (re-search-forward "\\loadglsentries\\(\\[.*\\]\\){\\(?1:.*\\)}" nil t)
(external (when (re-search-forward "\\\\loadglsentries\\(\\[.*\\]\\)?{\\(?1:.*\\)}" nil t)
(match-string 1)))
(glsentries (and external
(or (cdr (assoc external org-ref-glsentries))
(progn
(cl-pushnew (cons external
(s-trim (shell-command-to-string
(string-trim (shell-command-to-string
(format "kpsewhich tex %s" external))))
org-ref-glsentries)
(cdr (assoc external org-ref-glsentries))))))
@@ -502,30 +696,50 @@ The plist maps to \newacronym{<label>}{<abbrv>}{<full>}"
"Activate function for an acronym link.
set data on text with properties
Set face property, and help-echo."
(let ((data (or-parse-acronym-entry path)))
(let ((data (or (or-parse-acronym-entry path)
;; Try multi-file lookup if enabled and not found in current buffer
(or-parse-acronym-entry-multi-file path))))
(add-text-properties
start end
(list 'or-glossary data
'face (if data
'org-ref-acronym-face
'font-lock-warning-face)))))
'font-lock-warning-face)
;; Suppress spell-checking with nospell property.
;; For jinx users: add 'nospell to jinx-exclude-properties:
;; (setq jinx-exclude-properties '((org-mode read-only nospell)))
;; Or exclude by face using jinx-exclude-faces:
;; (add-to-list 'jinx-exclude-faces 'org-ref-acronym-face)
'nospell t))))
(defun or-follow-acronym (label)
"Go to the definition of the acronym LABEL."
"Go to the definition of the acronym LABEL.
If entry is in an included file, opens that file and navigates to the acronym table."
(org-mark-ring-push)
(cond
;; table first
((progn (goto-char (point-min))
(and (re-search-forward "#\\+name: acronyms" nil t)
(re-search-forward label nil t)))
nil)
((progn (goto-char (point-min)) (re-search-forward (format "\\newacronym{%s}" label) nil t))
(goto-char (match-beginning 0)))
(t
(message "no entry found for %s" label))))
(let* ((data (get-text-property (point) 'or-glossary))
(file (plist-get data :file))
(entry-label (plist-get data :label)))
(cond
;; Entry in external file
(file
(find-file file)
(goto-char (point-min))
(if (and (re-search-forward "^[ \t]*#\\+name:[ \t]+acronyms" nil t)
(re-search-forward (regexp-quote entry-label) nil t))
(goto-char (line-beginning-position))
(message "Entry %s not found in %s" entry-label file)))
;; Entry in current buffer - try table first
((progn (goto-char (point-min))
(and (re-search-forward "#\\+name: acronyms" nil t)
(re-search-forward label nil t)))
nil)
;; Try LaTeX format
((progn (goto-char (point-min))
(re-search-forward (format "\\newacronym{%s}" label) nil t))
(goto-char (match-beginning 0)))
(t
(message "no entry found for %s" label)))))
;;** Tooltips on acronyms
@@ -539,17 +753,17 @@ Set face property, and help-echo."
The entry is in WINDOW and OBJECT at POSITION.
Used in fontification.
WINDOW and OBJECT are ignored."
(with-current-buffer buffer
(save-excursion
(goto-char position)
(let* ((acronym-data (get-text-property position 'or-glossary))
(abbrv (plist-get acronym-data :abbrv))
(full (plist-get acronym-data :full)))
(if acronym-data
(format
"%s: %s"
abbrv full)
(format "This is not defined in this file."))))))
(when org-ref-glossary-show-tooltips
(with-current-buffer buffer
(save-excursion
(goto-char position)
(let ((acronym-data (get-text-property position 'or-glossary)))
(if acronym-data
(let ((abbrv (plist-get acronym-data :abbrv))
(full (plist-get acronym-data :full)))
(when (and abbrv full)
(format "%s: %s" abbrv full)))
"This is not defined in this file."))))))
(defvar org-ref-acronym-types
@@ -934,5 +1148,40 @@ Meant for non-LaTeX exports."
(make-string (org-element-property :post-blank lnk) ? )))))))
;;** Jinx spell-checker integration
;; Automatically configure jinx to exclude glossary and acronym links from
;; spell-checking if jinx is loaded. This prevents jinx from marking these
;; links as typos.
;;
;; This configuration applies immediately if jinx is already loaded, or will
;; take effect when jinx is loaded in the future.
(defun org-ref-glossary--configure-jinx ()
"Add glossary and acronym faces to jinx-exclude-faces for org-mode."
(when (boundp 'jinx-exclude-faces)
;; jinx-exclude-faces is an alist: ((mode face1 face2 ...) ...)
;; We need to add our faces to the org-mode entry
(let ((org-entry (assq 'org-mode jinx-exclude-faces)))
(if org-entry
;; org-mode entry exists, add our faces to it
(progn
(unless (memq 'org-ref-glossary-face org-entry)
(setcdr org-entry (cons 'org-ref-glossary-face (cdr org-entry))))
(unless (memq 'org-ref-acronym-face org-entry)
(setcdr org-entry (cons 'org-ref-acronym-face (cdr org-entry)))))
;; org-mode entry doesn't exist, create it
(push '(org-mode org-ref-glossary-face org-ref-acronym-face)
jinx-exclude-faces)))
(message "org-ref-glossary: Configured jinx to exclude glossary/acronym faces in org-mode")))
;; Configure immediately if jinx is already loaded
(when (featurep 'jinx)
(org-ref-glossary--configure-jinx))
;; Also configure when jinx loads in the future
(with-eval-after-load 'jinx
(org-ref-glossary--configure-jinx))
(provide 'org-ref-glossary)
;;; org-ref-glossary.el ends here