From 1aaef485965ff03dddb1a70a78a02e1fac5baf46 Mon Sep 17 00:00:00 2001 From: Daniel Weschke Date: Sat, 27 Jun 2026 11:34:21 +0200 Subject: [PATCH] update packages --- lisp/company/company-capf.el | 3 +- lisp/company/company-childframe.el | 253 ++++ lisp/company/company-pkg.el | 9 +- lisp/company/company.el | 328 ++++- lisp/company/company.info | 84 +- lisp/company/icons/vscode-dark/filter.svg | 3 + lisp/company/icons/vscode-dark/search.svg | 3 + lisp/company/icons/vscode-light/filter.svg | 3 + lisp/company/icons/vscode-light/search.svg | 3 + lisp/compat/NEWS.org | 48 +- lisp/compat/compat-25.el | 260 ---- lisp/compat/compat-26.el | 10 +- lisp/compat/compat-27.el | 2 +- lisp/compat/compat-28.el | 10 +- lisp/compat/compat-29.el | 74 +- lisp/compat/compat-30.el | 26 +- lisp/compat/compat-31.el | 416 ++++++ lisp/compat/compat-macs.el | 4 +- lisp/compat/compat-pkg.el | 2 +- lisp/compat/compat.el | 14 +- lisp/compat/compat.info | 698 +++++----- lisp/cond-let/cond-let-pkg.el | 10 +- lisp/cond-let/cond-let.el | 109 +- lisp/diff-hl/diff-hl-amend.el | 4 +- lisp/diff-hl/diff-hl-autoloads.el | 2 +- lisp/diff-hl/diff-hl-dired.el | 105 +- lisp/diff-hl/diff-hl-flydiff.el | 16 +- lisp/diff-hl/diff-hl-margin.el | 8 +- lisp/diff-hl/diff-hl-pkg.el | 8 +- lisp/diff-hl/diff-hl-show-hunk-inline.el | 33 +- lisp/diff-hl/diff-hl-show-hunk.el | 13 +- lisp/diff-hl/diff-hl.el | 209 +-- lisp/emacsql/emacsql-compiler.el | 9 +- lisp/emacsql/emacsql-pg.el | 22 +- lisp/emacsql/emacsql-pkg.el | 8 +- lisp/emacsql/emacsql.el | 8 +- lisp/ess/ess-bugs-d.el | 13 +- lisp/ess/ess-pkg.el | 6 +- lisp/ess/ess-r-flymake.el | 2 +- lisp/ess/ess-r-syntax.el | 3 +- lisp/ess/ess-roxy.el | 6 +- lisp/ess/ess.el | 4 +- lisp/ess/ess.info | 2 +- lisp/ess/etc/ESSR/R/completion.R | 1 + .../flycheck-posframe-pkg.el | 6 +- lisp/flycheck-posframe/flycheck-posframe.el | 12 +- lisp/flycheck/flycheck-pkg.el | 6 +- lisp/flycheck/flycheck.el | 31 +- lisp/gnuplot/gnuplot-context.el | 6 +- lisp/gnuplot/gnuplot-pkg.el | 10 +- lisp/gnuplot/gnuplot.el | 6 +- lisp/indent-guide/indent-guide-pkg.el | 8 +- lisp/indent-guide/indent-guide.el | 104 +- lisp/ivy/ivy-autoloads.el | 2 +- lisp/ivy/ivy-pkg.el | 6 +- lisp/ivy/ivy.el | 6 +- lisp/ledger-mode/ledger-check.el | 5 +- lisp/ledger-mode/ledger-complete.el | 7 +- lisp/ledger-mode/ledger-context.el | 9 + lisp/ledger-mode/ledger-exec.el | 5 + lisp/ledger-mode/ledger-flymake.el | 22 +- lisp/ledger-mode/ledger-fonts.el | 3 +- lisp/ledger-mode/ledger-init.el | 4 +- lisp/ledger-mode/ledger-mode-pkg.el | 6 +- lisp/ledger-mode/ledger-mode.el | 25 +- lisp/ledger-mode/ledger-occur.el | 28 +- lisp/ledger-mode/ledger-reconcile.el | 6 +- lisp/ledger-mode/ledger-regex.el | 8 +- lisp/ledger-mode/ledger-report.el | 126 +- lisp/ledger-mode/ledger-schedule.el | 9 +- lisp/llama/llama-pkg.el | 12 +- lisp/llama/llama.el | 12 +- lisp/magit-section/magit-section-pkg.el | 8 +- lisp/magit-section/magit-section.el | 68 +- lisp/magit/.dir-locals.el | 5 +- lisp/magit/AUTHORS.md | 4 + lisp/magit/git-commit.el | 76 +- lisp/magit/git-hooks/applypatch-msg | 8 - lisp/magit/git-hooks/commit-msg | 8 - lisp/magit/git-hooks/fallthrough | 8 - lisp/magit/git-hooks/fsmonitor-watchman | 8 - lisp/magit/git-hooks/p4-changelist | 8 - lisp/magit/git-hooks/p4-post-changelist | 8 - lisp/magit/git-hooks/p4-pre-submit | 8 - lisp/magit/git-hooks/p4-prepare-changelist | 8 - lisp/magit/git-hooks/post-applypatch | 8 - lisp/magit/git-hooks/post-checkout | 8 - lisp/magit/git-hooks/post-commit | 13 - lisp/magit/git-hooks/post-index-change | 8 - lisp/magit/git-hooks/post-merge | 13 - lisp/magit/git-hooks/post-receive | 8 - lisp/magit/git-hooks/post-rewrite | 13 - lisp/magit/git-hooks/post-update | 8 - lisp/magit/git-hooks/pre-applypatch | 8 - lisp/magit/git-hooks/pre-auto-gc | 8 - lisp/magit/git-hooks/pre-commit | 8 - lisp/magit/git-hooks/pre-merge-commit | 8 - lisp/magit/git-hooks/pre-push | 8 - lisp/magit/git-hooks/pre-rebase | 8 - lisp/magit/git-hooks/pre-receive | 8 - lisp/magit/git-hooks/prepare-commit-msg | 8 - lisp/magit/git-hooks/proc-receive | 8 - lisp/magit/git-hooks/push-to-checkout | 8 - lisp/magit/git-hooks/reference-transaction | 8 - lisp/magit/git-hooks/sendemail-validate | 8 - lisp/magit/git-hooks/update | 8 - lisp/magit/git-rebase.el | 21 +- lisp/magit/githooks/config | 6 + lisp/magit/githooks/magit-run-git-hook | 8 + lisp/magit/magit-apply.el | 12 +- lisp/magit/magit-autorevert.el | 3 +- lisp/magit/magit-base.el | 69 +- lisp/magit/magit-bisect.el | 23 +- lisp/magit/magit-blame.el | 27 +- lisp/magit/magit-bookmark.el | 10 +- lisp/magit/magit-branch.el | 86 +- lisp/magit/magit-bundle.el | 10 +- lisp/magit/magit-clone.el | 10 +- lisp/magit/magit-commit.el | 57 +- lisp/magit/magit-core.el | 10 +- lisp/magit/magit-diff.el | 144 +- lisp/magit/magit-dired.el | 10 +- lisp/magit/magit-ediff.el | 23 +- lisp/magit/magit-extras.el | 48 +- lisp/magit/magit-fetch.el | 10 +- lisp/magit/magit-files.el | 65 +- lisp/magit/magit-git.el | 637 +++++---- lisp/magit/magit-gitignore.el | 39 +- lisp/magit/magit-log.el | 43 +- lisp/magit/magit-margin.el | 10 +- lisp/magit/magit-merge.el | 10 +- lisp/magit/magit-mode.el | 17 +- lisp/magit/magit-notes.el | 10 +- lisp/magit/magit-patch.el | 10 +- lisp/magit/magit-pkg.el | 10 +- lisp/magit/magit-process.el | 120 +- lisp/magit/magit-pull.el | 10 +- lisp/magit/magit-push.el | 10 +- lisp/magit/magit-reflog.el | 10 +- lisp/magit/magit-refs.el | 10 +- lisp/magit/magit-remote.el | 10 +- lisp/magit/magit-repos.el | 12 +- lisp/magit/magit-reset.el | 10 +- lisp/magit/magit-sequence.el | 14 +- lisp/magit/magit-sparse-checkout.el | 14 +- lisp/magit/magit-stash.el | 39 +- lisp/magit/magit-status.el | 14 +- lisp/magit/magit-submodule.el | 14 +- lisp/magit/magit-subtree.el | 10 +- lisp/magit/magit-tag.el | 41 +- lisp/magit/magit-transient.el | 18 +- lisp/magit/magit-wip.el | 59 +- lisp/magit/magit-worktree.el | 13 +- lisp/magit/magit.el | 25 +- lisp/magit/magit.info | 419 +++--- lisp/markdown-mode/markdown-mode-pkg.el | 6 +- lisp/markdown-mode/markdown-mode.el | 29 +- lisp/multiple-cursors/mc-mark-more.el | 4 +- lisp/multiple-cursors/multiple-cursors-pkg.el | 6 +- lisp/multiple-cursors/multiple-cursors.el | 4 +- lisp/my/my-autoloads.el | 4 +- lisp/my/my-org-article-autoloads.el | 107 ++ lisp/nerd-icons-dired/nerd-icons-dired-pkg.el | 6 +- lisp/nerd-icons-dired/nerd-icons-dired.el | 18 +- lisp/nerd-icons/nerd-icons-autoloads.el | 3 +- lisp/nerd-icons/nerd-icons-pkg.el | 6 +- lisp/nerd-icons/nerd-icons.el | 57 +- lisp/notmuch/notmuch-pkg.el | 4 +- lisp/ol-notmuch/ol-notmuch-pkg.el | 10 +- lisp/ol-notmuch/ol-notmuch.el | 8 +- lisp/olivetti/olivetti-pkg.el | 6 +- lisp/olivetti/olivetti.el | 43 +- lisp/org-roam/org-roam-capture.el | 2 +- lisp/org-roam/org-roam-dailies.el | 4 +- lisp/org-roam/org-roam-export.el | 2 - lisp/org-roam/org-roam-graph.el | 2 - lisp/org-roam/org-roam-node.el | 68 +- lisp/org-roam/org-roam-overlay.el | 2 - lisp/org-roam/org-roam-pkg.el | 12 +- lisp/org-roam/org-roam-protocol.el | 3 +- lisp/org-roam/org-roam.el | 6 +- lisp/org-roam/org-roam.info | 120 +- lisp/org-wc/org-wc-autoloads.el | 59 + lisp/org-wc/org-wc-pkg.el | 7 + lisp/org-wc/org-wc.el | 326 +++++ lisp/org/doc/org-manual.org | 4 +- lisp/org/doc/org-version.inc | 4 +- lisp/org/doc/org.texi | 6 +- lisp/org/doc/orgguide.texi | 4 +- lisp/org/ob-core.el | 5 +- lisp/org/ob-ditaa.el | 3 +- lisp/org/ob-tangle.el | 9 +- lisp/org/org-agenda.el | 2 +- lisp/org/org-clock.el | 16 +- lisp/org/org-colview.el | 1 + lisp/org/org-element.el | 4 +- lisp/org/org-list.el | 2 +- lisp/org/org-pkg.el | 2 +- lisp/org/org-table.el | 8 +- lisp/org/org-timer.el | 2 +- lisp/org/org-version.el | 4 +- lisp/org/org.el | 9 +- lisp/org/org.info | 1196 ++++++++--------- lisp/org/orgguide.info | 128 +- lisp/org/ox-html.el | 3 +- lisp/org/ox-koma-letter.el | 1 - lisp/org/ox-latex.el | 4 +- lisp/org/ox-odt.el | 2 +- lisp/org/ox-texinfo.el | 14 +- lisp/orgit/orgit-pkg.el | 15 +- lisp/orgit/orgit.el | 231 +++- lisp/plantuml-mode/plantuml-mode-pkg.el | 6 +- lisp/plantuml-mode/plantuml-mode.el | 461 +++++-- lisp/polymode/polymode-pkg.el | 6 +- lisp/polymode/polymode.el | 6 +- lisp/posframe/posframe-pkg.el | 6 +- lisp/posframe/posframe.el | 220 +-- lisp/simple-httpd/simple-httpd-pkg.el | 15 +- lisp/simple-httpd/simple-httpd.el | 970 +++++++------ lisp/spacemacs-theme/spacemacs-theme-pkg.el | 6 +- lisp/spacemacs-theme/spacemacs-theme.el | 18 +- lisp/tablist/tablist-pkg.el | 8 +- lisp/tablist/tablist.el | 54 +- lisp/transient/transient-pkg.el | 11 +- lisp/transient/transient.el | 242 ++-- lisp/transient/transient.info | 2 +- lisp/update-autoloads.el | 1 + lisp/vterm/CMakeLists.txt | 4 + lisp/vterm/vterm-module.c | 48 +- lisp/vterm/vterm-module.h | 1 - lisp/vterm/vterm-pkg.el | 6 +- lisp/vterm/vterm.el | 27 +- lisp/web-mode/web-mode-pkg.el | 6 +- lisp/web-mode/web-mode.el | 10 +- lisp/with-editor/with-editor-pkg.el | 12 +- lisp/with-editor/with-editor.el | 72 +- lisp/with-editor/with-editor.info | 2 +- lisp/yasnippet/doc/faq.org | 87 ++ lisp/yasnippet/doc/index.org | 47 + lisp/yasnippet/doc/snippet-development.org | 474 +++++++ lisp/yasnippet/doc/snippet-expansion.org | 284 ++++ lisp/yasnippet/doc/snippet-menu.org | 68 + lisp/yasnippet/doc/snippet-organization.org | 132 ++ lisp/yasnippet/doc/snippet-reference.org | 12 + lisp/yasnippet/yasnippet-debug.el | 354 +++++ lisp/yasnippet/yasnippet-pkg.el | 3 + 246 files changed, 7997 insertions(+), 4359 deletions(-) create mode 100644 lisp/company/company-childframe.el create mode 100644 lisp/company/icons/vscode-dark/filter.svg create mode 100644 lisp/company/icons/vscode-dark/search.svg create mode 100644 lisp/company/icons/vscode-light/filter.svg create mode 100644 lisp/company/icons/vscode-light/search.svg delete mode 100644 lisp/compat/compat-25.el create mode 100644 lisp/compat/compat-31.el delete mode 100755 lisp/magit/git-hooks/applypatch-msg delete mode 100755 lisp/magit/git-hooks/commit-msg delete mode 100755 lisp/magit/git-hooks/fallthrough delete mode 100755 lisp/magit/git-hooks/fsmonitor-watchman delete mode 100755 lisp/magit/git-hooks/p4-changelist delete mode 100755 lisp/magit/git-hooks/p4-post-changelist delete mode 100755 lisp/magit/git-hooks/p4-pre-submit delete mode 100755 lisp/magit/git-hooks/p4-prepare-changelist delete mode 100755 lisp/magit/git-hooks/post-applypatch delete mode 100755 lisp/magit/git-hooks/post-checkout delete mode 100755 lisp/magit/git-hooks/post-commit delete mode 100755 lisp/magit/git-hooks/post-index-change delete mode 100755 lisp/magit/git-hooks/post-merge delete mode 100755 lisp/magit/git-hooks/post-receive delete mode 100755 lisp/magit/git-hooks/post-rewrite delete mode 100755 lisp/magit/git-hooks/post-update delete mode 100755 lisp/magit/git-hooks/pre-applypatch delete mode 100755 lisp/magit/git-hooks/pre-auto-gc delete mode 100755 lisp/magit/git-hooks/pre-commit delete mode 100755 lisp/magit/git-hooks/pre-merge-commit delete mode 100755 lisp/magit/git-hooks/pre-push delete mode 100755 lisp/magit/git-hooks/pre-rebase delete mode 100755 lisp/magit/git-hooks/pre-receive delete mode 100755 lisp/magit/git-hooks/prepare-commit-msg delete mode 100755 lisp/magit/git-hooks/proc-receive delete mode 100755 lisp/magit/git-hooks/push-to-checkout delete mode 100755 lisp/magit/git-hooks/reference-transaction delete mode 100755 lisp/magit/git-hooks/sendemail-validate delete mode 100755 lisp/magit/git-hooks/update create mode 100644 lisp/magit/githooks/config create mode 100755 lisp/magit/githooks/magit-run-git-hook create mode 100644 lisp/my/my-org-article-autoloads.el create mode 100644 lisp/org-wc/org-wc-autoloads.el create mode 100644 lisp/org-wc/org-wc-pkg.el create mode 100644 lisp/org-wc/org-wc.el create mode 100644 lisp/yasnippet/doc/faq.org create mode 100644 lisp/yasnippet/doc/index.org create mode 100644 lisp/yasnippet/doc/snippet-development.org create mode 100644 lisp/yasnippet/doc/snippet-expansion.org create mode 100644 lisp/yasnippet/doc/snippet-menu.org create mode 100644 lisp/yasnippet/doc/snippet-organization.org create mode 100644 lisp/yasnippet/doc/snippet-reference.org create mode 100644 lisp/yasnippet/yasnippet-debug.el diff --git a/lisp/company/company-capf.el b/lisp/company/company-capf.el index 8d159608..5c7b8486 100644 --- a/lisp/company/company-capf.el +++ b/lisp/company/company-capf.el @@ -36,7 +36,8 @@ :group 'company) (defcustom company-capf-disabled-functions '(tags-completion-at-point-function - ispell-completion-at-point) + ispell-completion-at-point + company--fake-capf-complete-common) "List of completion functions which should be ignored in this backend. By default it contains the functions that duplicate the built-in backends diff --git a/lisp/company/company-childframe.el b/lisp/company/company-childframe.el new file mode 100644 index 00000000..025e71a6 --- /dev/null +++ b/lisp/company/company-childframe.el @@ -0,0 +1,253 @@ +;;; company-childframe.el --- Graphical popup frontend for Company -*- lexical-binding: t -*- + +;; Copyright (C) 2026 Free Software Foundation, Inc. + +;; 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 . + + +;;; Commentary: +;; +;; Tooltip completion menu frontend for Company that uses a child frame. +;; +;; A lot of the code here was imported from the package `company-posframe', +;; credit to Clément Pit-Claudel, Feng Shu and others. + +;;; Code: + +(require 'company) +(require 'posframe) + +(defgroup company-childframe nil + "Group group group" + :group 'company) + +(defcustom company-childframe-font nil + "The font used by company-childframe's frame. +Using current frame's font if it is nil." + :type 'face) + +(defcustom company-childframe-border-width 1 + "The width of the popup's border, in graphical frames. + +Users of HiDPI screens might like to set it to 2." + :type 'integer) + +(defvar company-childframe-buffer " *company-childframe-buffer*" + "company-childframe's buffer which used by posframe.") + +(defvar company-childframe--frame nil) + +(defvar company-childframe-show-params nil + "List of extra parameters passed to `posframe-show' in + `company-childframe-show'.") + +(defvar company-childframe-last-status nil) + +(defvar company-childframe-buffer-map + (let ((keymap (make-sparse-keymap))) + (set-keymap-parent keymap company-active-map) + (define-key keymap [wheel-down] 'company-childframe-wheel-up) + (define-key keymap [wheel-up] 'company-childframe-wheel-down) + keymap) + "Keymap for the child frame's popup/buffer.") + +(defun company-childframe-wheel-up () + "Scroll up the displayed candidates." + (interactive) + (company-childframe--wheel-scroll 3)) + +(defun company-childframe-wheel-down () + "Scroll up the displayed candidates." + (interactive) + (company-childframe--wheel-scroll -3)) + +(defun company-childframe--wheel-scroll (amount) + (let ((parent-frame (frame-parameter nil 'parent-frame)) + (parent-buffer (frame-parameter nil 'posframe-parent-buffer))) + (when (and parent-frame + parent-buffer) + (select-frame parent-frame) + (select-window (get-buffer-window (cdr parent-buffer))) + (company-select-next amount)))) + +(defvar company-childframe-poshandler + #'company-childframe-show-at-prefix + "Poshandler for the completion dialog.") + +(defun company-childframe-show-at-prefix (info) + "Poshandler showing `company-childframe' at `company-prefix'." + (let* ((parent-window (plist-get info :parent-window)) + (point (- (plist-get info :position) + (plist-get info :company-prefix-length))) + (after-string-width + (with-current-buffer (window-buffer parent-window) + (thread-last + (and (= point (point-max)) + (overlays-in point point)) + (mapcar (lambda (o) (company--string-pixel-width + (overlay-get o 'after-string)))) + (cl-reduce #'+)))) + (posn (posn-at-point point parent-window)) + ;; TODO: Strictly speaking, if company-childframe-font is not nil, that + ;; should be used to find the default width... + (expected-margin-width (* (plist-get info :company-margin) (default-font-width))) + (xy (posn-x-y posn))) + (setcar xy (- (car xy) expected-margin-width + (if (display-graphic-p) + company-childframe-border-width + 0) + ;; Might bite us if the posn-at-point behavior changes + ;; someday, but the odds seem low. + after-string-width)) + (posframe-poshandler-point-bottom-left-corner (plist-put info :position posn)))) + +(defun company-childframe-show () + "Show company-childframe candidate menu." + (defvar x-wait-for-event-timeout) + (defvar x-fast-protocol-requests) + (let* ((x-wait-for-event-timeout (and (>= emacs-major-version 31) + ;; debbugs#80662 + (bound-and-true-p + x-wait-for-event-timeout))) + (before-make-frame-hook) + (after-make-frame-functions) + (x-fast-protocol-requests t) + (height (min company-tooltip-limit + (if company-search-mode + (1+ company-candidates-length) + company-candidates-length))) + (company-lines (company--create-lines company-selection height)) + (margin (car company-lines)) + (lines (cdr company-lines)) + (width (length (car lines))) + (contents (mapconcat #'identity lines "\n")) + (buffer (get-buffer-create company-childframe-buffer))) + (when (and (eq (frame-live-p company-childframe--frame) 'x) + (not (eq (car (frame-list)) company-childframe--frame))) + ;; Make sure it's the first in the list, to avoid premature sync when some + ;; other frame is redisplayed first. Again, non-atomic updated on X11. + ;; https://debbugs.gnu.org/80662#185 + (delete-frame company-childframe--frame)) + (apply #'posframe-show buffer + :string contents + :height height + :width (if (or (<= company-candidates-length + height) + (not (display-graphic-p))) + width + (1- width)) + :font company-childframe-font + :background-color (face-attribute 'company-tooltip :background) + :lines-truncate t + :override-parameters '((inhibit-double-buffering . t)) + :border-width (and (display-graphic-p) company-childframe-border-width) + ;; :border-color "light salmon" + ;; :border-color "light steel blue" + ;; We'll probably want a separate face for it. + :border-color (face-attribute 'company-tooltip-scrollbar-track :background) + :poshandler company-childframe-poshandler + :poshandler-extra-info + (list :company-margin margin + :company-prefix-length (length (car (company--boundaries)))) + company-childframe-show-params) + (with-current-buffer buffer + (use-local-map company-childframe-buffer-map) + (setq company-childframe--frame posframe--frame) + ;; FIXME: Does not honor remappings by minor modes in the parent buffer, + ;; e.g. the special behavior of C-d with parent-mode, etc. + (add-hook 'pre-command-hook + #'company-childframe--pre-command + nil t)))) + +(defun company-childframe-hide () + "Hide company-childframe candidate menu." + (when (and (frame-live-p company-childframe--frame) + (frame-visible-p company-childframe--frame)) + ;; PGTK/NS/W32 protocols can update the display atomically. + (when (and (eq window-system 'x) + ;; https://debbugs.gnu.org/80961 + (< 32 emacs-major-version)) + ;; Seems to help avoid the final flicker - probably by keeping the parent's + ;; display matrix up to date (so it can repaint on Expose immediately). + (redisplay)) + (make-frame-invisible company-childframe--frame))) + +;;;###autoload +(defun company-childframe-frontend (command) + "`company-mode' frontend using childframe. +For COMMAND refer to `company-frontends'." + (setq company-childframe-last-status + (list (selected-window) + (current-buffer))) + (cl-case command + (pre-command + (when (not (posframe-workable-p)) + (user-error "Child frames not supported"))) + (show (setq company--tooltip-current-width 0)) + (hide + (company-childframe-hide)) + (post-command + (when (equal (window-buffer (selected-window)) + (current-buffer)) + (company-childframe-show))) + (select-mouse + (company-childframe--select-mouse)))) + +(defun company-childframe--select-mouse () + (let ((event-col-row (company--event-col-row company-mouse-event)) + (event-window (posn-window (event-start company-mouse-event)))) + (cond ((and event-window + (equal (buffer-name (window-buffer event-window)) + company-childframe-buffer)) + (company-set-selection (+ (cdr event-col-row) + company-tooltip-offset + (if (and (eq company-tooltip-offset-display 'lines) + (not (zerop company-tooltip-offset))) + -1 0))) + t)))) + +(defun company-childframe--pre-command () + (let ((parent-frame (frame-parameter nil 'parent-frame)) + (parent-buffer (cdr (frame-parameter nil 'posframe-parent-buffer)))) + (when (and + (not (memq this-command + '(company-childframe-wheel-up + company-childframe-wheel-down))) + parent-frame parent-buffer) + (select-frame parent-frame) + (select-window (get-buffer-window parent-buffer))))) + +;;;###autoload +(defun company-childframe-unless-just-one-frontend (command) + "`company-childframe-frontend', but not shown for single candidates." + (if (company--show-inline-p) + (and (member command '(post-command hide)) + (company-childframe-hide)) + (and (memq command '(post-command show unhide hide select-mouse)) + (company-childframe-frontend command)))) + +(defun company-childframe-window-change () + "Hide posframe on window change." + (when (posframe-workable-p) + (unless (or (equal (buffer-name) company-childframe-buffer) + (equal company-childframe-last-status + (list (selected-window) + (current-buffer)))) + (company-childframe-hide)))) + +(add-hook 'window-configuration-change-hook + #'company-childframe-window-change) + +(provide 'company-childframe) +;;; company-childframe.el ends here diff --git a/lisp/company/company-pkg.el b/lisp/company/company-pkg.el index 0432754d..fad7c178 100644 --- a/lisp/company/company-pkg.el +++ b/lisp/company/company-pkg.el @@ -1,9 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "company" "20260331.245" +(define-package "company" "20260627.324" "Modular text completion framework." - '((emacs "26.1")) + '((emacs "26.1") + (posframe "1.5.1")) :url "http://company-mode.github.io/" - :commit "59626254bbac187fc2b8d7a189aca90976ab36a8" - :revdesc "59626254bbac" + :commit "a703d9f9ce57d37d6b0c073b54348e8b620cebc1" + :revdesc "a703d9f9ce57" :keywords '("abbrev" "convenience" "matching") :maintainers '(("Dmitry Gutov" . "dmitry@gutov.dev"))) diff --git a/lisp/company/company.el b/lisp/company/company.el index 92a1144d..0704f5df 100644 --- a/lisp/company/company.el +++ b/lisp/company/company.el @@ -1,14 +1,14 @@ ;;; company.el --- Modular text completion framework -*- lexical-binding: t -*- -;; Copyright (C) 2009-2025 Free Software Foundation, Inc. +;; Copyright (C) 2009-2026 Free Software Foundation, Inc. ;; Author: Nikolaj Schumacher ;; Maintainer: Dmitry Gutov ;; URL: http://company-mode.github.io/ -;; Package-Version: 20260331.245 -;; Package-Revision: 59626254bbac +;; Package-Version: 20260627.324 +;; Package-Revision: a703d9f9ce57 ;; Keywords: abbrev, convenience, matching -;; Package-Requires: ((emacs "26.1")) +;; Package-Requires: ((emacs "26.1") (posframe "1.5.1")) ;; This file is part of GNU Emacs. @@ -99,11 +99,11 @@ "Face used for the deprecated items.") (defface company-tooltip-search - '((default :inherit highlight)) + '((default :inherit isearch)) "Face used for the search string in the tooltip.") (defface company-tooltip-search-selection - '((default :inherit highlight)) + '((default :inherit isearch)) "Face used for the search string inside the selection in the tooltip.") (defface company-tooltip-mouse @@ -175,7 +175,7 @@ "Face used for the common part of the completion preview.") (defface company-preview-search - '((default :inherit company-tooltip-common-selection)) + '((default :inherit isearch)) "Face used for the search string in the completion preview.") (defface company-echo nil @@ -192,21 +192,22 @@ (defun company-frontends-set (variable value) ;; Uniquify. - (let ((value (delete-dups (copy-sequence value)))) - (and (or (and (memq 'company-pseudo-tooltip-unless-just-one-frontend value) - (memq 'company-pseudo-tooltip-frontend value)) - (and (memq 'company-pseudo-tooltip-unless-just-one-frontend-with-delay value) - (memq 'company-pseudo-tooltip-frontend value)) - (and (memq 'company-pseudo-tooltip-unless-just-one-frontend-with-delay value) - (memq 'company-pseudo-tooltip-unless-just-one-frontend value))) - (user-error "Pseudo tooltip frontend cannot be used more than once")) - (and (or (and (memq 'company-preview-if-just-one-frontend value) - (memq 'company-preview-frontend value)) - (and (memq 'company-preview-if-just-one-frontend value) - (memq 'company-preview-common-frontend value)) - (and (memq 'company-preview-frontend value) - (memq 'company-preview-common-frontend value)) - ) + (let ((value (delete-dups (copy-sequence value))) + (tooltip-frontends + '(company-pseudo-tooltip-frontend + company-pseudo-tooltip-unless-just-one-frontend + company-pseudo-tooltip-unless-just-one-frontend-with-delay + company-childframe-frontend + company-childframe-unless-just-one-frontend)) + (preview-frontends + '(company-preview-if-just-one-frontend + company-preview-common-frontend + company-preview-frontend))) + (and (> (cl-count-if (lambda (el) (member el tooltip-frontends)) value) + 1) + (user-error "Any tooltip frontend can be used only once")) + (and (> (cl-count-if (lambda (el) (member el preview-frontends)) value) + 1) (user-error "Preview frontend cannot be used twice")) (and (memq 'company-echo value) (memq 'company-echo-metadata-frontend value) @@ -217,7 +218,11 @@ (setq value (append (delq f value) (list f))))) (set variable value))) -(defcustom company-frontends '(company-pseudo-tooltip-unless-just-one-frontend +(defcustom company-frontends `(,@(list + (if (or (memq window-system '(ns mac w32 pgtk)) + (< 30 emacs-major-version)) + 'company-childframe-unless-just-one-frontend + 'company-pseudo-tooltip-unless-just-one-frontend)) company-preview-if-just-one-frontend company-echo-metadata-frontend) "The list of active frontends (visualizations). @@ -243,20 +248,25 @@ for technical reasons. The visualized data is stored in `company-prefix', `company-candidates', `company-common', `company-selection', `company-point' and `company-search-string'." + :package-version '(company . "1.1.0") :set 'company-frontends-set :type '(repeat (choice (const :tag "echo" company-echo-frontend) (const :tag "echo, strip common" company-echo-strip-common-frontend) - (const :tag "show echo meta-data in echo" + (const :tag "show completion's meta-data in echo" company-echo-metadata-frontend) - (const :tag "pseudo tooltip" + (const :tag "graphical tooltip" + company-childframe-frontend) + (const :tag "graphical tooltip, multiple completions only" + company-childframe-unless-just-one-frontend) + (const :tag "overlays based tooltip" company-pseudo-tooltip-frontend) - (const :tag "pseudo tooltip, multiple only" + (const :tag "overlays based tooltip, multiple completions only" company-pseudo-tooltip-unless-just-one-frontend) - (const :tag "pseudo tooltip, multiple only, delayed" + (const :tag "overlays based tooltip, multiple completions only, delayed" company-pseudo-tooltip-unless-just-one-frontend-with-delay) (const :tag "preview" company-preview-frontend) - (const :tag "preview, unique only" + (const :tag "preview, unique completion only" company-preview-if-just-one-frontend) (const :tag "preview, common" company-preview-common-frontend) @@ -272,22 +282,22 @@ When that many lines are not available between point and the bottom of the window, display the tooltip above point." :type 'integer) -(defcustom company-tooltip-minimum-width 0 +(defcustom company-tooltip-minimum-width 15 "The minimum width of the tooltip's inner area. This doesn't include the margins and the scroll bar." :type 'integer - :package-version '(company . "0.8.0")) + :package-version '(company . "1.1.0")) -(defcustom company-tooltip-maximum-width most-positive-fixnum +(defcustom company-tooltip-maximum-width 100 "The maximum width of the tooltip's inner area. This doesn't include the margins and the scroll bar." :type 'integer - :package-version '(company . "0.9.5")) + :package-version '(company . "1.1.0")) -(defcustom company-tooltip-width-grow-only nil +(defcustom company-tooltip-width-grow-only 50 "When non-nil, the tooltip width is not allowed to decrease." :type 'boolean - :package-version '(company . "0.10.0")) + :package-version '(company . "1.1.0")) (defcustom company-tooltip-margin 1 "Width of margin columns to show around the toolip." @@ -882,15 +892,17 @@ asynchronous call into synchronous.") ;;; mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defvar company-mode-map (make-sparse-keymap) +(defvar company-mode-map + (let ((keymap (make-sparse-keymap))) + (define-key keymap [remap indent-for-tab-command] 'company-indent-for-tab-command) + (define-key keymap [remap c-indent-line-or-region] 'company-indent-for-tab-command) + keymap) "Keymap used by `company-mode'.") (defvar company-active-map (let ((keymap (make-sparse-keymap))) (define-key keymap "\e\e\e" 'company-abort) (define-key keymap "\C-g" 'company-abort) - (define-key keymap (kbd "M-n") 'company--select-next-and-warn) - (define-key keymap (kbd "M-p") 'company--select-previous-and-warn) (define-key keymap (kbd "C-n") 'company-select-next-or-abort) (define-key keymap (kbd "C-p") 'company-select-previous-or-abort) (define-key keymap (kbd "") 'company-select-next-or-abort) @@ -908,9 +920,12 @@ asynchronous call into synchronous.") (define-key keymap [tab] 'company-complete-common-or-cycle) (define-key keymap (kbd "TAB") 'company-complete-common-or-cycle) (define-key keymap [backtab] 'company-cycle-backward) - (define-key keymap (kbd "") 'company-show-doc-buffer) - (define-key keymap (kbd "C-h") 'company-show-doc-buffer) - (define-key keymap "\C-w" 'company-show-location) + (define-key keymap (kbd "C-M-i") 'company-complete-common) + (define-key keymap (kbd "") 'company--show-doc-buffer-and-warn) + (define-key keymap (kbd "C-h") 'company--show-doc-buffer-and-warn) + (define-key keymap (kbd "M-h") 'company-show-doc-buffer) + (define-key keymap (kbd "C-w") 'company--show-location-and-warn) + (define-key keymap (kbd "M-g") 'company-show-location) (define-key keymap "\C-s" 'company-search-candidates) (define-key keymap "\C-\M-s" 'company-filter-candidates) (company-keymap--bind-quick-access keymap) @@ -919,22 +934,12 @@ asynchronous call into synchronous.") (defvar company--disabled-backends nil) -(defun company--select-next-and-warn (&optional arg) - (interactive "p") - (company--warn-changed-binding) - (company-select-next arg)) - -(defun company--select-previous-and-warn (&optional arg) - (interactive "p") - (company--warn-changed-binding) - (company-select-previous arg)) - (defun company--warn-changed-binding () (interactive) (run-with-idle-timer 0.01 nil (lambda () - (message "Warning: default bindings are being changed to C-n and C-p")))) + (message "Warning: default bindings are being changed to M-h and M-g")))) (defun company-init-backend (backend) (and (symbolp backend) @@ -974,12 +979,16 @@ asynchronous call into synchronous.") (defvar company-lighter '(" " (company-candidates (:eval - (if (consp company-backend) - (when company-selection - (company--group-lighter (nth company-selection - company-candidates) - company-lighter-base)) - (symbol-name company-backend))) + (cond + ((consp company-backend) + (when company-selection + (company--group-lighter (nth company-selection + company-candidates) + company-lighter-base))) + ((symbolp company-backend) + (symbol-name company-backend)) + ((functionp company-backend) + "company-"))) company-lighter-base)) "Mode line lighter for Company. @@ -1045,8 +1054,20 @@ means that `company-mode' is always turned on except in `message-mode' buffers." (const :tag "Except" not) (repeat :inline t (symbol :tag "mode"))))) +(defcustom company-global-minibuffer t + "Non-nil to enable `company-mode' in the minibuffer. +The value can be t (meaning only enable if the minibuffer has a local +`completion-at-point-functions' value) or a custom predicate function. + +The overlay based popup is not supported, completion won't start in +minibuffer if it's in configured frontends: use `company-childframe'." + :type 'boolean) + ;;;###autoload -(define-globalized-minor-mode global-company-mode company-mode company-mode-on) +(define-globalized-minor-mode global-company-mode company-mode company-mode-on + (if global-company-mode + (add-hook 'minibuffer-setup-hook #'company--minibuffer-on 100) + (remove-hook 'minibuffer-setup-hook #'company--minibuffer-on))) (defun company-mode-on () (when (and (not (or noninteractive (eq (aref (buffer-name) 0) ?\s))) @@ -1057,6 +1078,14 @@ means that `company-mode' is always turned on except in `message-mode' buffers." (t (memq major-mode company-global-modes)))) (company-mode 1))) +(defun company--minibuffer-on () + (when (and company-global-minibuffer + (not (try-completion "company-pseudo-tooltip" company-frontends)) + (if (eq company-global-minibuffer t) + (local-variable-p 'completion-at-point-functions) + (funcall company-global-minibuffer))) + (company-mode 1))) + (defsubst company-assert-enabled () (unless company-mode (company-uninstall-map) @@ -1955,6 +1984,7 @@ end of the match." (event . "symbol-event.svg") (field . "symbol-field.svg") (file . "symbol-file.svg") + (filter . "filter.svg") (folder . "folder.svg") (interface . "symbol-interface.svg") (keyword . "symbol-keyword.svg") @@ -1965,6 +1995,7 @@ end of the match." (operator . "symbol-operator.svg") (property . "symbol-property.svg") (reference . "references.svg") + (search . "search.svg") (snippet . "symbol-snippet.svg") (string . "symbol-string.svg") (struct . "symbol-structure.svg") @@ -2068,6 +2099,7 @@ end of the match." (enum "e" font-lock-builtin-face) (field "f" font-lock-variable-name-face) (file "f" font-lock-string-face) + (filter "!" minibuffer-prompt) (folder "d" font-lock-doc-face) (interface "i" font-lock-type-face) (keyword "k" font-lock-keyword-face) @@ -2078,6 +2110,7 @@ end of the match." (operator "o" font-lock-comment-delimiter-face) (property "p" font-lock-variable-name-face) (reference "r" font-lock-doc-face) + (search "q" minibuffer-prompt) (snippet "S" font-lock-string-face) (string "s" font-lock-string-face) (struct "%" font-lock-variable-name-face) @@ -2235,7 +2268,7 @@ Searches for each in the currently visible part of the current buffer and prioritizes the matches according to `company-occurrence-weight-function'. The rest of the list is appended unchanged. Keywords and function definition names are ignored." - (let* ((w-start (window-start)) + (let* ((w-start (max (window-start) (field-beginning))) (w-end (window-end)) (start-point (point)) occurs @@ -2659,7 +2692,12 @@ For more details see `company-insertion-on-trigger' and (cancel-timer company-timer) (setq company-timer nil)) (company-echo-cancel t) - (company-uninstall-map)) + (unless (memq this-original-command + '(describe-key + describe-key-briefly + describe-map + describe-bindings)) + (company-uninstall-map))) (defun company-post-command () (when (and company-candidates @@ -2727,7 +2765,7 @@ For more details see `company-insertion-on-trigger' and ;;; search ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defcustom company-search-regexp-function #'regexp-quote +(defcustom company-search-regexp-function #'company-search-words-in-any-order-regexp "Function to construct the search regexp from input. It's called with one argument, the current search input. It must return either a regexp without groups, or one where groups don't intersect and @@ -2738,10 +2776,13 @@ each one wraps a part of the input string." (const :tag "Words separated with spaces, in any order" company-search-words-in-any-order-regexp) (const :tag "All characters in given order, with anything in between" - company-search-flex-regexp))) + company-search-flex-regexp) + (const :tag "Space separated words in any order, all chars inside a word with anything in between" + company-search-flex-words-in-any-order-regexp))) (defvar-local company-search-string "") +;; FIXME: Delete later. (defvar company-search-lighter '(" " (company-search-filtering "Filter" "Search") ": \"" @@ -2771,12 +2812,23 @@ each one wraps a part of the input string." (defun company-search-flex-regexp (input) (if (zerop (length input)) "" - (concat (regexp-quote (string (aref input 0))) + (concat (format "\\(%s\\)" (regexp-quote (string (aref input 0)))) (mapconcat (lambda (c) (concat "[^" (string c) "]*" - (regexp-quote (string c)))) + (format "\\(%s\\)" + (regexp-quote (string c))))) (substring input 1) "")))) +(defun company-search-flex-words-in-any-order-regexp (input) + (let* ((words (mapcar (lambda (word) (format "\\(?:%s\\)" + (company-search-flex-regexp word))) + (split-string input " +" t))) + (permutations (company--permutations words))) + (mapconcat (lambda (words) + (mapconcat #'identity words ".*")) + permutations + "\\|"))) + (defun company--permutations (lst) (if (not lst) '(nil) @@ -2827,9 +2879,16 @@ each one wraps a part of the input string." (let* ((selection (or company-selection 0)) (pos (company--search new (nthcdr selection company-candidates)))) (if (null pos) + (let ((pos (company--search new (nthcdr (- company-candidates-length + selection) + (reverse company-candidates))))) + (if (null pos) + (ding) + (setq company-search-string new) + (company-set-selection (- selection pos 1) t))) (ding) - (setq company-search-string new) - (company-set-selection (+ selection pos) t)))) + (setq company-search-string new) + (company-set-selection (+ selection pos) t)))) (defun company--search-assert-input () (company--search-assert-enabled) @@ -2842,7 +2901,7 @@ each one wraps a part of the input string." (company--search-assert-input) (let* ((selection (or company-selection 0)) (pos (company--search company-search-string - (cdr (nthcdr selection company-candidates))))) + (cdr (nthcdr selection company-candidates))))) (if (null pos) (ding) (company-set-selection (+ selection pos 1) t)))) @@ -2941,7 +3000,7 @@ each one wraps a part of the input string." "Search mode for completion candidates. Don't start this directly, use `company-search-candidates' or `company-filter-candidates'." - :lighter company-search-lighter + :lighter nil (if company-search-mode (if (company-manual-begin) (progn @@ -2989,8 +3048,8 @@ uses the search string to filter the completion candidates." This works the same way as `company-search-candidates' immediately followed by `company-search-toggle-filtering'." (interactive) - (company-search-mode 1) - (setq company-search-filtering t)) + (setq company-search-filtering t) + (company-search-mode 1)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -3527,6 +3586,25 @@ from the candidates list.") (let ((win (display-buffer doc-buffer t))) (set-window-start win (if start start (point-min))))))) +(defun company--fake-capf-complete-common (&rest _) + (company-complete-common) + (list (point) (point) nil)) + +(defun company-indent-for-tab-command (&optional arg) + "Like `indent-for-tab-command' which see but calls `company-complete-common' +instead of `completion-at-point' as the fallback. That only happens when +`tab-always-indent' is `complete', and only when reindentation was a no-op." + (interactive) + (unwind-protect + (progn + (add-hook 'completion-at-point-functions + #'company--fake-capf-complete-common + nil t) + (funcall-interactively #'indent-for-tab-command arg)) + (remove-hook 'completion-at-point-functions + #'company--fake-capf-complete-common + t))) + (defun company-show-doc-buffer (&optional toggle-auto-update) "Show the documentation buffer for the selection. With a prefix argument TOGGLE-AUTO-UPDATE, toggle the value of @@ -3539,6 +3617,12 @@ automatically show the documentation buffer for each selection." (company--show-doc-buffer))) (put 'company-show-doc-buffer 'company-keep t) +(defun company--show-doc-buffer-and-warn (&optional toggle-auto-update) + (interactive "P") + (company--warn-changed-binding) + (company-show-doc-buffer toggle-auto-update)) +(put 'company--show-doc-buffer-and-warn 'company-keep t) + (defun company-show-location () "Temporarily display a buffer showing the selected candidate in context." (interactive) @@ -3560,6 +3644,12 @@ automatically show the documentation buffer for each selection." (set-window-start nil (point))))))) (put 'company-show-location 'company-keep t) +(defun company--show-location-and-warn () + (interactive) + (company--warn-changed-binding) + (company-show-location)) +(put 'company--show-location-and-warn 'company-keep t) + ;;; package functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar-local company-callback nil) @@ -4055,6 +4145,8 @@ but adjust the expected values appropriately." previous remainder scrollbar-bounds) + (when company-search-mode + (cl-decf limit)) ;; Maybe clear old offset. (when (< len (+ company-tooltip-offset limit)) @@ -4131,7 +4223,13 @@ but adjust the expected values appropriately." width)))) (when company-tooltip-width-grow-only - (setq width (max company--tooltip-current-width width)) + (setq width (max + (min + (if (numberp company-tooltip-width-grow-only) + company-tooltip-width-grow-only + most-positive-fixnum) + company--tooltip-current-width) + width)) (setq company--tooltip-current-width width)) (let ((items (nreverse items)) @@ -4169,6 +4267,10 @@ but adjust the expected values appropriately." (when remainder (push (company--scrollpos-line remainder width left-margin-size) new)) + (when company-search-mode + (push (company--search-line width right-margin) + new)) + (cons left-margin-size (nreverse new))))) @@ -4221,6 +4323,29 @@ Value of SELECTED determines the added face." 'company-tooltip-quick-access-selection 'company-tooltip-quick-access))) +(defun company--search-line (width right-margin) + (let* ((company-backend (lambda (command &rest _) + (and (eq command 'kind) + (if company-search-filtering + 'filter + 'search)))) + (left (if company-format-margin-function + (funcall company-format-margin-function "" nil) + (concat + (company-space-string company-tooltip-margin) + (format "%s: " (company-call-backend 'kind))))) + (line (concat + company-search-string)) + (width (+ (company--string-width left) width (length right-margin)))) + (unless (display-graphic-p) (cl-incf width)) + (setq line (company-safe-substring (concat left + (propertize + company-search-string + 'face 'underline)) + 0 width)) + (add-face-text-property 0 width 'company-tooltip nil line) + line)) + ;; show (defvar-local company-pseudo-tooltip-overlay nil) @@ -4244,7 +4369,10 @@ Value of SELECTED determines the added face." Returns a negative number if the tooltip should be displayed above point." (let* ((lines (company--row)) (below (- (company--window-height) 1 lines))) - (if (and (< below (min company-tooltip-minimum company-candidates-length)) + (if (and (< below (min company-tooltip-minimum + (if company-search-mode + (1+ company-candidates-length) + company-candidates-length))) (> lines below)) (- (max 3 (min company-tooltip-limit lines))) (max 3 (min company-tooltip-limit below))))) @@ -4468,6 +4596,9 @@ Delay is determined by `company-tooltip-idle-delay'." (defvar-local company-preview-overlay nil) (defun company-preview-show-at-point (pos completion &optional boundaries) + (when (minibufferp) + (company-echo-hide)) + (company-preview-hide) (let* ((boundaries (or boundaries (company--boundaries completion))) @@ -4528,6 +4659,8 @@ Delay is determined by `company-tooltip-idle-delay'." (let ((ov company-preview-overlay)) (overlay-put ov (if (> end beg) 'display 'after-string) completion) + ;; Show before minibuffer-message-overlay if there. + (overlay-put ov 'priority 1101) (overlay-put ov 'window (selected-window)))))) (defun company-preview-hide () @@ -4607,9 +4740,31 @@ Delay is determined by `company-tooltip-idle-delay'." (defun company-echo-show (&optional getter) (let ((last-msg company-echo-last-msg) (message-log-max nil) + (preview-o company-preview-overlay) (message-truncate-lines company-echo-truncate-lines)) (when getter (setq company-echo-last-msg (funcall getter))) + (when-let* ((mini-window (and company-echo-truncate-lines + (active-minibuffer-window))) + (posn (posn-at-point + (with-current-buffer (window-buffer mini-window) + (max (point-min) + (1- (point-max)))) + mini-window)) + (max-len (max 0 + (- (window-width mini-window) + (car + (posn-col-row posn)) + (if preview-o + (company--string-width + (or + (overlay-get preview-o 'display) + (overlay-get preview-o 'after-string) + "")) + 0) + 5)))) + (when (> (length company-echo-last-msg) max-len) + (setq company-echo-last-msg (substring company-echo-last-msg 0 max-len)))) ;; Avoid modifying the echo area if we don't have anything to say, and we ;; didn't put the previous message there (thus there's nothing to clear), ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=62816#20 @@ -4695,8 +4850,7 @@ Delay is determined by `company-tooltip-idle-delay'." (defun company-echo-hide () (unless (string-empty-p company-echo-last-msg) - (setq company-echo-last-msg "") - (company-echo-show))) + (company-echo-show #'ignore))) (defun company-echo-frontend (command) "`company-mode' frontend showing the candidates in the echo area." @@ -4713,9 +4867,35 @@ Delay is determined by `company-tooltip-idle-delay'." (defun company-echo-metadata-frontend (command) "`company-mode' frontend showing the documentation in the echo area." (pcase command + (`pre-command + (when (and (> company-echo-delay 0) + (or (not (minibufferp)) + (memq this-command + '(self-insert-command + delete-backward-char + company-select-next + company-select-previous + company-select-next-or-abort + company-select-previous-or-abort + company-next-page + company-previous-page + company-search-repeat-forward + company-search-repeat-backward + company-complete-common-or-cycle)))) + (company-echo-show))) (`post-command (company-echo-show-soon 'company-fetch-metadata)) (`unhide (company-echo-show)) (`hide (company-echo-hide)))) +(eldoc-add-command-completions "company-") + +(defun company--eldoc-no-inteference-p () + (or (not company-candidates) + (member company-echo-last-msg '(nil "")))) + +(advice-add #'eldoc-display-message-no-interference-p + :after-while + #'company--eldoc-no-inteference-p) + (provide 'company) ;;; company.el ends here diff --git a/lisp/company/company.info b/lisp/company/company.info index 5e8cadde..abc60e95 100644 --- a/lisp/company/company.info +++ b/lisp/company/company.info @@ -9,7 +9,8 @@ Copyright © 2021-2024 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software - Foundation. + Foundation, with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. INFO-DIR-SECTION Emacs misc features START-INFO-DIR-ENTRY * Company: (company). A modular text completion framework. @@ -35,7 +36,8 @@ Copyright © 2021-2024 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software - Foundation. + Foundation, with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. * Menu: @@ -1773,45 +1775,45 @@ Concept Index  Tag Table: -Node: Top573 -Node: Overview1999 -Node: Terminology2407 -Node: Structure3710 -Node: Getting Started5200 -Node: Installation5478 -Node: Initial Setup5861 -Node: Usage Basics6709 -Node: Commands7683 -Ref: Commands-Footnote-110079 -Node: Customization10246 -Node: Customization Interface10718 -Node: Configuration File11251 -Ref: company-selection-wrap-around13565 -Node: Frontends16054 -Node: Tooltip Frontends17023 -Ref: Tooltip Frontends-Footnote-127719 -Node: Preview Frontends27956 -Ref: Preview Frontends-Footnote-129214 -Node: Echo Frontends29341 -Node: Candidates Search30870 -Node: Filter Candidates32202 -Node: Quick Access a Candidate32982 -Node: Backends34600 -Node: Backends Usage Basics35630 -Ref: Backends Usage Basics-Footnote-137062 -Node: Grouped Backends37146 -Node: Package Backends38657 -Node: Code Completion39584 -Node: Text Completion45101 -Node: File Name Completion49525 -Node: Template Expansion51071 -Node: Candidates Post-Processing51790 -Node: Troubleshooting54367 -Node: Index56038 -Node: Key Index56201 -Node: Variable Index57700 -Node: Function Index62553 -Node: Concept Index67253 +Node: Top653 +Node: Overview2159 +Node: Terminology2567 +Node: Structure3870 +Node: Getting Started5360 +Node: Installation5638 +Node: Initial Setup6021 +Node: Usage Basics6869 +Node: Commands7843 +Ref: Commands-Footnote-110239 +Node: Customization10406 +Node: Customization Interface10878 +Node: Configuration File11411 +Ref: company-selection-wrap-around13725 +Node: Frontends16214 +Node: Tooltip Frontends17183 +Ref: Tooltip Frontends-Footnote-127879 +Node: Preview Frontends28116 +Ref: Preview Frontends-Footnote-129374 +Node: Echo Frontends29501 +Node: Candidates Search31030 +Node: Filter Candidates32362 +Node: Quick Access a Candidate33142 +Node: Backends34760 +Node: Backends Usage Basics35790 +Ref: Backends Usage Basics-Footnote-137222 +Node: Grouped Backends37306 +Node: Package Backends38817 +Node: Code Completion39744 +Node: Text Completion45261 +Node: File Name Completion49685 +Node: Template Expansion51231 +Node: Candidates Post-Processing51950 +Node: Troubleshooting54527 +Node: Index56198 +Node: Key Index56361 +Node: Variable Index57860 +Node: Function Index62713 +Node: Concept Index67413  End Tag Table diff --git a/lisp/company/icons/vscode-dark/filter.svg b/lisp/company/icons/vscode-dark/filter.svg new file mode 100644 index 00000000..5af96469 --- /dev/null +++ b/lisp/company/icons/vscode-dark/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/lisp/company/icons/vscode-dark/search.svg b/lisp/company/icons/vscode-dark/search.svg new file mode 100644 index 00000000..eba698fe --- /dev/null +++ b/lisp/company/icons/vscode-dark/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/lisp/company/icons/vscode-light/filter.svg b/lisp/company/icons/vscode-light/filter.svg new file mode 100644 index 00000000..30a559dc --- /dev/null +++ b/lisp/company/icons/vscode-light/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/lisp/company/icons/vscode-light/search.svg b/lisp/company/icons/vscode-light/search.svg new file mode 100644 index 00000000..578a48af --- /dev/null +++ b/lisp/company/icons/vscode-light/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/lisp/compat/NEWS.org b/lisp/compat/NEWS.org index 0234be86..992aca29 100644 --- a/lisp/compat/NEWS.org +++ b/lisp/compat/NEWS.org @@ -1,7 +1,38 @@ -#+link: compat-srht https://todo.sr.ht/~pkal/compat/ #+link: compat-gh https://github.com/emacs-compat/compat/issues/ #+options: toc:nil num:nil author:nil +* Release of "Compat" Version 31.0.0.1 + +- compat-31: Improve =with-work-buffer= implementation. + +(Release <2026-05-03 Sun>) + +* Release of "Compat" Version 31.0.0.0 + +- compat-28: New pcase pattern =cl-type=. +- compat-29: Add =string-glyph-compose= and =string-glyph-decompose=. +- compat-31: New macros =static-when= and =static-unless=. +- compat-31: New functions =oddp= and =evenp=. +- compat-31: New functions =minusp= and =plusp=. +- compat-31: New macros =incf= and =decf=. +- compat-31: New function =color-blend=. +- compat-31: New function =completion-table-with-metadata=. +- compat-31: New function =completion-list-candidate-at-point=. +- compat-31: New macro =with-work-buffer=. +- compat-31: New function =unbuttonize-region=. +- compat-31: New extended function =seconds-to-string=. +- compat-31: New function =hash-table-contains-p=. +- compat-31: New function =remove-display-text-property=. +- compat-31: New functions =drop-while=, =take-while=, =member-if=, =any=, =all=. +- compat-31: New function =set-local=. +- compat-31: New function =ensure-proper-list=. +- compat-31: New error API functions =error-type-p=, =error-has-type-p=, =error-type= + and =error-slot-value=. +- Drop support for Emacs 24.x. Emacs 25.1 is required now. In case + Emacs 24.x support is still needed, Compat 30 can be used. + +(Release <2026-05-01 Fri>) + * Release of "Compat" Version 30.1.0.1 - compat-28: Fix =named-let= tail recursion. @@ -275,7 +306,7 @@ * Release of "Compat" Version 28.1.2.2 -This is a minor release that hopes to address [[compat-srht:7]]. +This is a minor release. (Release <2022-08-25 Thu>) @@ -297,8 +328,8 @@ include much more documentation that had been the case previously. The main change of this release has been the major simplification of Compat's initialisation system, improving the situation around issues -people had been reporting ([[compat-srht:4]], once again) with unconventional -or unpopular packaging systems. +people had been reporting with unconventional or unpopular packaging +systems. In addition to this, the following functional changes have been made: @@ -314,7 +345,6 @@ Minor improvements to manual are also part of this release. This release just contains a hot-fix for an issue introduced in the last version, where compat.el raises an error during byte compilation. -See [[compat-srht:4]]. (Release <2022-06-19 Sun>) @@ -322,11 +352,9 @@ See [[compat-srht:4]]. Two main changes have necessitated a new patch release: -1. Fix issues related to the loading of compat when uncompiled. See - [[https://lists.sr.ht/~pkal/compat-devel/%3C20220530191000.2183047-1-jonas%40bernoul.li%3E][this thread]] for more details on the problem. +1. Fix issues related to the loading of compat when uncompiled. 2. Fix issues related to the loading of compat on old pre-releases - (think of 28.0.50). See [[https://lists.sr.ht/~pkal/compat-devel/%3Cf8635d7d-e233-448f-b325-9e850363241c%40www.fastmail.com%3E][this thread]] for more details on the - problem. + (think of 28.0.50). (Released <2022-06-22 Wed>) @@ -334,7 +362,7 @@ Two main changes have necessitated a new patch release: This is a minor release fixing a bug in =json-serialize=, that could cause unintended side-effects, not related to packages using Compat -directly (see [[compat-srht:2]]). +directly. (Released <2022-05-05 Thu>) diff --git a/lisp/compat/compat-25.el b/lisp/compat/compat-25.el deleted file mode 100644 index 51c4f068..00000000 --- a/lisp/compat/compat-25.el +++ /dev/null @@ -1,260 +0,0 @@ -;;; compat-25.el --- Functionality added in Emacs 25.1 -*- lexical-binding: t; -*- - -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Functionality added in Emacs 25.1, needed by older Emacs versions. - -;;; Code: - -(eval-when-compile (load "compat-macs.el" nil t t)) - -(compat-version "25.1") - -;;;; Defined in alloc.c - -(compat-defun bool-vector (&rest objects) ;; - "Return a new bool-vector with specified arguments as elements. -Allows any number of arguments, including zero. -usage: (bool-vector &rest OBJECTS)" - (let ((vec (make-bool-vector (length objects) nil)) - (i 0)) - (while objects - (when (car objects) - (aset vec i t)) - (setq objects (cdr objects) - i (1+ i))) - vec)) - -;;;; Defined in editfns.c - -(compat-defalias format-message format) ;; - -;;;; Defined in fileio.c - -(compat-defun directory-name-p (name) ;; - "Return non-nil if NAME ends with a directory separator character." - (eq (eval-when-compile - (if (memq system-type '(cygwin windows-nt ms-dos)) - ?\\ ?/)) - (aref name (1- (length name))))) - -;;;; Defined in doc.c - -(compat-defvar text-quoting-style nil ;; - "Style to use for single quotes in help and messages. - -The value of this variable determines substitution of grave accents -and apostrophes in help output (but not for display of Info -manuals) and in functions like `message' and `format-message', but not -in `format'. - -The value should be one of these symbols: - `curve': quote with curved single quotes ‘like this’. - `straight': quote with straight apostrophes \\='like this\\='. - `grave': quote with grave accent and apostrophe \\=`like this\\='; - i.e., do not alter the original quote marks. - nil: like `curve' if curved single quotes are displayable, - and like `grave' otherwise. This is the default. - -You should never read the value of this variable directly from a Lisp -program. Use the function `text-quoting-style' instead, as that will -compute the correct value for the current terminal in the nil case.") - -;;;; Defined in simple.el - -;; `save-excursion' behaved like `save-mark-and-excursion' before 25.1. -(compat-defalias save-mark-and-excursion save-excursion) ;; - -(declare-function region-bounds nil) ;; Defined in compat-26.el -(compat-defun region-noncontiguous-p () ;; - "Return non-nil if the region contains several pieces. -An example is a rectangular region handled as a list of -separate contiguous regions for each line." - (let ((bounds (region-bounds))) (and (cdr bounds) bounds))) - -;;;; Defined in subr.el - -(compat-defun string-greaterp (string1 string2) ;; - "Return non-nil if STRING1 is greater than STRING2 in lexicographic order. -Case is significant. -Symbols are also allowed; their print names are used instead." - (string-lessp string2 string1)) - -(compat-defmacro with-file-modes (modes &rest body) ;; - "Execute BODY with default file permissions temporarily set to MODES. -MODES is as for `set-default-file-modes'." - (declare (indent 1) (debug t)) - (let ((umask (make-symbol "umask"))) - `(let ((,umask (default-file-modes))) - (unwind-protect - (progn - (set-default-file-modes ,modes) - ,@body) - (set-default-file-modes ,umask))))) - -(compat-defmacro if-let (spec then &rest else) ;; - "Bind variables according to SPEC and evaluate THEN or ELSE. -Evaluate each binding in turn, as in `let*', stopping if a -binding value is nil. If all are non-nil return the value of -THEN, otherwise the last form in ELSE. - -Each element of SPEC is a list (SYMBOL VALUEFORM) that binds -SYMBOL to the value of VALUEFORM. An element can additionally be -of the form (VALUEFORM), which is evaluated and checked for nil; -i.e. SYMBOL can be omitted if only the test result is of -interest. It can also be of the form SYMBOL, then the binding of -SYMBOL is checked for nil. - -As a special case, interprets a SPEC of the form \(SYMBOL SOMETHING) -like \((SYMBOL SOMETHING)). This exists for backward compatibility -with an old syntax that accepted only one binding." - (declare (indent 2) - (debug ([&or (symbolp form) - (&rest [&or symbolp (symbolp form) (form)])] - body))) - (when (and (<= (length spec) 2) (not (listp (car spec)))) - ;; Adjust the single binding case - (setq spec (list spec))) - (let ((empty (make-symbol "s")) - (last t) list) - (dolist (var spec) - (push `(,(if (cdr var) (car var) empty) - (and ,last ,(if (cdr var) (cadr var) (car var)))) - list) - (when (or (cdr var) (consp (car var))) - (setq last (caar list)))) - `(let* ,(nreverse list) - (if ,(caar list) ,then ,@else)))) - -(compat-defmacro when-let (spec &rest body) ;; - "Bind variables according to SPEC and conditionally evaluate BODY. -Evaluate each binding in turn, stopping if a binding value is nil. -If all are non-nil, return the value of the last form in BODY. - -The variable list SPEC is the same as in `if-let'." - (declare (indent 1) (debug if-let)) - (list 'if-let spec (macroexp-progn body))) - -;;;; Defined in subr-x.el - -(compat-defun hash-table-empty-p (hash-table) ;; - "Check whether HASH-TABLE is empty (has 0 elements)." - (zerop (hash-table-count hash-table))) - -(compat-defmacro thread-first (&rest forms) ;; - "Thread FORMS elements as the first argument of their successor. -Example: - (thread-first - 5 - (+ 20) - (/ 25) - - - (+ 40)) -Is equivalent to: - (+ (- (/ (+ 5 20) 25)) 40) -Note how the single `-' got converted into a list before -threading." - (declare (indent 1) - (debug (form &rest [&or symbolp (sexp &rest form)]))) - (let ((body (car forms))) - (dolist (form (cdr forms)) - (when (symbolp form) - (setq form (list form))) - (setq body (append (list (car form)) - (list body) - (cdr form)))) - body)) - -(compat-defmacro thread-last (&rest forms) ;; - "Thread FORMS elements as the last argument of their successor. -Example: - (thread-last - 5 - (+ 20) - (/ 25) - - - (+ 40)) -Is equivalent to: - (+ 40 (- (/ 25 (+ 20 5)))) -Note how the single `-' got converted into a list before -threading." - (declare (indent 1) (debug thread-first)) - (let ((body (car forms))) - (dolist (form (cdr forms)) - (when (symbolp form) - (setq form (list form))) - (setq body (append form (list body)))) - body)) - -;;;; Defined in macroexp.el - -(compat-defun macroexp-parse-body (body) ;; - "Parse a function BODY into (DECLARATIONS . EXPS)." - (let ((decls ())) - (while (and (cdr body) - (let ((e (car body))) - (or (stringp e) - (memq (car-safe e) - '(:documentation declare interactive cl-declare))))) - (push (pop body) decls)) - (cons (nreverse decls) body))) - -(compat-defun macroexp-quote (v) ;; - "Return an expression E such that `(eval E)' is V. - -E is either V or (quote V) depending on whether V evaluates to -itself or not." - (if (and (not (consp v)) - (or (keywordp v) - (not (symbolp v)) - (memq v '(nil t)))) - v - (list 'quote v))) - -(compat-defun macroexpand-1 (form &optional environment) ;; - "Perform (at most) one step of macro expansion." - (cond - ((consp form) - (let* ((head (car form)) - (env-expander (assq head environment))) - (if env-expander - (if (cdr env-expander) - (apply (cdr env-expander) (cdr form)) - form) - (if (not (and (symbolp head) (fboundp head))) - form - (let ((def (autoload-do-load (symbol-function head) head 'macro))) - (cond - ;; Follow alias, but only for macros, otherwise we may end up - ;; skipping an important compiler-macro (e.g. cl--block-wrapper). - ((and (symbolp def) (macrop def)) (cons def (cdr form))) - ((not (consp def)) form) - (t - (if (eq 'macro (car def)) - (apply (cdr def) (cdr form)) - form)))))))) - (t form))) - -;;;; Defined in minibuffer.el - -(compat-defun completion--category-override (category tag) ;; - "Return completion category override for CATEGORY and TAG." - (assq tag (cdr (assq category completion-category-overrides)))) - -(provide 'compat-25) -;;; compat-25.el ends here diff --git a/lisp/compat/compat-26.el b/lisp/compat/compat-26.el index 9f5e1998..f1ab3a25 100644 --- a/lisp/compat/compat-26.el +++ b/lisp/compat/compat-26.el @@ -1,6 +1,6 @@ ;;; compat-26.el --- Functionality added in Emacs 26.1 -*- lexical-binding: t; -*- -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. +;; Copyright (C) 2021-2026 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -67,11 +67,7 @@ SEQUENCE may be a list, a vector, a boolean vector, or a string." Value is a list of one or more cons cells of the form (START . END). It will have more than one cons cell when the region is non-contiguous, see `region-noncontiguous-p' and `extract-rectangle-bounds'." - (if (eval-when-compile (< emacs-major-version 25)) - ;; FIXME: The `region-extract-function' of Emacs 24 has no support for the - ;; bounds argument. - (list (cons (region-beginning) (region-end))) - (funcall region-extract-function 'bounds))) + (funcall region-extract-function 'bounds)) ;;;; Defined in subr.el @@ -108,7 +104,7 @@ If you just want to check `major-mode', use `derived-mode-p'." (compat-defun alist-get (key alist &optional default remove testfn) ;; "Handle optional argument TESTFN." - :extended "25.1" + :extended t (ignore remove) (let ((x (if (not testfn) (assq key alist) diff --git a/lisp/compat/compat-27.el b/lisp/compat/compat-27.el index 30a49474..4c2bf84e 100644 --- a/lisp/compat/compat-27.el +++ b/lisp/compat/compat-27.el @@ -1,6 +1,6 @@ ;;; compat-27.el --- Functionality added in Emacs 27.1 -*- lexical-binding: t; -*- -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. +;; Copyright (C) 2021-2026 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by diff --git a/lisp/compat/compat-28.el b/lisp/compat/compat-28.el index ed05b870..54367a7e 100644 --- a/lisp/compat/compat-28.el +++ b/lisp/compat/compat-28.el @@ -1,6 +1,6 @@ ;;; compat-28.el --- Functionality added in Emacs 28.1 -*- lexical-binding: t; -*- -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. +;; Copyright (C) 2021-2026 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -853,5 +853,13 @@ function will never return nil." :type-error "This field should contain a nonnegative integer" :match-alternatives '(natnump))) +;;;; Defined in pcase.el + +(compat-guard t ;; + (pcase-defmacro cl-type (type) + "Pcase pattern that matches objects of TYPE. +TYPE is a type descriptor as accepted by `cl-typep', which see." + `(pred (lambda (x) (cl-typep x ',type))))) + (provide 'compat-28) ;;; compat-28.el ends here diff --git a/lisp/compat/compat-29.el b/lisp/compat/compat-29.el index a0831e66..554d1cdd 100644 --- a/lisp/compat/compat-29.el +++ b/lisp/compat/compat-29.el @@ -1,6 +1,6 @@ ;;; compat-29.el --- Functionality added in Emacs 29.1 -*- lexical-binding: t; -*- -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. +;; Copyright (C) 2021-2026 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -584,47 +584,15 @@ be marked unmodified, effectively ignoring those changes." (equal ,hash (buffer-hash))) (restore-buffer-modified-p nil)))))))) -(compat-defun add-display-text-property (start end prop value ;; - &optional object) - "Add display property PROP with VALUE to the text from START to END. -If any text in the region has a non-nil `display' property, those -properties are retained. +(compat-defun add-display-text-property (start end spec value &optional object) ;; + "Add the display specification (SPEC VALUE) to the text from START to END. +If any text in the region has a non-nil `display' property, the existing +display specifications are retained. -If OBJECT is non-nil, it should be a string or a buffer. If nil, -this defaults to the current buffer." - (let ((sub-start start) - (sub-end 0) - disp) - (while (< sub-end end) - (setq sub-end (next-single-property-change sub-start 'display object - (if (stringp object) - (min (length object) end) - (min end (point-max))))) - (if (not (setq disp (get-text-property sub-start 'display object))) - ;; No old properties in this range. - (put-text-property sub-start sub-end 'display (list prop value) - object) - ;; We have old properties. - (let ((vector nil)) - ;; Make disp into a list. - (setq disp - (cond - ((vectorp disp) - (setq vector t) - (append disp nil)) - ((not (consp (car disp))) - (list disp)) - (t - disp))) - ;; Remove any old instances. - (when-let ((old (assoc prop disp))) - (setq disp (delete old disp))) - (setq disp (cons (list prop value) disp)) - (when vector - (setq disp (vconcat disp))) - ;; Finally update the range. - (put-text-property sub-start sub-end 'display disp object))) - (setq sub-start sub-end)))) +OBJECT is either a string or a buffer to add the specification to. +If omitted, OBJECT defaults to the current buffer." + (declare-function add-remove--display-text-property "compat-31") + (add-remove--display-text-property start end spec value object)) (compat-defmacro while-let (spec &rest body) ;; "Bind variables according to SPEC and conditionally evaluate BODY. @@ -641,6 +609,30 @@ The variable list SPEC is the same as in `if-let*'." ,@body) (throw ',done nil)))))) +;;;; Defined in ucs-normalize.el + +(compat-defun string-glyph-compose (string) ;; + "Compose STRING according to the Unicode NFC. +This returns a new string obtained by canonical decomposition +of STRING (see `ucs-normalize-NFC-string') followed by canonical +composition, a.k.a. the \"Unicode Normalization Form C\" of STRING. +For instance: + + (string-glyph-compose \"Å\") => \"Å\"" + (unless (fboundp 'ucs-normalize-NFC-string) + (require 'ucs-normalize)) + (ucs-normalize-NFC-string string)) + +(compat-defun string-glyph-decompose (string) ;; + "Decompose STRING according to the Unicode NFD. +This returns a new string that is the canonical decomposition of STRING, +a.k.a. the \"Unicode Normalization Form D\" of STRING. For instance: + + (ucs-normalize-NFD-string \"Å\") => \"Å\"" + (unless (fboundp 'ucs-normalize-NFD-string) + (require 'ucs-normalize)) + (ucs-normalize-NFD-string string)) + ;;;; Defined in files.el (compat-defun directory-abbrev-make-regexp (directory) ;; diff --git a/lisp/compat/compat-30.el b/lisp/compat/compat-30.el index 2decb937..a3c9dcdb 100644 --- a/lisp/compat/compat-30.el +++ b/lisp/compat/compat-30.el @@ -1,6 +1,6 @@ ;;; compat-30.el --- Functionality added in Emacs 30 -*- lexical-binding: t; -*- -;; Copyright (C) 2023-2025 Free Software Foundation, Inc. +;; Copyright (C) 2023-2026 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -431,7 +431,7 @@ The following arguments are defined: For compatibility, the calling convention (sort SEQ LESSP) can also be used; in this case, sorting is always done in-place." :extended t - (let ((in-place t) (reverse nil) (orig-seq seq)) + (let ((in-place t) (reverse nil)) (when (or (not lessp) rest) (setq rest (if lessp (cons lessp rest) rest) @@ -442,24 +442,10 @@ in this case, sorting is always done in-place." (if key (lambda (a b) (funcall < (funcall key a) (funcall key b))) <)) - seq (if (or (and (eval-when-compile (< emacs-major-version 25)) (vectorp orig-seq)) - in-place) - seq - (copy-sequence seq)))) - ;; Emacs 24 does not support vectors. Convert to list. - (when (and (eval-when-compile (< emacs-major-version 25)) (vectorp orig-seq)) - (setq seq (append seq nil))) - (setq seq (if reverse - (nreverse (sort (nreverse seq) lessp)) - (sort seq lessp))) - ;; Emacs 24: Convert back to vector. - (if (and (eval-when-compile (< emacs-major-version 25)) (vectorp orig-seq)) - (if in-place - (cl-loop for i from 0 for x in seq - do (aset orig-seq i x) - finally return orig-seq) - (apply #'vector seq)) - seq))) + seq (if in-place seq (copy-sequence seq)))) + (if reverse + (nreverse (sort (nreverse seq) lessp)) + (sort seq lessp)))) ;;;; Defined in mule-cmds.el diff --git a/lisp/compat/compat-31.el b/lisp/compat/compat-31.el new file mode 100644 index 00000000..2b61749d --- /dev/null +++ b/lisp/compat/compat-31.el @@ -0,0 +1,416 @@ +;;; compat-31.el --- Functionality added in Emacs 31 -*- lexical-binding: t; -*- + +;; Copyright (C) 2025-2026 Free Software Foundation, Inc. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Functionality added in Emacs 31, needed by older Emacs versions. + +;;; Code: + +(eval-when-compile (load "compat-macs.el" nil t t)) +(compat-require compat-30 "30.1") + +;; TODO Update to 31.1 as soon as the Emacs emacs-31 branch version bumped +(compat-version "31.0.50") + +;;;; Defined in subr.el + +(compat-defun error-type-p (symbol) ;; + "Return non-nil if SYMBOL is a condition type." + (get symbol 'error-conditions)) + +(compat-defun error-has-type-p (error condition) ;; + "Return non-nil if ERROR is of type CONDITION (or a subtype of it)." + (unless (let ((type (car-safe error))) + (and type (symbolp type) (listp (cdr error)) + (error-type-p type))) + (signal 'wrong-type-argument (list error))) + (or (eq condition t) + (memq condition (get (car error) 'error-conditions)))) + +(compat-defalias error-type car ;; + "Return the symbol which represents the type of ERROR. +\n(fn ERROR)") + +(compat-defalias error-slot-value elt ;; + "Access the SLOT of object ERROR. +Slots are specified by position, and slot 0 is the error symbol. +\n(fn ERROR SLOT)") + +(compat-defun ensure-proper-list (object) ;; + "Return OBJECT as a list. +If OBJECT is already a proper list, return OBJECT itself. If it's not a +proper list, return a one-element list containing OBJECT. + +`ensure-list' is usually preferable because that function runs in +constant time, but this one has to traverse the whole of OBJECT." + (declare (side-effect-free error-free)) + (if (proper-list-p object) + object + (list object))) + +(compat-defun set-local (variable value) ;; + "Make VARIABLE buffer local and set it to VALUE." + (set (make-local-variable variable) value)) + +(compat-defun take-while (pred list) ;; + "Return the longest prefix of LIST whose elements satisfy PRED." + (let ((r nil)) + (while (and list (funcall pred (car list))) + (push (car list) r) + (setq list (cdr list))) + (nreverse r))) + +(compat-defun drop-while (pred list) ;; + "Skip initial elements of LIST satisfying PRED and return the rest." + (while (and list (funcall pred (car list))) + (setq list (cdr list))) + list) + +(compat-defun all (pred list) ;; + "Non-nil if PRED is true for all elements in LIST." + (not (drop-while pred list))) + +(compat-defun member-if (pred list) ;; + "Non-nil if PRED is true for at least one element in LIST. +Returns the LIST suffix starting at the first element that satisfies PRED, +or nil if none does." + (drop-while (lambda (x) (not (funcall pred x))) list)) + +(compat-defalias any member-if) ;; + +(compat-defun hash-table-contains-p (key table) ;; + "Return non-nil if TABLE has an element with KEY." + (declare (side-effect-free t)) + (let ((missing '#:missing)) + (not (eq (gethash key table missing) missing)))) + +(compat-defmacro static-when (condition &rest body) ;; + "A conditional compilation macro. +Evaluate CONDITION at macro-expansion time. If it is non-nil, +expand the macro to evaluate all BODY forms sequentially and return +the value of the last one, or nil if there are none." + (declare (indent 1) (debug t)) + (if body + (if (eval condition lexical-binding) + (cons 'progn body) + nil) + (macroexp-warn-and-return (format-message "`static-when' with empty body") + (list 'progn nil nil) '(empty-body static-when) t))) + +(compat-defmacro static-unless (condition &rest body) ;; + "A conditional compilation macro. +Evaluate CONDITION at macro-expansion time. If it is nil, +expand the macro to evaluate all BODY forms sequentially and return +the value of the last one, or nil if there are none." + (declare (indent 1) (debug t)) + (if body + (if (eval condition lexical-binding) + nil + (cons 'progn body)) + (macroexp-warn-and-return (format-message "`static-unless' with empty body") + (list 'progn nil nil) '(empty-body static-unless) t))) + +(compat-defun oddp (integer) ;; + "Return t if INTEGER is odd." + (not (eq (% integer 2) 0))) + +(compat-defun evenp (integer) ;; + "Return t if INTEGER is even." + (eq (% integer 2) 0)) + +(compat-defun plusp (number) ;; + "Return t if NUMBER is positive." + (> number 0)) + +(compat-defun minusp (number) ;; + "Return t if NUMBER is negative." + (< number 0)) + +(compat-defmacro incf (place &optional delta) ;; + "Increment PLACE by DELTA (default to 1). + +The DELTA is first added to PLACE, and then stored in PLACE. +Return the incremented value of PLACE. + +See also `decf'." + (gv-letplace (getter setter) place + (funcall setter `(+ ,getter ,(or delta 1))))) + +(compat-defmacro decf (place &optional delta) ;; + "Decrement PLACE by DELTA (default to 1). + +The DELTA is first subtracted from PLACE, and then stored in PLACE. +Return the decremented value of PLACE. + +See also `incf'." + (gv-letplace (getter setter) place + (funcall setter `(- ,getter ,(or delta 1))))) + +;;;; Defined in color.el + +(compat-defun color-blend (a b &optional alpha) ;; + "Blend the two colors A and B in linear space with ALPHA. +A and B should be lists (RED GREEN BLUE), where each element is +between 0.0 and 1.0, inclusive. ALPHA controls the influence A +has on the result and should be between 0.0 and 1.0, inclusive. + +For instance: + + (color-blend \\='(1 0.5 1) \\='(0 0 0) 0.75) + => (0.75 0.375 0.75)" + (setq alpha (or alpha 0.5)) + (let (blend) + (dotimes (i 3) + (push (+ (* (nth i a) alpha) (* (nth i b) (- 1 alpha))) blend)) + (nreverse blend))) + +;;;; Defined in time-date.el + +(compat-defvar seconds-to-string ;; + (list (list 1 "ms" 0.001) + (list 100 "s" 1) + (list (* 60 100) "m" 60.0) + (list (* 3600 30) "h" 3600.0) + (list (* 3600 24 400) "d" (* 3600.0 24.0)) + (list nil "y" (* 365.25 24 3600))) + "Formatting used by the function `seconds-to-string'.") + +(compat-defvar seconds-to-string-readable ;; + `(("Y" "year" "years" ,(round (* 60 60 24 365.2425))) + ("M" "month" "months" ,(round (* 60 60 24 30.436875))) + ("w" "week" "weeks" ,(* 60 60 24 7)) + ("d" "day" "days" ,(* 60 60 24)) + ("h" "hour" "hours" ,(* 60 60)) + ("m" "minute" "minutes" 60) + ("s" "second" "seconds" 1)) + "Formatting used by the function `seconds-to-string' with READABLE set. +The format is an alist, with string keys ABBREV-UNIT, and elements like: + + (ABBREV-UNIT UNIT UNIT-PLURAL SECS) + +where UNIT is a unit of time, ABBREV-UNIT is the abbreviated form of +UNIT, UNIT-PLURAL is the plural form of UNIT, and SECS is the number of +seconds per UNIT.") + +(compat-defun seconds-to-string (delay &optional readable abbrev precision) ;; + "Handle optional arguments READABLE, ABBREV and PRECISION." + :extended t + (cond + ((< delay 0) + (concat "-" (seconds-to-string (- delay) readable precision))) + (readable + (let* ((stsa seconds-to-string-readable) + (expanded (eq readable 'expanded)) + digits + (round-to (cond + ((wholenump precision) + (setq digits precision) + (expt 10 (- precision))) + ((and (floatp precision) (< precision 1.)) + (setq digits (- (floor (log precision 10)))) + precision) + (t (setq digits 0) 1))) + (dformat (if (> digits 0) (format "%%0.%df" digits))) + (padding (if abbrev "" " ")) + here cnt cnt-pre here-pre cnt-val isfloatp) + (if (= (round delay round-to) 0) + (format "0%s" (if abbrev "s" " seconds")) + (while (and (setq here (pop stsa)) stsa + (< (/ delay (nth 3 here)) 1))) + (or (and + expanded stsa ; smaller unit remains + (progn + (setq + here-pre here here (car stsa) + cnt-pre (floor (/ (float delay) (nth 3 here-pre))) + cnt (round + (/ (- (float delay) (* cnt-pre (nth 3 here-pre))) + (nth 3 here)) + round-to)) + (if (> cnt 0) t (setq cnt cnt-pre here here-pre here-pre nil)))) + (setq cnt (round (/ (float delay) (nth 3 here)) round-to))) + (setq cnt-val (* cnt round-to) + isfloatp (and (> digits 0) + (> (- cnt-val (floor cnt-val)) 0.))) + (cl-labels + ((unit (val here &optional plural) + (cond (abbrev (car here)) + ((and (not plural) (<= (floor val) 1)) (nth 1 here)) + (t (nth 2 here))))) + (concat + (when here-pre + (concat (number-to-string cnt-pre) padding + (unit cnt-pre here-pre) " ")) + (if isfloatp (format dformat cnt-val) + (number-to-string (floor cnt-val))) + padding + (unit cnt-val here isfloatp)))))) ; float formats are always plural + ((= 0 delay) "0s") + (t (let ((sts seconds-to-string) here) + (while (and (car (setq here (pop sts))) + (<= (car here) delay))) + (concat (format "%.2f" (/ delay (car (cddr here)))) (cadr here)))))) + +;;;; Defined in minibuffer.el + +(compat-defun completion-list-candidate-at-point (&optional pt) ;; + "Candidate string and bounds at PT in completions buffer. +The return value has the format (STR BEG END). +The optional argument PT defaults to (point)." + (let ((pt (or pt (point))) beg end) + (cond + ((and (/= pt (point-max)) (get-text-property pt 'mouse-face)) + (setq end pt beg (1+ pt))) + ((and (/= pt (point-min)) (get-text-property (1- pt) 'mouse-face)) + (setq end (1- pt) beg pt))) + (when (and beg end) + (setq beg (previous-single-property-change beg 'mouse-face)) + (setq end (or (next-single-property-change end 'mouse-face) (point-max))) + (list (or (get-text-property beg 'completion--string) + (buffer-substring beg end)) + beg end)))) + +(compat-defun 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)))) + +;;;; Defined in subr-x.el + +(compat-defun add-remove--display-text-property (start end spec value &optional object remove) ;; + "Helper function for `add-display-text-property' and `remove-display-text-property'." + (let ((sub-start start) + (sub-end 0) + (limit (if (stringp object) + (min (length object) end) + (min end (point-max)))) + disp) + (while (< sub-end end) + (setq sub-end (next-single-property-change sub-start 'display object + limit)) + (if (not (setq disp (get-text-property sub-start 'display object))) + (unless remove + (put-text-property sub-start sub-end 'display (list spec value) + object)) + (let ((changed nil) + type) + (setq disp + (cond + ((vectorp disp) + (setq type 'vector) + (seq-into disp 'list)) + ((or (not (consp (car-safe disp))) + (eq (caar disp) 'margin)) + (setq type 'scalar) + (list disp)) + (t + (setq type 'list) + disp))) + (when-let* ((old (assoc spec disp))) + (setq disp (if (eq type 'list) + (remove old disp) + (delete old disp)) + changed t)) + (unless remove + (setq disp (cons (list spec value) disp) + changed t)) + (when changed + (if (not disp) + (remove-text-properties sub-start sub-end '(display nil) object) + (when (eq type 'vector) + (setq disp (seq-into disp 'vector))) + (put-text-property sub-start sub-end 'display disp object))))) + (setq sub-start sub-end)))) + +(compat-defun remove-display-text-property (start end spec &optional object) ;; + "Remove the display specification SPEC from the text from START to END. +SPEC is the car of the display specification to remove, e.g. `height'. +If any text in the region has other display specifications, those specs +are retained. + +OBJECT is either a string or a buffer to remove the specification from. +If omitted, OBJECT defaults to the current buffer." + (add-remove--display-text-property start end spec nil object 'remove)) + +(compat-defvar work-buffer-limit 10 ;; + "Maximum number of reusable work buffers. +When this limit is exceeded, newly allocated work buffers are +automatically killed, which means that in a such case +`with-work-buffer' becomes equivalent to `with-temp-buffer'.") + +;; On Emacs 29 and newer `kill-all-local-variables' has a KILL-PERMANENT argument. +(static-if (< emacs-major-version 29) nil + (compat-defvar work-buffer--list nil ;; + "List of work buffers.") + + (compat-defun work-buffer--get () ;; + "Get a work buffer." + (let ((buffer (pop work-buffer--list))) + (if (buffer-live-p buffer) + buffer + (generate-new-buffer " *work*" t)))) + + (compat-defun work-buffer--release (buffer) ;; + "Release work BUFFER." + (if (buffer-live-p buffer) + (with-current-buffer buffer + (let ((inhibit-read-only t)) + (erase-buffer) + (delete-all-overlays)) + (let (change-major-mode-hook) + (setq buffer-read-only nil) + (kill-all-local-variables t)) + (push buffer work-buffer--list))) + (when (> (length work-buffer--list) work-buffer-limit) + (mapc #'kill-buffer (nthcdr work-buffer-limit work-buffer--list)) + (setq work-buffer--list (ntake work-buffer-limit work-buffer--list))))) + +(compat-defmacro with-work-buffer (&rest body) ;; + "Create a work buffer, and evaluate BODY there like `progn'. +Like `with-temp-buffer', but reuse an already created temporary buffer +when possible, instead of creating a new one on each call. Avoid +retaining state referring to a work buffer, and kill any indirect +buffers you create that use a work buffer as a base." + (declare (indent 0) (debug t)) + (static-if (< emacs-major-version 29) + `(with-temp-buffer ,@body) + (let ((work-buffer (make-symbol "work-buffer"))) + `(let ((,work-buffer (work-buffer--get))) + (with-current-buffer ,work-buffer + (unwind-protect + (progn ,@body) + (work-buffer--release ,work-buffer))))))) + +;;;; Defined in button.el + +(compat-defun unbuttonize-region (start end) ;; + "Remove all the buttons between START and END. +This removes both text-property and overlay based buttons." + (dolist (o (overlays-in start end)) + (when (overlay-get o 'button) + (delete-overlay o))) + (with-silent-modifications + (remove-text-properties start end (button--properties nil nil nil)) + (add-face-text-property start end 'button nil))) + +(provide 'compat-31) +;;; compat-31.el ends here diff --git a/lisp/compat/compat-macs.el b/lisp/compat/compat-macs.el index c5567da0..b3206f7f 100644 --- a/lisp/compat/compat-macs.el +++ b/lisp/compat/compat-macs.el @@ -1,6 +1,6 @@ ;;; compat-macs.el --- Compatibility Macros -*- lexical-binding: t; no-byte-compile: t; -*- -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. +;; Copyright (C) 2021-2026 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -103,7 +103,7 @@ REST are attributes and the function BODY." (lambda (extended obsolete body) (when (stringp extended) (compat-macs--assert - (and (version< extended compat-macs--version) (version< "24.4" extended)) + (and (version< extended compat-macs--version) (version< "25.1" extended)) "Invalid :extended version %s for %s %s" extended type name) (setq extended (version<= extended emacs-version))) (compat-macs--strict (eq extended (fboundp name)) diff --git a/lisp/compat/compat-pkg.el b/lisp/compat/compat-pkg.el index 4b26a4f6..9c8a76ff 100644 --- a/lisp/compat/compat-pkg.el +++ b/lisp/compat/compat-pkg.el @@ -1,2 +1,2 @@ ;; Generated package description from compat.el -*- no-byte-compile: t -*- -(define-package "compat" "30.1.0.1" "Emacs Lisp Compatibility Library" '((emacs "24.4") (seq "2.23")) :commit "cccd41f549fa88031a32deb26253b462021d7e12" :authors '(("Philip Kaludercic" . "philipk@posteo.net") ("Daniel Mendler" . "mail@daniel-mendler.de")) :maintainer '("Compat Development" . "~pkal/compat-devel@lists.sr.ht") :keywords '("lisp" "maint") :url "https://github.com/emacs-compat/compat") +(define-package "compat" "31.0.0.1" "Emacs Lisp Compatibility Library" '((emacs "25.1")) :commit "b5b48183689b536f72b1214106afeabc465da9d4" :authors '(("Philip Kaludercic" . "philipk@posteo.net") ("Daniel Mendler" . "mail@daniel-mendler.de")) :maintainer '(("Philip Kaludercic" . "philipk@posteo.net") ("Daniel Mendler" . "mail@daniel-mendler.de")) :keywords '("lisp" "maint") :url "https://github.com/emacs-compat/compat") diff --git a/lisp/compat/compat.el b/lisp/compat/compat.el index 7fc500f5..deec1316 100644 --- a/lisp/compat/compat.el +++ b/lisp/compat/compat.el @@ -1,12 +1,12 @@ ;;; compat.el --- Emacs Lisp Compatibility Library -*- lexical-binding: t; -*- -;; Copyright (C) 2021-2025 Free Software Foundation, Inc. +;; Copyright (C) 2021-2026 Free Software Foundation, Inc. ;; Author: Philip Kaludercic , Daniel Mendler -;; Maintainer: Compat Development <~pkal/compat-devel@lists.sr.ht> -;; Version: 30.1.0.1 +;; Maintainer: Philip Kaludercic , Daniel Mendler +;; Version: 31.0.0.1 ;; URL: https://github.com/emacs-compat/compat -;; Package-Requires: ((emacs "24.4") (seq "2.23")) +;; Package-Requires: ((emacs "25.1")) ;; Keywords: lisp, maint ;; This program is free software; you can redistribute it and/or modify @@ -50,9 +50,9 @@ ;; time and runtime, but only if needed. (eval-when-compile (defmacro compat--maybe-require () - (when (version< emacs-version "30.1") - (require 'compat-30) - '(require 'compat-30)))) + (when (< emacs-major-version 31) + (require 'compat-31) + '(require 'compat-31)))) (compat--maybe-require) ;;;; Macros for extended compatibility function calls diff --git a/lisp/compat/compat.info b/lisp/compat/compat.info index afaf9571..dec022ce 100644 --- a/lisp/compat/compat.info +++ b/lisp/compat/compat.info @@ -1,7 +1,7 @@ -This is docA0lLBy.info, produced by makeinfo version 7.1.1 from +This is docvwepFX.info, produced by makeinfo version 7.3 from compat.texi. -Copyright © 2022-2025 Free Software Foundation, Inc. +Copyright © 2022-2026 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, @@ -20,16 +20,16 @@ START-INFO-DIR-ENTRY END-INFO-DIR-ENTRY  -File: docA0lLBy.info, Node: Top, Next: Introduction, Up: (dir) +File: docvwepFX.info, Node: Top, Next: Introduction, Up: (dir) "Compat" Manual *************** This manual documents the usage of the "Compat" Emacs lisp library, the forward-compatibility library for Emacs Lisp, corresponding to version -30.1.0.1. +31.0.0.1. - Copyright © 2022-2025 Free Software Foundation, Inc. + Copyright © 2022-2026 Free Software Foundation, Inc. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, @@ -60,16 +60,15 @@ Introduction Support -* Emacs 25.1:: Compatibility support for Emacs 25.1 * Emacs 26.1:: Compatibility support for Emacs 26.1 * Emacs 27.1:: Compatibility support for Emacs 27.1 * Emacs 28.1:: Compatibility support for Emacs 28.1 * Emacs 29.1:: Compatibility support for Emacs 29.1 * Emacs 30.1:: Compatibility support for Emacs 30.1 - +* Emacs 31.1:: Compatibility support for Emacs 31.1  -File: docA0lLBy.info, Node: Introduction, Next: Support, Prev: Top, Up: Top +File: docvwepFX.info, Node: Introduction, Next: Support, Prev: Top, Up: Top 1 Introduction ************** @@ -81,7 +80,7 @@ File: docA0lLBy.info, Node: Introduction, Next: Support, Prev: Top, Up: Top * Limitations::  -File: docA0lLBy.info, Node: Overview, Next: Usage, Up: Introduction +File: docvwepFX.info, Node: Overview, Next: Usage, Up: Introduction 1.1 Overview ============ @@ -91,12 +90,12 @@ for Emacs Lisp. By using Compat, an Elisp package does not have to make the decision to either use new and useful functionality or support old versions of Emacs. - The library provides support back until Emacs 24.4. The intended + The library provides support back until Emacs 25.1. The intended audience are package developers that are interested in using newer developments, without having to break compatibility.  -File: docA0lLBy.info, Node: Usage, Next: Limitations, Prev: Overview, Up: Introduction +File: docvwepFX.info, Node: Usage, Next: Limitations, Prev: Overview, Up: Introduction 1.2 Usage ========= @@ -106,9 +105,9 @@ as a dependency in the header. The version of the Compat library mirrors the version of Emacs releases. The current version of Compat corresponds to the current Emacs release. - ;; Package-Requires: ((emacs "24.4") (compat "30.1.0.1")) + ;; Package-Requires: ((emacs "25.1") (compat "31.0.0.1")) - There is no need to depend on ‘emacs 24.4’ specifically. One can + There is no need to depend on ‘emacs 25.1’ specifically. One can choose any newer version, if features not provided by Compat necessitate it, for example bug fixes or UI improvements. @@ -177,15 +176,8 @@ using continuous integration. All functions provided by Compat are covered by the test suite. There is a link to the corresponding test on the first line of each definition. - You may want to subscribe to the compat-announce -(https://lists.sr.ht/~pkal/compat-announce) mailing list to be notified -when new versions are released or relevant changes are made. We also -provide a development mailing list -(https://lists.sr.ht/~pkal/compat-devel) (~pkal/compat-devel@lists.sr.ht -<~pkal/compat-devel@lists.sr.ht>). -  -File: docA0lLBy.info, Node: Limitations, Prev: Usage, Up: Introduction +File: docvwepFX.info, Node: Limitations, Prev: Usage, Up: Introduction 1.3 Limitations =============== @@ -196,12 +188,12 @@ technical reasons. The scope is intentionally restricted in order to limit the size of Compat and to ensure that the library stays maintainable. - Emacs version 24.4 is chosen as the oldest version supported by + Emacs version 25.1 is chosen as the oldest version supported by Compat, since Elisp has seen significant changes at that version. Since -24.4 Emacs major versions consistently bump the major version number. -On the library level, subr-x was introduced in 24.4. Most popular Emacs -packages already require 24.4 or even newer versions of Emacs. -Supporting for more historical Emacs versions would complicate +25.1 Emacs major versions consistently bump the major version number. +On the library level, seq and generics were introduced in 25.1. Most +popular Emacs packages already require 25.1 or even newer versions of +Emacs. Supporting for more historical Emacs versions would complicate maintenance while only few packages and users would benefit. Below we list a number of reasons why certain functionality cannot be @@ -277,7 +269,7 @@ belong here, a *note report: Development. would be much appreciated. on the level of Compat.  -File: docA0lLBy.info, Node: Support, Next: Development, Prev: Introduction, Up: Top +File: docvwepFX.info, Node: Support, Next: Development, Prev: Introduction, Up: Top 2 Support ********* @@ -287,232 +279,22 @@ manage to provide for each Emacs version. * Menu: -* Emacs 25.1:: Compatibility support for Emacs 25.1 * Emacs 26.1:: Compatibility support for Emacs 26.1 * Emacs 27.1:: Compatibility support for Emacs 27.1 * Emacs 28.1:: Compatibility support for Emacs 28.1 * Emacs 29.1:: Compatibility support for Emacs 29.1 * Emacs 30.1:: Compatibility support for Emacs 30.1 +* Emacs 31.1:: Compatibility support for Emacs 31.1  -File: docA0lLBy.info, Node: Emacs 25.1, Next: Emacs 26.1, Up: Support +File: docvwepFX.info, Node: Emacs 26.1, Next: Emacs 27.1, Up: Support -2.1 Emacs 25.1 +2.1 Emacs 26.1 ============== 2.1.1 Added Definitions ----------------------- -The following functions and macros are implemented in Emacs 25.1. These -functions are made available by Compat on Emacs versions older than -25.1. - - -- User Option: text-quoting-style - The value of this user option is a symbol that specifies the style - Emacs should use for single quotes in the wording of help and - messages. If the option's value is ‘curve’, the style is ‘like - this’ with curved single quotes. If the value is ‘straight’, the - style is 'like this' with straight apostrophes. If the value is - ‘grave’, quotes are not translated and the style is `like this' - with grave accent and apostrophe, the standard style before Emacs - version 25. The default value ‘nil’ acts like ‘curve’ if curved - single quotes seem to be displayable, and like ‘grave’ otherwise. - - This option is useful on platforms that have problems with curved - quotes. You can customize it freely according to your personal - preference. - - -- Function: region-bounds - Return the boundaries of the region. Value is a list of one or - more cons cells of the form ‘(start . end)’. It will have more - than one cons cell when the region is non-contiguous, see - ‘region-noncontiguous-p’ and ‘extract-rectangle-bounds’. - - -- Function: region-noncontiguous-p - Return non-nil if the region contains several pieces. An example - is a rectangular region handled as a list of separate contiguous - regions for each line. - - -- Macro: save-mark-and-excursion body... - This macro is like ‘save-excursion’, but also saves and restores - the mark location and ‘mark-active’. This macro does what - ‘save-excursion’ did before Emacs 25.1. - - -- Function: format-message string &rest objects - This function acts like ‘format’, except it also converts any grave - accents (`) and apostrophes (') in STRING as per the value of - ‘text-quoting-style’. - - Typically grave accent and apostrophe in the format translate to - matching curved quotes, e.g., "Missing `%s'" might result in - "Missing ‘foo’". *Note (elisp)Text Quoting Style::, for how to - influence or inhibit this translation. - - *note (elisp)Formatting Strings::. - - -- Function: directory-name-p filename - This function returns non-‘nil’ if FILENAME ends with a directory - separator character. This is the forward slash ‘/’ on GNU and - other POSIX-like systems; MS-Windows and MS-DOS recognize both the - forward slash and the backslash ‘\’ as directory separators. - - *Note (elisp)Directory Names::. - - -- Function: string-greaterp string1 string2 - This function returns the result of comparing STRING1 and STRING2 - in the opposite order, i.e., it is equivalent to calling - ‘(string-lessp STRING2 STRING1)’. - - *Note (elisp)Text Comparison::. - - -- Macro: with-file-modes mode body... - This macro evaluates the BODY forms with the default permissions - for new files temporarily set to MODES (whose value is as for - ‘set-file-modes’ above). When finished, it restores the original - default file permissions, and returns the value of the last form in - BODY. - - This is useful for creating private files, for example. - - *Note (elisp)Changing Files::. - - -- Function: alist-get key alist &optional default remove - This function is similar to ‘assq’. It finds the first association - ‘(KEY . VALUE)’ by comparing KEY with ALIST elements, and, if - found, returns the VALUE of that association. If no association is - found, the function returns DEFAULT. - - This is a generalized variable (*note (elisp)Generalized - Variables::) that can be used to change a value with ‘setf’. When - using it to set a value, optional argument REMOVE non-‘nil’ means - to remove KEY's association from ALIST if the new value is ‘eql’ to - DEFAULT. - - *note (elisp)Association Lists::. - - -- Macro: if-let (bindings...) then &rest else... - As with ‘let*’, BINDINGS will consist of ‘(SYMBOL VALUE-FORM)’ - entries that are evaluated and bound sequentially. If all - VALUE-FORM evaluate to non-‘nil’ values, then THEN is evaluated as - were the case with a regular ‘let*’ expression, with all the - variables bound. If any VALUE-FORM evaluates to ‘nil’, ELSE is - evaluated, without any bound variables. - - A binding may also optionally drop the SYMBOL, and simplify to - ‘(VALUE-FORM)’ if only the test is of interest. - - For the sake of backwards compatibility, it is possible to write a - single binding without a binding list: - - (if-let* (SYMBOL (test)) foo bar) - ≡ - (if-let* ((SYMBOL (test))) foo bar) - - -- Macro: when-let (bindings...) &rest body - As with ‘when’, if one is only interested in the case where all - BINDINGS are non-nil. Otherwise BINDINGS are interpreted just as - they are by ‘if-let*’. - - -- Function: hash-table-empty hash-table - Check whether HASH-TABLE is empty (has 0 elements). - - -- Macro: thread-first &rest forms - Combine FORMS into a single expression by "threading" each element - as the _first_ argument of their successor. Elements of FORMS can - either be an list of an atom. - - For example, consider the threading expression and it's equivalent - macro expansion: - - (thread-first - 5 - (+ 20) - (/ 25) - - - (+ 40)) - ≡ - (+ (- (/ (+ 5 20) 25)) 40) - - Note how the single ‘-’ got converted into a list before threading. - This example uses arithmetic functions, but ‘thread-first’ is not - restricted to arithmetic or side-effect free code. - - -- Macro: thread-last &rest forms - Combine FORMS into a single expression by "threading" each element - as the _last_ argument of their successor. Elements of FORMS can - either be an list of an atom. - - For example, consider the threading expression and it's equivalent - macro expansion: - - (thread-first - 5 - (+ 20) - (/ 25) - - - (+ 40)) - ≡ - (+ 40 (- (/ 25 (+ 20 5)))) - - Note how the single ‘-’ got converted into a list before threading. - This example uses arithmetic functions, but ‘thread-last’ is not - restricted to arithmetic or side-effect free code. - - -- Function: macroexpand-1 form &optional environment - This function expands macros like ‘macroexpand’, but it only - performs one step of the expansion: if the result is another macro - call, ‘macroexpand-1’ will not expand it. - - *Note Expansion: (elisp)Expansion. - - -- Function: macroexp-quote e - Return an expression E such that ‘(eval e)’ is V. - - -- Function: macroexp-parse body - Parse a function BODY into ‘(declarations . exps)’. - - -- Function: bool-vector &rest objects - This function creates and returns a bool-vector whose elements are - the arguments, OBJECTS. - - *Note (elisp)Bool-Vectors::. - -2.1.2 Missing Definitions -------------------------- - -Compat does not provide support for the following Lisp features -implemented in 25.1: - - • The function ‘macroexp-macroexpand’. - • The macro ‘macroexp-let2*’. - • The function ‘directory-files-recursively’. - • New ‘pcase’ patterns. - • The hook ‘prefix-command-echo-keystrokes-functions’ and - ‘prefix-command-preserve-state-hook’. - • The hook ‘pre-redisplay-functions’. - • The function ‘make-process’. - • Support for the variable ‘inhibit-message’. - • The ‘define-inline’ functionality. - • The functions ‘string-collate-lessp’ and ‘string-collate-equalp’. - • The function ‘funcall-interactively’. - • The function ‘buffer-substring-with-bidi-context’. - • The function ‘font-info’. - • The function ‘default-font-width’. - • The function ‘window-font-height’ and ‘window-font-width’. - • The function ‘window-max-chars-per-line’. - • The function ‘set-binary-mode’. - • The functions ‘bufferpos-to-filepos’ and ‘filepos-to-bufferpos’. - • The ‘thunk’ library. - - -File: docA0lLBy.info, Node: Emacs 26.1, Next: Emacs 27.1, Prev: Emacs 25.1, Up: Support - -2.2 Emacs 26.1 -============== - -2.2.1 Added Definitions ------------------------ - The following functions and macros are implemented in Emacs 26.1. These functions are made available by Compat on Emacs versions older than 26.1. @@ -776,7 +558,7 @@ functions are made available by Compat on Emacs versions older than (file-attribute-collect (file-attributes ".") 'type 'modes 'inode-number) ⇒ (t "drwxr-xr-x" 137819) -2.2.2 Extended Definitions +2.1.2 Extended Definitions -------------------------- These functions must be called explicitly via ‘compat-call’, since their @@ -890,7 +672,7 @@ calling convention or behavior was extended in Emacs 26.1: The compatibility version handles the optional arguments TRIM-LEFT and TRIM-RIGHT. -2.2.3 Missing Definitions +2.1.3 Missing Definitions ------------------------- Compat does not provide support for the following Lisp features @@ -916,12 +698,12 @@ implemented in 26.1: • The ‘svg’ library (published separately as a :core package).  -File: docA0lLBy.info, Node: Emacs 27.1, Next: Emacs 28.1, Prev: Emacs 26.1, Up: Support +File: docvwepFX.info, Node: Emacs 27.1, Next: Emacs 28.1, Prev: Emacs 26.1, Up: Support -2.3 Emacs 27.1 +2.2 Emacs 27.1 ============== -2.3.1 Added Definitions +2.2.1 Added Definitions ----------------------- The following functions and macros are implemented in Emacs 27.1. These @@ -1255,7 +1037,7 @@ functions are made available by Compat on Emacs versions older than *Note (Property Search)elisp::. -2.3.2 Extended Definitions +2.2.2 Extended Definitions -------------------------- These functions must be called explicitly via ‘compat-call’, since their @@ -1360,7 +1142,7 @@ calling convention or behavior was extended in Emacs 27.1: This compatibility version adds support to handle the optional second (REMOTE) argument. -2.3.3 Missing Definitions +2.2.3 Missing Definitions ------------------------- Compat does not provide support for the following Lisp features @@ -1385,20 +1167,20 @@ implemented in 27.1: • The ‘image-converter’ library.  -File: docA0lLBy.info, Node: Emacs 28.1, Next: Emacs 29.1, Prev: Emacs 27.1, Up: Support +File: docvwepFX.info, Node: Emacs 28.1, Next: Emacs 29.1, Prev: Emacs 27.1, Up: Support -2.4 Emacs 28.1 +2.3 Emacs 28.1 ============== -2.4.1 Added Definitions +2.3.1 Added Definitions ----------------------- The following functions and macros are implemented in Emacs 28.1. These functions are made available by Compat on Emacs versions older than 28.1. - The ‘defcustom’ type ‘natnum’ introduced in Emacs 28.1 is made -available by Compat. + The ‘defcustom’ type ‘natnum’ and the ‘pcase’ pattern ‘cl-type’ +introduced in Emacs 28.1 are made available by Compat. -- Function: process-lines-ignore-status program &rest args This function is just like ‘process-lines’, but does not signal an @@ -1772,7 +1554,7 @@ available by Compat. WINDOW is nil, use the selected window. Return the value of the last form in BODY. -2.4.2 Extended Definitions +2.3.2 Extended Definitions -------------------------- These functions must be called explicitly via ‘compat-call’, since their @@ -1807,7 +1589,7 @@ calling convention or behavior was extended in Emacs 28.1: This compatibility version handles the optional argument ALL-FRAMES. -2.4.3 Missing Definitions +2.3.3 Missing Definitions ------------------------- Compat does not provide support for the following Lisp features @@ -1845,12 +1627,12 @@ implemented in 28.1: • The ‘multisession’ library.  -File: docA0lLBy.info, Node: Emacs 29.1, Next: Emacs 30.1, Prev: Emacs 28.1, Up: Support +File: docvwepFX.info, Node: Emacs 29.1, Next: Emacs 30.1, Prev: Emacs 28.1, Up: Support -2.5 Emacs 29.1 +2.4 Emacs 29.1 ============== -2.5.1 Added Definitions +2.4.1 Added Definitions ----------------------- The following functions and macros are implemented in Emacs 29.1. These @@ -2174,6 +1956,17 @@ by Compat. *Note (elisp)Size of Displayed Text::. + -- Function: string-glyph-compose string + Compose STRING according to the Unicode NFC. This returns a new + string obtained by canonical decomposition of STRING followed by + canonical composition, a.k.a. the "Unicode Normalization Form C" + of STRING. + + -- Function: string-glyph-decompose string + Decompose STRING according to the Unicode NFD. This returns a new + string that is the canonical decomposition of STRING, a.k.a. the + "Unicode Normalization Form D" of STRING. + -- Macro: with-buffer-unmodified-if-unchanged &rest body... Evaluate BODY like ‘progn’, but change buffer-modified status only if buffer text changes. If the buffer was unmodified before @@ -2555,7 +2348,7 @@ by Compat. `(list ,x ,y ,x ,y (progn ,@forms)))) -2.5.2 Extended Definitions +2.4.2 Extended Definitions -------------------------- These functions must be called explicitly via ‘compat-call’, since their @@ -2657,7 +2450,7 @@ calling convention or behavior was extended in Emacs 29.1: This compatibility version handles the optional argument PREDICATE. -2.5.3 Missing Definitions +2.4.3 Missing Definitions ------------------------- Compat does not provide support for the following Lisp features @@ -2686,12 +2479,12 @@ implemented in 29.1: • Support for symbols with position information.  -File: docA0lLBy.info, Node: Emacs 30.1, Prev: Emacs 29.1, Up: Support +File: docvwepFX.info, Node: Emacs 30.1, Next: Emacs 31.1, Prev: Emacs 29.1, Up: Support -2.6 Emacs 30.1 +2.5 Emacs 30.1 ============== -2.6.1 Added Definitions +2.5.1 Added Definitions ----------------------- The following functions and macros are implemented in Emacs 30.1. These @@ -2891,7 +2684,7 @@ will be the need for changes, so use these functions with care. (let ((c-inside-line-break-advice t)) (c-indent-new-comment-line (ad-get-arg 0)))))) -2.6.2 Extended Definitions +2.5.2 Extended Definitions -------------------------- These functions must be called explicitly via ‘compat-call’, since their @@ -2992,14 +2785,235 @@ calling convention or behavior was extended in Emacs 30.1: it copies vectors and records too (and operates recursively on their elements). The TREE argument must not contain cycles. -2.6.3 Missing Definitions +2.5.3 Missing Definitions ------------------------- Compat does not provide support for the following Lisp features implemented in 30.1:  -File: docA0lLBy.info, Node: Development, Next: Function Index, Prev: Support, Up: Top +File: docvwepFX.info, Node: Emacs 31.1, Prev: Emacs 30.1, Up: Support + +2.6 Emacs 31.1 +============== + +2.6.1 Added Definitions +----------------------- + +The following functions and macros are implemented in Emacs 31.1. These +functions are made available by Compat on Emacs versions older than +31.1. Note that due to upstream changes, it might happen that there +will be the need for changes, so use these functions with care. + + -- Function: error-type-p symbol + This function returns non-‘nil’ if SYMBOL is a valid error + condition name. + + -- Function: error-has-type-p error condition + This function tests whether CONDITION is a parent of the error + symbol of the error descriptor ERROR. It returns non-‘nil’ if the + type of the error descriptor ERROR belongs to the condition name + CONDITION. + + -- Function: error-type error + This function returns the error symbol of the error descriptor + ERROR. + + -- Function: error-slot-value error pos + This function returns the value in the field number POS of the + error descriptor ERROR. The fields are numbered starting with 1. + E.g., for an error of type ‘wrong-type-argument’, + ‘(error-slot-value ERROR 2)’ returns the object that failed the + type test, and ‘(error-slot-value ERROR 1)’ returns the predicate + that failed. + + -- Function: ensure-proper-list object + This function returns OBJECT as a proper list (*note (elisp) Cons + Cells::). If OBJECT is already a proper list, the function returns + it; otherwise, the function returns a one-element list containing + OBJECT. + + If OBJECT might be a long list, prefer ‘ensure-list’, because the + latter function runs in constant time, whereas ‘ensure-proper-list’ + runs in linear time. For short lists this function is a convenient + way to treat cons-cells as non-lists: + + (ensure-list '(1 . 2)) + ⇒(1 . 2) + + (ensure-proper-list '(1 . 2)) + ⇒((1 . 2)) + + -- Function: set-local variable value + Make VARIABLE buffer local and set it to VALUE. + + -- Function: drop-while pred list + This function skips leading list elements for which the predicate + PRED returns non-‘nil’, and returns the rest. + + (drop-while #'numberp '(1 2 a b 3 4)) + ⇒ (a b 3 4) + + -- Function: take-while pred list + This function returns the leading list elements for which the + predicate PRED returns non-‘nil’, and ignores the rest. + + In general, ‘(append (take-while P LIST) (drop-while P LIST))’ will + return a list equal to LIST. + + (take-while #'numberp '(1 2 a b 3 4)) + ⇒ (1 2) + + -- Function: all pred list + This function returns ‘t’ if PRED is true for all elements in LIST. + + (all #'numberp '(1 2 3 4)) ⇒ t + (all #'numberp '(1 2 a b 3 4)) ⇒ nil + (all #'numberp '()) ⇒ t + + -- Function: any pred list + This function returns non-‘nil’ if PRED is true for at least one + element in LIST. The returned value is the longest LIST suffix + whose first element satisfies PRED. + + (any #'symbolp '(1 2 3 4)) ⇒ nil + (any #'symbolp '(1 2 a b 3 4)) ⇒ (a b 3 4) + (any #'symbolp '()) ⇒ nil + + -- Function: remove-display-text-property start end spec &optional + object + Remove the display specification SPEC from the text from START to + END. SPEC is the CAR of the display specification to remove, e.g. + ‘height’ or ‘'(margin nil)’. + + If any text in the region has any other ‘display’ properties, those + properties are retained. For instance: + + (add-display-text-property 1 8 'raise 0.5) + (add-display-text-property 4 8 'height 2.0) + (remove-display-text-property 2 6 'raise) + + After doing this, the text will have the following ‘display’ + properties: + + • The region from 1 to 2, only ‘raise’ + + • The region from 2 to 4, no properties + + • The region from 4 to 6, only ‘height’ + + • The region from 6 to 8, both ‘raise’ and ‘height’ + + OBJECT is either a string or a buffer to remove the specification + from. If omitted, OBJECT defaults to the current buffer. + + -- Macro: hash-table-contains-p key table + Return non-nil if TABLE has an element with KEY. + + -- Macro: static-when condition body... + Test CONDITION at macro-expansion time. If its value is non-‘nil’, + expand the macro to evaluate all BODY forms sequentially and return + the value of the last one, or ‘nil’ if there are none. + + -- Macro: static-unless condition body... + Test CONDITION at macro-expansion time. If its value is ‘nil’, + expand the macro to evaluate all BODY forms sequentially and return + the value of the last one, or ‘nil’ if there are none. + + -- Function: unbuttonize-region start end + This function removes all buttons between START and END in the + current buffer (both overlay and text-property based ones). + + -- Function: 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. + + -- Function: completion-list-candidate-at-point &optional pt + Candidate string and bounds at PT in completions buffer. The + return value has the format (STR BEG END). The optional argument + PT defaults to ‘(point)’. + + -- Macro: with-work-buffer &rest body + Create a work buffer, and evaluate BODY there like ‘progn’. Like + ‘with-temp-buffer’, but reuse an already created temporary buffer + when possible, instead of creating a new one on each call. + + -- Function: plusp number + This predicate tests whether its argument is positive, and returns + ‘t’ if so, ‘nil’ otherwise. The argument must be a number. + + -- Function: minusp number + This predicate tests whether its argument is negative, and returns + ‘t’ if so, ‘nil’ otherwise. The argument must be a number. + + -- Function: oddp integer + This predicate tests whether its argument is an odd number, and + returns ‘t’ if so, ‘nil’ otherwise. The argument must be an + integer. + + -- Function: evenp integer + This predicate tests whether its argument is an even number, and + returns ‘t’ if so, ‘nil’ otherwise. The argument must be an + integer. + + -- Macro: incf place &optional delta + This macro increments the number stored in PLACE by one, or by + DELTA if specified. It returns the incremented value. + + PLACE can be a symbol or a generalized variable, *note Generalized + Variables: (elisp)generalised variable. For example, ‘(incf i)’ is + equivalent to ‘(setq i (1+ i))’, and ‘(incf (car x) 2)’ is + equivalent to ‘(setcar x (+ (car x) 2))’. + + -- Macro: decf place &optional delta + This macro decrements the number stored in PLACE by one, or by + DELTA if specified. It returns the decremented value. + + -- Function: color-blend a b &optional alpha + Interpolate between the two colors A and B. Both arguments are a + list of three numbers ‘(RED GREEN BLUE)’ representing a coordinate + in a (0,1)-RGB color space. The optional argument ALPHA should be + a value between 0 and 1 and adjusts how strongly the function will + skew in either direction: Values towards 1 tend towards A, while + values towards 0 tend towards B. By default, the argument defaults + to 0.5. + +2.6.2 Extended Definitions +-------------------------- + +These functions must be called explicitly via ‘compat-call’, since their +calling convention or behavior was extended in Emacs 31.1: + + -- Function: compat-call seconds-to-string delay &optional readable + abbrev precision + Return a string describing a given DELAY (in seconds). By default, + this function formats the returned string as a floating-point + number in units selected according to the value of DELAY. For + example, a delay of 9861.5 seconds yields ‘2.74h’, since the value + of DELAY is longer than 1 hour, but shorter than 1 day. The output + formatting can be further controlled by the optional arguments, if + optional argument READABLE is non-‘nil’. If READABLE's value is + ‘expanded’, the returned string will describe DELAY using two + units; for example, a delay of 9861.5 seconds with READABLE set to + the symbol ‘expanded’ returns ‘2 hours 44 minutes’, but if READABLE + is ‘t’, the function returns ‘3 hours’. Optional argument ABBREV, + if non-‘nil’, means to abbreviate the units: use ‘h’ instead of + ‘hours’, ‘m’ instead of ‘minutes’, etc. If PRECISION is a whole + integer number, the function rounds the value of the smallest unit + it produces to that many digits after the decimal point; thus, + 9861.5 with PRECISION set to 3 yields ‘2.739 hours’. If PRECISION + is a non-negative float smaller than 1, the function rounds to that + value. + +2.6.3 Missing Definitions +------------------------- + +Compat does not provide support for the following Lisp features +implemented in 31.1: + + +File: docvwepFX.info, Node: Development, Next: Function Index, Prev: Support, Up: Top 3 Development ************* @@ -3009,9 +3023,7 @@ Compat is developed on GitHub. Bug reports, patches and comments are best sent to the issue tracker (https://github.com/emacs-compat/compat/issues). These may include issues in the compatibility code, missing definitions or performance -issues. We also provide a development mailing list -(https://lists.sr.ht/~pkal/compat-devel) (~pkal/compat-devel@lists.sr.ht -<~pkal/compat-devel@lists.sr.ht>). +issues. Please note that as a GNU ELPA package, Compat requires contributors to have signed the FSF copyright assignment @@ -3036,7 +3048,7 @@ version has been reasonably stabilized, e.g., around the time when the Emacs version branch is created in the Emacs repository on Savannah.  -File: docA0lLBy.info, Node: Function Index, Next: Variable Index, Prev: Development, Up: Top +File: docvwepFX.info, Node: Function Index, Next: Variable Index, Prev: Development, Up: Top Appendix A Function Index ************************* @@ -3045,12 +3057,12 @@ Appendix A Function Index * Menu: * add-display-text-property: Emacs 29.1. (line 196) -* alist-get: Emacs 25.1. (line 82) +* all: Emacs 31.1. (line 73) * always: Emacs 28.1. (line 83) * and-let*: Emacs 26.1. (line 158) +* any: Emacs 31.1. (line 80) * assoc-delete-all: Emacs 26.1. (line 13) * bignump: Emacs 27.1. (line 55) -* bool-vector: Emacs 25.1. (line 177) * bounds-of-thing-at-mouse: Emacs 28.1. (line 296) * buffer-hash: Emacs 26.1. (line 80) * buffer-local-boundp: Emacs 28.1. (line 129) @@ -3061,10 +3073,11 @@ Appendix A Function Index * buttonize-region: Emacs 29.1. (line 171) * char-to-name: Emacs 30.1. (line 50) * char-uppercase-p: Emacs 29.1. (line 133) -* cl-constantly: Emacs 29.1. (line 642) -* cl-once-only: Emacs 29.1. (line 665) -* cl-with-gensyms: Emacs 29.1. (line 646) +* cl-constantly: Emacs 29.1. (line 653) +* cl-once-only: Emacs 29.1. (line 676) +* cl-with-gensyms: Emacs 29.1. (line 657) * closurep: Emacs 30.1. (line 57) +* color-blend: Emacs 31.1. (line 179) * color-dark-p: Emacs 28.1. (line 326) * color-oklab-to-srgb: Emacs 30.1. (line 44) * color-oklab-to-xyz: Emacs 30.1. (line 34) @@ -3078,21 +3091,22 @@ Appendix A Function Index * compat-call completion-metadata-get: Emacs 30.1. (line 287) * compat-call copy-tree: Emacs 30.1. (line 297) * compat-call count-windows: Emacs 28.1. (line 411) -* compat-call define-key: Emacs 29.1. (line 762) +* compat-call define-key: Emacs 29.1. (line 773) * compat-call executable-find: Emacs 27.1. (line 429) * compat-call file-size-human-readable: Emacs 27.1. (line 397) * compat-call line-number-at-pos: Emacs 26.1. (line 330) * compat-call lookup-key: Emacs 27.1. (line 356) * compat-call make-temp-file: Emacs 26.1. (line 278) -* compat-call plist-get: Emacs 29.1. (line 778) -* compat-call plist-member: Emacs 29.1. (line 802) -* compat-call plist-put: Emacs 29.1. (line 790) +* compat-call plist-get: Emacs 29.1. (line 789) +* compat-call plist-member: Emacs 29.1. (line 813) +* compat-call plist-put: Emacs 29.1. (line 801) * compat-call recenter: Emacs 27.1. (line 346) * compat-call regexp-opt: Emacs 27.1. (line 385) -* compat-call set-transient-map: Emacs 29.1. (line 717) +* compat-call seconds-to-string: Emacs 31.1. (line 194) +* compat-call set-transient-map: Emacs 29.1. (line 728) * compat-call setq-local: Emacs 27.1. (line 370) * compat-call sort: Emacs 30.1. (line 212) -* compat-call string-lines: Emacs 29.1. (line 753) +* compat-call string-lines: Emacs 29.1. (line 764) * compat-call string-trim: Emacs 26.1. (line 375) * compat-call string-trim-left: Emacs 26.1. (line 359) * compat-call string-trim-right: Emacs 26.1. (line 367) @@ -3100,11 +3114,14 @@ Appendix A Function Index * compat-function: Usage. (line 58) * compiled-function-p: Emacs 29.1. (line 232) * completion-lazy-hilit: Emacs 30.1. (line 182) +* completion-list-candidate-at-point: Emacs 31.1. (line 138) +* completion-table-with-metadata: Emacs 31.1. (line 133) * count-sentences: Emacs 29.1. (line 31) * cXXXr: Emacs 26.1. (line 66) * cXXXXr: Emacs 26.1. (line 67) * date-days-in-month: Emacs 27.1. (line 213) * date-ordinal-to-time: Emacs 27.1. (line 220) +* decf: Emacs 31.1. (line 175) * decoded-time-day: Emacs 27.1. (line 175) * decoded-time-dst: Emacs 27.1. (line 195) * decoded-time-hour: Emacs 27.1. (line 170) @@ -3115,23 +3132,29 @@ Appendix A Function Index * decoded-time-weekday: Emacs 27.1. (line 190) * decoded-time-year: Emacs 27.1. (line 185) * decoded-time-zone: Emacs 27.1. (line 200) -* define-keymap: Emacs 29.1. (line 524) -* defvar-keymap: Emacs 29.1. (line 592) +* define-keymap: Emacs 29.1. (line 535) +* defvar-keymap: Emacs 29.1. (line 603) * delete-line: Emacs 29.1. (line 65) -* directory-abbrev-apply: Emacs 29.1. (line 388) -* directory-abbrev-make-regexp: Emacs 29.1. (line 385) +* directory-abbrev-apply: Emacs 29.1. (line 399) +* directory-abbrev-make-regexp: Emacs 29.1. (line 396) * directory-empty-p: Emacs 28.1. (line 247) -* directory-name-p: Emacs 25.1. (line 56) * dlet: Emacs 28.1. (line 147) * dolist-with-progress-reporter: Emacs 27.1. (line 119) * drop: Emacs 30.1. (line 110) +* drop-while: Emacs 31.1. (line 56) * ensure-list: Emacs 28.1. (line 162) -* ert-with-temp-directory: Emacs 29.1. (line 630) -* ert-with-temp-file: Emacs 29.1. (line 624) +* ensure-proper-list: Emacs 31.1. (line 36) +* error-has-type-p: Emacs 31.1. (line 18) +* error-slot-value: Emacs 31.1. (line 28) +* error-type: Emacs 31.1. (line 24) +* error-type-p: Emacs 31.1. (line 14) +* ert-with-temp-directory: Emacs 29.1. (line 641) +* ert-with-temp-file: Emacs 29.1. (line 635) +* evenp: Emacs 31.1. (line 161) * file-attribute-access-time: Emacs 26.1. (line 223) * file-attribute-collect: Emacs 26.1. (line 260) * file-attribute-device-number: Emacs 26.1. (line 255) -* file-attribute-file-identifier: Emacs 29.1. (line 345) +* file-attribute-file-identifier: Emacs 29.1. (line 356) * file-attribute-group-id: Emacs 26.1. (line 218) * file-attribute-inode-number: Emacs 26.1. (line 250) * file-attribute-link-number: Emacs 26.1. (line 208) @@ -3142,21 +3165,20 @@ Appendix A Function Index * file-attribute-type: Emacs 26.1. (line 203) * file-attribute-user-id: Emacs 26.1. (line 213) * file-backup-file-names: Emacs 28.1. (line 351) -* file-has-changed-p: Emacs 29.1. (line 370) +* file-has-changed-p: Emacs 29.1. (line 381) * file-local-name: Emacs 26.1. (line 167) * file-modes-number-to-symbolic: Emacs 28.1. (line 345) * file-name-concat: Emacs 28.1. (line 53) -* file-name-parent-directory: Emacs 29.1. (line 361) +* file-name-parent-directory: Emacs 29.1. (line 372) * file-name-quote: Emacs 26.1. (line 102) * file-name-quoted-p: Emacs 26.1. (line 95) -* file-name-split: Emacs 29.1. (line 351) +* file-name-split: Emacs 29.1. (line 362) * file-name-unquote: Emacs 26.1. (line 90) * file-name-with-extension: Emacs 28.1. (line 229) * file-size-human-readable-iec: Emacs 27.1. (line 251) * find-buffer: Emacs 30.1. (line 120) * fixnump: Emacs 27.1. (line 60) * flatten-tree: Emacs 27.1. (line 132) -* format-message: Emacs 25.1. (line 44) * format-prompt: Emacs 28.1. (line 258) * funcall-with-delayed-message: Emacs 29.1. (line 154) * function-alias-p: Emacs 29.1. (line 239) @@ -3165,33 +3187,30 @@ Appendix A Function Index * get-display-property: Emacs 29.1. (line 179) * get-scratch-buffer-create: Emacs 29.1. (line 44) * get-truename-buffer: Emacs 30.1. (line 115) -* hash-table-empty: Emacs 25.1. (line 119) -* if-let: Emacs 25.1. (line 96) +* hash-table-contains-p: Emacs 31.1. (line 116) * if-let*: Emacs 26.1. (line 149) * ignore-errors: Emacs 27.1. (line 106) * image-property: Emacs 26.1. (line 198) +* incf: Emacs 31.1. (line 166) * insert-into-buffer: Emacs 28.1. (line 92) * interpreted-function-p: Emacs 30.1. (line 62) -* key-parse: Emacs 29.1. (line 425) -* key-valid-p: Emacs 29.1. (line 393) -* keymap-global-lookup: Emacs 29.1. (line 520) -* keymap-global-set: Emacs 29.1. (line 454) -* keymap-global-unset: Emacs 29.1. (line 474) -* keymap-local-lookup: Emacs 29.1. (line 516) -* keymap-local-set: Emacs 29.1. (line 464) -* keymap-local-unset: Emacs 29.1. (line 489) -* keymap-lookup: Emacs 29.1. (line 503) -* keymap-set: Emacs 29.1. (line 430) -* keymap-substitute: Emacs 29.1. (line 495) +* key-parse: Emacs 29.1. (line 436) +* key-valid-p: Emacs 29.1. (line 404) +* keymap-global-lookup: Emacs 29.1. (line 531) +* keymap-global-set: Emacs 29.1. (line 465) +* keymap-global-unset: Emacs 29.1. (line 485) +* keymap-local-lookup: Emacs 29.1. (line 527) +* keymap-local-set: Emacs 29.1. (line 475) +* keymap-local-unset: Emacs 29.1. (line 500) +* keymap-lookup: Emacs 29.1. (line 514) +* keymap-set: Emacs 29.1. (line 441) +* keymap-substitute: Emacs 29.1. (line 506) * length<: Emacs 28.1. (line 45) * length=: Emacs 28.1. (line 42) * length>: Emacs 28.1. (line 50) * list-of-strings-p: Emacs 29.1. (line 68) * macroexp-file-name: Emacs 28.1. (line 305) -* macroexp-parse: Emacs 25.1. (line 174) -* macroexp-quote: Emacs 25.1. (line 171) * macroexp-warn-and-return: Emacs 28.1. (line 309) -* macroexpand-1: Emacs 25.1. (line 164) * major-mode-restore: Emacs 27.1. (line 22) * major-mode-suspend: Emacs 27.1. (line 13) * make-empty-file: Emacs 27.1. (line 254) @@ -3203,12 +3222,15 @@ Appendix A Function Index * match-buffers: Emacs 29.1. (line 298) * merge-ordered-lists: Emacs 30.1. (line 135) * minibuffer-history-value: Emacs 27.1. (line 32) +* minusp: Emacs 31.1. (line 152) * named-let: Emacs 28.1. (line 211) * native-comp-available-p: Emacs 28.1. (line 375) * ntake: Emacs 29.1. (line 219) * obarray-clear: Emacs 30.1. (line 54) +* oddp: Emacs 31.1. (line 156) * package-get-version: Emacs 27.1. (line 205) * plistp: Emacs 29.1. (line 71) +* plusp: Emacs 31.1. (line 148) * pos-bol: Emacs 29.1. (line 125) * pos-eol: Emacs 29.1. (line 129) * primitive-function-p: Emacs 30.1. (line 65) @@ -3220,21 +3242,23 @@ Appendix A Function Index * read-char-from-minibuffer: Emacs 27.1. (line 44) * read-multiple-choice: Emacs 26.1. (line 185) * readablep: Emacs 29.1. (line 34) -* region-bounds: Emacs 25.1. (line 28) -* region-noncontiguous-p: Emacs 25.1. (line 34) +* remove-display-text-property: Emacs 31.1. (line 89) * replace-regexp-in-string: Emacs 28.1. (line 108) * replace-string-in-region: Emacs 28.1. (line 102) * require-with-check: Emacs 30.1. (line 124) * ring-resize: Emacs 27.1. (line 28) -* save-mark-and-excursion: Emacs 25.1. (line 39) +* set-local: Emacs 31.1. (line 53) * static-if: Emacs 30.1. (line 187) +* static-unless: Emacs 31.1. (line 124) +* static-when: Emacs 31.1. (line 119) * string-chop-newline: Emacs 28.1. (line 206) * string-clean-whitespace: Emacs 28.1. (line 175) * string-distance: Emacs 27.1. (line 86) * string-equal-ignore-case: Emacs 29.1. (line 252) * string-fill: Emacs 28.1. (line 182) +* string-glyph-compose: Emacs 29.1. (line 330) +* string-glyph-decompose: Emacs 29.1. (line 336) * string-glyph-split: Emacs 29.1. (line 307) -* string-greaterp: Emacs 25.1. (line 64) * string-lines: Emacs 28.1. (line 190) * string-pad: Emacs 28.1. (line 197) * string-replace: Emacs 28.1. (line 77) @@ -3243,38 +3267,37 @@ Appendix A Function Index * subr-primitive-p: Emacs 28.1. (line 368) * substitute-quotes: Emacs 29.1. (line 40) * take: Emacs 29.1. (line 205) +* take-while: Emacs 31.1. (line 63) * temporary-file-directory: Emacs 26.1. (line 137) * text-property-search-backward: Emacs 27.1. (line 332) * text-property-search-forward: Emacs 27.1. (line 260) * text-quoting-style: Emacs 28.1. (line 28) * thing-at-mouse: Emacs 28.1. (line 289) -* thread-first: Emacs 25.1. (line 122) -* thread-last: Emacs 25.1. (line 143) * time-equal-p: Emacs 27.1. (line 208) * trusted-content-p: Emacs 30.1. (line 29) +* unbuttonize-region: Emacs 31.1. (line 129) * use-region-beginning: Emacs 29.1. (line 53) * use-region-end: Emacs 29.1. (line 50) * use-region-noncontiguous-p: Emacs 29.1. (line 47) * value<: Emacs 30.1. (line 69) -* when-let: Emacs 25.1. (line 114) * when-let*: Emacs 26.1. (line 153) -* while-let: Emacs 29.1. (line 612) -* window-configuration-equal-p: Emacs 29.1. (line 618) -* with-buffer-unmodified-if-unchanged: Emacs 29.1. (line 330) +* while-let: Emacs 29.1. (line 623) +* window-configuration-equal-p: Emacs 29.1. (line 629) +* with-buffer-unmodified-if-unchanged: Emacs 29.1. (line 341) * with-delayed-message: Emacs 29.1. (line 137) * with-environment-variables: Emacs 28.1. (line 313) * with-existing-directory: Emacs 28.1. (line 136) -* with-file-modes: Emacs 25.1. (line 71) * with-memoization: Emacs 29.1. (line 74) * with-minibuffer-selected-window: Emacs 27.1. (line 38) * with-restriction: Emacs 29.1. (line 79) * with-suppressed-warnings: Emacs 27.1. (line 65) * with-window-non-dedicated: Emacs 28.1. (line 383) +* with-work-buffer: Emacs 31.1. (line 143) * without-restriction: Emacs 29.1. (line 113) * xor: Emacs 27.1. (line 142)  -File: docA0lLBy.info, Node: Variable Index, Prev: Function Index, Up: Top +File: docvwepFX.info, Node: Variable Index, Prev: Function Index, Up: Top Appendix B Variable Index ************************* @@ -3289,33 +3312,32 @@ Appendix B Variable Index * lisp-directory: Emacs 29.1. (line 23) * mounted-file-systems: Emacs 26.1. (line 133) * regexp-unmatchable: Emacs 27.1. (line 152) -* set-transient-map-timeout: Emacs 29.1. (line 743) -* text-quoting-style: Emacs 25.1. (line 13) +* set-transient-map-timeout: Emacs 29.1. (line 754) * trusted-content: Emacs 30.1. (line 14) * untrusted-content: Emacs 29.1. (line 17) -  Tag Table: -Node: Top831 -Node: Introduction2328 -Node: Overview2491 -Node: Usage3017 -Node: Limitations7396 -Node: Support12246 -Node: Emacs 25.112899 -Node: Emacs 26.121371 -Node: Emacs 27.138655 -Node: Emacs 28.159021 -Node: Emacs 29.179131 -Node: Emacs 30.1116768 -Node: Development131578 -Node: Function Index133312 -Node: Variable Index150131 +Node: Top829 +Node: Introduction2325 +Node: Overview2488 +Node: Usage3014 +Node: Limitations7055 +Node: Support11917 +Node: Emacs 26.112570 +Node: Emacs 27.129835 +Node: Emacs 28.150201 +Node: Emacs 29.170354 +Node: Emacs 30.1108494 +Node: Emacs 31.1123323 +Node: Development132678 +Node: Function Index134260 +Node: Variable Index151882  End Tag Table  Local Variables: coding: utf-8 +Info-documentlanguage: en End: diff --git a/lisp/cond-let/cond-let-pkg.el b/lisp/cond-let/cond-let-pkg.el index fca99cdc..41b9ab84 100644 --- a/lisp/cond-let/cond-let-pkg.el +++ b/lisp/cond-let/cond-let-pkg.el @@ -1,8 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "cond-let" "20260201.1500" +(define-package "cond-let" "20260601.1457" "Additional and improved binding conditionals." '((emacs "28.1")) :url "https://github.com/tarsius/cond-let" - :commit "8bf87d45e169ebc091103b2aae325aece3aa804d" - :revdesc "8bf87d45e169" - :keywords '("extensions")) + :commit "21b9e9835756ff5cd1acb971cf9eb56fff671c8b" + :revdesc "21b9e9835756" + :keywords '("extensions") + :authors '(("Jonas Bernoulli" . "emacs.cond-let@jonas.bernoulli.dev")) + :maintainers '(("Jonas Bernoulli" . "emacs.cond-let@jonas.bernoulli.dev"))) diff --git a/lisp/cond-let/cond-let.el b/lisp/cond-let/cond-let.el index 9539944e..6e789e1b 100644 --- a/lisp/cond-let/cond-let.el +++ b/lisp/cond-let/cond-let.el @@ -5,12 +5,12 @@ ;; May contain traces of Emacs, which is ;; Copyright (C) 1985-2025 Free Software Foundation, Inc. -;; Authors: Jonas Bernoulli +;; Author: Jonas Bernoulli ;; Homepage: https://github.com/tarsius/cond-let ;; Keywords: extensions -;; Package-Version: 20260201.1500 -;; Package-Revision: 8bf87d45e169 +;; Package-Version: 20260601.1457 +;; Package-Revision: 21b9e9835756 ;; Package-Requires: ((emacs "28.1")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -30,16 +30,12 @@ ;;; Commentary: -;; This is a BETA release! -;; Breaking changes are unlikely but still possible! -;; See https://github.com/tarsius/cond-let/wiki. - ;; Emacs provides the binding conditionals `if-let', `if-let*', ;; `when-let', `when-let*', `and-let*' and `while-let'. -;; This package implements the missing `and-let' and `while-let*', +;; This package implements the missing `and-let' and `while-let*'; ;; and the original `cond-let', `cond-let*', `when$', `and$' and -;; `and>'. +;; `thread$'. ;; This package additionally provides more consistent and improved ;; implementations of the binding conditionals already provided by @@ -59,13 +55,17 @@ ;; Local Variables: ;; read-symbol-shorthands: ( -;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") -;; ("when$" . "cond-let--when$") -;; ("when-let" . "cond-let--when-let") -;; ("while-let" . "cond-let--while-let")) +;; ("and$" . "cond-let--and$") +;; ("thread$" . "cond-let--thread$") +;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") +;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") +;; ("while-let" . "cond-let--while-let")) ;; End: ;; You can think of these file-local settings as import statements of @@ -76,8 +76,8 @@ ;; Due to limitations of the shorthand implementation this has to be ;; done for each individual library. "dir-locals.el" cannot be used. -;; If you use `when$', `and$' and `and>', you might want to add this -;; to your configuration: +;; If you use `when$', `and$' and `thread$', you might want to add +;; this to your configuration: ;; (with-eval-after-load 'cond-let ;; (font-lock-add-keywords 'emacs-lisp-mode @@ -198,7 +198,8 @@ remaining clauses and binding vectors. Evaluate all VALUEFORMs before binding their respective SYMBOLs. Unlike for the previous form, bind all SYMBOLs, even if a VALUEFORM yields nil. Always proceed to the next clause." - (declare (indent 0) (debug cond-let*)) + (declare (indent 0) + (debug cond-let*)) (let ((tag (gensym ":cond-let"))) `(catch ',tag ,@(cond-let--prepare-clauses tag nil clauses)))) @@ -289,7 +290,8 @@ nil, and evaluate neither the remaining VALUEFORMs nor BODYFORM. If all VALUEFORMs yield non-nil, evaluate BODYFORM with the bindings in effect, and return its value; or if there is no BODYFORM, the value of the last VALUEFORM." - (declare (indent 1) (debug cond-let--and-let*)) + (declare (indent 1) + (debug cond-let--and-let*)) (pcase-let ((`(,anon ,set ,bind ,lastvar) (cond-let--prepare-varforms varlist))) (cond (anon @@ -303,19 +305,10 @@ VALUEFORM." `(and ,lastvar ,bodyform) lastvar)))))) -(defmacro cond-let--and$ (varform bodyform) - "Bind variable `$' to value of VARFORM and conditionally evaluate BODYFORM. +;;; Thread -If VARFORM yields a non-nil value, bind the symbol `$' to that value, -evaluate BODYFORM with that binding in effect, and return the value of -BODYFORM. If VARFORM yields nil, do not evaluate BODYFORM, and return -nil." - (declare (debug (form form))) - `(let (($ ,varform)) - (and $ ,bodyform))) - -(defmacro cond-let--and> (form form2 &rest forms) - "Bind variables according to each VARFORM until one of them yields nil. +(defmacro cond-let--and$ (form form2 &rest forms) + "Bind variables according to each FORM until one of them yields nil. Evaluate the first FORM and if that yields a non-nil value, bind the symbol `$' to that value, and evaluate the next FORM with that binding @@ -324,7 +317,8 @@ nil, then return nil without evaluate the remaining FORMs. If all FORMs yield non-nil, return the value of the last FORM. \(fn FORM FORM...)" - (declare (debug (form form body))) + (declare (indent 0) + (debug t)) `(,(if forms 'let* 'let) (($ ,form) ,@(and forms @@ -335,6 +329,25 @@ FORMs yield non-nil, return the value of the last FORM. ,(or (car (last forms)) form2)))) +(defmacro cond-let--thread$ (form form2 &rest forms) + "Bind variable `$' to value of nth FORM before evaluating nth+1 FORM. + +Evaluate the first FORM and bind the symbol `$' to its value. +Then evaluate the next FORM with that binding in effect. Repeat this +process with subsequent FORMs, and return the value of the last FORM. + +\(fn FORM FORM...)" + (declare (indent 0) + (debug t)) + `(,(if forms 'let* 'let) + (($ ,form) + ,@(and forms + (mapcar (lambda (form) + `($ ,form)) + (cons form2 (butlast forms))))) + ,(or (car (last forms)) + form2))) + ;;; If (defmacro cond-let--if-let* (varlist then &rest else) @@ -378,7 +391,8 @@ value of the last form; or if there are no ELSE forms return nil. The bindings from VARLIST do _not_ extend to the ELSE forms. \(fn VARLIST THEN [ELSE...])" - (declare (indent 2) (debug cond-let--if-let*)) + (declare (indent 2) + (debug cond-let--if-let*)) (pcase-let* ((`(,anon ,set ,bind ,_) (cond-let--prepare-varforms varlist t)) (set (if (length= set 1) (car set) (cons 'and set)))) @@ -432,7 +446,8 @@ BODY must be one or more expressions. If VARLIST is empty, do nothing and return nil. \(fn VARLIST BODY...)" - (declare (indent 1) (debug cond-let--when-let*)) + (declare (indent 1) + (debug cond-let--when-let*)) (pcase-let ((`(,anon ,set ,bind ,lastvar) (cond-let--prepare-varforms varlist))) (cond (anon @@ -454,8 +469,9 @@ last form. If VARFORM yields nil, do not evaluate BODY, and return nil. BODY must be one or more expressions. If VARLIST is empty, do nothing and return nil. -\(fn VARLIST BODY...)" - (declare (indent 1) (debug (form form))) +\(fn VARFORM BODY...)" + (declare (indent 1) + (debug t)) `(let (($ ,varform)) (when $ ,bodyform ,@body))) @@ -478,7 +494,8 @@ nor the BODY forms, and instead return, always yielding nil. BODY can be zero or more expressions. \(fn VARLIST [BODY...])" - (declare (indent 1) (debug cond-let--if-let*)) + (declare (indent 1) + (debug ((&rest (symbolp form)) body))) (pcase-let ((`(,varlist ,lastvar) (cond-let--prepare-varlist varlist)) (tag (gensym ":while-let*"))) @@ -505,7 +522,8 @@ nor the BODY forms, and instead return, always yielding nil. BODY can be one or more expressions. \(fn VARLIST BODY...)" - (declare (indent 1) (debug cond-let--if-let*)) + (declare (indent 1) + (debug ((&rest (symbolp form)) form body))) (pcase-let ((`(,anon ,set ,bind ,lastvar) (cond-let--prepare-varforms varlist)) (tag (gensym ":while-let"))) @@ -533,5 +551,18 @@ BODY can be one or more expressions. To add these keywords, add this to your configuration: \(font-lock-add-keywords \\='emacs-lisp-mode cond-let-font-lock-keywords t)") +;;; Compatibility + +(defalias 'cond-let--and> #'cond-let--and$ + "Instead of this alias, use `cond-let--and$' via `and$' shorthand. + +This alias will likely be declared obsolete in 2027. If you would like +to continue to use it, please get in contact before then. This alias +might eventually be removed altogether; again subject to user feedback. + +If you do not care about the symbol `cond-let--and>', but want to keep +using the respective `and>' shorthand, you can future-proof that now, +by changing the shorthand definition to (\"and>\" . \"cond-let--and$\").") + (provide 'cond-let) ;;; cond-let.el ends here diff --git a/lisp/diff-hl/diff-hl-amend.el b/lisp/diff-hl/diff-hl-amend.el index 95f2b384..c2996c38 100644 --- a/lisp/diff-hl/diff-hl-amend.el +++ b/lisp/diff-hl/diff-hl-amend.el @@ -44,7 +44,7 @@ Currently only supports Git, Mercurial and Bazaar." (diff-hl-update))) (defun diff-hl-amend-setup () - (let ((backend (vc-backend buffer-file-name))) + (let ((backend (vc-backend (diff-hl--buffer-file-name)))) (when backend (setq-local diff-hl-reference-revision (cl-case backend @@ -62,7 +62,7 @@ Currently only supports Git, Mercurial and Bazaar." (defun turn-on-diff-hl-amend-mode () "Turn on `diff-hl-amend-mode' in a buffer if appropriate." - (and buffer-file-name (diff-hl-amend-mode 1))) + (and (diff-hl--buffer-file-name) (diff-hl-amend-mode 1))) (provide 'diff-hl-amend) diff --git a/lisp/diff-hl/diff-hl-autoloads.el b/lisp/diff-hl/diff-hl-autoloads.el index 032ae940..fe1ee568 100644 --- a/lisp/diff-hl/diff-hl-autoloads.el +++ b/lisp/diff-hl/diff-hl-autoloads.el @@ -189,7 +189,7 @@ disabled. (fn &optional ARG)" t) (autoload 'diff-hl-dired-mode-unless-remote "diff-hl-dired") -(register-definition-prefixes "diff-hl-dired" '("diff-hl-dired-")) +(register-definition-prefixes "diff-hl-dired" '("diff-hl-dir")) ;;; Generated autoloads from diff-hl-flydiff.el diff --git a/lisp/diff-hl/diff-hl-dired.el b/lisp/diff-hl/diff-hl-dired.el index 4a643af4..3b6fcf0f 100644 --- a/lisp/diff-hl/diff-hl-dired.el +++ b/lisp/diff-hl/diff-hl-dired.el @@ -1,6 +1,6 @@ ;;; diff-hl-dired.el --- Highlight changed files in Dired -*- lexical-binding: t -*- -;; Copyright (C) 2012-2017, 2023 Free Software Foundation, Inc. +;; Copyright (C) 2012-2017, 2023-2026 Free Software Foundation, Inc. ;; This file is part of GNU Emacs. @@ -86,7 +86,7 @@ status indicators." (progn (diff-hl-maybe-define-bitmaps) (set (make-local-variable 'diff-hl-dired-process-buffer) nil) - (add-hook 'dired-after-readin-hook 'diff-hl-dired-update nil t)) + (add-hook 'dired-after-readin-hook 'diff-hl-dired-update 10 t)) (remove-hook 'dired-after-readin-hook 'diff-hl-dired-update t) (diff-hl-dired-clear))) @@ -97,7 +97,6 @@ status indicators." (buffer (current-buffer)) dirs-alist files-alist) (when (and backend (not (memq backend diff-hl-dired-ignored-backends))) - (diff-hl-dired-clear) (if (buffer-live-p diff-hl-dired-process-buffer) (let ((proc (get-buffer-process diff-hl-dired-process-buffer))) (when proc (kill-process proc))) @@ -109,30 +108,29 @@ status indicators." (diff-hl-dired-status-files backend def-dir (when diff-hl-dired-extra-indicators - (cl-loop for file in (directory-files def-dir) - unless (member file '("." ".." ".hg")) - collect file)) + (with-current-buffer buffer + (diff-hl-dired-nondirectory-files))) (lambda (entries &optional more-to-come) (when (buffer-live-p buffer) (with-current-buffer buffer (dolist (entry entries) (cl-destructuring-bind (file state &rest r) entry - ;; Work around http://debbugs.gnu.org/18605 - (setq file (replace-regexp-in-string "\\` " "" file)) - (let ((type (plist-get - '( edited change added insert removed delete - unregistered unknown ignored ignored) - state))) - (if (string-match "\\`\\([^/]+\\)/" file) - (let* ((dir (match-string 1 file)) - (value (cdr (assoc dir dirs-alist)))) + (unless (eq state 'up-to-date) + (let ((type (plist-get '( edited change added insert removed delete + unregistered unknown ignored ignored) + state)) + (dirs (cl-loop with pos = 0 + while (string-match "/" file pos) + do (setq pos (match-end 0)) + collect (substring file 0 (1- pos))))) + (dolist (dir dirs) + (let ((value (cdr (assoc dir dirs-alist)))) (unless (eq value type) (cond - ((eq state 'up-to-date)) ((null value) (push (cons dir type) dirs-alist)) ((not (eq type 'ignored)) - (setcdr (assoc dir dirs-alist) 'change))))) + (setcdr (assoc dir dirs-alist) 'change)))))) (push (cons file type) files-alist))))) (unless more-to-come (diff-hl-dired-highlight-items @@ -142,19 +140,84 @@ status indicators." ))))) (defun diff-hl-dired-status-files (backend dir files update-function) - "Using version control BACKEND, return list of (FILE STATE EXTRA) entries -for DIR containing FILES. Call UPDATE-FUNCTION as entries are added." - (vc-call-backend backend 'dir-status-files dir files update-function)) + "Using VC BACKEND, fetch list of (FILE STATE EXTRA) entries for DIR. +Call UPDATE-FUNCTION as entries are added." + (vc-call-backend + backend 'dir-status-files + dir nil + (lambda (entries &optional more-to-come) + (if (or more-to-come + (not diff-hl-dired-extra-indicators)) + (funcall update-function entries more-to-come) + (diff-hl-dir-status-ignored-files + backend + dir + files + (lambda (ignored-entries &optional more-to-come) + (funcall update-function ignored-entries t) + (unless more-to-come + (funcall update-function entries nil)))) + )))) + +(defun diff-hl-dired-nondirectory-files () + (cl-mapcan + (lambda (entry) + (let* ((dir (file-relative-name (car entry))) + (all (file-name-all-completions "" dir)) + res) + (dolist (file all) + (unless (directory-name-p file) + (push + (if (equal dir "./") + file + (concat dir file)) + res))) + res)) + dired-subdir-alist)) + +(declare-function vc-git-dir-status-goto-stage "vc-git") +(declare-function make-vc-git-dir-status-state "vc-git") +(declare-function vc-hg-command "vc-hg") +(declare-function vc-hg--program-version "vc-hg") +(declare-function vc-hg-after-dir-status "vc-hg") + +(defun diff-hl-dir-status-ignored-files (backend dir files update-function) + (cond + ((eq backend 'Git) + (vc-git-dir-status-goto-stage + (make-vc-git-dir-status-state :stage 'ls-files-ignored + :files files + :update-function update-function))) + ((eq backend 'Hg) + (let ((default-directory dir)) + (erase-buffer) + (apply #'vc-hg-command (current-buffer) 'async files + "status" "-i" + (if (version<= "4.2" (vc-hg--program-version)) + '("--config" "commands.status.relative=1") + '("re:" "-I" ".")))) + (static-if (fboundp 'vc-run-delayed-success) + (vc-run-delayed-success 0 + (vc-hg-after-dir-status update-function)) + (vc-run-delayed + (vc-hg-after-dir-status update-function)))) + ;; No specialized solution for "list only ignored state", list all. + ;; If the backend doesn't use several process calls (like Git), the + ;; difference should be trivial. + (t + (vc-call-backend backend 'dir-status-files dir files + update-function)))) (defun diff-hl-dired-highlight-items (alist) "Highlight ALIST containing (FILE . TYPE) elements." + (diff-hl-dired-clear) ;; clear overlays right before drawing to avoid flicker (dolist (pair alist) (let ((file (car pair)) (type (cdr pair))) (save-excursion (goto-char (point-min)) (when (and type (dired-goto-file-1 - file (expand-file-name file) nil)) + (file-name-nondirectory file) (expand-file-name file) nil)) (let* ((diff-hl-fringe-bmp-function diff-hl-dired-fringe-bmp-function) (diff-hl-fringe-face-function 'diff-hl-dired-face-from-type) (o (diff-hl-add-highlighting type 'single))) diff --git a/lisp/diff-hl/diff-hl-flydiff.el b/lisp/diff-hl/diff-hl-flydiff.el index 1a6b11f6..04a67166 100644 --- a/lisp/diff-hl/diff-hl-flydiff.el +++ b/lisp/diff-hl/diff-hl-flydiff.el @@ -1,4 +1,4 @@ -;; Copyright (C) 2015-2025 Free Software Foundation, Inc. -*- lexical-binding: t -*- +;; Copyright (C) 2015-2026 Free Software Foundation, Inc. -*- lexical-binding: t -*- ;; Author: Jonathan Hayase ;; URL: https://github.com/dgutov/diff-hl @@ -42,7 +42,6 @@ (defun diff-hl-flydiff-changes-buffer (file backend &optional new-rev buffer) (setq buffer (or buffer " *diff-hl-diff*")) - (setq diff-hl-flydiff-modified-tick (buffer-chars-modified-tick)) (if new-rev (diff-hl-with-diff-switches (diff-hl-diff-against-reference file backend buffer new-rev)) @@ -52,13 +51,16 @@ (unless (or (not diff-hl-mode) (eq diff-hl-flydiff-modified-tick (buffer-chars-modified-tick)) - (not buffer-file-name) - (file-remote-p default-directory) - (not (file-exists-p buffer-file-name))) + (let ((file (diff-hl--buffer-file-name))) + (or (not file) + (file-remote-p default-directory) + (not (file-exists-p file))))) + (setq diff-hl-flydiff-modified-tick (buffer-chars-modified-tick)) (diff-hl-update))) -(defun diff-hl-flydiff/modified-p (_state) - (buffer-modified-p)) +(defun diff-hl-flydiff/modified-p (state) + (unless (memq state '(added missing nil)) + (buffer-modified-p))) ;;;###autoload (define-minor-mode diff-hl-flydiff-mode diff --git a/lisp/diff-hl/diff-hl-margin.el b/lisp/diff-hl/diff-hl-margin.el index 26c1e718..d5b2abc6 100644 --- a/lisp/diff-hl/diff-hl-margin.el +++ b/lisp/diff-hl/diff-hl-margin.el @@ -132,13 +132,15 @@ You probably shouldn't use this function directly." #'diff-hl-highlight-on-margin) (setq-local diff-hl-highlight-reference-function #'diff-hl-highlight-on-margin-flat) - (setq-local diff-hl-margin-old-width (symbol-value width-var)) - (set width-var 1)) + (when (zerop (symbol-value width-var)) + (setq-local diff-hl-margin-old-width (symbol-value width-var)) + (set width-var 1))) (when diff-hl-margin-old-highlight-function (setq diff-hl-highlight-function diff-hl-margin-old-highlight-function diff-hl-highlight-reference-function diff-hl-margin-old-highlight-ref-function diff-hl-margin-old-highlight-function nil)) - (set width-var diff-hl-margin-old-width) + (when diff-hl-margin-old-width + (set width-var diff-hl-margin-old-width)) (kill-local-variable 'diff-hl-margin-old-width))) (dolist (win (get-buffer-window-list)) (set-window-buffer win (current-buffer)))) diff --git a/lisp/diff-hl/diff-hl-pkg.el b/lisp/diff-hl/diff-hl-pkg.el index 1e7662be..088860ff 100644 --- a/lisp/diff-hl/diff-hl-pkg.el +++ b/lisp/diff-hl/diff-hl-pkg.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "diff-hl" "20260328.1925" +(define-package "diff-hl" "20260627.208" "Highlight uncommitted changes using VC." '((cl-lib "0.2") - (emacs "26.1")) + (emacs "27.1")) :url "https://github.com/dgutov/diff-hl" - :commit "b965e19e6e7f9933199e421849a49229207c1c9f" - :revdesc "b965e19e6e7f" + :commit "2d7d0714d9637f54af672987c65b6973b31e56a2" + :revdesc "2d7d0714d963" :keywords '("vc" "diff") :authors '(("Dmitry Gutov" . "dmitry@gutov.dev")) :maintainers '(("Dmitry Gutov" . "dmitry@gutov.dev"))) diff --git a/lisp/diff-hl/diff-hl-show-hunk-inline.el b/lisp/diff-hl/diff-hl-show-hunk-inline.el index 3b5214bb..2ee6f85b 100644 --- a/lisp/diff-hl/diff-hl-show-hunk-inline.el +++ b/lisp/diff-hl/diff-hl-show-hunk-inline.el @@ -74,6 +74,13 @@ the hunk consist only on added lines, then `diff-hl-show-hunk--no-lines-removed-message' it is shown." :type 'boolean) +(defcustom diff-hl-show-hunk-inline-scroll-indicators '(" ⬆ " . " ⬇ ") + "Strings used to indicate hidden inline popup content. +The car is used for hidden content above the popup; the cdr is used for +hidden content below it. Each string includes any surrounding padding." + :type '(cons (string :tag "Above") + (string :tag "Below"))) + (defun diff-hl-show-hunk-inline--splice (list offset length) "Compute a sublist of LIST starting at OFFSET, of LENGTH." (butlast @@ -105,27 +112,37 @@ Compute it from LINES starting at INDEX with a WINDOW-SIZE." (index (min index (- len window-size)))) (diff-hl-show-hunk-inline--splice lines index window-size))) +(defun diff-hl-show-hunk-inline--underline-face () + "Return the face used for inline popup underlines." + `(:underline ,(if (>= emacs-major-version 29) '(:position t) t))) + (defun diff-hl-show-hunk-inline--compute-header (width &optional header) "Compute the header of the popup. Compute it from some WIDTH, and some optional HEADER text." - (let* ((scroll-indicator (if (eq diff-hl-show-hunk-inline--current-index 0) " " " ⬆ ")) + (let* ((above-indicator (car diff-hl-show-hunk-inline-scroll-indicators)) + (scroll-indicator + (if (eq diff-hl-show-hunk-inline--current-index 0) + (make-string (length above-indicator) ?\s) + above-indicator)) (header (or header "")) (new-width (- width (length header) (length scroll-indicator))) (header (if (< new-width 0) "" header)) (new-width (- width (length header) (length scroll-indicator))) (line (propertize (concat (diff-hl-show-hunk-inline--separator new-width) header scroll-indicator ) - 'face '(:underline t)))) + 'face (diff-hl-show-hunk-inline--underline-face)))) (concat line "\n") )) (defun diff-hl-show-hunk-inline--compute-footer (width &optional footer) "Compute the header of the popup. Compute it from some WIDTH, and some optional FOOTER text." - (let* ((scroll-indicator (if (>= diff-hl-show-hunk-inline--current-index - (- (length diff-hl-show-hunk-inline--current-lines) - diff-hl-show-hunk-inline--height)) - " " - " ⬇ ")) + (let* ((below-indicator (cdr diff-hl-show-hunk-inline-scroll-indicators)) + (scroll-indicator + (if (>= diff-hl-show-hunk-inline--current-index + (- (length diff-hl-show-hunk-inline--current-lines) + diff-hl-show-hunk-inline--height)) + (make-string (length below-indicator) ?\s) + below-indicator)) (footer (or footer "")) (new-width (- width (length footer) (length scroll-indicator))) (footer (if (< new-width 0) "" footer)) @@ -133,7 +150,7 @@ Compute it from some WIDTH, and some optional FOOTER text." (blank-line (if (display-graphic-p) "" (concat "\n" (propertize (diff-hl-show-hunk-inline--separator width) - 'face '(:underline t))))) + 'face (diff-hl-show-hunk-inline--underline-face))))) (line (propertize (concat (diff-hl-show-hunk-inline--separator new-width) footer scroll-indicator) 'face '(:overline t)))) diff --git a/lisp/diff-hl/diff-hl-show-hunk.el b/lisp/diff-hl/diff-hl-show-hunk.el index 641683d7..c9b21788 100644 --- a/lisp/diff-hl/diff-hl-show-hunk.el +++ b/lisp/diff-hl/diff-hl-show-hunk.el @@ -129,15 +129,16 @@ Then put the differences inside a special buffer and set the point in that buffer to the corresponding line of the original buffer." (defvar vc-sentinel-movepoint) - (let* ((buffer (or (buffer-base-buffer) (current-buffer))) + (let* ((buffer (current-buffer)) (diff-hl-update-async nil) (line (line-number-at-pos)) (dest-buffer diff-hl-show-hunk-diff-buffer-name)) (with-current-buffer buffer - (if (buffer-modified-p) - (diff-hl-diff-buffer-with-reference buffer-file-name dest-buffer) - (diff-hl-changes-buffer buffer-file-name (vc-backend buffer-file-name) - nil dest-buffer)) + (let ((file (diff-hl--buffer-file-name))) + (if (buffer-modified-p) + (diff-hl-diff-buffer-with-reference file dest-buffer) + (diff-hl-changes-buffer file (vc-backend file) + nil dest-buffer))) (switch-to-buffer dest-buffer) (diff-hl-diff-skip-to line) (setq vc-sentinel-movepoint (point))) @@ -303,7 +304,7 @@ end of the OVERLAY, so posframe/inline is placed below the hunk." The backend is determined by `diff-hl-show-hunk-function'." (interactive) - (unless (vc-backend buffer-file-name) + (unless (vc-backend (diff-hl--buffer-file-name)) (user-error "The buffer is not under version control")) (diff-hl-find-current-hunk) diff --git a/lisp/diff-hl/diff-hl.el b/lisp/diff-hl/diff-hl.el index 067686ab..92a5b1f1 100644 --- a/lisp/diff-hl/diff-hl.el +++ b/lisp/diff-hl/diff-hl.el @@ -1,13 +1,13 @@ ;;; diff-hl.el --- Highlight uncommitted changes using VC -*- lexical-binding: t -*- -;; Copyright (C) 2012-2025 Free Software Foundation, Inc. +;; Copyright (C) 2012-2026 Free Software Foundation, Inc. ;; Author: Dmitry Gutov ;; URL: https://github.com/dgutov/diff-hl ;; Keywords: vc, diff -;; Package-Version: 20260328.1925 -;; Package-Revision: b965e19e6e7f -;; Package-Requires: ((cl-lib "0.2") (emacs "26.1")) +;; Package-Version: 20260627.208 +;; Package-Revision: 2d7d0714d963 +;; Package-Requires: ((cl-lib "0.2") (emacs "27.1")) ;; This file is part of GNU Emacs. @@ -315,6 +315,16 @@ It can be a relative expression as well, such as \"HEAD^\" with Git, or (lambda (value) (or (null value) (stringp value)))) +(defun diff-hl--target-buffer (&optional buf) + "Return the correct buffer for the situation, preferring the base buffer." + (let ((buf (or buf (current-buffer)))) + (or (buffer-base-buffer buf) buf))) + +(defun diff-hl--buffer-file-name (&optional buffer) + "Return the file name of the BUFFER or its base buffer. +BUFFER defaults to the current buffer." + (buffer-file-name (diff-hl--target-buffer buffer))) + (defun diff-hl-define-bitmaps () (let* ((scale (if (and (boundp 'text-scale-mode-amount) (numberp text-scale-mode-amount)) @@ -486,7 +496,7 @@ It can be a relative expression as well, such as \"HEAD^\" with Git, or buffer) (defun diff-hl-changes () - (let* ((file buffer-file-name) + (let* ((file (diff-hl--buffer-file-name)) (backend (vc-backend file)) (hide-staged (and (eq backend 'Git) (not diff-hl-show-staged-changes)))) (when backend @@ -531,7 +541,7 @@ It can be a relative expression as well, such as \"HEAD^\" with Git, or (or (assoc-default backend diff-hl-head-revision-alist) ;; It's usually cached already (e.g. for mode-line). ;; So this is basically an optimization for rare cases. - (vc-working-revision buffer-file-name backend))) + (vc-working-revision (diff-hl--buffer-file-name) backend))) (defun diff-hl-adjust-changes (old new) "Adjust changesets in OLD using changes in NEW. @@ -596,15 +606,7 @@ contents as they are (or would be) after applying the changes in NEW." (let (res) (goto-char (point-min)) (unless (eobp) - ;; TODO: When 27.1 is the minimum requirement, we can drop - ;; these bindings: that version, in addition to switching over - ;; called-interactively-p check, so refinement can't be - ;; triggered by code calling the navigation functions, only by - ;; direct interactive invocations. - (ignore-errors - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-beginning-of-hunk t)))) + (diff-beginning-of-hunk t) (while (looking-at diff-hunk-header-re-unified) (let ((line (string-to-number (match-string 3))) (beg (point))) @@ -637,7 +639,7 @@ contents as they are (or would be) after applying the changes in NEW." ;; TODO: debounce if a thread is already running. (let ((buf (current-buffer)) (temp-buffer - (if (< emacs-major-version 28) + (static-if (< emacs-major-version 28) (generate-new-buffer " *temp*") (generate-new-buffer " *temp*" t)))) ;; Switch buffer temporarily, to "unlock" it for other threads. @@ -731,27 +733,35 @@ Return a list of line overlays used." (diff-hl--resolve reference (lambda (ref-changes) - (let ((ref-changes (diff-hl-adjust-changes ref-changes changes)) - reuse) - (with-current-buffer orig - (diff-hl-remove-overlays) - (let ((diff-hl-highlight-function - diff-hl-highlight-reference-function) - (diff-hl-fringe-face-function - diff-hl-fringe-reference-face-function)) - (setq reuse (diff-hl--update-overlays ref-changes nil))) - (diff-hl--update-overlays changes reuse) - (when (not (or changes ref-changes)) - (diff-hl--autohide-margin)))))))))) + (when (buffer-live-p orig) + (let ((ref-changes (diff-hl-adjust-changes ref-changes changes)) + (base (diff-hl--target-buffer orig))) + (dolist (buf (buffer-list)) + (when (and (eq (diff-hl--target-buffer buf) base) + (buffer-local-value 'diff-hl-mode buf)) + (with-current-buffer buf + (diff-hl-remove-overlays) + (let (reuse) + (when ref-changes + (let ((diff-hl-highlight-function + diff-hl-highlight-reference-function) + (diff-hl-fringe-face-function + diff-hl-fringe-reference-face-function)) + (setq reuse (diff-hl--update-overlays ref-changes nil)))) + (when changes + (diff-hl--update-overlays changes reuse))) + (unless (or changes ref-changes) + (diff-hl--autohide-margin))))))))))))) (defun diff-hl--resolve (value-or-buffer cb) (if (listp value-or-buffer) (funcall cb value-or-buffer) (static-if (fboundp 'vc-run-delayed-success) ;; Emacs 31. - (with-current-buffer value-or-buffer - (vc-run-delayed-success 1 - (funcall cb (diff-hl-changes-from-buffer (current-buffer))))) + (when (get-buffer value-or-buffer) + (with-current-buffer value-or-buffer + (vc-run-delayed-success 1 + (funcall cb (diff-hl-changes-from-buffer (current-buffer)))))) (diff-hl--when-done value-or-buffer #'diff-hl-changes-from-buffer cb)))) @@ -844,7 +854,8 @@ Return a list of line overlays used." (defun diff-hl-diff-goto-hunk-1 (historic rev1) (defvar vc-sentinel-movepoint) - (vc-buffer-sync) + (with-current-buffer (diff-hl--target-buffer) + (vc-buffer-sync)) (let* ((line (line-number-at-pos)) (buffer (current-buffer)) rev2) @@ -868,7 +879,7 @@ Return a list of line overlays used." With double prefix argument (C-u C-u), the diff is made against the reference revision." (interactive (list current-prefix-arg)) - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) + (with-current-buffer (current-buffer) (if (equal historic '(16)) (diff-hl-diff-reference-goto-hunk) (diff-hl-diff-goto-hunk-1 historic nil)))) @@ -876,7 +887,7 @@ reference revision." (defun diff-hl-diff-reference-goto-hunk () "Run VC diff command against the reference and go to the corresponding line." (interactive) - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) + (with-current-buffer (current-buffer) (diff-hl-diff-goto-hunk-1 nil diff-hl-reference-revision))) (defun diff-hl-root-diff-reference-goto-hunk () @@ -885,7 +896,7 @@ And if the current buffer is visiting a file, and it has changes, the diff buffer will show the position corresponding to its current line." (interactive) (defvar vc-sentinel-movepoint) - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) + (with-current-buffer (current-buffer) (let ((backend (vc-deduce-backend)) (default-directory default-directory) rootdir fileset @@ -894,11 +905,14 @@ buffer will show the position corresponding to its current line." (setq rootdir (vc-call-backend backend 'root default-directory) default-directory rootdir fileset `(,backend (,rootdir)) - relname (if buffer-file-name (file-relative-name buffer-file-name - rootdir))) + relname (let ((file (diff-hl--buffer-file-name))) + (when file + (file-relative-name file rootdir)))) (error "Directory is not version controlled")) (setq fileset (or fileset (vc-deduce-fileset))) - (vc-buffer-sync-fileset fileset t) + (static-if (< emacs-major-version 28) + (when buffer-file-name (vc-buffer-sync t)) + (vc-buffer-sync-fileset fileset t)) (let* ((line (line-number-at-pos))) (vc-diff-internal (if (boundp 'vc-allow-async-diff) @@ -910,7 +924,7 @@ buffer will show the position corresponding to its current line." (setq vc-sentinel-movepoint (point)))))))) (defun diff-hl-diff-read-revisions (rev1-default) - (let* ((file buffer-file-name) + (let* ((file (diff-hl--buffer-file-name)) (files (list file)) (backend (vc-backend file)) (rev2-default nil)) @@ -1002,7 +1016,8 @@ that file, if it's present." (defun diff-hl-revert-hunk-1 () (save-restriction (widen) - (vc-buffer-sync) + (with-current-buffer (diff-hl--target-buffer) + (vc-buffer-sync)) (let* ((diff-buffer (get-buffer-create (generate-new-buffer-name "*diff-hl-revert*"))) (buffer (current-buffer)) @@ -1010,7 +1025,7 @@ that file, if it's present." (line (save-excursion (diff-hl-find-current-hunk) (line-number-at-pos))) - (file buffer-file-name) + (file (diff-hl--buffer-file-name)) (backend (vc-backend file))) (unwind-protect (progn @@ -1029,9 +1044,7 @@ that file, if it's present." (when (eobp) (with-current-buffer buffer (diff-hl-remove-overlays)) (user-error "Buffer is up-to-date")) - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-hl-diff-skip-to line))) + (diff-hl-diff-skip-to line) (setq m-end (diff-hl-split-away-changes 3)) (setq m-beg (point-marker)) (funcall diff-hl-highlight-revert-hunk-function m-end) @@ -1041,9 +1054,8 @@ that file, if it's present." (if (>= wbh (- end-line beg-line)) (recenter (/ (+ wbh (- beg-line end-line) 2) 2)) (recenter 1))) - (with-no-warnings - (when diff-auto-refine-mode - (diff-refine-hunk))) + (when (eq diff-refine 'navigation) + (diff-refine-hunk)) (if diff-hl-ask-before-revert-hunk (unless (yes-or-no-p (format "Revert current hunk in %s? " file)) @@ -1090,7 +1102,7 @@ its end position." (defun diff-hl-revert-hunk () "Revert the diff hunk with changes at or above the point." (interactive) - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) + (with-current-buffer (current-buffer) (diff-hl-revert-hunk-1))) (defun diff-hl-hunk-overlay-at (pos) @@ -1146,7 +1158,7 @@ its end position." (push-mark (overlay-end hunk) nil t))) (defun diff-hl--ensure-staging-supported () - (let ((backend (vc-backend buffer-file-name))) + (let ((backend (vc-backend (diff-hl--buffer-file-name)))) (unless (eq backend 'Git) (user-error "Only Git supports staging; this file is controlled by %s" backend)))) @@ -1173,7 +1185,7 @@ Only supported with Git." (diff-hl--ensure-staging-supported) (diff-hl-find-current-hunk) (let* ((line (line-number-at-pos)) - (file buffer-file-name) + (file (diff-hl--buffer-file-name)) (dest-buffer (get-buffer-create " *diff-hl-stage*")) (orig-buffer (current-buffer)) ;; FIXME: If the file name has double quotes, these need to be quoted. @@ -1186,9 +1198,7 @@ Only supported with Git." diff-hl-update-async) (diff-hl-diff-buffer-with-reference file dest-buffer nil 3)) (with-current-buffer dest-buffer - (with-no-warnings - (let (diff-auto-refine-mode) - (diff-hl-diff-skip-to line))) + (diff-hl-diff-skip-to line) (let ((inhibit-read-only t)) (diff-hl-split-away-changes 3) (save-excursion @@ -1214,13 +1224,14 @@ Only supported with Git." Only supported with Git." (interactive) - (unless buffer-file-name - (user-error "No current file")) - (diff-hl--ensure-staging-supported) - (vc-git-command nil 0 buffer-file-name "reset") - (message "Unstaged all") - (unless diff-hl-show-staged-changes - (diff-hl-update))) + (let ((file (diff-hl--buffer-file-name))) + (unless file + (user-error "No current file")) + (diff-hl--ensure-staging-supported) + (vc-git-command nil 0 file "reset") + (message "Unstaged all") + (unless diff-hl-show-staged-changes + (diff-hl-update)))) (defun diff-hl-stage-dwim (&optional with-edit) "Stage the current hunk or choose the hunks to stage. @@ -1247,7 +1258,7 @@ Pops up a diff buffer that can be edited to choose the changes to stage." (diff-hl--ensure-staging-supported) (let* ((line-beg (and beg (line-number-at-pos beg t))) (line-end (and end (line-number-at-pos end t))) - (file buffer-file-name) + (file (diff-hl--buffer-file-name)) (dest-buffer (get-buffer-create "*diff-hl-stage-some*")) (orig-buffer (current-buffer)) (diff-hl-update-async nil) @@ -1391,14 +1402,14 @@ The value of this variable is a mode line template as in (declare-function smartrep-define-key 'smartrep) (let (smart-keys) (cl-labels ((scan (map) - (map-keymap - (lambda (event binding) - (if (consp binding) - (scan binding) - (when (and (characterp event) - (not (memq binding diff-hl-repeat-exceptions))) - (push (cons (string event) binding) smart-keys)))) - map))) + (map-keymap + (lambda (event binding) + (if (consp binding) + (scan binding) + (when (and (characterp event) + (not (memq binding diff-hl-repeat-exceptions))) + (push (cons (string event) binding) smart-keys)))) + map))) (scan diff-hl-command-map) (smartrep-define-key diff-hl-mode-map diff-hl-command-prefix smart-keys)))) @@ -1423,22 +1434,24 @@ The value of this variable is a mode line template as in (let* ((topdir (magit-toplevel)) (modified-files (magit-git-items "diff-tree" "-z" "--name-only" "-r" "HEAD~" "HEAD")) - (unmodified-states '(up-to-date ignored unregistered))) + (unmodified-states '(up-to-date ignored unregistered)) + file) (dolist (buf (buffer-list)) - (when (and (buffer-local-value 'diff-hl-mode buf) - (not (buffer-modified-p buf)) - ;; Solve the "cloned indirect buffer" problem - ;; (diff-hl-mode could be non-nil there, even if - ;; buffer-file-name is nil): - (buffer-file-name buf) - (file-in-directory-p (buffer-file-name buf) topdir) - (file-exists-p (buffer-file-name buf))) + (setq file (diff-hl--buffer-file-name buf)) + (when (and + (buffer-local-value 'diff-hl-mode buf) + (not (buffer-modified-p buf)) + ;; Solve the "cloned indirect buffer" problem + ;; (diff-hl-mode could be non-nil there, even if + ;; buffer-file-name is nil): + file + (file-in-directory-p file topdir) + (file-exists-p file)) (with-current-buffer buf - (let* ((file buffer-file-name) - (backend (vc-backend file))) + (let* ((backend (vc-backend file))) (when backend (cond - ((member file modified-files) + ((member (file-relative-name file topdir) modified-files) (when (memq (vc-state file) unmodified-states) (vc-state-refresh file backend)) (diff-hl-update)) @@ -1519,11 +1532,15 @@ The diffs are computed in the buffer DEST-BUFFER. This requires the `diff-program' to be in your `exec-path'. CONTEXT-LINES is the size of the unified diff context, defaults to 0." (require 'diff) - (vc-ensure-vc-buffer) + (unless file + (error "Buffer %s is not visiting a file" (buffer-name))) + (setq backend (or backend (vc-backend file))) + (unless backend + (error "File %s is not under version control" file)) (save-current-buffer (let* ((dest-buffer (or dest-buffer "*diff-hl-diff-buffer-with-reference*")) - (backend (or backend (vc-backend file))) (temporary-file-directory diff-hl-temporary-directory) + (enable-local-variables nil) (rev (if (and (eq backend 'Git) (not diff-hl-reference-revision) @@ -1537,7 +1554,7 @@ CONTEXT-LINES is the size of the unified diff context, defaults to 0." backend (or diff-hl-reference-revision (assoc-default backend diff-hl-head-revision-alist))) - (diff-hl-working-revision buffer-file-name backend))))) + (diff-hl-working-revision (diff-hl--buffer-file-name) backend))))) (switches (format "-U %d --strip-trailing-cr" (or context-lines 0)))) (diff-no-select rev (current-buffer) switches (not (diff-hl--use-async-p)) (get-buffer-create dest-buffer)) @@ -1566,7 +1583,8 @@ CONTEXT-LINES is the size of the unified diff context, defaults to 0." (goto-char (point-min)) (buffer-substring-no-properties (point) (line-end-position)))) ((eq backend 'JJ) - (car (last (vc-jj--process-lines "log" "--no-graph" + (car (last (vc-jj--process-lines nil + "log" "--no-graph" "-r" revision "-T" "change_id" "-n" "1")))) (t @@ -1605,13 +1623,14 @@ CONTEXT-LINES is the size of the unified diff context, defaults to 0." ;;;###autoload (defun turn-on-diff-hl-mode () "Turn on `diff-hl-mode' or `diff-hl-dir-mode' in a buffer if appropriate." - (cond - (buffer-file-name - (unless (and diff-hl-disable-on-remote - (file-remote-p buffer-file-name)) - (diff-hl-mode 1))) - ((eq major-mode 'vc-dir-mode) - (diff-hl-dir-mode 1)))) + (let ((file (diff-hl--buffer-file-name))) + (cond + (file + (unless (and diff-hl-disable-on-remote + (file-remote-p file)) + (diff-hl-mode 1))) + ((eq major-mode 'vc-dir-mode) + (diff-hl-dir-mode 1))))) ;;;###autoload (defun diff-hl--global-turn-on () @@ -1729,11 +1748,15 @@ effect." (message "Showing changes against %s (project %s)" rev name))))) (defun diff-hl--project-root (proj) - ;; Emacs 26 and 27 don't have `project-root'. + ;; Emacs 27 does not have `project-root'. (expand-file-name (static-if (>= emacs-major-version 28) (project-root proj) (project-roots proj)))) +;; Commands below will only work with recent enough project.el. +(declare-function project-name "project") +(declare-function project-buffers "project") + (defun diff-hl-set-reference-rev-in-project-internal (rev proj) (let* ((root (diff-hl--project-root proj))) ;; newly opened files will share this value diff --git a/lisp/emacsql/emacsql-compiler.el b/lisp/emacsql/emacsql-compiler.el index 575af47c..66afdc32 100644 --- a/lisp/emacsql/emacsql-compiler.el +++ b/lisp/emacsql/emacsql-compiler.el @@ -81,7 +81,7 @@ (defun emacsql-quote-identifier (string) "Double-quote (identifier) STRING for use in a SQL expression." - (format "\"%s\"" (replace-regexp-in-string "\"" "\"\"" string))) + (format "\"%s\"" (string-replace "\"" "\"\"" string))) (defun emacsql-escape-identifier (identifier) "Escape an identifier, if needed, for SQL." @@ -99,7 +99,7 @@ (if (string-match-p ":" name) (mapconcat #'emacsql-escape-identifier (mapcar #'intern (split-string name ":")) ".") - (let ((print (replace-regexp-in-string "-" "_" (format "%S" identifier))) + (let ((print (string-replace "-" "_" (format "%S" identifier))) (special "[]-\000-\040!\"#%&'()*+,./:;<=>?@[\\^`{|}~\177]")) (if (or (string-match-p special print) (string-match-p "^[0-9$]" print) @@ -133,7 +133,7 @@ (defun emacsql-escape-format (thing) "Escape THING for use as a `format' spec." - (replace-regexp-in-string "%" "%%" thing)) + (string-replace "%" "%%" thing)) ;;; Schema compiler @@ -146,8 +146,7 @@ (defun emacsql--from-keyword (keyword) "Convert KEYWORD into SQL." - (let ((name (substring (symbol-name keyword) 1))) - (upcase (replace-regexp-in-string "-" " " name)))) + (upcase (string-replace "-" " " (substring (symbol-name keyword) 1)))) (defun emacsql--prepare-constraints (constraints) "Compile CONSTRAINTS into a partial SQL expression." diff --git a/lisp/emacsql/emacsql-pg.el b/lisp/emacsql/emacsql-pg.el index 649423f2..744e0711 100644 --- a/lisp/emacsql/emacsql-pg.el +++ b/lisp/emacsql/emacsql-pg.el @@ -10,8 +10,7 @@ ;;; Commentary: ;; This library provides an EmacSQL back-end for PostgreSQL, which -;; uses the `pg' package to directly speak to the database. This -;; library requires at least Emacs 28.1. +;; uses the `pg' package to directly speak to the database. ;; (For an alternative back-end for PostgreSQL, see `emacsql-psql'.) @@ -19,14 +18,10 @@ (require 'emacsql) -(if (>= emacs-major-version 28) - (require 'pg nil t) - (message "emacsql-pg.el requires Emacs 28.1 or later")) -(declare-function pg-connect "ext:pg" - ( dbname user &optional - (password "") (host "localhost") (port 5432) (tls nil))) +(require 'pg nil t) +(declare-function pg-connect-plist "ext:pg") (declare-function pg-disconnect "ext:pg" (con)) -(declare-function pg-exec "ext:pg" (connection &rest args)) +(declare-function pg-exec "ext:pg" (con &rest args)) (declare-function pg-result "ext:pg" (result what &rest arg)) (defclass emacsql-pg-connection (emacsql-connection) @@ -41,11 +36,14 @@ (nil "TEXT")))) "A connection to a PostgreSQL database via pg.el.") -(cl-defun emacsql-pg (dbname user &key - (host "localhost") (password "") (port 5432) debug) +(cl-defun emacsql-pg ( dbname user &key + (host "localhost") (password nil) (port 5432) debug) "Connect to a PostgreSQL server using pg.el." (require 'pg) - (let* ((pgcon (pg-connect dbname user password host port)) + (let* ((pgcon (pg-connect-plist dbname user + :password password + :host host + :port port)) (connection (make-instance 'emacsql-pg-connection :handle (and (fboundp 'pgcon-process) (pgcon-process pgcon)) diff --git a/lisp/emacsql/emacsql-pkg.el b/lisp/emacsql/emacsql-pkg.el index 3bd56f36..86383d4f 100644 --- a/lisp/emacsql/emacsql-pkg.el +++ b/lisp/emacsql/emacsql-pkg.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "emacsql" "20260401.1220" +(define-package "emacsql" "20260601.1722" "High-level SQL database front-end." - '((emacs "26.1")) + '((emacs "28.1")) :url "https://github.com/magit/emacsql" - :commit "2fe6d4562b32a170a750d5e80514fbb6b6694803" - :revdesc "2fe6d4562b32" + :commit "d811bbefcb5e27841af55cae53aa939ba720de77" + :revdesc "d811bbefcb5e" :authors '(("Christopher Wellons" . "wellons@nullprogram.com")) :maintainers '(("Jonas Bernoulli" . "emacs.emacsql@jonas.bernoulli.dev"))) diff --git a/lisp/emacsql/emacsql.el b/lisp/emacsql/emacsql.el index 9cfdb3aa..94c3e7a2 100644 --- a/lisp/emacsql/emacsql.el +++ b/lisp/emacsql/emacsql.el @@ -6,9 +6,9 @@ ;; Maintainer: Jonas Bernoulli ;; Homepage: https://github.com/magit/emacsql -;; Package-Version: 20260401.1220 -;; Package-Revision: 2fe6d4562b32 -;; Package-Requires: ((emacs "26.1")) +;; Package-Version: 20260601.1722 +;; Package-Revision: d811bbefcb5e +;; Package-Requires: ((emacs "28.1")) ;; SPDX-License-Identifier: Unlicense @@ -38,7 +38,7 @@ "The EmacSQL SQL database front-end." :group 'comm) -(defconst emacsql-version "4.3.6") +(defconst emacsql-version "4.4.1") (defvar emacsql-global-timeout 30 "Maximum number of seconds to wait before bailing out on a SQL command. diff --git a/lisp/ess/ess-bugs-d.el b/lisp/ess/ess-bugs-d.el index 9fc98108..062e9514 100644 --- a/lisp/ess/ess-bugs-d.el +++ b/lisp/ess/ess-bugs-d.el @@ -59,9 +59,9 @@ font-lock-keyword-face) (cons (concat "\\ ;; Created: 12 Oct 2015 ;; Maintainer: ESS-core @@ -310,6 +310,7 @@ content. Return nil when the end of the buffer is reached." (defvar ess-r-operators-list '("+" "-" "*" "/" "%%" "**" "^" + "%*%" "%/%" "%in%" "%notin%" "%o%" "%x%" "%||%" ; = ls(pattern = "^%", baseenv()) "&" "&&" "|" "||" "!" "?" "~" "==" "!=" "<" "<=" ">=" ">" "=" "<-" "<<-" "->" "->>" diff --git a/lisp/ess/ess-roxy.el b/lisp/ess/ess-roxy.el index 00b172c2..a8a7c56f 100644 --- a/lisp/ess/ess-roxy.el +++ b/lisp/ess/ess-roxy.el @@ -713,7 +713,11 @@ block before the point." (save-excursion (let ((end-of-entry (ess-roxy-end-of-entry)) (beg-of-entry (ess-roxy-beg-of-entry))) - (hs-hide-block-at-point nil (list beg-of-entry end-of-entry))))) + (if (= (cdr (func-arity 'hs-hide-block-at-point)) 1) + ;; Emacs 31 ++ signature: single optional COMMENT-REG -- /emacs-ess/ESS/issues/1334 + (hs-hide-block-at-point (list beg-of-entry end-of-entry)) + ;; Emacs <= 30.x signature: (END-OF-BLOCK &optional COMMENT-REG) + (hs-hide-block-at-point nil (list beg-of-entry end-of-entry)))))) (defun ess-roxy-toggle-hiding () "Toggle hiding/showing of a block. diff --git a/lisp/ess/ess.el b/lisp/ess/ess.el index 6bb79a0f..e2c96f25 100644 --- a/lisp/ess/ess.el +++ b/lisp/ess/ess.el @@ -17,8 +17,8 @@ ;; ;; Maintainer: ESS Core Team ;; Created: 7 Jan 1994 -;; Package-Version: 20260322.1703 -;; Package-Revision: 4e112590d1c1 +;; Package-Version: 20260526.1432 +;; Package-Revision: da7d7dc1d2cf ;; URL: https://ess.r-project.org/ ;; Package-Requires: ((emacs "25.1")) ;; ESSR-Version: 1.8 diff --git a/lisp/ess/ess.info b/lisp/ess/ess.info index 51c26407..dbeb9521 100644 --- a/lisp/ess/ess.info +++ b/lisp/ess/ess.info @@ -14,7 +14,7 @@ File: ess.info, Node: Top, Next: Introduction, Up: (dir) ESS: Emacs Speaks Statistics **************************** -ESS version 26.01.0 +ESS version 26.05.0 by A.J. Rossini, R.M. Heiberger, diff --git a/lisp/ess/etc/ESSR/R/completion.R b/lisp/ess/etc/ESSR/R/completion.R index 50017df0..e50f9a3e 100644 --- a/lisp/ess/etc/ESSR/R/completion.R +++ b/lisp/ess/etc/ESSR/R/completion.R @@ -108,6 +108,7 @@ local({ } +## builds on R`s functionality in src/library/utils/R/completion.R : .ess_get_completions <- function(string, end, suffix = " = ") { oldopts <- utils::rc.options(funarg.suffix = suffix) on.exit(utils::rc.options(oldopts)) diff --git a/lisp/flycheck-posframe/flycheck-posframe-pkg.el b/lisp/flycheck-posframe/flycheck-posframe-pkg.el index 7c96d236..b7a52ce1 100644 --- a/lisp/flycheck-posframe/flycheck-posframe-pkg.el +++ b/lisp/flycheck-posframe/flycheck-posframe-pkg.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "flycheck-posframe" "20220715.133" +(define-package "flycheck-posframe" "20260409.14" "Show flycheck error messages using posframe.el." '((flycheck "0.24") (emacs "26") (posframe "0.7.0")) :url "https://github.com/alexmurray/flycheck-posframe" - :commit "19896b922c76a0f460bf3fe8d8ebc2f9ac9028d8" - :revdesc "19896b922c76" + :commit "aeccb14e90ba25f45e1919b776777fc6ec95e251" + :revdesc "aeccb14e90ba" :authors '(("Alex Murray" . "murray.alex@gmail.com")) :maintainers '(("Alex Murray" . "murray.alex@gmail.com"))) diff --git a/lisp/flycheck-posframe/flycheck-posframe.el b/lisp/flycheck-posframe/flycheck-posframe.el index c1077fcd..0eb5d4e3 100644 --- a/lisp/flycheck-posframe/flycheck-posframe.el +++ b/lisp/flycheck-posframe/flycheck-posframe.el @@ -5,8 +5,8 @@ ;; Author: Alex Murray ;; Maintainer: Alex Murray ;; URL: https://github.com/alexmurray/flycheck-posframe -;; Package-Version: 20220715.133 -;; Package-Revision: 19896b922c76 +;; Package-Version: 20260409.14 +;; Package-Revision: aeccb14e90ba ;; Package-Requires: ((flycheck "0.24") (emacs "26") (posframe "0.7.0")) ;; This file is not part of GNU Emacs. @@ -199,6 +199,10 @@ Only the `foreground' is used in this face." "Hide posframe if position has changed since last display." (not (flycheck-posframe-check-position))) +(defun flycheck-posframe-point-position-p () + "Return non-nil if `flycheck-posframe-position' is a point-based position." + (string-prefix-p "point-" (symbol-name flycheck-posframe-position))) + (defun flycheck-posframe-show-posframe (errors) "Display ERRORS, using posframe.el library." (posframe-hide flycheck-posframe-buffer) @@ -218,7 +222,9 @@ Only the `foreground' is used in this face." (flycheck-posframe-highest-error-level-face errors) 'flycheck-posframe-border-face) nil t) :poshandler poshandler - :hidehandler #'flycheck-posframe-hidehandler)))) + :hidehandler #'flycheck-posframe-hidehandler + :y-pixel-offset (when (flycheck-posframe-point-position-p) + flycheck-posframe-border-width))))) ;;;###autoload (defun flycheck-posframe-configure-pretty-defaults () diff --git a/lisp/flycheck/flycheck-pkg.el b/lisp/flycheck/flycheck-pkg.el index 4011a0d4..85b240ff 100644 --- a/lisp/flycheck/flycheck-pkg.el +++ b/lisp/flycheck/flycheck-pkg.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "flycheck" "20260320.1715" +(define-package "flycheck" "20260604.2002" "On-the-fly syntax checking." '((emacs "27.1") (seq "2.24")) :url "https://github.com/flycheck/flycheck" - :commit "0e5eb8300d32fd562724216c19eaf199ee1451ab" - :revdesc "0e5eb8300d32" + :commit "96f1852c7e352c969393e6e66176178177e933be" + :revdesc "96f1852c7e35" :keywords '("convenience" "languages" "tools") :authors '(("Sebastian Wiesner" . "swiesner@lunaryorn.com")) :maintainers '(("Clément Pit-Claudel" . "clement.pitclaudel@live.com") diff --git a/lisp/flycheck/flycheck.el b/lisp/flycheck/flycheck.el index cdaceccd..1094e6a5 100644 --- a/lisp/flycheck/flycheck.el +++ b/lisp/flycheck/flycheck.el @@ -10,8 +10,8 @@ ;; Bozhidar Batsov ;; URL: https://github.com/flycheck/flycheck ;; Keywords: convenience, languages, tools -;; Package-Version: 20260320.1715 -;; Package-Revision: 0e5eb8300d32 +;; Package-Version: 20260604.2002 +;; Package-Revision: 96f1852c7e35 ;; Package-Requires: ((emacs "27.1") (seq "2.24")) ;; This file is not part of GNU Emacs. @@ -6268,8 +6268,12 @@ PROCESS, and terminates standard input with EOF." ;; can easily use pipes. (process-connection-type nil) ;; Force English messages from checker processes so that - ;; error patterns can match reliably. - (process-environment (cons "LC_ALL=C" process-environment))) + ;; error patterns can match reliably. We set LC_MESSAGES + ;; rather than LC_ALL so that the character encoding + ;; (LC_CTYPE) is left untouched; using LC_ALL=C forces an + ;; ASCII locale that breaks checkers reading UTF-8 input, + ;; such as hledger (see #2170). + (process-environment (cons "LC_MESSAGES=C" process-environment))) ;; We do not associate the process with any buffer, by ;; passing nil for the BUFFER argument of `start-process'. ;; Instead, we just remember the buffer being checked in a @@ -7527,7 +7531,7 @@ See URL `https://asciidoctor.org'." (warning line-start "asciidoctor: WARNING: : Line " line ": " (message) line-end)) - :modes adoc-mode) + :modes (adoc-mode asciidoc-mode)) (defun flycheck-awk-gawk-fix-message (err) "Remove the repeated file-name/line from the error message of ERR." @@ -7553,7 +7557,10 @@ See URL `https://asciidoctor.org'." "GNU awk's built-in --lint checker." :command ("gawk" ;; Avoid code execution. See https://github.com/w0rp/ale/pull/1411 - "--source" "BEGIN{exit} END{exit 1}" + ;; The BEGIN/END blocks short-circuit the script's own rules so + ;; only linting happens; exit 0 so that valid scripts don't get + ;; flagged as a suspicious non-zero exit. + "--source" "BEGIN{exit} END{exit}" "-f" source "--lint" "/dev/null") @@ -8549,6 +8556,10 @@ See `https://credo-ci.org/'." ;; file-local eval: directives during byte-compilation. (setq enable-local-eval nil enable-local-variables :safe) + ;; The subprocess only byte-compiles to collect warnings; producing + ;; .eln files is a wasted side effect that also pollutes the user's + ;; native-comp cache, so disable native compilation entirely. + (setq no-native-compile t) ;; Keep track of the generated bytecode files, to delete them after byte ;; compilation. (require 'bytecomp) @@ -10771,7 +10782,11 @@ See URL `https://docs.astral.sh/ruff/'." line-end) (warning line-start (or "-" (file-name)) ":" line ":" (optional column ":") " " - (id (one-or-more (any alpha)) (one-or-more digit)) " " + ;; ruff >= 0.15.7 in preview mode wraps the rule code in a + ;; severity tag, e.g. "error[F401]" instead of just "F401" + (optional (one-or-more (any alpha)) "[") + (id (one-or-more (any alpha)) (one-or-more digit)) + (optional "]") " " (message (one-or-more not-newline)) line-end)) :error-explainer flycheck-python-ruff-explainer @@ -12488,7 +12503,7 @@ See URL `https://textlint.github.io/'." ;; user to add mode->plugin mappings manually in ;; `flycheck-textlint-plugin-alist'. :modes - (text-mode markdown-mode gfm-mode message-mode adoc-mode + (text-mode markdown-mode gfm-mode message-mode adoc-mode asciidoc-mode mhtml-mode latex-mode LaTeX-mode org-mode rst-mode) :enabled (lambda () (flycheck--textlint-get-plugin)) diff --git a/lisp/gnuplot/gnuplot-context.el b/lisp/gnuplot/gnuplot-context.el index 46105053..07fbe128 100644 --- a/lisp/gnuplot/gnuplot-context.el +++ b/lisp/gnuplot/gnuplot-context.el @@ -612,7 +612,7 @@ name; otherwise continues tokenizing up to the token at point. FIXME." (let ((name (car chunk)) (code (cdr chunk))) (setf (aref object-code i) `(label ,name)) - (cl-incf i) + (incf i) (puthash name i name->offset) (while code (setf (aref object-code i) (car code) @@ -1770,8 +1770,8 @@ there." (when start-symbol ; HACK FIXME (let ((look-for `(label ,start-symbol))) (while (not (equal (aref instructions pc) look-for)) - (cl-incf pc)) - (cl-incf pc))) + (incf pc)) + (incf pc))) (setq gnuplot-context--completions nil gnuplot-context--eldoc nil diff --git a/lisp/gnuplot/gnuplot-pkg.el b/lisp/gnuplot/gnuplot-pkg.el index 464b48ab..afa17bee 100644 --- a/lisp/gnuplot/gnuplot-pkg.el +++ b/lisp/gnuplot/gnuplot-pkg.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "gnuplot" "20260322.20" +(define-package "gnuplot" "20260623.1111" "Major-mode and interactive frontend for gnuplot." - '((emacs "28.1") - (compat "30")) + '((emacs "29.1") + (compat "31")) :url "https://github.com/emacs-gnuplot/gnuplot" - :commit "39ba1dec5e8e227ba093a30ca07b20d8eb038f29" - :revdesc "39ba1dec5e8e" + :commit "81e3cb30297f0d12df41b865d2a76c8ba179089c" + :revdesc "81e3cb30297f" :keywords '("data" "gnuplot" "plotting") :maintainers '(("Maxime Tréca" . "maxime@gmail.com") ("Daniel Mendler" . "mail@daniel-mendler.de"))) diff --git a/lisp/gnuplot/gnuplot.el b/lisp/gnuplot/gnuplot.el index cefe01f1..20df5914 100644 --- a/lisp/gnuplot/gnuplot.el +++ b/lisp/gnuplot/gnuplot.el @@ -5,11 +5,11 @@ ;; Author: Jon Oddie, Bruce Ravel, Phil Type ;; Maintainer: Maxime Tréca , Daniel Mendler ;; Created: 1998 -;; Package-Version: 20260322.20 -;; Package-Revision: 39ba1dec5e8e +;; Package-Version: 20260623.1111 +;; Package-Revision: 81e3cb30297f ;; Keywords: data gnuplot plotting ;; URL: https://github.com/emacs-gnuplot/gnuplot -;; Package-Requires: ((emacs "28.1") (compat "30")) +;; Package-Requires: ((emacs "29.1") (compat "31")) ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by diff --git a/lisp/indent-guide/indent-guide-pkg.el b/lisp/indent-guide/indent-guide-pkg.el index a56a7a20..f3961e55 100644 --- a/lisp/indent-guide/indent-guide-pkg.el +++ b/lisp/indent-guide/indent-guide-pkg.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "indent-guide" "20260211.1005" +(define-package "indent-guide" "20260515.1152" "Show vertical lines to guide indentation." () - :url "http://hins11.yu-yake.com/" - :commit "f3455c6c798b568a6ea1013b7eea1153d2e092be" - :revdesc "f3455c6c798b") + :url "http://zk-phi.github.io/" + :commit "ab71cac290505caf6c374cb8594b0b78d5109af1" + :revdesc "ab71cac29050") diff --git a/lisp/indent-guide/indent-guide.el b/lisp/indent-guide/indent-guide.el index 20662e1a..ff4c6bf2 100644 --- a/lisp/indent-guide/indent-guide.el +++ b/lisp/indent-guide/indent-guide.el @@ -17,9 +17,9 @@ ;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ;; Author: zk_phi -;; URL: http://hins11.yu-yake.com/ -;; Package-Version: 20260211.1005 -;; Package-Revision: f3455c6c798b +;; URL: http://zk-phi.github.io/ +;; Package-Version: 20260515.1152 +;; Package-Revision: ab71cac29050 ;;; Commentary: @@ -71,12 +71,13 @@ ;; 2.2.0 add option "indent-guide-threshold" ;; 2.3.0 use regexp search to find the beginning of level ;; 2.3.1 add option "indent-guide-lispy-modes" +;; 2.4.0 add option "indent-guide-char-top" and "-bottom" ;;; Code: (require 'cl-lib) -(defconst indent-guide-version "2.4") +(defconst indent-guide-version "2.4.0") ;; * customs @@ -85,7 +86,7 @@ :group 'environment) (defcustom indent-guide-char "|" - "Character used for the guide line." + "Character used for the guide line." :type 'string :group 'indent-guide) @@ -146,14 +147,6 @@ blocks are NOT placed at beginning of line." ;; * utilities -(defun indent-guide--active-overlays () - "Return the list of all overlays created by indent-guide." - (delq nil - (mapcar - (lambda (ov) - (and (eq (overlay-get ov 'category) 'indent-guide) ov)) - (overlays-in (point-min) (point-max))))) - (defun indent-guide--indentation-candidates (level) "*Internal function for `indent-guide--beginning-of-level'." (cond ((<= level 0) @@ -169,21 +162,39 @@ blocks are NOT placed at beginning of line." (cons (make-string level ?\s) (indent-guide--indentation-candidates (1- level)))))) +;; Note(vmargb): `indent-guide--beginning-of-level' is called repeatedly +;; even within the same indentation level when the cursor is moved around +;; so we cache and reuse it until the user changes to another indent level +(defvar-local indent-guide--regex-cache nil +"Stores the last computed regex with the inputs used to build it. +Format: ((BASE-LEVEL . TAB-WIDTH) . REGEX-STRING).") + (defun indent-guide--beginning-of-level () "Move to the beginning of current indentation level and return -the point. When no such points are found, just return nil." +the point. When no such points are found, just return nil." (back-to-indentation) (let* ((base-level (if (not (eolp)) (current-column) (max (save-excursion - (skip-chars-forward "\s\t\n") + (skip-chars-forward " \t\n") (current-column)) (save-excursion - (skip-chars-backward "\s\t\n") + (skip-chars-backward " \t\n") (back-to-indentation) (current-column))))) - (candidates (indent-guide--indentation-candidates (1- base-level))) - (regex (concat "^" (regexp-opt candidates t) "[^\s\t\n]"))) + (cache-key (cons base-level tab-width)) ; key: indent depth & tab width + ;; check if current inputs match regex-cache + (regex (if (equal (car indent-guide--regex-cache) cache-key) + (cdr indent-guide--regex-cache) ; reuse regex string + ; recompute regex + (let ((candidates (indent-guide--indentation-candidates + (1- base-level)))) + (setq indent-guide--regex-cache + (cons cache-key + (concat "^" + (regexp-opt candidates t) + "[^ \t\n]"))) + (cdr indent-guide--regex-cache))))) (unless (zerop base-level) (and (search-backward-regexp regex nil t) (goto-char (match-end 1)))))) @@ -195,7 +206,7 @@ the point. When no such points are found, just return nil." indent-guide-char (cond ((= line line-start) (or indent-guide-char-top indent-guide-char)) - ((= line line-end) (or indent-guide-char-bottom indent-guide-char)) + ((= line line-end) (or indent-guide-char-bottom indent-guide-char)) (t indent-guide-char))) ) @@ -220,7 +231,7 @@ the point. When no such points are found, just return nil." (lambda (ov) (when (eq (overlay-get ov 'category) 'indent-guide) ov)) - (overlays-in (point) (point)))) + (overlays-at (point)))) ;; we already have an overlay here => append to the existing overlay ;; (important when "recursive" is enabled) (setq string (let ((str (overlay-get ov 'before-string))) @@ -278,11 +289,11 @@ the point. When no such points are found, just return nil." (interactive) ;;; NOTE(arka): redraw only when needed (unless (active-minibuffer-window) - (indent-guide-remove) - (let ((win-start (window-start)) (win-end (window-end nil t)) line-col line-start line-end) + ;;; only clear overlays in the visible viewport + (indent-guide-remove win-start win-end) ;; decide line-col, line-start (save-excursion (indent-guide--beginning-of-level) @@ -313,32 +324,41 @@ the point. When no such points are found, just return nil." (indent-guide--make-overlay (+ line-start tmp) line-col line-start line-end)) (remove-overlays (point) (point) 'category 'indent-guide))))) -(defun indent-guide-remove () - (dolist (ov (indent-guide--active-overlays)) - (delete-overlay ov))) +;; use built-in `remove-overlays' +(defun indent-guide-remove (&optional beg end) + "Remove indent-guide overlays between BEG and END. +Defaults to the whole buffer if not provided." + (remove-overlays (or beg (point-min)) (or end (point-max)) + 'category 'indent-guide)) ;; * minor-mode -(defun indent-guide-post-command-hook () - (if (null indent-guide-delay) - (indent-guide-show) - (when (null indent-guide--timer-object) - (setq indent-guide--timer-object - (run-with-idle-timer indent-guide-delay nil - (lambda () - (indent-guide-show) - (setq indent-guide--timer-object nil))))))) +;; use named function to prevent a lambda closure being +;; allocated repeatedly on every debounce +(defun indent-guide--run-timer () + (indent-guide-show) + (setq indent-guide--timer-object nil)) ;;; NOTE(arka): root cause of flickering effect. we don't actually need -;;; pre-hook to redraw guides on each command. +;;; pre-hook to redraw guides on each command. ;; (defun indent-guide-pre-command-hook () ;; ;; some commands' behavior may affected by indent-guide overlays, so ;; ;; remove all overlays in pre-command-hook. ;; (indent-guide-remove)) -;;; NOTE(arka): fn to fix flickering effect when scrolling. -(defun indent-guide--window-scroll-hook (&rest _) - (indent-guide-show)) +;; Note(vmargb): the timer now behaves like a proper `debounce' +;; every new command cancels the old idle timer and schedules a new one +;; so `indent-guide-show' only runs after the user has paused, not after +;; the first command in a burst. +;; Used by both hooks: `post-command-hook' & `window-scroll-functions'. +(defun indent-guide--request-show (&rest _) + (if (null indent-guide-delay) + (indent-guide-show) ; no delay, show immediately + (when indent-guide--timer-object ; is delay, so cancel/debounce + (cancel-timer indent-guide--timer-object)) + (setq indent-guide--timer-object ; schedule new timer + (run-with-idle-timer indent-guide-delay nil + #'indent-guide--run-timer)))) ;;;###autoload (define-minor-mode indent-guide-mode @@ -349,10 +369,10 @@ the point. When no such points are found, just return nil." (if indent-guide-mode (progn ;;; NOTE(arka): only use post-hook. pre-hook is now depricated - (add-hook 'post-command-hook 'indent-guide-post-command-hook nil t) - (add-hook 'window-scroll-functions 'indent-guide--window-scroll-hook nil t)) - (remove-hook 'post-command-hook 'indent-guide-post-command-hook t) - (remove-hook 'window-scroll-functions 'indent-guide--window-scroll-hook t))) + (add-hook 'post-command-hook 'indent-guide--request-show nil t) + (add-hook 'window-scroll-functions 'indent-guide--request-show nil t)) + (remove-hook 'post-command-hook 'indent-guide--request-show t) + (remove-hook 'window-scroll-functions 'indent-guide--request-show t))) ;;;###autoload (define-globalized-minor-mode indent-guide-global-mode diff --git a/lisp/ivy/ivy-autoloads.el b/lisp/ivy/ivy-autoloads.el index 3c23564b..6fb68c7c 100644 --- a/lisp/ivy/ivy-autoloads.el +++ b/lisp/ivy/ivy-autoloads.el @@ -35,7 +35,7 @@ PREDICATE is applied to filter out the COLLECTION immediately. This argument is for compatibility with `completing-read'. When REQUIRE-MATCH is non-nil, only members of COLLECTION can be -selected. In can also be a lambda. +selected. It can also be a lambda. If INITIAL-INPUT is non-nil, then insert that input in the minibuffer initially. diff --git a/lisp/ivy/ivy-pkg.el b/lisp/ivy/ivy-pkg.el index bd3c20d1..99d7bc16 100644 --- a/lisp/ivy/ivy-pkg.el +++ b/lisp/ivy/ivy-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "ivy" "20260318.1355" +(define-package "ivy" "20260413.2102" "Incremental Vertical completYon." '((emacs "24.5")) :url "https://github.com/abo-abo/swiper" - :commit "1005bff8a700b92dc464f770aff8a0db5b4a1c0b" - :revdesc "1005bff8a700" + :commit "0d02f5063d36ff4fa6138f0973c83c6d3874fba0" + :revdesc "0d02f5063d36" :keywords '("matching") :authors '(("Oleh Krehel" . "ohwoeowho@gmail.com")) :maintainers '(("Basil L. Contovounesios" . "basil@contovou.net"))) diff --git a/lisp/ivy/ivy.el b/lisp/ivy/ivy.el index 298862a9..3564926a 100644 --- a/lisp/ivy/ivy.el +++ b/lisp/ivy/ivy.el @@ -5,8 +5,8 @@ ;; Author: Oleh Krehel ;; Maintainer: Basil L. Contovounesios ;; URL: https://github.com/abo-abo/swiper -;; Package-Version: 20260318.1355 -;; Package-Revision: 1005bff8a700 +;; Package-Version: 20260413.2102 +;; Package-Revision: 0d02f5063d36 ;; Package-Requires: ((emacs "24.5")) ;; Keywords: matching @@ -2200,7 +2200,7 @@ PREDICATE is applied to filter out the COLLECTION immediately. This argument is for compatibility with `completing-read'. When REQUIRE-MATCH is non-nil, only members of COLLECTION can be -selected. In can also be a lambda. +selected. It can also be a lambda. If INITIAL-INPUT is non-nil, then insert that input in the minibuffer initially. diff --git a/lisp/ledger-mode/ledger-check.el b/lisp/ledger-mode/ledger-check.el index 4f0e1ac8..7a5265b1 100644 --- a/lisp/ledger-mode/ledger-check.el +++ b/lisp/ledger-mode/ledger-check.el @@ -69,7 +69,8 @@ ;; one will ever have an account named "e342asd2131". If ;; someones does, this will probably still work for them. ;; I should only highlight error and warning lines. - "ledger bal e342asd2131 --strict --explicit " + (format "%s bal e342asd2131 --strict --explicit " + (shell-quote-argument ledger-binary-path)) t nil) (goto-char data-pos) @@ -88,7 +89,7 @@ (point-marker)))))) (add-text-properties (line-beginning-position) (line-end-position) (list 'font-lock-face 'ledger-font-report-clickable-face)) - (setq have-warnings 'true) + (setq have-warnings t) (end-of-line)))) (if (not have-warnings) (insert "No errors or warnings reported.")))) diff --git a/lisp/ledger-mode/ledger-complete.el b/lisp/ledger-mode/ledger-complete.el index 8ef40981..59f8bfc2 100644 --- a/lisp/ledger-mode/ledger-complete.el +++ b/lisp/ledger-mode/ledger-complete.el @@ -84,7 +84,7 @@ If nil, full account names are offered for completion." (sort (delete-dups payees-list) #'string-lessp))) (defun ledger-payees-list () - "Return a list of all known account names as strings. + "Return a list of all known payees as strings. Looks in `ledger-payees-file' if set, otherwise the current buffer." (if ledger-payees-file (let ((f ledger-payees-file)) @@ -355,9 +355,6 @@ an alist (ACCOUNT-ELEMENT . NODE)." (when (and realign-after ledger-post-auto-align) (ledger-post-align-postings (line-beginning-position) (line-end-position))))))))) -(defun ledger-trim-trailing-whitespace (str) - (replace-regexp-in-string "[ \t]*$" "" str)) - (defun ledger-comments-list () "Collect comments from the buffer." (let ((comments '())) @@ -379,7 +376,7 @@ Interactively, if point is after a payee, complete the transaction with the details from the last transaction to that payee." (interactive) - (let* ((name (ledger-trim-trailing-whitespace + (let* ((name (string-trim-right (buffer-substring (save-excursion (unless (eq (ledger-thing-at-point) 'transaction) diff --git a/lisp/ledger-mode/ledger-context.el b/lisp/ledger-mode/ledger-context.el index 7f000d68..2a10a339 100644 --- a/lisp/ledger-mode/ledger-context.el +++ b/lisp/ledger-mode/ledger-context.el @@ -183,31 +183,40 @@ specified line, returns nil." (ledger-context-at-point))))) (defun ledger-context-line-type (context-info) + "Return the line-type symbol component of CONTEXT-INFO." (nth 0 context-info)) (defun ledger-context-current-field (context-info) + "Return the symbol naming the field at point in CONTEXT-INFO." (nth 1 context-info)) (defun ledger-context-field-info (context-info field-name) + "Return the (FIELD VALUE POSITION) cell for FIELD-NAME in CONTEXT-INFO." (assoc field-name (nth 2 context-info))) (defun ledger-context-field-present-p (context-info field-name) + "Return non-nil if FIELD-NAME is present in CONTEXT-INFO." (not (null (ledger-context-field-info context-info field-name)))) (defun ledger-context-field-value (context-info field-name) + "Return the string value of FIELD-NAME in CONTEXT-INFO." (nth 1 (ledger-context-field-info context-info field-name))) (defun ledger-context-field-position (context-info field-name) + "Return the buffer position of FIELD-NAME's start in CONTEXT-INFO." (nth 2 (ledger-context-field-info context-info field-name))) (defun ledger-context-field-end-position (context-info field-name) + "Return the buffer position one past FIELD-NAME's end in CONTEXT-INFO." (+ (ledger-context-field-position context-info field-name) (length (ledger-context-field-value context-info field-name)))) (defun ledger-context-goto-field-start (context-info field-name) + "Move point to the start of FIELD-NAME in CONTEXT-INFO." (goto-char (ledger-context-field-position context-info field-name))) (defun ledger-context-goto-field-end (context-info field-name) + "Move point one past the end of FIELD-NAME in CONTEXT-INFO." (goto-char (ledger-context-field-end-position context-info field-name))) (provide 'ledger-context) diff --git a/lisp/ledger-mode/ledger-exec.el b/lisp/ledger-mode/ledger-exec.el index ff304a5b..799179d3 100644 --- a/lisp/ledger-mode/ledger-exec.el +++ b/lisp/ledger-mode/ledger-exec.el @@ -35,6 +35,9 @@ (defvar ledger-works nil "Non-nil if the ledger binary can support `ledger-mode' interactive features.") +(defvar ledger-exec--args-only nil + "Internal variable, used for testing.") + (defgroup ledger-exec nil "Interface to the Ledger command-line accounting program." :group 'ledger) @@ -90,6 +93,8 @@ otherwise the error output is displayed and an error is raised." (append (list (point-min) (point-max) ledger-binary-path nil (list outbuf errfile) nil "-f" "-") (list "--date-format" ledger-default-date-format) + (when ledger-exec--args-only + (list "--args-only")) args))))) (if (ledger-exec-success-p exit-code outbuf) outbuf diff --git a/lisp/ledger-mode/ledger-flymake.el b/lisp/ledger-mode/ledger-flymake.el index ba8aba05..3cb52c1f 100644 --- a/lisp/ledger-mode/ledger-flymake.el +++ b/lisp/ledger-mode/ledger-flymake.el @@ -33,10 +33,6 @@ (require 'ledger-exec) ; for `ledger-binary-path' (require 'ledger-report) ; for `ledger-master-file' -;; To silence byte compiler warnings in Emacs 25 and older: -(declare-function flymake-diag-region "flymake" (buffer line &optional col)) -(declare-function flymake-make-diagnostic "flymake" (buffer beg end type text &optional data overlay-properties)) - (defvar-local ledger--flymake-proc nil) (defcustom ledger-flymake-be-pedantic nil @@ -114,14 +110,14 @@ Flymake calls this with REPORT-FN as needed." (group-n 1 "Error: " (one-or-more not-newline) "\n")) nil t) for msg = (match-string 1) - for (beg . end) = (flymake-diag-region - source - (string-to-number (match-string 2))) - for type = :error + for region = (flymake-diag-region + source + (string-to-number (match-string 2))) + when region collect (flymake-make-diagnostic source - beg - end - type + (car region) + (cdr region) + :error msg) into diags finally (funcall report-fn diags))) @@ -134,11 +130,9 @@ Flymake calls this with REPORT-FN as needed." ;;;###autoload (defun ledger-flymake-enable () "Enable `flymake-mode' in `ledger-mode' buffers." - (unless (> emacs-major-version 25) - (error "Ledger-flymake requires Emacs version 26 or higher")) ;; Add `ledger-flymake' to `flymake-diagnostic-functions' so that flymake can ;; work in ledger-mode: - (add-hook 'flymake-diagnostic-functions 'ledger-flymake nil t) + (add-hook 'flymake-diagnostic-functions #'ledger-flymake nil t) (flymake-mode)) (provide 'ledger-flymake) diff --git a/lisp/ledger-mode/ledger-fonts.el b/lisp/ledger-mode/ledger-fonts.el index 78838869..ee806cad 100644 --- a/lisp/ledger-mode/ledger-fonts.el +++ b/lisp/ledger-mode/ledger-fonts.el @@ -307,7 +307,8 @@ (defface ledger-font-N-symbol-face `((t :inherit default)) - "Face for symbol in N directives") + "Face for symbol in N directives" + :group 'ledger-faces) (defface ledger-font-payee-directive-face `((t :inherit ledger-font-directive-face)) diff --git a/lisp/ledger-mode/ledger-init.el b/lisp/ledger-mode/ledger-init.el index acf1d615..4a6166f2 100644 --- a/lisp/ledger-mode/ledger-init.el +++ b/lisp/ledger-mode/ledger-init.el @@ -27,7 +27,9 @@ ;;; Code: (defcustom ledger-init-file-name "~/.ledgerrc" - "Location of the ledger initialization file. nil if you don't have one." + "Location of the ledger initialization file. + +nil if you don't have one or don't wish to read it." :type '(choice (const :tag "Do not read ledger initialization file" nil) file) :group 'ledger-exec) diff --git a/lisp/ledger-mode/ledger-mode-pkg.el b/lisp/ledger-mode/ledger-mode-pkg.el index 41503395..9d8d97ff 100644 --- a/lisp/ledger-mode/ledger-mode-pkg.el +++ b/lisp/ledger-mode/ledger-mode-pkg.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "ledger-mode" "20251219.2350" +(define-package "ledger-mode" "20260609.609" "Helper code for use with the \"ledger\" command-line tool." '((emacs "26.1")) :url "https://github.com/ledger/ledger-mode" - :commit "40e6a167530e21968e3ce7b8cb74e7595cb6009a" - :revdesc "40e6a167530e") + :commit "b0ee99feb2dcae5e304ad735d82d488f2191a51c" + :revdesc "b0ee99feb2dc") diff --git a/lisp/ledger-mode/ledger-mode.el b/lisp/ledger-mode/ledger-mode.el index 746b2a56..855f5317 100644 --- a/lisp/ledger-mode/ledger-mode.el +++ b/lisp/ledger-mode/ledger-mode.el @@ -4,9 +4,10 @@ ;; This file is not part of GNU Emacs. -;; Package-Version: 20251219.2350 -;; Package-Revision: 40e6a167530e +;; Package-Version: 20260609.609 +;; Package-Revision: b0ee99feb2dc ;; Package-Requires: ((emacs "26.1")) +;; URL: https://github.com/ledger/ledger-mode ;; This 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 @@ -70,7 +71,7 @@ (defun ledger-mode-dump-variable (var) "Format VAR for dump to buffer." (if var - (insert (format " %s: %S\n" (symbol-name var) (eval var))))) + (insert (format " %s: %S\n" (symbol-name var) (symbol-value var))))) (defun ledger-mode-dump-group (group) "Dump GROUP customizations to current buffer." @@ -156,12 +157,10 @@ the balance into that." (ledger-exec-ledger buffer (current-buffer) "stats") (buffer-substring-no-properties (point-min) (1- (point-max)))))) (when balance - (message balance)))) + (message "%s" balance)))) (defvar ledger-mode-abbrev-table) -(defvar ledger-date-string-today (ledger-format-date)) - ;;; Editing commands @@ -386,7 +385,9 @@ With prefix ARG, decrement by that many instead." (define-key map (kbd "C-c C-o C-a") #'ledger-report-redo) (define-key map (kbd "C-c C-o C-e") #'ledger-report-edit-report) - (define-key map (kbd "C-c C-o C-g") #'ledger-report-goto) + ;; `C-g' is reserved as the universal quit key, so use `C-v' (visit) for + ;; ledger-report-goto instead. + (define-key map (kbd "C-c C-o C-v") #'ledger-report-goto) (define-key map (kbd "C-c C-o C-k") #'ledger-report-quit) (define-key map (kbd "C-c C-o C-r") #'ledger-report) (define-key map (kbd "C-c C-o C-s") #'ledger-report-save) @@ -399,7 +400,7 @@ With prefix ARG, decrement by that many instead." (define-key map (kbd "S-") #'ledger-date-down) ;; Reset the `text-mode' override of this standard binding - (define-key map (kbd "C-M-i") 'completion-at-point) + (define-key map (kbd "C-M-i") #'completion-at-point) map) "Keymap for `ledger-mode'.") @@ -447,11 +448,11 @@ With prefix ARG, decrement by that many instead." (define-derived-mode ledger-mode text-mode "Ledger" "A mode for editing ledger data files." (ledger-check-version) - (setq font-lock-defaults - '(ledger-font-lock-keywords t nil nil nil)) - (add-hook 'font-lock-extend-region-functions 'ledger-fontify-extend-region) + (setq-local font-lock-defaults + '(ledger-font-lock-keywords t nil nil nil)) + (add-hook 'font-lock-extend-region-functions #'ledger-fontify-extend-region nil t) (add-hook 'completion-at-point-functions #'ledger-complete-at-point nil t) - (add-hook 'after-save-hook 'ledger-report-redo nil t) + (add-hook 'after-save-hook 'ledger-report-redo-after-save nil t) (add-hook 'post-command-hook 'ledger-highlight-xact-under-point nil t) (add-hook 'before-revert-hook 'ledger-highlight--before-revert nil t) diff --git a/lisp/ledger-mode/ledger-occur.el b/lisp/ledger-mode/ledger-occur.el index 11d94e8a..4b23b0f6 100644 --- a/lisp/ledger-mode/ledger-occur.el +++ b/lisp/ledger-mode/ledger-occur.el @@ -153,25 +153,27 @@ Argument OVL-BOUNDS contains bounds for the transactions to be left visible." (when-let* ((endpoint (re-search-forward regex nil 'end)) (bounds (ledger-navigate-find-element-extents endpoint))) (push bounds lines) - ;; move to the end of the xact, no need to search inside it more - (goto-char (cadr bounds)))) + ;; Move to the end of the xact, no need to search inside it more. + ;; Defensive: if extent end is at or before point, advance past the + ;; match end so the loop can never wedge. + (goto-char (max (cadr bounds) (1+ (match-end 0)))))) (nreverse lines)))) (defun ledger-occur-compress-matches (buffer-matches) "Identify sequential xacts to reduce number of overlays required. BUFFER-MATCHES should be a list of (BEG END) lists." - (if buffer-matches - (let ((points (list)) - (current-beginning (caar buffer-matches)) - (current-end (cl-cadar buffer-matches))) - (dolist (match (cdr buffer-matches)) - (if (< (- (car match) current-end) 2) - (setq current-end (cadr match)) - (push (list current-beginning current-end) points) - (setq current-beginning (car match)) - (setq current-end (cadr match)))) - (nreverse (push (list current-beginning current-end) points))))) + (when buffer-matches + (let ((points (list)) + (current-beginning (caar buffer-matches)) + (current-end (cl-cadar buffer-matches))) + (dolist (match (cdr buffer-matches)) + (if (< (- (car match) current-end) 2) + (setq current-end (cadr match)) + (push (list current-beginning current-end) points) + (setq current-beginning (car match)) + (setq current-end (cadr match)))) + (nreverse (push (list current-beginning current-end) points))))) (provide 'ledger-occur) diff --git a/lisp/ledger-mode/ledger-reconcile.el b/lisp/ledger-mode/ledger-reconcile.el index bcf7319f..fbcb0067 100644 --- a/lisp/ledger-mode/ledger-reconcile.el +++ b/lisp/ledger-mode/ledger-reconcile.el @@ -156,8 +156,10 @@ described above." :type 'boolean :group 'ledger-reconcile) -(defvar-local ledger-reconcile-last-balance-message nil) -(defvar-local ledger-reconcile-last-balance-equals-target nil) +(defvar-local ledger-reconcile-last-balance-message nil + "Most recent cleared/pending balance line, displayed in the reconcile header.") +(defvar-local ledger-reconcile-last-balance-equals-target nil + "Non-nil when the most recent balance equals the reconciliation target.") (defface ledger-reconcile-last-balance-equals-target-face '((t :inherit (header-line success))) diff --git a/lisp/ledger-mode/ledger-regex.el b/lisp/ledger-mode/ledger-regex.el index 97bd5d2e..b0781c42 100644 --- a/lisp/ledger-mode/ledger-regex.el +++ b/lisp/ledger-mode/ledger-regex.el @@ -40,7 +40,7 @@ "\\(^[~=A-Za-z].+\\)+") (defconst ledger-comment-regex - "^[;#|\\*%].*\\|[ \t]+;.*") + "^[;#|*%].*\\|[ \t]+;.*") (defconst ledger-multiline-comment-start-regex "^!comment$") @@ -87,12 +87,6 @@ (defconst ledger-account-name-or-directive-regex (format "\\(?:%s\\|%s\\)" ledger-account-any-status-regex ledger-account-directive-regex)) -(defconst ledger-account-pending-regex - (concat "\\(^[ \t]+\\)!" ledger-account-name-maybe-virtual-regex)) - -(defconst ledger-account-cleared-regex - (concat "\\(^[ \t]+\\)*" ledger-account-name-maybe-virtual-regex)) - (defmacro ledger-define-regexp (name regex docs &rest args) "Simplify the creation of a Ledger regex and helper functions." (let* ((regex (eval regex)) diff --git a/lisp/ledger-mode/ledger-report.el b/lisp/ledger-mode/ledger-report.el index 7726eb35..a7498784 100644 --- a/lisp/ledger-mode/ledger-report.el +++ b/lisp/ledger-mode/ledger-report.el @@ -1,4 +1,4 @@ -;; ledger-report.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- +;;; ledger-report.el --- Helper code for use with the "ledger" command-line tool -*- lexical-binding: t; -*- ;; Copyright (C) 2003-2016 John Wiegley (johnw AT gnu DOT org) @@ -32,6 +32,7 @@ (declare-function ledger-read-string-with-default "ledger-mode" (prompt default)) (declare-function ledger-read-account-with-prompt "ledger-mode" (prompt)) (declare-function ledger-read-payee-with-prompt "ledger-mode" (prompt)) +(declare-function ledger-read-date "ledger-mode" (prompt)) (require 'easymenu) (require 'ansi-color) @@ -70,6 +71,8 @@ specifier." ("payee" . ledger-report-payee-format-specifier) ("account" . ledger-report-account-format-specifier) ("month" . ledger-report-month-format-specifier) + ("amount" . ledger-report-amount-format-specifier) + ("date" . ledger-report-date-format-specifier) ("tagname" . ledger-report-tagname-format-specifier) ("tagvalue" . ledger-report-tagvalue-format-specifier)) "An alist mapping ledger report format specifiers to implementing functions. @@ -162,7 +165,12 @@ Calls `shrink-window-if-larger-than-buffer'." (defvar ledger-report-buffer-name "*Ledger Report*") (defvar-local ledger-report-name nil) -(defvar-local ledger-report-cmd nil) +(defvar-local ledger-report-cmd nil + "The raw command template for the current ledger report buffer. +Format specifiers such as %(binary) and %(ledger-file) are left +unexpanded and resolved at execution time in `ledger-do-report', +so the current values of `ledger-binary-path' and the ledger file +take effect on each run.") (defvar-local ledger-report-saved nil) (defvar-local ledger-report-current-month nil) (defvar-local ledger-report-is-reversed nil) @@ -193,10 +201,11 @@ See documentation for the function `ledger-master-file'") (save-excursion (reverse-region (point) (point-max))))) -(defun ledger-report-maybe-shrink-window () - "Shrink window if `ledger-report-resize-window' is non-nil." +(defun ledger-report-maybe-shrink-window (buf) + "Shrink window displaying BUF if `ledger-report-resize-window' is non-nil." (when ledger-report-resize-window - (shrink-window-if-larger-than-buffer))) + (when-let* ((w (get-buffer-window buf))) + (shrink-window-if-larger-than-buffer w)))) (defvar ledger-report-mode-map (let ((map (make-sparse-keymap))) @@ -237,7 +246,7 @@ See documentation for the function `ledger-master-file'") (define-derived-mode ledger-report-mode special-mode "Ledger-Report" "A mode for viewing ledger reports." - (setq-local revert-buffer-function #'ledger-report-redo) + (setq-local revert-buffer-function #'ledger-report--revert-buffer) (hack-dir-local-variables-non-file-buffer)) (defconst ledger-report--extra-args-marker "[[ledger-mode-flags]]") @@ -259,6 +268,14 @@ See documentation for the function `ledger-master-file'") ;; values, but it remains to be implemented. (ledger-read-string-with-default "Tag Value" nil)) +(defun ledger-report-amount-format-specifier () + "Return a commoditized amount." + (ledger-commodity-to-string (ledger-read-commodity-string "Amount"))) + +(defun ledger-report-date-format-specifier () + "Return a date." + (ledger-read-date "Date: ")) + (defun ledger-report-read-name () "Read the name of a ledger report to use, with completion. @@ -305,7 +322,7 @@ used to generate the buffer, navigating the buffer, etc." (with-silent-modifications (erase-buffer) (ledger-do-report ledger-report-cmd)) - (ledger-report-maybe-shrink-window) + (ledger-report-maybe-shrink-window (current-buffer)) (run-hooks 'ledger-report-after-report-hook) (message (substitute-command-keys (concat "\\[ledger-report-quit] to quit; " "\\[ledger-report-redo] to redo; " @@ -339,7 +356,7 @@ returns nil." (defun ledger-report-read-command (report-cmd) "Read the command line to create a report from REPORT-CMD." (read-from-minibuffer "Report command line: " - (if (null report-cmd) "ledger " report-cmd) + (if (null report-cmd) "%(binary) " report-cmd) nil nil 'ledger-report-cmd-prompt-history)) (defun ledger-report-ledger-file-format-specifier () @@ -416,7 +433,7 @@ MONTH is of the form (YEAR . INDEX) where INDEX ranges from (defun ledger-report-month-format-specifier () "Substitute current month." - (with-current-buffer (or ledger-report-buffer-name (current-buffer)) + (with-current-buffer ledger-report-buffer-name (let* ((month (or ledger-report-current-month (ledger-report--current-month))) (year (car month)) (month-index (cdr month))) @@ -427,7 +444,9 @@ MONTH is of the form (YEAR . INDEX) where INDEX ranges from Format specifiers are defined in the `ledger-report-format-specifiers' alist. The functions are -called in the ledger buffer for which the report is being run." +called in the ledger buffer for which the report is being run. + +This function must be called from a report buffer." (let ((ledger-buf ledger-report-ledger-buf)) (with-temp-buffer (save-excursion (insert report-cmd)) @@ -465,13 +484,19 @@ called in the ledger buffer for which the report is being run." (defun ledger-report-cmd (report-name edit) "Get the command line to run the report name REPORT-NAME. -Optionally EDIT the command." +Optionally EDIT the command. + +The returned command retains its format specifiers (e.g., %(binary) +and %(ledger-file)) so that `ledger-do-report' can expand them each +time the report runs. This keeps saved reports in `ledger-reports' +responsive to later changes in `ledger-binary-path' or the current +ledger file, rather than baking in the values observed when the +report was first run." (let ((report-cmd (car (cdr (assoc report-name ledger-reports))))) ;; logic for substitution goes here (when (or (null report-cmd) edit) (setq report-cmd (ledger-report-read-command report-cmd)) (setq ledger-report-saved nil)) ;; this is a new report, or edited report - (setq report-cmd (ledger-report-expand-format-specifiers report-cmd)) (setq ledger-report-cmd report-cmd) (or (string-empty-p report-name) (ledger-report-name-exists report-name) @@ -491,7 +516,7 @@ Optionally EDIT the command." (previous-month (ledger-report--shift-month current-month shift))) (setq ledger-report-current-month previous-month) (ledger-report-cmd ledger-report-name nil) - (ledger-report-redo))) + (revert-buffer))) (defun ledger-report--add-links () "Replace file and line annotations with buttons." @@ -518,16 +543,19 @@ Optionally EDIT the command." (defun ledger-do-report (cmd) "Run a report command line CMD. -CMD may contain a (shell-quoted) version of -`ledger-report--extra-args-marker', which will be replaced by -arguments returned by `ledger-report--compute-extra-args'." +CMD may contain format specifiers (e.g., %(binary), %(ledger-file)) +which are expanded via `ledger-report-expand-format-specifiers' +each time the report runs. It may also contain a (shell-quoted) +version of `ledger-report--extra-args-marker', which will be +replaced by arguments returned by `ledger-report--compute-extra-args'." (goto-char (point-min)) - (let* ((marker ledger-report--extra-args-marker) + (let* ((expanded-cmd (ledger-report-expand-format-specifiers cmd)) + (marker ledger-report--extra-args-marker) (marker-re (concat " *" (regexp-quote marker))) - (args (ledger-report--compute-extra-args cmd)) + (args (ledger-report--compute-extra-args expanded-cmd)) (args-str (concat " " (mapconcat #'shell-quote-argument args " "))) - (clean-cmd (replace-regexp-in-string marker-re "" cmd t t)) - (real-cmd (replace-regexp-in-string marker-re args-str cmd t t))) + (clean-cmd (replace-regexp-in-string marker-re "" expanded-cmd t t)) + (real-cmd (replace-regexp-in-string marker-re args-str expanded-cmd t t))) (setq header-line-format (and ledger-report-use-header-line `(:eval (ledger-report--compute-header-line ,clean-cmd)))) @@ -541,7 +569,7 @@ arguments returned by `ledger-report--compute-extra-args'." (setq report (ansi-color-apply report))) (save-excursion (insert report)) - (when (ledger-report--cmd-needs-links-p cmd) + (when (ledger-report--cmd-needs-links-p expanded-cmd) (save-excursion (ledger-report--add-links)))))) @@ -571,31 +599,42 @@ specific posting at point instead." (if (not rbuf) (error "There is no ledger report buffer")) (pop-to-buffer rbuf) - (ledger-report-maybe-shrink-window))) + (ledger-report-maybe-shrink-window rbuf))) -(defun ledger-report-redo (&optional _ignore-auto _noconfirm) - "Redo the report in the current ledger report buffer. +(defun ledger-report-redo-after-save () + "If `ledger-report-auto-refresh' is non-nil, redo the report buffer. + +This is intended to be added to `after-save-hook' by `ledger-mode'." + (when (and ledger-report-auto-refresh + (get-buffer ledger-report-buffer-name)) + (with-current-buffer ledger-report-buffer-name + (revert-buffer)))) + +(defun ledger-report--revert-buffer (&optional _ignore-auto _noconfirm) + "Redo the report in the current buffer. IGNORE-AUTO and NOCONFIRM are for compatibility with `revert-buffer-function' and are currently ignored." + (when (buffer-live-p ledger-report-ledger-buf) + (setq ledger-report-cursor-line-number (line-number-at-pos)) + (with-silent-modifications + (erase-buffer) + (ledger-do-report ledger-report-cmd) + (when ledger-report-is-reversed + (ledger-report-reverse-lines)) + (when ledger-report-auto-refresh-sticky-cursor + (forward-line (- ledger-report-cursor-line-number 5)))) + (ledger-report-maybe-shrink-window (current-buffer)) + (run-hooks 'ledger-report-after-report-hook))) + +(defun ledger-report-redo () + "Redo the report in the ledger report buffer." (interactive) (unless (or (derived-mode-p 'ledger-mode) (derived-mode-p 'ledger-report-mode)) (user-error "Not in a ledger-mode or ledger-report-mode buffer")) - (let ((cur-buf (current-buffer))) - (when (and ledger-report-auto-refresh - (get-buffer ledger-report-buffer-name)) - (pop-to-buffer (get-buffer ledger-report-buffer-name)) - (ledger-report-maybe-shrink-window) - (setq ledger-report-cursor-line-number (line-number-at-pos)) - (with-silent-modifications - (erase-buffer) - (ledger-do-report ledger-report-cmd) - (when ledger-report-is-reversed - (ledger-report-reverse-lines)) - (when ledger-report-auto-refresh-sticky-cursor - (forward-line (- ledger-report-cursor-line-number 5)))) - (run-hooks 'ledger-report-after-report-hook) - (pop-to-buffer cur-buf)))) + (when (get-buffer ledger-report-buffer-name) + (with-current-buffer ledger-report-buffer-name + (revert-buffer)))) (defun ledger-report-quit () "Quit the ledger report buffer and kill its buffer." @@ -614,8 +653,11 @@ IGNORE-AUTO and NOCONFIRM are for compatibility with (defun ledger-report-edit-report () "Edit the current report command in the mini buffer and re-run the report." (interactive) - (setq ledger-report-cmd (ledger-report-read-command ledger-report-cmd)) - (ledger-report-redo)) + (unless (derived-mode-p 'ledger-report-mode) + (user-error "Not a ledger report buffer")) + (with-current-buffer ledger-report-buffer-name + (setq ledger-report-cmd (ledger-report-read-command ledger-report-cmd)) + (revert-buffer))) (define-obsolete-function-alias 'ledger-report-select-report #'ledger-report "ledger 4.0.0") @@ -673,7 +715,7 @@ IGNORE-AUTO and NOCONFIRM are for compatibility with (setq ledger-report-cmd (replace-match "" nil nil ledger-report-cmd)) (setq ledger-report-cmd (concat ledger-report-cmd " --exchange " ledger-reconcile-default-commodity)))) - (ledger-report-redo)) + (revert-buffer)) (provide 'ledger-report) diff --git a/lisp/ledger-mode/ledger-schedule.el b/lisp/ledger-mode/ledger-schedule.el index 2c1508ed..d70f0a2d 100644 --- a/lisp/ledger-mode/ledger-schedule.el +++ b/lisp/ledger-mode/ledger-schedule.el @@ -74,11 +74,6 @@ abbreviation for a day and the number of that day in the week." :type '(alist :key-type string :value-type (group integer)) :group 'ledger-schedule) -(defsubst ledger-between (val low high) - "Return TRUE if VAL >= LOW and <= HIGH." - (declare (obsolete <= "Ledger-mode v4.0.1")) - (<= low val high)) - (defun ledger-schedule-days-in-month (month year) "Return number of days in the MONTH, MONTH is from 1 to 12. If YEAR is nil, assume it is not a leap year" @@ -142,8 +137,8 @@ The dates are given by the pairs MONTH1 DAY1 and MONTH2 DAY2." (target-month (gensym)) (target-day (gensym))) `(let* ((,decoded (decode-time date)) - (,target-month (nth 4 decoded)) - (,target-day (nth 3 decoded))) + (,target-month (nth 4 ,decoded)) + (,target-day (nth 3 ,decoded))) (and (and (> ,target-month ,month1) (< ,target-month ,month2)) (and (> ,target-day ,day1) diff --git a/lisp/llama/llama-pkg.el b/lisp/llama/llama-pkg.el index 5734b973..31b7899d 100644 --- a/lisp/llama/llama-pkg.el +++ b/lisp/llama/llama-pkg.el @@ -1,9 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "llama" "20260301.1253" +(define-package "llama" "20260601.1455" "Compact syntax for short lambda." '((emacs "26.1") - (compat "30.1")) + (compat "31.0")) :url "https://github.com/tarsius/llama" - :commit "d430d48e0b5afd2a34b5531f103dcb110c3539c4" - :revdesc "d430d48e0b5a" - :keywords '("extensions")) + :commit "4d4024048053b898a01521046e0f063ee47615b0" + :revdesc "4d4024048053" + :keywords '("extensions") + :authors '(("Jonas Bernoulli" . "emacs.llama@jonas.bernoulli.dev")) + :maintainers '(("Jonas Bernoulli" . "emacs.llama@jonas.bernoulli.dev"))) diff --git a/lisp/llama/llama.el b/lisp/llama/llama.el index 18c6cb33..653579b3 100644 --- a/lisp/llama/llama.el +++ b/lisp/llama/llama.el @@ -2,15 +2,15 @@ ;; Copyright (C) 2020-2026 Jonas Bernoulli -;; Authors: Jonas Bernoulli +;; Author: Jonas Bernoulli ;; Homepage: https://github.com/tarsius/llama ;; Keywords: extensions -;; Package-Version: 20260301.1253 -;; Package-Revision: d430d48e0b5a +;; Package-Version: 20260601.1455 +;; Package-Revision: 4d4024048053 ;; Package-Requires: ( ;; (emacs "26.1") -;; (compat "30.1")) +;; (compat "31.0")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -362,7 +362,7 @@ expansion, and the looks of this face should hint at that.") (put-text-property (match-beginning 0) (point) 'font-lock-multiline t) (llama--fontify (cdr expr) nil nil t))))) - (list re end))) ; Silence compiler. + (progn re end nil))) ; Silence compiler. (defun llama--fontify (expr &optional fnpos backquoted top) (static-if (fboundp 'bare-symbol) @@ -419,7 +419,7 @@ expansion, and the looks of this face should hint at that.") (throw t nil)))) (when expr (llama--fontify expr fnpos)))))) - (list expr fnpos backquoted top)) ; Silence compiler. + (and expr fnpos backquoted top nil)) ; Silence compiler. (defvar llama-fontify-mode-lighter nil) diff --git a/lisp/magit-section/magit-section-pkg.el b/lisp/magit-section/magit-section-pkg.el index 4cc2cc52..a9938028 100644 --- a/lisp/magit-section/magit-section-pkg.el +++ b/lisp/magit-section/magit-section-pkg.el @@ -1,14 +1,14 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "magit-section" "20260330.1102" +(define-package "magit-section" "20260514.937" "Sections for read-only buffers." '((emacs "28.1") - (compat "30.1") + (compat "31.0") (cond-let "0.2") (llama "1.0") (seq "2.24")) :url "https://github.com/magit/magit" - :commit "89a51310bd8f8087c44f7ac5c902cc82dddbbe2a" - :revdesc "89a51310bd8f" + :commit "be5a3b0e9f7a64bcb222ba546a18e6b09922e0a9" + :revdesc "be5a3b0e9f7a" :keywords '("tools") :authors '(("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) :maintainers '(("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev"))) diff --git a/lisp/magit-section/magit-section.el b/lisp/magit-section/magit-section.el index e7b6e807..246f68f4 100644 --- a/lisp/magit-section/magit-section.el +++ b/lisp/magit-section/magit-section.el @@ -8,11 +8,11 @@ ;; Homepage: https://github.com/magit/magit ;; Keywords: tools -;; Package-Version: 20260330.1102 -;; Package-Revision: 89a51310bd8f +;; Package-Version: 20260514.937 +;; Package-Revision: be5a3b0e9f7a ;; Package-Requires: ( ;; (emacs "28.1") -;; (compat "30.1") +;; (compat "31.0") ;; (cond-let "0.2") ;; (llama "1.0") ;; (seq "2.24")) @@ -51,15 +51,10 @@ (require 'llama) ; For (##these ...) see M-x describe-function RET # # RET. (require 'subr-x) -;; For older Emacs releases we depend on an updated `seq' release from GNU -;; ELPA, for `seq-keep'. Unfortunately something else may require `seq' -;; before `package' had a chance to put this version on the `load-path'. -(when (and (featurep 'seq) - (not (fboundp 'seq-keep))) - (unload-feature 'seq 'force)) -(require 'seq) -;; Furthermore, by default `package' just silently refuses to upgrade. -(defconst magit--core-upgrade-instructions "\ +(defun magit--display-core-upgrade-instructions (package version) + (display-warning 'magit + (substitute-command-keys + (format "\ Magit requires `%s' >= %s, but due to bad defaults, Emacs' package manager, refuses to upgrade this and other built-in packages to higher releases @@ -87,13 +82,29 @@ reinstalling Magit. If you don't use the `package' package manager but still get this warning, then your chosen package manager likely has a -similar defect.") -(unless (fboundp 'seq-keep) - (display-warning 'magit (substitute-command-keys - (format magit--core-upgrade-instructions - 'seq "2.24" 'seq 'seq 'seq 'seq)) +similar defect." + package version package package package package)) :emergency)) +;; For older Emacs releases we depend on an updated `seq' release from GNU +;; ELPA, for `seq-keep'. Unfortunately something else may require `seq' +;; before `package' had a chance to put this version on the `load-path'. +(when (and (featurep 'seq) + (not (fboundp 'seq-keep))) + (unload-feature 'seq 'force)) +(require 'seq) +;; Furthermore, by default `package' just silently refuses to upgrade. +(unless (fboundp 'seq-keep) + (magit--display-core-upgrade-instructions 'seq "2.24")) + +;; Likewise, we require a recent `transient'. +(when (and (featurep 'transient) + (not (fboundp 'transient--advise-this-command))) + (unload-feature 'transient 'force)) +(require 'transient) +(unless (fboundp 'transient--advise-this-command) + (magit--display-core-upgrade-instructions 'transient "0.13")) + (require 'cursor-sensor) (require 'format-spec) @@ -116,6 +127,9 @@ similar defect.") That function in turn is used by all section movement commands. See also info node `(magit)Section Movement'.") +(defvar magit-mouse-set-point-hook nil + "Hook run by `magit-mouse-set-point-hook'.") + (defvar magit-section-set-visibility-hook (list #'magit-section-cached-visibility) "Hook used to set the initial visibility of a section. @@ -878,10 +892,10 @@ If there is no previous sibling section, then move to the parent." ((magit-section-backward)))) (defun magit-mouse-set-point (event &optional promote-to-region) - "Like `mouse-set-point' but also call `magit-section-movement-hook'." + "Like `mouse-set-point' but also call `magit-mouse-set-point-hook'." (interactive "e\np") (mouse-set-point event promote-to-region) - (run-hook-with-args 'magit-section-movement-hook (magit-current-section))) + (run-hook-with-args 'magit-mouse-set-point-hook (magit-current-section))) (defun magit-section-goto (arg) "Run `magit-section-movement-hook'. @@ -1105,7 +1119,7 @@ sections." (cl-do* ((s section (oref s parent)) (i (1- (length (magit-section-ident s))) - (cl-decf i))) + (decf i))) ((cond ((< i level) (magit-section-show-children s (- level i 1)) t) ((= i level) (magit-section-hide s) t)) (magit-section-goto s)))))) @@ -2239,8 +2253,8 @@ forms CONDITION can take." (setq siblings nil))) (setq sections (nreverse sections)) (and (or (not condition) - (seq-every-p (##magit-section-match condition %) - sections)) + (all (##magit-section-match condition %) + sections)) sections)))))))) (defun magit-map-sections (function &optional section) @@ -2675,11 +2689,15 @@ with the variables' values as arguments, which were recorded by ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/.dir-locals.el b/lisp/magit/.dir-locals.el index 07a40bba..21399e2c 100644 --- a/lisp/magit/.dir-locals.el +++ b/lisp/magit/.dir-locals.el @@ -2,7 +2,10 @@ (indent-tabs-mode . nil)) (emacs-lisp-mode (checkdoc-allow-quoting-nil-and-t . t) - (lisp-indent-local-overrides . ((cond . 0) (interactive . 0)))) + (lisp-indent-local-overrides + . ((cond . 0) + (interactive . 0) + (make-obsolete-variable . 1)))) (makefile-mode (indent-tabs-mode . t) (mode . outline-minor) diff --git a/lisp/magit/AUTHORS.md b/lisp/magit/AUTHORS.md index b1b22374..d36edfde 100644 --- a/lisp/magit/AUTHORS.md +++ b/lisp/magit/AUTHORS.md @@ -87,6 +87,7 @@ All Contributors - Bryan Shell - Buster Copley - Cameron Chaparro +- Carl Lei - Carl Lieberman - Chillar Anand - Chris Bernard @@ -195,6 +196,7 @@ All Contributors - Johannes Altmanninger - Johannes Maier - Johann Klähn +- John Eismeier - John Mastro - John Morris - John Wiegley @@ -305,6 +307,7 @@ All Contributors - Paul Pogonyshev - Paul Stadig - Pavel Holejsovsky +- Pedro Ribeiro Mendes Júnior - Pekka Pessi - Pengji Zhang - Peter Eisentraut @@ -386,6 +389,7 @@ All Contributors - Teruki Shigitani - Thierry Volpiatto - Thomas A Caswell +- Thomas Ferrand - Thomas Fini Hansen - Thomas Frössman - Thomas Jost diff --git a/lisp/magit/git-commit.el b/lisp/magit/git-commit.el index 9535c635..fa3c2b7e 100644 --- a/lisp/magit/git-commit.el +++ b/lisp/magit/git-commit.el @@ -247,7 +247,7 @@ See also manpage git-interpret-trailer(1). This package does not use that Git command, but the initial description still serves as a good introduction." :group 'git-commit - :safe (##and (listp %) (seq-every-p #'stringp %)) + :safe (##and (listp %) (all #'stringp %)) :type '(repeat string)) (defcustom git-commit-use-local-message-ring nil @@ -760,7 +760,7 @@ With a numeric prefix ARG, go back ARG messages." (when-let* ((message (git-commit-buffer-message)) (_(not (ring-member log-edit-comment-ring message)))) (ring-insert log-edit-comment-ring message) - (cl-incf arg) + (incf arg) (setq len (ring-length log-edit-comment-ring))) ;; Delete the message but not the instructions at the end. (save-restriction @@ -1020,32 +1020,48 @@ completion candidates. The input must have the form \"NAME \"." (git-commit--insert-trailer trailer (format "%s <%s>" name email))) (defun git-commit--insert-trailer (trailer value) + (git-commit--insert-trailer-1 (format "%s: %s\n" trailer value))) + +(defun git-commit--insert-trailer-1 (string &optional before-trailers) (save-excursion - (let ((string (format "%s: %s" trailer value)) - (leading-comment-end nil)) - ;; Make sure we skip forward past any leading comments. + (goto-char (point-min)) + (while (looking-at comment-start) + (forward-line)) + (when (or (eobp) (looking-at "diff --git")) (goto-char (point-min)) - (while (looking-at comment-start) - (forward-line)) - (setq leading-comment-end (point)) + (save-excursion (insert ?\n))) + (let ((bound (and (not (or (bobp) (eobp))) (point)))) (goto-char (point-max)) - (cond - ;; Look backwards for existing trailers. - ((re-search-backward (git-commit--trailer-regexp) nil t) - (end-of-line) - (insert ?\n string) - (unless (= (char-after) ?\n) - (insert ?\n))) - ;; Or place the new trailer right before the first non-leading - ;; comments. - (t - (while (re-search-backward (concat "^" comment-start) - leading-comment-end t)) - (unless (looking-back "\n\n" nil) - (insert ?\n)) - (insert string ?\n)))) - (unless (or (eobp) (= (char-after) ?\n)) - (insert ?\n)))) + (unless (or (bobp) (= (char-before) ?\n)) + (insert ?\n)) + (cond (before-trailers + (git-commit--goto-insert-position bound) + (while (re-search-backward (git-commit--trailer-regexp) nil t)) + (unless (looking-back "\n\n" nil) + (insert ?\n))) + ((re-search-backward (git-commit--trailer-regexp) nil t) + (goto-char (match-end 0)) + (if (eobp) (insert ?\n) (forward-char))) + (t + (git-commit--goto-insert-position bound) + (unless (looking-back "\n\n" nil) + (insert ?\n))))) + (insert string) + (unless (ignore-errors (= (char-before) ?\n)) (insert ?\n)) + (unless (ignore-errors (= (char-after) ?\n)) (insert ?\n)))) + +(defun git-commit--goto-insert-position (bound) + (let ((match (point))) + (cond ((re-search-backward (format "^%s -+ >8 -+" comment-start) nil t)) + ((and (eobp) (bolp)) + (forward-line -1))) + (while (and (or (looking-at comment-start) + (looking-at "[\s\t]*$")) + (or (not bound) (> (point) bound)) + (not (bobp))) + (setq match (point)) + (forward-line -1)) + (goto-char match))) ;;; Font-Lock @@ -1357,11 +1373,15 @@ commit, then the hook is not run at all." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/git-hooks/applypatch-msg b/lisp/magit/git-hooks/applypatch-msg deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/applypatch-msg +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/commit-msg b/lisp/magit/git-hooks/commit-msg deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/commit-msg +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/fallthrough b/lisp/magit/git-hooks/fallthrough deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/fallthrough +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/fsmonitor-watchman b/lisp/magit/git-hooks/fsmonitor-watchman deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/fsmonitor-watchman +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/p4-changelist b/lisp/magit/git-hooks/p4-changelist deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/p4-changelist +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/p4-post-changelist b/lisp/magit/git-hooks/p4-post-changelist deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/p4-post-changelist +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/p4-pre-submit b/lisp/magit/git-hooks/p4-pre-submit deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/p4-pre-submit +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/p4-prepare-changelist b/lisp/magit/git-hooks/p4-prepare-changelist deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/p4-prepare-changelist +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/post-applypatch b/lisp/magit/git-hooks/post-applypatch deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/post-applypatch +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/post-checkout b/lisp/magit/git-hooks/post-checkout deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/post-checkout +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/post-commit b/lisp/magit/git-hooks/post-commit deleted file mode 100755 index 79b9aa0e..00000000 --- a/lisp/magit/git-hooks/post-commit +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -if [[ "$INSIDE_EMACS" == *magit ]] -then - for arg in "$@"; do args+="\"$arg\""; done - $GIT_EDITOR --eval \ - "(magit-run-git-hook '(common-post-commit post-commit) ${args[@]})" -fi - -if [[ -x "$SHADOWED_GITHOOK_DIRECTORY" ]] -then - "$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" "$@" -fi diff --git a/lisp/magit/git-hooks/post-index-change b/lisp/magit/git-hooks/post-index-change deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/post-index-change +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/post-merge b/lisp/magit/git-hooks/post-merge deleted file mode 100755 index 5716dee0..00000000 --- a/lisp/magit/git-hooks/post-merge +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -if [[ "$INSIDE_EMACS" == *magit ]] -then - for arg in "$@"; do args+="\"$arg\""; done - $GIT_EDITOR --eval \ - "(magit-run-git-hook '(common-post-commit post-merge) ${args[@]})" -fi - -if [[ -x "$SHADOWED_GITHOOK_DIRECTORY" ]] -then - "$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" "$@" -fi diff --git a/lisp/magit/git-hooks/post-receive b/lisp/magit/git-hooks/post-receive deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/post-receive +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/post-rewrite b/lisp/magit/git-hooks/post-rewrite deleted file mode 100755 index 128ddaaa..00000000 --- a/lisp/magit/git-hooks/post-rewrite +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -if [[ "$INSIDE_EMACS" == *magit ]] -then - for arg in "$@"; do args+="\"$arg\""; done - $GIT_EDITOR --eval \ - "(magit-run-git-hook '(common-post-commit post-rewrite) ${args[@]})" -fi - -if [[ -x "$SHADOWED_GITHOOK_DIRECTORY" ]] -then - "$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" "$@" -fi diff --git a/lisp/magit/git-hooks/post-update b/lisp/magit/git-hooks/post-update deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/post-update +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-applypatch b/lisp/magit/git-hooks/pre-applypatch deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-applypatch +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-auto-gc b/lisp/magit/git-hooks/pre-auto-gc deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-auto-gc +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-commit b/lisp/magit/git-hooks/pre-commit deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-commit +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-merge-commit b/lisp/magit/git-hooks/pre-merge-commit deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-merge-commit +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-push b/lisp/magit/git-hooks/pre-push deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-push +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-rebase b/lisp/magit/git-hooks/pre-rebase deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-rebase +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/pre-receive b/lisp/magit/git-hooks/pre-receive deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/pre-receive +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/prepare-commit-msg b/lisp/magit/git-hooks/prepare-commit-msg deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/prepare-commit-msg +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/proc-receive b/lisp/magit/git-hooks/proc-receive deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/proc-receive +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/push-to-checkout b/lisp/magit/git-hooks/push-to-checkout deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/push-to-checkout +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/reference-transaction b/lisp/magit/git-hooks/reference-transaction deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/reference-transaction +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/sendemail-validate b/lisp/magit/git-hooks/sendemail-validate deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/sendemail-validate +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-hooks/update b/lisp/magit/git-hooks/update deleted file mode 100755 index 51928e7f..00000000 --- a/lisp/magit/git-hooks/update +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -hook="$SHADOWED_GITHOOK_DIRECTORY/$(basename $0)" - -if [[ -x "$hook" ]] -then - "$hook" "$@" -fi diff --git a/lisp/magit/git-rebase.el b/lisp/magit/git-rebase.el index 7482847c..0a8135ea 100644 --- a/lisp/magit/git-rebase.el +++ b/lisp/magit/git-rebase.el @@ -894,12 +894,11 @@ except for the \"pick\" command." (defun git-rebase--insert-descriptions (alist) (pcase-dolist (`(,cmd . ,desc) alist) - (insert (format (propertize "%s %s %s\n" - 'font-lock-face 'font-lock-comment-face) - comment-start - (string-pad - (substitute-command-keys (format "\\[%s]" cmd)) 8) - (replace-regexp-in-string "#" comment-start desc))))) + (insert + (format (propertize "%s %s %s\n" 'font-lock-face 'font-lock-comment-face) + comment-start + (string-pad (substitute-command-keys (format "\\[%s]" cmd)) 8) + (string-replace "#" comment-start desc))))) (add-hook 'git-rebase-mode-hook #'git-rebase-mode-show-keybindings t) @@ -945,11 +944,15 @@ is used as a value for `imenu-extract-index-name-function'." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/githooks/config b/lisp/magit/githooks/config new file mode 100644 index 00000000..cc44e82b --- /dev/null +++ b/lisp/magit/githooks/config @@ -0,0 +1,6 @@ +# -*- mode: gitconfig -*- +[hook "magit-common-post-commit"] + event = post-commit + event = post-merge + event = post-rewrite + command = magit-run-git-hook magit-common-git-post-commit-functions diff --git a/lisp/magit/githooks/magit-run-git-hook b/lisp/magit/githooks/magit-run-git-hook new file mode 100755 index 00000000..0d6dfd1d --- /dev/null +++ b/lisp/magit/githooks/magit-run-git-hook @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +if [ -n "$MAGIT_HOOK_EDITOR" ] +then + for arg in "$@"; do args+="\"$arg\""; done + $MAGIT_HOOK_EDITOR --eval "(magit-run-git-hook ${args[@]})" +else + true +fi diff --git a/lisp/magit/magit-apply.el b/lisp/magit/magit-apply.el index d8a31a04..a4992da4 100644 --- a/lisp/magit/magit-apply.el +++ b/lisp/magit/magit-apply.el @@ -374,7 +374,7 @@ ignored) files." (url (let ((default-directory (file-name-as-directory (expand-file-name repo)))) (or (magit-get "remote" (magit-get-some-remote) "url") - (concat (file-name-as-directory ".") repo)))) + (file-name-concat "." repo)))) (package (and (equal borg-user-emacs-directory topdir) (file-name-nondirectory (directory-file-name repo))))) @@ -819,11 +819,15 @@ a separate commit. A typical workflow would be: ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-autorevert.el b/lisp/magit/magit-autorevert.el index f684c54f..97428067 100644 --- a/lisp/magit/magit-autorevert.el +++ b/lisp/magit/magit-autorevert.el @@ -30,6 +30,7 @@ ;;; Code: (require 'autorevert) +(require 'compat) (declare-function magit-file-tracked-p "magit-git" (file)) (declare-function magit-toplevel "magit-git" (&optional directory)) @@ -259,7 +260,7 @@ defaults to nil) for any BUFFER." (file-in-directory-p dir top)))))) (define-advice auto-revert-buffers (:around (fn) buffer-list-filter) - (cl-incf magit-auto-revert-counter) + (incf magit-auto-revert-counter) (if (or global-auto-revert-mode (not auto-revert-buffer-list) (not auto-revert-buffer-list-filter)) diff --git a/lisp/magit/magit-base.el b/lisp/magit/magit-base.el index 5f5ec068..9a0440ea 100644 --- a/lisp/magit/magit-base.el +++ b/lisp/magit/magit-base.el @@ -43,15 +43,6 @@ (require 'llama) ; For (##these ...) see M-x describe-function RET # # RET. (require 'subr-x) -;; For older Emacs releases we depend on an updated `seq' release from -;; GNU ELPA, for `seq-keep'. Unfortunately something else may already -;; have required `seq', before `package' had a chance to put the more -;; recent version earlier on the `load-path'. -(when (and (featurep 'seq) - (not (fboundp 'seq-keep))) - (unload-feature 'seq 'force)) -(require 'seq) - (require 'crm) (require 'magit-section) @@ -504,6 +495,9 @@ and delay of your graphical environment or operating system." (heading-highlight-face :initform 'magit-diff-hunk-heading-highlight) (heading-selection-face :initform 'magit-diff-hunk-heading-selection))) +(defun magit--meta-hunk-p (section) + (not (cdr (oref section value)))) + (setf (alist-get 'file magit--section-type-alist) 'magit-file-section) (setf (alist-get 'module magit--section-type-alist) 'magit-module-section) (setf (alist-get 'hunk magit--section-type-alist) 'magit-hunk-section) @@ -824,13 +818,13 @@ ACTION is a member of option `magit-slow-confirm'." (or (cond ((and (not (eq action t)) (or (eq magit-no-confirm t) (memq action magit-no-confirm) - (magit--any (pcase-lambda (`(,key ,var . ,sub)) - (and (memq key magit-no-confirm) - (memq action sub) - (or (not var) - (and (boundp var) - (symbol-value var))))) - magit--no-confirm-alist))) + (any (pcase-lambda (`(,key ,var . ,sub)) + (and (memq key magit-no-confirm) + (memq action sub) + (or (not var) + (and (boundp var) + (symbol-value var))))) + magit--no-confirm-alist))) (or (not sitems) items)) ((not sitems) (magit-y-or-n-p prompt action)) @@ -917,7 +911,7 @@ match was against a string, then that has to be provided as STRING." `(let* ((,s ,string) ,@(save-match-data (seq-keep (lambda (sym) - (cl-incf i) + (incf i) (and (not (eq (aref (symbol-name sym) 0) ?_)) `(,sym (match-str ,i ,s)))) varlist))) @@ -1025,12 +1019,6 @@ and return a new string, instead use `magit--remove-text-properties'." If PROPS is nil, remove all properties." (magit--delete-text-properties (copy-sequence string) props)) -;;; Emacs Compatibility - -(static-if (fboundp 'member-if) ; Emacs 31.1 - (defalias 'magit--any 'member-if) - (defalias 'magit--any 'cl-member-if)) - ;;; Kludges for Emacs Bugs (defun magit-which-function () @@ -1042,6 +1030,20 @@ setting `imenu--index-alist' to nil before calling that function." (setq imenu--index-alist nil) (which-function)) +(static-when (version< emacs-version "31.1") + (define-advice dabbrev-capf (:around (fn) git-commit) + "Backport bugfix from debbug#80645 / a7d05207214 / 31.1. +See #5551, #5556 and #5558 (I wish I had not rushed this)." + (pcase-let ((`(,beg ,end ,table . ,rest) (funcall fn))) + `( ,beg ,end + ,(lambda (&rest args) + (condition-case err + (apply table args) + (user-error + (unless (string-prefix-p "No dynamic expansion" (cadr err)) + (signal (car err) (cdr err)))))) + ,@rest)))) + ;;; Kludges for Custom (defun magit-custom-initialize-reset (symbol exp) @@ -1208,11 +1210,12 @@ Like `message', except that `message-log-max' is bound to nil." (defun magit--find-buffer (&rest plist) "Like `find-buffer' but take multiple VARIABLE-VALUE pairs." (seq-find (lambda (buf) - (while (and plist - (equal (buffer-local-value (car plist) buf) - (cadr plist))) - (setq plist (cddr plist))) - (not plist)) + (let ((plist plist)) + (while (and plist + (equal (buffer-local-value (car plist) buf) + (cadr plist))) + (setq plist (cddr plist))) + (not plist))) (buffer-list))) ;;; _ @@ -1220,11 +1223,15 @@ Like `message', except that `message-log-max' is bound to nil." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-bisect.el b/lisp/magit/magit-bisect.el index b1b56225..8b11cf33 100644 --- a/lisp/magit/magit-bisect.el +++ b/lisp/magit/magit-bisect.el @@ -31,7 +31,7 @@ ;;; Options (defcustom magit-bisect-show-graph t - "Whether to use `--graph' in the log showing commits yet to be bisected." + "Whether to use \"--graph\" in the log showing commits yet to be bisected." :package-version '(magit . "2.8.0") :group 'magit-status :type 'boolean) @@ -190,16 +190,15 @@ to test. This command lets Git choose a different one." (defun magit-bisect-run (cmdline &optional bad good args) "Bisect automatically by running commands after each step. -Unlike `git bisect run' this can be used before bisecting has -begun. In that case it behaves like `git bisect start; git -bisect run'." +Unlike \"git bisect run\" this can be used before bisecting has begun. +In that case it behaves like \"git bisect start; git bisect run\"." (interactive (let ((args (and (not (magit-bisect-in-progress-p)) (magit-bisect-start-read-args)))) (cons (read-shell-command "Bisect shell command: ") args))) (when (and bad good) (magit-bisect-start--assert bad good args) ;; Avoid `magit-git-bisect' because it's asynchronous, but the - ;; next `git bisect run' call requires the bisect to be started. + ;; next "git bisect run" call requires the bisect to be started. (magit-with-toplevel (magit-process-git (list :file (expand-file-name "BISECT_CMD_OUTPUT" (magit-gitdir))) @@ -244,13 +243,13 @@ bisect run'." (magit-file-lines (expand-file-name "BISECT_TERMS" (magit-gitdir)))) (defun magit-insert-bisect-output () - "While bisecting, insert section with output from `git bisect'." + "While bisecting, insert section with output from \"git bisect\"." (when (magit-bisect-in-progress-p) (let* ((lines (or (magit-file-lines (expand-file-name "BISECT_CMD_OUTPUT" (magit-gitdir))) (list "Bisecting: (no saved bisect output)" - "It appears you have invoked `git bisect' from a shell." + "It appears you have invoked \"git bisect\" from a shell." "There is nothing wrong with that, we just cannot display" "anything useful here. Consult the shell output instead."))) (done-re "^\\([a-z0-9]\\{40,\\}\\) is the first bad commit$") @@ -318,11 +317,15 @@ bisect run'." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-blame.el b/lisp/magit/magit-blame.el index d037a7a6..3b224253 100644 --- a/lisp/magit/magit-blame.el +++ b/lisp/magit/magit-blame.el @@ -345,7 +345,8 @@ in `magit-blame-read-only-mode-map' instead." (setq magit-blame--style (car magit-blame-styles))) (setq magit-blame--make-margin-overlays (and (cl-find-if (##assq 'margin-format (cdr %)) - magit-blame-styles))) + magit-blame-styles) + t)) (magit-blame--update-margin 'enable)) (t (when (process-live-p magit-blame-process) @@ -558,8 +559,8 @@ modes is toggled, then this mode also gets toggled automatically. (oref chunk orig-rev))) (setq beg (magit-blame--line-beginning-position (oset chunk final-line (oref before final-line)))) - (cl-incf (oref chunk num-lines) - (oref before num-lines))) + (incf (oref chunk num-lines) + (oref before num-lines))) (magit-blame--remove-overlays beg end) (when magit-blame--make-margin-overlays (magit-blame--make-margin-overlays chunk revinfo beg end)) @@ -579,7 +580,7 @@ modes is toggled, then this mode also gets toggled automatically. (while (< (point) end) (magit-blame--make-margin-overlay chunk revinfo line) (forward-line) - (cl-incf line))))) + (incf line))))) (defun magit-blame--make-margin-overlay (chunk revinfo line) (let* ((end (line-end-position)) @@ -702,11 +703,11 @@ modes is toggled, then this mode also gets toggled automatically. (cdr (assoc k1 revinfo)) (cdr (assoc k2 revinfo))) f))) - `((?H . ,(p0 rev 'magit-blame-hash)) + `((?H . ,(p0 rev 'magit-blame-hash)) (?h . ,(p0 (magit-blame--abbrev-hash rev) 'magit-blame-hash)) - (?s . ,(p1 "summary" 'magit-blame-summary)) - (?a . ,(p1 "author" 'magit-blame-name)) - (?c . ,(p1 "committer" 'magit-blame-name)) + (?s . ,(p1 "summary" 'magit-blame-summary)) + (?a . ,(p1 "author" 'magit-blame-name)) + (?c . ,(p1 "committer" 'magit-blame-name)) (?A . ,(p2 "author-time" "author-tz" 'magit-blame-date)) (?C . ,(p2 "committer-time" "committer-tz" 'magit-blame-date)) (?f . ""))))))) @@ -1003,11 +1004,15 @@ instead of the hash, like `kill-ring-save' would." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-bookmark.el b/lisp/magit/magit-bookmark.el index 416989da..76c5df93 100644 --- a/lisp/magit/magit-bookmark.el +++ b/lisp/magit/magit-bookmark.el @@ -160,11 +160,15 @@ ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-branch.el b/lisp/magit/magit-branch.el index 603576b1..799bd5c8 100644 --- a/lisp/magit/magit-branch.el +++ b/lisp/magit/magit-branch.el @@ -50,6 +50,13 @@ (const :tag "Read upstream first" t) (const :tag "Read upstream first, with fallback" fallback))) +(defcustom magit-branch-name-suggestions nil + "List of names and/or prefixes suggested when naming a new branch." + :package-version '(magit . "4.6.0") + :group 'magit-commands + :type '(repeat string) + :safe (##and (listp %) (all #'stringp %))) + (defcustom magit-branch-prefer-remote-upstream nil "Whether to favor remote upstreams when creating new branches. @@ -78,7 +85,7 @@ them invalid as a branch name. Recommended characters to use to trigger interpretation as a regexp are \"*\" and \"^\". Some other characters which you might expect to be invalid, actually are not, e.g., \".+$\" are all perfectly valid. More precisely, -if `git check-ref-format --branch STRING' exits with a non-zero +if \"git check-ref-format --branch STRING\" exits with a non-zero status, then treat STRING as a regexp. Assuming the chosen branch matches these conditions you would end @@ -420,29 +427,50 @@ when using `magit-branch-and-checkout'." (magit-run-git "checkout" "--orphan" branch start-point)) (defun magit-branch-read-args (prompt &optional default-start) - (if magit-branch-read-upstream-first - (let ((choice (magit-read-starting-point prompt nil default-start))) - (cond - ((magit-rev-verify choice) - (list (magit-read-string-ns - (if magit-completing-read--silent-default - (format "%s (starting at `%s')" prompt choice) - "Name for new branch") - (let ((def (string-join (cdr (split-string choice "/")) "/"))) - (and (member choice (magit-list-remote-branch-names)) - (not (member def (magit-list-local-branch-names))) - def))) - choice)) - ((eq magit-branch-read-upstream-first 'fallback) - (list choice - (magit-read-starting-point prompt choice default-start))) - ((user-error "Not a valid starting-point: %s" choice)))) - (let ((branch (magit-read-string-ns (concat prompt " named")))) - (if (magit-branch-p branch) - (magit-branch-read-args - (format "Branch `%s' already exists; pick another name" branch) - default-start) - (list branch (magit-read-starting-point prompt branch default-start)))))) + (cond-let + ((not magit-branch-read-upstream-first) + (let ((name (magit-branch--read-name (concat prompt " named")))) + (list name (magit-read-starting-point prompt name default-start)))) + [[start (magit-read-starting-point prompt nil default-start)]] + ((magit-rev-verify start) + (list (magit-branch--read-name + (if magit-completing-read--silent-default + (format "%s (starting at `%s')" prompt start) + "Name for new branch") + start) + start)) + [[name start]] + ((eq magit-branch-read-upstream-first 'fallback) + (list name (magit-read-starting-point prompt name default-start))) + ((user-error "Not a valid starting-point: %s" name)))) + +(defun magit-branch--read-name (prompt &optional start-point) + (let ((taken (magit-list-local-branch-names)) + (choices magit-branch-name-suggestions)) + (when (and start-point + (magit-remote-branch-p start-point) + (string-match "/" start-point)) + (cl-pushnew (substring start-point (match-end 0)) + choices :test #'equal)) + (magit-completing-read + prompt (seq-difference choices taken) nil + (lambda (&optional choice) + (cond + ((not choice) + ;; This function ought to be called with one argument (see + ;; `completing-read') but Ivy calls it with zero arguments. + ;; Since Ivy doesn't tell us what choice the user made, we + ;; also cannot validate it, and assume it is valid. + t) + ((member choice taken) + (run-with-timer + 0 nil (##minibuffer-message "conflicts with existing branch")) + nil) + ((not (magit-git-success "check-ref-format" "--branch" choice)) + (run-with-timer + 0 nil (##minibuffer-message "not a valid branch name")) + nil) + (t)))))) ;;;###autoload (defun magit-branch-spinout (branch &optional from) @@ -974,11 +1002,15 @@ Also rename the respective reflog file." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-bundle.el b/lisp/magit/magit-bundle.el index f808593c..23a8e93e 100644 --- a/lisp/magit/magit-bundle.el +++ b/lisp/magit/magit-bundle.el @@ -139,11 +139,15 @@ ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-clone.el b/lisp/magit/magit-clone.el index c25e1ccc..3b68f23c 100644 --- a/lisp/magit/magit-clone.el +++ b/lisp/magit/magit-clone.el @@ -351,11 +351,15 @@ Then show the status buffer for the new repository." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-commit.el b/lisp/magit/magit-commit.el index 912127b4..d9cb927c 100644 --- a/lisp/magit/magit-commit.el +++ b/lisp/magit/magit-commit.el @@ -102,40 +102,14 @@ Also see https://github.com/magit/magit/issues/4132." (defvar magit-common-git-post-commit-functions nil "Hook run by Git hooks `post-commit', `post-merge' and `post-rewrite'. -This hook is run if `magit-overriding-githook-directory' is non-nil. -The functions are called with the same arguments as the Git hook. +There is not a single Git hook, which is called after a commit is +created; to achieve that, all of these Git hooks have to be used. -This hook is still experimental.") - -(defvar magit-git-post-commit-functions nil - "Hook run by Git hook `post-commit'. - -This hook is run if `magit-overriding-githook-directory' is non-nil. -The functions are called with the same arguments as the Git hook. - -See also `magit-common-git-post-commit-functions'. - -This hook is still experimental.") - -(defvar magit-git-post-merge-functions nil - "Hook run by Git hook `post-merge'. - -This hook is run if `magit-overriding-githook-directory' is non-nil. -The functions are called with the same arguments as the Git hook. - -See also `magit-common-git-post-commit-functions'. - -This hook is still experimental.") - -(defvar magit-git-post-rewrite-functions nil - "Hook run by Git hook `post-rewrite'. - -This hook is run if `magit-overriding-githook-directory' is non-nil. -The functions are called with the same arguments as the Git hook. - -See also `magit-common-git-post-commit-functions'. - -This hook is still experimental.") +Git hooks are documented in the githooks(5) manpage. This Lisp hook +is only run if `magit-run-hooks-from-githooks' is non-nil, and Magit +runs Git asynchronously and on the local machine. The hook functions +are called with the same arguments as the Git hook; see the mentioned +manpage for details.") ;;; Popup @@ -376,7 +350,7 @@ the user explicitly select a commit, in a buffer dedicated to that task. Leave the original commit message of the targeted commit untouched. -Like `magit-commit-fixup' but also run a `--autofixup' rebase." +Like `magit-commit-fixup' but also run a \"--autofixup\" rebase." (interactive (list (magit-commit-at-point) (magit-commit-arguments))) (magit-commit-squash-internal "--fixup=" commit args nil nil 'rebase)) @@ -393,7 +367,7 @@ Turing the rebase phase, when the two commits are being squashed, ask the user to author the final commit message, based on the original message of the targeted commit. -Like `magit-commit-squash' but also run a `--autofixup' rebase." +Like `magit-commit-squash' but also run a \"--autofixup\" rebase." (interactive (list (magit-commit-at-point) (magit-commit-arguments))) (magit-commit-squash-internal "--squash=" commit args nil nil 'rebase)) @@ -802,7 +776,8 @@ actually insert the entry." (with-current-buffer buffer (undo-boundary) (goto-char (point-max)) - (while (re-search-backward (concat "^" comment-start) nil t)) + (git-commit--goto-insert-position nil) + (while (re-search-backward (git-commit--trailer-regexp) nil t)) (save-restriction (narrow-to-region (point-min) (point)) (cond ((re-search-backward (format "* %s\\(?: (\\([^)]+\\))\\)?: " file) @@ -868,11 +843,15 @@ Also see `git-commit-post-finish-hook'." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-core.el b/lisp/magit/magit-core.el index c0831616..79eb3813 100644 --- a/lisp/magit/magit-core.el +++ b/lisp/magit/magit-core.el @@ -123,11 +123,15 @@ Each of these options falls into one or more of these categories: ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-diff.el b/lisp/magit/magit-diff.el index 9d465762..6fe9fe63 100644 --- a/lisp/magit/magit-diff.el +++ b/lisp/magit/magit-diff.el @@ -346,12 +346,12 @@ that many spaces. Otherwise, highlight neither." :type 'boolean) (defcustom magit-diff-extra-stat-arguments nil - "Additional arguments to be used alongside `--stat'. + "Additional arguments to be used alongside \"--stat\". A list of zero or more arguments or a function that takes no argument and returns such a list. These arguments are allowed -here: `--stat-width', `--stat-name-width', `--stat-graph-width' -and `--compact-summary'. See the git-diff(1) manpage." +here: \"--stat-width\", \"--stat-name-width\", \"--stat-graph-width\" +and \"--compact-summary\". See the git-diff(1) manpage." :package-version '(magit . "3.0.0") :group 'magit-diff :type `(radio (function-item ,#'magit-diff-use-window-width-as-stat-width) @@ -439,7 +439,7 @@ CommitDate: %cd All headers in revision buffers are inserted by the section inserter `magit-insert-revision-headers'. Some of the headers -are created by calling `git show --format=FORMAT' where FORMAT +are created by calling \"git show --format=FORMAT\" where FORMAT is the format specified here. Other headers are hard coded or subject to option `magit-revision-insert-related-refs'." :package-version '(magit . "2.3.0") @@ -1136,12 +1136,11 @@ and `:slant'." :multi-value t) (defun magit-read-files (prompt initial-input history &optional list-fn) - (magit-with-toplevel - (magit-completing-read-multiple prompt - (funcall (or list-fn #'magit-list-files)) - nil nil - (or initial-input (magit-file-at-point)) - history))) + (magit-completing-read-multiple prompt + (funcall (or list-fn #'magit-list-files)) + nil nil + (or initial-input (magit-file-at-point)) + history)) (transient-define-argument magit-diff:-U () :description "Context lines" @@ -1403,11 +1402,11 @@ If no DWIM context is found, nil is returned." ((string= (magit-rev-parse revB) base) (format "%s..%s" revB revA)) (interactive - (let ((main (magit-completing-read "View changes along" - (list revA revB) - nil t nil nil revB))) - (format "%s...%s" - (if (string= main revB) revA revB) main))) + (let ((main (magit-completing-read "View changes along" + (list revA revB) + nil t nil nil revB))) + (format "%s...%s" + (if (string= main revB) revA revB) main))) ((format "%s...%s" revA revB)))) (format "%s..%s" revA revB))))) @@ -1605,7 +1604,7 @@ for a revision." (while (or (< l line) (= (char-after) ?-)) (unless (= (char-after) ?-) - (cl-incf l)) + (incf l)) (forward-line))) (setq found (if (= (char-after) ?+) 'line 'hunk)) (forward-char (1+ column)) @@ -1995,7 +1994,7 @@ the Magit-Status buffer for DIRECTORY." (unless (string-search (if goto-from "+" "-") (buffer-substring (point) (+ (point) prefix))) - (cl-incf offset)) + (incf offset)) (forward-line)) offset)))))) @@ -2026,13 +2025,13 @@ the Magit-Status buffer for DIRECTORY." ( to-len (string-to-number (match-str 4)))) (if (<= from-beg line) (if (< (+ from-beg from-len) line) - (cl-incf offset (- to-len from-len)) + (incf offset (- to-len from-len)) (let ((rest (- line from-beg))) (while (> rest 0) (pcase (char-after) - (?\s (cl-decf rest)) - (?- (cl-decf offset) (cl-decf rest)) - (?+ (cl-incf offset))) + (?\s (decf rest)) + (?- (decf offset) (decf rest)) + (?+ (incf offset))) (forward-line)))) (throw 'found nil)))))) (+ line offset))) @@ -2103,10 +2102,10 @@ the Magit-Status buffer for DIRECTORY." (while (and (not (eobp)) (memq (char-after) '(?\s ?- ?+))) (pcase (char-after) - (?\s (cl-incf -line) - (cl-incf +line)) - (?- (push (cl-incf -line) -lines)) - (?+ (push (cl-incf +line) +lines))) + (?\s (incf -line) + (incf +line)) + (?- (push (incf -line) -lines)) + (?+ (push (incf +line) +lines))) (forward-line)))) (list (nreverse -lines) (nreverse +lines)))) @@ -2193,30 +2192,32 @@ commit or stash at point, then prompt for a commit." (defun magit-section-cycle-diffs () "Cycle visibility of diff-related sections in the current buffer." (interactive) - (when-let ((sections - (cond ((derived-mode-p 'magit-status-mode) - (mapcan (lambda (section) - (and section - (progn - (when (oref section hidden) - (magit-section-show section)) - (oref section children)))) - (list (magit-get-section '((staged) (status))) - (magit-get-section '((unstaged) (status)))))) - ((derived-mode-p 'magit-diff-mode) - (seq-filter #'magit-file-section-p - (oref magit-root-section children)))))) - (if (seq-some (##oref % hidden) sections) - (dolist (s sections) - (magit-section-show s) - (magit-section-hide-children s)) - (let ((children (mapcan (##copy-sequence (oref % children)) sections))) - (cond ((and (seq-some (##oref % hidden) children) - (seq-some (##oref % children) children)) - (mapc #'magit-section-show-headings sections)) - ((seq-some #'magit-section-hidden-body children) - (mapc #'magit-section-show-children sections)) - ((mapc #'magit-section-hide sections))))))) + (cond-let* + [[sections + (cond ((derived-mode-p 'magit-status-mode) + (mapcan (lambda (section) + (and section + (progn + (when (oref section hidden) + (magit-section-show section)) + (copy-sequence (oref section children))))) + (list (magit-get-section '((staged) (status))) + (magit-get-section '((unstaged) (status)))))) + ((derived-mode-p 'magit-diff-mode) + (seq-filter #'magit-file-section-p + (oref magit-root-section children))))]] + ((not sections)) + ((seq-some (##oref % hidden) sections) + (dolist (s sections) + (magit-section-show s) + (magit-section-hide-children s))) + [[children (mapcan (##copy-sequence (oref % children)) sections)]] + ((and (seq-some (##oref % hidden) children) + (seq-some (##oref % children) children)) + (mapc #'magit-section-show-headings sections)) + ((seq-some #'magit-section-hidden-body children) + (mapc #'magit-section-show-children sections)) + ((mapc #'magit-section-hide sections)))) ;;;; Jump Commands @@ -2505,7 +2506,7 @@ keymap is the parent of their keymaps." (unless (equal cmd "merge-tree") (push "--ita-visible-in-index" args)) (setq args (magit-diff--maybe-add-stat-arguments args)) - (when (magit--any (##string-prefix-p "--color-moved" %) args) + (when (any (##string-prefix-p "--color-moved" %) args) (push "--color=always" args) (setq magit-git-global-arguments (append magit-diff--reset-non-color-moved @@ -2553,7 +2554,7 @@ keymap is the parent of their keymaps." "\\(-*\\)$")) ; del (defun magit-diff-use-window-width-as-stat-width () - "Use the `window-width' as the value of `--stat-width'." + "Use the `window-width' as the value of \"--stat-width\"." (and$ (get-buffer-window (current-buffer) 'visible) (list (format "--stat-width=%d" (window-width $))))) @@ -2640,7 +2641,7 @@ keymap is the parent of their keymaps." (if (looking-at "^$") (forward-line) (insert "\n")))))) (defun magit-diff-wash-diff (args) - (when (magit--any (##string-prefix-p "--color-moved" %) args) + (when (any (##string-prefix-p "--color-moved" %) args) (require 'ansi-color) (ansi-color-apply-on-region (point-min) (point-max))) (cond @@ -2756,8 +2757,10 @@ keymap is the parent of their keymaps." (setq orig (magit-decode-git-path orig))) (setq file (magit-decode-git-path file)) (setq header (nreverse header)) - ;; KLUDGE `git-log' ignores `--no-prefix' when `-L' is used. - (when (and (derived-mode-p 'magit-log-mode) + ;; KLUDGE Before v2.54, "git log" ignored "--no-prefix" + ;; when "-L" is used. + (when (and (magit-git-version< "2.54") + (derived-mode-p 'magit-log-mode) (seq-some (##string-prefix-p "-L" %) magit-buffer-log-args)) (when orig @@ -3022,7 +3025,7 @@ Staging and applying changes is documented in info node This function only inserts anything when `magit-show-commit' is called with a tag as argument, when that is called with a commit or a ref which is not a branch, then it inserts nothing." - (when (magit-tag-p magit-buffer-revision) + (when (magit-annotated-tag-p magit-buffer-revision) (magit-insert-section (taginfo) (let ((beg (point))) ;; "git verify-tag -v" would output what we need, but the gpg @@ -3684,10 +3687,11 @@ actually a `diff' but a `diffstat' section." (magit--add-face-text-property bol (+ bol (if merging 2 1)) sign-face))) (forward-line))) - (when (eq magit-diff-fontify-hunk 'all) - (magit-diff--update-hunk-syntax section)) - (when (eq magit-diff-refine-hunk 'all) - (magit-diff-update-hunk-refinement section)) + (unless (magit--meta-hunk-p section) + (when (eq magit-diff-fontify-hunk 'all) + (magit-diff--update-hunk-syntax section)) + (when (eq magit-diff-refine-hunk 'all) + (magit-diff-update-hunk-refinement section))) (oset section painted (if highlight 'highlight 'plain))) ;;;; Whitespace @@ -3761,10 +3765,11 @@ actually a `diff' but a `diffstat' section." ;;;; Refinement (cl-defmethod magit-section--refine ((section magit-hunk-section)) - (when (eq magit-diff-fontify-hunk t) - (magit-diff--update-hunk-syntax section)) - (when (eq magit-diff-refine-hunk t) - (magit-diff-update-hunk-refinement section))) + (unless (magit--meta-hunk-p section) + (when (eq magit-diff-fontify-hunk t) + (magit-diff--update-hunk-syntax section)) + (when (eq magit-diff-refine-hunk t) + (magit-diff-update-hunk-refinement section)))) (defun magit-diff-update-hunk-refinement (&optional section allow-remove) (if section @@ -3821,7 +3826,8 @@ actually a `diff' but a `diffstat' section." (forward-line 1)))))) (named-let update ((section magit-root-section)) (if (magit-section-match 'hunk section) - (magit-diff--update-hunk-syntax section) + (unless (magit--meta-hunk-p section) + (magit-diff--update-hunk-syntax section)) (dolist (child (oref section children)) (update child)))))) @@ -4016,11 +4022,15 @@ If `magit-diff-visit-previous-blob' is nil, then always return nil." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-dired.el b/lisp/magit/magit-dired.el index be67f9fa..b00ee88a 100644 --- a/lisp/magit/magit-dired.el +++ b/lisp/magit/magit-dired.el @@ -113,11 +113,15 @@ Interactively, open the file at point." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-ediff.el b/lisp/magit/magit-ediff.el index 6846c554..613b2ae2 100644 --- a/lisp/magit/magit-ediff.el +++ b/lisp/magit/magit-ediff.el @@ -243,14 +243,21 @@ FILE has to be relative to the top directory of the repository." (buffer-local-value 'buffer-file-coding-system bufC)) (bufA (magit-ediff--find-file "HEAD" file)) (bufB (magit-ediff--find-file "{index}" file)) - (lockB (buffer-local-value 'buffer-read-only bufB))) - (with-current-buffer bufB (setq buffer-read-only nil)) + (lockB (buffer-local-value 'buffer-read-only bufB)) + (modeB (buffer-local-value 'magit-blob-mode bufB))) + (with-current-buffer bufB + ;; Make writable and don't shadow self-insert-command. + (magit-blob-mode -1)) (magit-ediff-buffers bufA bufB bufC nil (lambda () (when (buffer-live-p ediff-buffer-B) (when lockB - (with-current-buffer bufB (setq buffer-read-only t))) + (with-current-buffer bufB + (if modeB + (magit-blob-mode 1) + (setq-local buffer-read-only t) + (setq-local read-only-mode--state t)))) (when (buffer-modified-p ediff-buffer-B) (with-current-buffer ediff-buffer-B (magit-update-index)))) @@ -562,11 +569,15 @@ is done setting up buffers." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-extras.el b/lisp/magit/magit-extras.el index e863f688..e538b788 100644 --- a/lisp/magit/magit-extras.el +++ b/lisp/magit/magit-extras.el @@ -125,7 +125,7 @@ alternative commands." ;;;###autoload (defun magit-run-git-gui-blame (commit filename &optional linenum) - "Run `git gui blame' on the given FILENAME and COMMIT. + "Run \"git gui blame\" on the given FILENAME and COMMIT. Interactively run it for the current file and the `HEAD', with a prefix or when the current file cannot be determined let the user choose. When the current buffer is visiting FILENAME instruct @@ -165,7 +165,7 @@ blame to center around the line point is on." ;;;###autoload (defun magit-run-git-gui () - "Run `git gui' for the current git repository." + "Run \"git gui\" for the current git repository." (interactive) (magit-with-toplevel (magit-process-git 0 "gui"))) @@ -353,8 +353,8 @@ on a position in a file-visiting buffer." "Edit the commit that added the current line. With a prefix argument edit the commit that removes the line, -if any. The commit is determined using `git blame' and made -editable using `git rebase --interactive' if it is reachable +if any. The commit is determined using \"git blame\" and made +editable using \"git rebase --interactive\" if it is reachable from `HEAD', or by checking out the commit (or a branch that points at it) otherwise." (interactive (list (and current-prefix-arg 'removal))) @@ -493,7 +493,7 @@ list returned by `magit-rebase-arguments'." (and (not magit-reshelve-since-committer-only) (format "export GIT_AUTHOR_DATE=\"%s\"; " date)) (format "export GIT_COMMITTER_DATE=\"%s\";;" date)) - (cl-incf date 60))) + (incf date 60))) (magit-git-lines "rev-list" "--reverse" range) " ")) (and keyid @@ -543,7 +543,7 @@ should contain \"%N\", which is replaced with the number that was determined in the previous step. Both formats, if non-nil and after removing %N, are then expanded -using `git show --format=FORMAT ...' inside TOPLEVEL. +using \"git show --format=FORMAT ...\" inside TOPLEVEL. The expansion of POINT-FORMAT is inserted at point, and the expansion of EOB-FORMAT is inserted at the end of the buffer (if @@ -616,32 +616,24 @@ the minibuffer too." (pnt-args nil) (eob-args nil)) (when (listp pnt-format) - (setq pnt-args (cdr pnt-format)) - (setq pnt-format (car pnt-format))) + (pcase-setq `(,pnt-format . ,pnt-args) pnt-format)) (when (listp eob-format) - (setq eob-args (cdr eob-format)) - (setq eob-format (car eob-format))) + (pcase-setq `(,eob-format . ,eob-args) eob-format)) (when pnt-format (when idx-format (setq pnt-format (string-replace "%N" idx pnt-format))) + (when (and (bolp) comment-start (looking-at comment-start)) + (save-excursion (insert ?\n))) (magit-rev-insert-format pnt-format rev pnt-args) (delete-char -1)) (when eob-format (when idx-format (setq eob-format (string-replace "%N" idx eob-format))) - (save-excursion - (goto-char (point-max)) - (skip-syntax-backward ">-") - (beginning-of-line) - (if (and comment-start (looking-at comment-start)) - (while (looking-at comment-start) - (forward-line -1)) - (forward-line) - (unless (= (current-column) 0) - (insert ?\n))) - (insert ?\n) - (magit-rev-insert-format eob-format rev eob-args) - (delete-char -1)))))) + (git-commit--insert-trailer-1 + (with-temp-buffer + (magit-rev-insert-format eob-format rev eob-args) + (string-trim (buffer-string))) + t))))) ;;;###autoload (defun magit-copy-section-value (arg) @@ -830,11 +822,15 @@ In Magit diffs, also skip over - and + at the beginning of the line." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-fetch.el b/lisp/magit/magit-fetch.el index 7a60d2a1..de06c4f8 100644 --- a/lisp/magit/magit-fetch.el +++ b/lisp/magit/magit-fetch.el @@ -185,11 +185,15 @@ with a prefix argument." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-files.el b/lisp/magit/magit-files.el index f98edeee..fadfe5a7 100644 --- a/lisp/magit/magit-files.el +++ b/lisp/magit/magit-files.el @@ -101,7 +101,6 @@ Non-interactively REV can also be a blob object." (rev rev))) (topdir (magit-toplevel)) (file (expand-file-name file topdir)) - (file-relative (file-relative-name file topdir)) (buffer (cond-let ((equal rev "{worktree}") @@ -118,7 +117,7 @@ Non-interactively REV can also be a blob object." (unless (file-in-directory-p file topdir) (error "%s is not inside Git repository %s" file topdir)) (with-current-buffer - (magit--get-blob-buffer rev file-relative volatile) + (magit--get-blob-buffer rev file volatile) (if (magit-blob-p rev) (setq magit-buffer-blob-oid--init (magit-rev-parse rev)) (setq magit-buffer-revision rev)) @@ -126,18 +125,20 @@ Non-interactively REV can also be a blob object." (setq default-directory (if (file-exists-p defdir) defdir topdir)) (setq-local revert-buffer-function #'magit--revert-blob-buffer) + (setq-local buffer-read-only t) + (setq-local read-only-mode--state t) (magit--refresh-blob-buffer) (current-buffer))) ((error "Unexpected error"))))) (when (and (not no-restore-position) - (equal (magit-file-relative-name) file-relative)) + (equal magit-buffer-file-name file)) (let ((pos (magit-find-file--position))) (with-current-buffer buffer (apply #'magit-find-file--restore-position pos)))) buffer)) (defun magit--get-blob-buffer (obj file &optional volatile) - ;; If OBJ is a commit, is assummed to be abbreviated. + ;; If OBJ is a commit, is assumed to be abbreviated. ;; FILE is assumed to be relative to the top-level. (cond-let ([buf (if (magit-blob-p obj) @@ -215,7 +216,6 @@ Non-interactively REV can also be a blob object." ;; The FIND-FILE argument wasn't designed for our use case, ;; so we have to use this strange invocation to achieve that. (normal-mode (not enable-local-variables))) - (setq buffer-read-only t) (set-buffer-modified-p nil) (run-hooks 'magit-find-blob-hook))) @@ -303,10 +303,10 @@ Age is tracked in seconds. If nil, only use `magit--blob-cache-limit'.") (when-let* ((_ magit--blob-cache-limit) (ceiling (- magit--blob-cache-limit (length active))) (_ (length> rest ceiling))) - (let ((sorted (static-if (>= emacs-major-version 30) - (sort rest :key (##float-time (cdr %)) - :lessp #'< :reverse t) - (cl-sort rest #'> :key (##float-time (cdr %)))))) + (let ((sorted (compat-call sort rest + :key (##float-time (cdr %)) + :lessp #'< + :reverse t))) (dolist (kill (nthcdr ceiling sorted)) (kill-buffer (car kill))) (setq rest (ntake ceiling sorted)))) @@ -472,6 +472,7 @@ to `magit-dispatch'." (defvar-keymap magit-blob-mode-map :doc "Keymap for `magit-blob-mode'." + " " #'magit-blob-mode "g" #'revert-buffer "p" #'magit-blob-previous "n" #'magit-blob-next @@ -481,11 +482,22 @@ to `magit-dispatch'." "q" #'magit-bury-or-kill-buffer) (define-minor-mode magit-blob-mode - "Enable some Magit features in blob-visiting buffers. + "Enable key bindings, which are useful in blob-visiting buffers. -Currently this only adds the following key bindings. -\n\\{magit-blob-mode-map}" - :package-version '(magit . "2.3.0")) +\\{magit-blob-mode-map} +When the user disables `read-only-mode', these bindings would conflict +with bindings for `self-insert-command'. To avoid this conflict, +`read-only-mode' is remapped to `magit-blob-mode' and disabling the +latter disables both modes. Likewise, enabling it, also enables +`read-only-mode'. + +When this mode is disabled, many of the commands, for which it would +single-character bindings, are accessible via \\[magit-file-dispatch]." + :package-version '(magit . "2.3.0") + ;; Don't actually call `read-only-mode'. Because + ;; that could enable the incompatible `view-mode'. + (setq-local buffer-read-only magit-blob-mode) + (setq-local read-only-mode--state magit-blob-mode)) (defun magit-bury-buffer (&optional kill-buffer) "Bury the current buffer, or with a prefix argument kill it. @@ -710,15 +722,14 @@ Git, then fallback to using `delete-file'." (car (member (or default (magit-current-file)) files))))) (defun magit-read-file (prompt &optional tracked-only) - (magit-with-toplevel - (let ((choices (nconc (magit-list-files) - (and (not tracked-only) - (magit-untracked-files))))) - (magit-completing-read - prompt choices nil t nil nil - (car (member (or (magit-section-value-if '(file submodule)) - (magit-file-relative-name nil tracked-only)) - choices)))))) + (let ((choices (nconc (magit-list-files) + (and (not tracked-only) + (magit-untracked-files))))) + (magit-completing-read + prompt choices nil t nil nil + (car (member (or (magit-section-value-if '(file submodule)) + (magit-file-relative-name nil tracked-only)) + choices))))) (defun magit-read-tracked-file (prompt) (magit-read-file prompt t)) @@ -781,11 +792,15 @@ If DEFAULT is non-nil, use this as the default value instead of ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-git.el b/lisp/magit/magit-git.el index dec96fc5..5f0f0fe9 100644 --- a/lisp/magit/magit-git.el +++ b/lisp/magit/magit-git.el @@ -103,11 +103,11 @@ this." (ignore-error file-missing (apply #'process-lines-ignore-status program args)))) -(defvar magit-git-w32-path-hack nil +(defvar magit--git-w32-path-hack nil "Alist of (EXE . (PATHENTRY)). This specifies what additional PATH setting needs to be added to the environment in order to run the non-wrapper git executables -successfully.") +successfully. Set when `magit-git-executable' is (re)initialized.") (defcustom magit-git-executable (or (and (eq system-type 'windows-nt) @@ -121,7 +121,7 @@ successfully.") exec "-c" "alias.X=!x() { which \"$1\" | cygpath -mf -; }; x" "X" "git"))) - (hack-entry (assoc core-exe magit-git-w32-path-hack)) + (hack-entry (assoc core-exe magit--git-w32-path-hack)) ;; Running the libexec/git-core executable ;; requires some extra PATH entries. (path-hack @@ -135,7 +135,7 @@ successfully.") ;; idempotent. (if hack-entry (setcdr hack-entry path-hack) - (push (cons core-exe path-hack) magit-git-w32-path-hack)) + (push (cons core-exe path-hack) magit--git-w32-path-hack)) core-exe))) (and (eq system-type 'darwin) (executable-find "git")) @@ -155,48 +155,57 @@ option." :group 'magit-process :type 'string) -(defvar magit--overriding-githook-directory nil) +(defcustom magit-run-hooks-from-githooks t + "Whether Git hooks may run Lisp hooks. -(defcustom magit-overriding-githook-directory nil - "Directory containing the Git hook scripts used by Magit. +By default the Lisp hook `magit-common-git-post-commit-functions' is +run by the Git hooks `post-commit', `post-merge' and `post-rewrite'. +Use `magit-user-githook-file' (which see) to define additional hooks. -No Magit-specific Git hook scripts are used if this is nil, which it -is the default. This feature is still experimental. - -Git does not allow overriding just an individual hook. It is only -possible to point Git at an alternative directory containing hook -scripts, using the Git variable `core.hooksPath'. When doing that, -the hooks located in `$GIT_DIR/hooks' are ignored. - -If `magit', use the directory containing Git hook scripts distributed -with Magit. To counteract Git's limited granularity, Magit provides a -script for every Git hook, most of which only run the respective script -located in `$GIT_DIR/hooks', provided it exists and is executable. - -A few Git hooks additionally run Lisp hooks: - -- `post-commit' runs `magit-git-post-commit-functions' -- `post-merge' runs `magit-git-post-merge-functions' -- `post-rewrite' runs `magit-git-post-rewrite-functions' - -All of these hooks also run `magit-common-git-post-rewrite-functions'. -For many uses this hook variable is more useful than the three above. - -If you want to teach additional Git hooks to run Lisp hooks, you have to -copy Magit's hook script directory elsewhere, modify the hook scripts in -question, and point this variable at the used directory. - -Magit only sets `core.hooksPath' when calling Git asynchronously. Doing -the same when calling Git synchronously would cause Git and Magit to wait -on one another." - :package-version '(magit . "4.5.0") +Git hooks can only run Lisp hooks, if Magit invokes Git asynchronously +and on the local machine, and at least Git v2.54.0 is used." + :package-version '(magit . "4.6.0") :group 'magit-process - :set (lambda (symbol value) - (set-default-toplevel-value symbol value) - (setq magit--overriding-githook-directory nil)) - :type '(choice (const :tag "Do not shadow Git's hook directory" nil) - (const :tag "Use Magit's hook directory" magit) - (directory :tag "Custom directory"))) + :type 'boolean) + +(defcustom magit-user-githook-file (locate-user-emacs-file "magit-githooks") + "File containing user Git hook to Lisp hook mappings. + +Magit ships with one such mapping, defined in the included file +\"githooks/config\", which looks like this: + +[hook \"magit-common-post-commit\"] + event = post-commit + event = post-merge + event = post-rewrite + command = magit-run-git-hook magit-common-git-post-commit-functions + +That file should not be edited by users, as those edits would be lost +when Magit is updated; instead the file specified by this option, has +to be used, to add additional mappings. + +See the git-hook(1) and githooks(5) manpages for details about the +Git part. The command should always use the \"magit-run-git-hook\" +executable, which takes the name of the Lisp hook to be run as the +first argument. + +See also `magit-run-hooks-from-githooks'." + :package-version '(magit . "4.6.0") + :group 'magit-process + :type 'file) + +(defvar magit-githook-directory nil + "Directory containing files Magit needs to map Git to Lisp hooks. + +This directory must contains two files; \"config\", which maps Git +hooks to the `magit-common-git-post-commit-functions' hook, and an +executable \"magit-run-git-hook\", which is used in that, and +potentially other hooks. + +The value of this variable is set by `magit-process-git-arguments', +when it is first needed. Users can set it to another directory, +but that should rarely be necessary. Additional hook mappings +should instead be defined in `magit-user-githook-file'.") (defcustom magit-git-global-arguments `("--no-pager" "--literal-pathspecs" @@ -209,17 +218,16 @@ on one another." (list "-c" "i18n.logOutputEncoding=UTF-8"))) "Global Git arguments. -The arguments set here are used every time the git executable is -run as a subprocess. They are placed right after the executable -itself and before the git command - as in `git HERE... COMMAND -REST'. See the manpage `git(1)' for valid arguments. +The arguments set here are used every time the git executable is run +as a subprocess. They are placed right after the executable itself +and before the git command - as in \"git HERE... COMMAND REST\". +See the manpage `git(1)' for valid arguments. -Be careful what you add here, especially if you are using Tramp -to connect to servers with ancient Git versions. Never remove -anything that is part of the default value, unless you really -know what you are doing. And think very hard before adding -something; it will be used every time Magit runs Git for any -purpose." +Be careful what you add here, especially if you are using Tramp to +connect to servers with ancient Git versions. Never remove anything +that is part of the default value, unless you really know what you +are doing. And think very hard before adding something; it will be +used every time Magit runs Git for any purpose." :package-version '(magit . "4.3.2") :group 'magit-commands :group 'magit-process @@ -262,7 +270,7 @@ is called by functions like `magit-list-branch-names' to generate the collection of refs. By default, refs are sorted according to their full refname (i.e., \"refs/...\"). -Any value accepted by the `--sort' flag of \"git for-each-ref\" can +Any value accepted by the \"--sort\" flag of \"git for-each-ref\" can be used. For example, \"-creatordate\" places refs with more recent committer or tagger dates earlier in the list. A list of strings can also be given in order to pass multiple sort keys to @@ -281,6 +289,28 @@ framework ultimately determines how the collection is displayed." :group 'magit-miscellaneous :type '(choice string (repeat string))) +(defcustom magit-cygwin-mount-points + (and (eq system-type 'windows-nt) + (compat-call + sort (mapcar (lambda (mount) + (if (string-match "^\\(.*\\) on \\(.*\\) type" mount) + (cons (file-name-as-directory (match-str 2 mount)) + (file-name-as-directory (match-str 1 mount))) + (lwarn '(magit) :error + "Failed to parse Cygwin mount: %S" mount))) + ;; If --exec-path is not a native Windows path, + ;; then we probably have a cygwin git. + (and-let ((dirs (magit--early-process-lines + magit-git-executable "--exec-path"))) + (and (not (string-match-p "\\`[a-zA-Z]:" (car dirs))) + (magit--early-process-lines "mount")))) + :lessp #'< :reverse t :key (pcase-lambda (`(,cyg . ,_win)) (length cyg)))) + "Alist of (CYGWIN . WIN32) directory names. +Sorted from longest to shortest CYGWIN name." + :package-version '(magit . "2.3.0") + :group 'magit-process + :type '(alist :key-type string :value-type directory)) + ;;; Git (defvar magit-git-debug nil @@ -325,9 +355,9 @@ See info node `(magit)Debugging Tools' for more information." `(if magit--refresh-cache (let ((,k ,key)) (if-let ((,hit (assoc ,k (cdr magit--refresh-cache)))) - (progn (cl-incf (caar magit--refresh-cache)) + (progn (incf (caar magit--refresh-cache)) (cdr ,hit)) - (cl-incf (cdar magit--refresh-cache)) + (incf (cdar magit--refresh-cache)) (let ((value ,(macroexp-progn body))) (push (cons ,k value) (cdr magit--refresh-cache)) @@ -342,14 +372,15 @@ rebase sequences.") (defmacro magit-with-editor (&rest body) "Like `with-editor*' but let-bind some more variables. -Also respect the value of `magit-with-editor-envvar'." +Also respect the value of `magit-with-editor-envvar', prevent the +process buffer from popping up, and on Windows possibly employ a +kludge to fix an unusable value of `shell-file-name'." (declare (indent 0) (debug (body))) `(let ((magit-process-popup-time -1) - ;; The user may have customized `shell-file-name' to - ;; something which results in `w32-shell-dos-semantics' nil - ;; (which changes the quoting style used by - ;; `shell-quote-argument'), but Git for Windows expects shell - ;; quoting in the dos style. + ;; The user may have customized `shell-file-name' to something + ;; which results in `w32-shell-dos-semantics' being nil (which + ;; changes the quoting style used by `shell-quote-argument'), + ;; but Git for Windows expects shell quoting in the dos style. (shell-file-name (if (and (eq system-type 'windows-nt) ;; If we have Cygwin mount points, ;; the git flavor is cygwin, so dos @@ -381,9 +412,16 @@ is remote." magit-remote-git-executable magit-git-executable)) -(defun magit-process-git-arguments--length () - (+ (length magit-git-global-arguments) - (if magit--overriding-githook-directory 2 0))) +(defun magit-process-git-arguments--split (program args) + (if (equal program (magit-git-executable)) + (let* ((length (length magit-git-global-arguments)) + (global (seq-take args length)) + (local (seq-drop args length))) + (while (equal (car local) "-c") + (setq global (append global (seq-take local 2))) + (setq local (seq-drop local 2))) + (list global local)) + (list nil args))) (defun magit-process-git-arguments (args &optional async) "Prepare ARGS for a function that invokes Git. @@ -393,34 +431,44 @@ pass arguments through this function before handing them to Git, to do the following. * Prepend `magit-git-global-arguments' to ARGS. -* If ASYNC is non-nil and `magit-overriding-githook-directory' is non-nil - and valid, set `core.hooksPath' by adding additional arguments to ARGS. +* If ASYNC is non-nil, potentially add additional arguments to load + the hook configuration in \"/path/to/magit/githooks/config\" and/or + `magit-user-githook-file'. * Flatten ARGS, removing nil arguments. * If `system-type' is `windows-nt', encode ARGS to `w32-ansi-code-page'." - (cond ((not async)) - (magit--overriding-githook-directory) - ((eq magit-overriding-githook-directory 'magit) - (setq magit--overriding-githook-directory - (expand-file-name "git-hooks" - (locate-dominating-file - (locate-library "magit.el") "git-hooks")))) - ((and magit-overriding-githook-directory - (file-directory-p magit-overriding-githook-directory)) - (setq magit--overriding-githook-directory - magit-overriding-githook-directory))) - (setq args - (append magit-git-global-arguments - (and magit--overriding-githook-directory - (list "-c" (format "core.hooksPath=%s" - magit--overriding-githook-directory))) - (flatten-tree args))) + (let ((githookp (and async + (magit-git-version>= "2.54") + (not (file-remote-p default-directory))))) + (cond + ((not githookp)) + (magit-githook-directory) + ((and (stringp magit-run-hooks-from-githooks) + (file-directory-p magit-run-hooks-from-githooks)) + (setq magit-githook-directory + (magit-convert-filename-for-git + magit-run-hooks-from-githooks))) + (magit-run-hooks-from-githooks + (setq magit-githook-directory + (magit-convert-filename-for-git + (expand-file-name "githooks" + (locate-dominating-file + (locate-library "magit.el") "githooks")))))) + (setq args + (append magit-git-global-arguments + (and githookp + magit-githook-directory + `("-c" ,(format "include.path=%s/config" + magit-githook-directory) + ,@(and magit-user-githook-file + (file-exists-p magit-user-githook-file) + `("-c" ,(concat "include.path=" + magit-user-githook-file))))) + (flatten-tree args)))) (if (and (eq system-type 'windows-nt) (boundp 'w32-ansi-code-page)) ;; On w32, the process arguments *must* be encoded in the ;; current code-page (see #3250). - (mapcar (lambda (arg) - (encode-coding-string - arg (intern (format "cp%d" w32-ansi-code-page)))) - args) + (let ((coding (intern (format "cp%d" w32-ansi-code-page)))) + (mapcar (##encode-coding-string % coding) args)) args)) (defun magit-git-exit-code (&rest args) @@ -663,7 +711,7 @@ executable." (list (concat (file-remote-p default-directory) (or (magit-git-string "--exec-path") - (error "`git --exec-path' failed")))) + (error "\"git --exec-path\" failed")))) exec-suffixes #'file-executable-p) (executable-find command t))) @@ -795,10 +843,10 @@ See info node `(magit)Debugging Tools' for more information." (defun magit-config-get-from-cached-list (key) (gethash - ;; `git config --list' downcases first and last components of the key. - (let* ((key (replace-regexp-in-string "\\`[^.]+" #'downcase key t t)) - (key (replace-regexp-in-string "[^.]+\\'" #'downcase key t t))) - key) + (thread$ key + ;; "git config --list" downcases first and last components. + (replace-regexp-in-string "\\`[^.]+" #'downcase $ t t) + (replace-regexp-in-string "[^.]+\\'" #'downcase $ t t)) (magit--with-refresh-cache (cons (magit-toplevel) 'config) (let ((configs (make-hash-table :test #'equal))) (dolist (conf (magit-git-items "config" "--list" "-z")) @@ -860,7 +908,8 @@ Also see `magit-git-config-p'." (dolist (v values) (magit-call-git "config" arg "--add" var v)))) -;;; Files +;;; Repository +;;;; Repository Locations (defun magit--safe-default-directory (&optional file) (catch 'unsafe-default-dir @@ -910,9 +959,9 @@ not located inside a Git repository, then return nil." ;; Kludge: git-annex converts submodule gitdirs to symlinks. See #3599. (when (file-symlink-p (directory-file-name gitdir)) (setq gitdir (file-truename gitdir))) - ;; We want to delete the entry for `topdir' here, rather than within - ;; (unless ...), in case a `--separate-git-dir' repository was switched to - ;; the standard structure (i.e., "topdir/.git/"). + ;; We want to delete the entry for `topdir' here, rather than + ;; within (unless ...), in case a "--separate-git-dir" repository + ;; was switched to the standard structure (i.e., "topdir/.git/"). (setq magit--separated-gitdirs (cl-delete topdir magit--separated-gitdirs :key #'car :test #'equal)) @@ -925,20 +974,19 @@ not located inside a Git repository, then return nil." From within the working tree or control directory of a repository return the absolute path to the toplevel directory of the working -tree. As a special case, from within a bare repository return -the control directory instead. When called outside a repository -then return nil. +tree. As a special case, from within a bare repository return the +control directory instead. When called outside a repository, return +nil. -When optional DIRECTORY is non-nil then return the toplevel for -that directory instead of the one for `default-directory'. +When optional DIRECTORY is non-nil, return the toplevel for that +directory, instead of the one for `default-directory'. -Try to respect the option `find-file-visit-truename', i.e., when -the value of that option is nil, then avoid needlessly returning -the truename. When a symlink to a sub-directory of the working -tree is involved, or when called from within a sub-directory of -the gitdir or from the toplevel of a gitdir, which itself is not -located within the working tree, then it is not possible to avoid -returning the truename." +Try to respect the option `find-file-visit-truename', i.e., when the +value of that option is nil, avoid needlessly returning the truename. +When a symlink to a sub-directory of the working tree is involved, +or when called from within a sub-directory of the gitdir or from the +toplevel of a gitdir, which itself is not located within the working +tree, then it is not possible to avoid returning the truename." (magit--with-refresh-cache (cons (or directory default-directory) 'magit-toplevel) (magit--with-safe-default-directory directory @@ -949,14 +997,13 @@ returning the truename." (;; Always honor these settings. [_(not find-file-visit-truename)] [_(not (getenv "GIT_WORK_TREE"))] - ;; `--show-cdup' is the relative path to the toplevel - ;; from `(file-truename default-directory)'. Here we - ;; pretend it is relative to `default-directory', and - ;; go to that directory. Then we check whether - ;; `--show-toplevel' still returns the same value and - ;; whether `--show-cdup' now is the empty string. If - ;; both is the case, then we are at the toplevel of - ;; the same working tree, but also avoided needlessly + ;; "--show-cdup" is the relative path to the toplevel from + ;; (file-truename default-directory). Here we pretend it is + ;; relative to `default-directory', and go to that directory. + ;; Then we check whether "--show-toplevel" still returns the + ;; same value and whether "--show-cdup" now is the empty + ;; string. If both is the case, then we are at the toplevel + ;; of the same working tree, but also avoided needlessly ;; following any symlinks. [updir (file-name-as-directory (magit-rev-parse-safe "--show-cdup"))] @@ -988,8 +1035,7 @@ returning the truename." ;; This has long been fixed, but old repository may still ;; exist that contain such a file. See #2364. [_(not (equal wtree ".git"))] - (concat (file-remote-p default-directory) - (file-name-directory wtree))) + (file-name-directory (expand-file-name wtree default-directory))) ;; The working directory may not be the parent ;; directory of .git if it was set up with ;; "git init --separate-git-dir". See #2955. @@ -1006,6 +1052,8 @@ returning the truename." `(let ((default-directory (magit--toplevel-safe))) ,@body)) +;;;; Repository Predicates + (define-error 'magit-outside-git-repo "Not inside Git repository") (define-error 'magit-corrupt-git-config "Corrupt Git configuration") (define-error 'magit-git-executable-not-found "Git executable cannot be found") @@ -1087,25 +1135,8 @@ a bare repository." (file-directory-p (expand-file-name "refs" directory)) (file-directory-p (expand-file-name "objects" directory)))))) -(defun magit-file-relative-name (&optional file tracked) - "Return the path of FILE relative to the repository root. - -If optional FILE is nil or omitted, return the relative path of -the file being visited in the current buffer, if any, else nil. -If the file is not inside a Git repository, then return nil. - -If TRACKED is non-nil, return the path only if it matches a -tracked file." - (with-current-buffer (or (buffer-base-buffer) (current-buffer)) - (and-let* ((file (or file - (magit-buffer-file-name) - (and (derived-mode-p 'dired-mode) - default-directory))) - (dir (magit-toplevel (magit--safe-default-directory - (file-name-parent-directory file)))) - (_(or (not tracked) - (magit-file-tracked-p file)))) - (file-relative-name file dir)))) +;;; Files +;;;; File Predicates (defun magit-file-ignored-p (file) (magit-git-string "ls-files" "--others" "--ignored" "--exclude-standard" @@ -1115,31 +1146,40 @@ tracked file." (magit-git-success "ls-files" "--error-unmatch" "--" (magit-convert-filename-for-git file))) +;;;; File Lists + (defun magit-list-files (&rest args) - (apply #'magit-git-items "ls-files" "-z" "--full-name" args)) + (magit-with-toplevel + (apply #'magit-git-items "ls-files" "-z" args))) (defun magit-tracked-files (&rest args) (magit-list-files "--cached" args)) -(defun magit-untracked-files (&optional all files &rest args) +(defun magit-untracked-files (&optional include-ignored directory) "Return a list of untracked files. - -Note that when using \"--directory\", the rules from \".gitignore\" -files from sub-directories are ignore, which is probably a Git bug. -See also `magit-list-untracked-files', which does not have this -issue." - (magit-list-files "--other" args - (and (not all) "--exclude-standard") - "--" files)) - -(defun magit--untracked-files (&optional directory all) +If optional INCLUDE-IGNORED is non-nil, include ignored files. +If optional DIRECTORY is non-nil, then limit to that directory. +DIRECTORY can also be a list of files and directories." (magit-with-toplevel - (seq-keep (##and (eq (aref % 0) ??) + (seq-keep (##and (memq (aref % 0) '(?? ?!)) (substring % 3)) (magit-git-items "status" "-z" "--porcelain" - (if all - "--untracked-files=all" - "--untracked-files=normal") + (and include-ignored "--ignored") + "--untracked-files=all" + "--" directory)))) + +(defun magit--untracked-directories (&optional include-ignored directory) + "Return a list of directories containing only untracked files. +If optional INCLUDE-IGNORED is non-nil, consider ignored files. +If optional DIRECTORY is non-nil, then limit to that directory. +DIRECTORY can also be a list of files and directories." + (magit-with-toplevel + (seq-keep (##and (memq (aref % 0) '(?? ?!)) + (eq (aref % (1- (length %))) ?/) + (substring % 3)) + (magit-git-items "status" "-z" "--porcelain" + (and include-ignored "--ignored") + "--untracked-files=normal" "--" directory)))) (defun magit-list-untracked-files (&optional files) @@ -1213,14 +1253,13 @@ See also `magit-untracked-files'." (magit-list-files "-v" args))) (defun magit-revision-files (rev) - (magit-with-toplevel - (magit-git-items "ls-tree" "-z" "-r" "--name-only" rev))) + (magit-git-items "ls-tree" "-z" "--full-tree" "-r" "--name-only" rev)) (defun magit-revision-directories (rev) "List directories that contain a tracked file in revision REV." - (magit-with-toplevel - (mapcar #'file-name-as-directory - (magit-git-items "ls-tree" "-z" "-r" "-d" "--name-only" rev)))) + (mapcar #'file-name-as-directory + (magit-git-items "ls-tree" "-z" "--full-tree" "-r" "-d" "--name-only" + rev))) (defun magit-changed-files (rev-or-range &optional other-rev) "Return list of files the have changed between two revisions. @@ -1238,6 +1277,28 @@ range. Otherwise, it can be any revision or range accepted by "--diff-filter=R" revA revB) 3))) +;;;; File Names + +(defun magit-file-relative-name (&optional file tracked) + "Return the path of FILE relative to the repository root. + +If optional FILE is nil or omitted, return the relative path of +the file being visited in the current buffer, if any, else nil. +If the file is not inside a Git repository, then return nil. + +If TRACKED is non-nil, return the path only if it matches a +tracked file." + (with-current-buffer (or (buffer-base-buffer) (current-buffer)) + (and-let* ((file (or file + (magit-buffer-file-name) + (and (derived-mode-p 'dired-mode) + default-directory))) + (dir (magit-toplevel (magit--safe-default-directory + (file-name-parent-directory file)))) + (_(or (not tracked) + (magit-file-tracked-p file)))) + (file-relative-name file dir)))) + (defun magit--rev-file-name (file rev other-rev) "For FILE, potentially renamed between REV and OTHER-REV, return name in REV. Return nil, if FILE appears neither in REV nor OTHER-REV, @@ -1246,46 +1307,6 @@ or if no rename is detected." (and$ (magit-renamed-files rev other-rev) (car (rassoc file $))))) -(defun magit-file-status (&rest args) - (magit--with-temp-process-buffer - (save-excursion (magit-git-insert "status" "-z" args)) - (let ((pos (point)) status) - (while (> (skip-chars-forward "[:print:]") 0) - (let ((x (char-after pos)) - (y (char-after (1+ pos))) - (file (buffer-substring (+ pos 3) (point)))) - (forward-char) - (cond ((memq x '(?R ?C)) - (setq pos (point)) - (skip-chars-forward "[:print:]") - (push (list file (buffer-substring pos (point)) x y) status) - (forward-char)) - ((push (list file nil x y) status)))) - (setq pos (point))) - status))) - -(defcustom magit-cygwin-mount-points - (and (eq system-type 'windows-nt) - (cl-sort (mapcar - (lambda (mount) - (if (string-match "^\\(.*\\) on \\(.*\\) type" mount) - (cons (file-name-as-directory (match-str 2 mount)) - (file-name-as-directory (match-str 1 mount))) - (lwarn '(magit) :error - "Failed to parse Cygwin mount: %S" mount))) - ;; If --exec-path is not a native Windows path, - ;; then we probably have a cygwin git. - (and-let ((dirs (magit--early-process-lines - magit-git-executable "--exec-path"))) - (and (not (string-match-p "\\`[a-zA-Z]:" (car dirs))) - (magit--early-process-lines "mount")))) - #'> :key (pcase-lambda (`(,cyg . ,_win)) (length cyg)))) - "Alist of (CYGWIN . WIN32) directory names. -Sorted from longest to shortest CYGWIN name." - :package-version '(magit . "2.3.0") - :group 'magit-process - :type '(alist :key-type string :value-type directory)) - (defun magit-expand-git-file-name (filename) (unless (file-name-absolute-p filename) (setq filename (expand-file-name filename))) @@ -1322,6 +1343,8 @@ Sorted from longest to shortest CYGWIN name." t) path)) +;;;; File Miscellaneous + (defun magit-file-at-point (&optional expand assert) (cond-let ([file (magit-section-case @@ -1339,6 +1362,24 @@ Sorted from longest to shortest CYGWIN name." (and (derived-mode-p 'magit-log-mode) (car magit-buffer-log-files)))) +(defun magit-file-status (&rest args) + (magit--with-temp-process-buffer + (save-excursion (magit-git-insert "status" "-z" args)) + (let ((pos (point)) status) + (while (> (skip-chars-forward "[:print:]") 0) + (let ((x (char-after pos)) + (y (char-after (1+ pos))) + (file (buffer-substring (+ pos 3) (point)))) + (forward-char) + (cond ((memq x '(?R ?C)) + (setq pos (point)) + (skip-chars-forward "[:print:]") + (push (list file (buffer-substring pos (point)) x y) status) + (forward-char)) + ((push (list file nil x y) status)))) + (setq pos (point))) + status))) + ;;; Blobs (defun magit-blob-p (obj) @@ -1437,30 +1478,30 @@ are considered." ;;; Revisions and References (defun magit-rev-parse (&rest args) - "Execute `git rev-parse ARGS', returning first line of output. + "Execute \"git rev-parse ARGS\", returning first line of output. If there is no output, return nil." (apply #'magit-git-string "rev-parse" args)) (defun magit-rev-parse-safe (&rest args) - "Execute `git rev-parse ARGS', returning first line of output. + "Execute \"git rev-parse ARGS\", returning first line of output. If there is no output, return nil. Like `magit-rev-parse' but ignore `magit-git-debug'." (apply #'magit-git-str "rev-parse" args)) (defun magit-rev-parse-true (&rest args) - "Execute `git rev-parse ARGS', returning t if it prints \"true\". + "Execute \"git rev-parse ARGS\", returning t if it prints \"true\". If it prints \"false\", then return nil. For any other output signal an error." (magit-git-true "rev-parse" args)) (defun magit-rev-parse-false (&rest args) - "Execute `git rev-parse ARGS', returning t if it prints \"false\". + "Execute \"git rev-parse ARGS\", returning t if it prints \"false\". If it prints \"true\", then return nil. For any other output signal an error." (magit-git-false "rev-parse" args)) (defun magit-rev-parse-p (&rest args) - "Execute `git rev-parse ARGS', returning t if it prints \"true\". + "Execute \"git rev-parse ARGS\", returning t if it prints \"true\". Return t if the first (and usually only) output line is the string \"true\", otherwise return nil." (equal (magit-git-str "rev-parse" args) "true")) @@ -1643,9 +1684,8 @@ to, or to some other symbolic-ref that points to the same ref." (branch (oref it value)) (commit (or (magit--painted-branch-at-point) (magit-name-branch (oref it value)))) - (pullreq (and (fboundp 'forge--pullreq-branch) - (magit-branch-p - (forge--pullreq-branch (oref it value))))) + (pullreq (and (fboundp 'forge--pullreq-branch-active) + (forge--pullreq-branch-active (oref it value)))) (related-refs (magit--painted-branch-at-point)) ((unpulled unpushed) (magit-ref-abbrev @@ -1699,11 +1739,10 @@ to, or to some other symbolic-ref that points to the same ref." (let ((rev (oref it value))) (or (magit-name-branch rev) rev)))) (tag (magit-ref-maybe-qualify (oref it value) "tags/")) - (pullreq (or (and (fboundp 'forge--pullreq-branch) - (magit-branch-p - (forge--pullreq-branch (oref it value)))) - (magit-ref-p (format "refs/pullreqs/%s" - (oref (oref it value) number))))) + (pullreq (or (and (fboundp 'forge--pullreq-branch-active) + (forge--pullreq-branch-active (oref it value))) + (and (fboundp 'forge--pullreq-ref) + (forge--pullreq-ref (oref it value))))) ((unpulled unpushed) (magit-ref-abbrev (replace-regexp-in-string "\\.\\.\\.?" "" (oref it value))))) @@ -1762,7 +1801,7 @@ The amount of time spent searching is limited by ((setq prev (magit-rev-verify (format "@{-%d}" i))) (or (not (setq prev (magit-rev-branch prev))) (equal prev current)))) - (cl-incf i)) + (incf i)) prev)) (defun magit--set-default-branch (newname oldname) @@ -1906,7 +1945,11 @@ according to the branch type." (magit--with-refresh-cache (list default-directory 'magit-get-push-branch branch verify) (and-let* - ((branch (magit-ref-abbrev (or branch (magit-get-current-branch)))) + ((branch (cond ((not branch) + (magit-get-current-branch)) + ((string-prefix-p "refs/" branch) + (magit-ref-abbrev branch)) + (branch))) (remote (magit-get-push-remote branch)) (target (concat remote "/" branch))) (and (or (not verify) @@ -2054,11 +2097,11 @@ where COMMITS is the number of commits in TAG but not in REV." When NAMESPACES is non-nil, list refs from these namespaces rather than those from `magit-list-refs-namespaces'. -FORMAT is passed to the `--format' flag of `git for-each-ref' +FORMAT is passed to the \"--format\" flag of \"git for-each-ref\" and defaults to \"%(refname)\". -SORTBY is a key or list of keys to pass to the `--sort' flag -of `git for-each-ref' to sort the refs within each namespace. +SORTBY is a key or list of keys to pass to the \"--sort\" flag +of \"git for-each-ref\" to sort the refs within each namespace. When nil, use `magit-list-refs-sortby'. If both are nil, use \"version:refname\", but only for \"refs/tags\"." (let ((format (concat "--format=%(symref) " (or format "%(refname)"))) @@ -2079,20 +2122,32 @@ When nil, use `magit-list-refs-sortby'. If both are nil, use (or namespaces magit-list-refs-namespaces)))))) (defun magit-list-branches () + "Return list of local and remote-tracking branches." (magit-list-refs (list "refs/heads" "refs/remotes"))) (defun magit-list-local-branches () + "Return list of local branches." (magit-list-refs "refs/heads")) (defun magit-list-remote-branches (&optional remote) + "Return list of remote-tracking branches. +If optional REMOTE is non-nil, return only branches from that remote." (magit-list-refs (concat "refs/remotes/" remote))) (defun magit-list-related-branches (relation &optional rev &rest args) + "Return list of branches related to REV. +RELATION must be one of \"--contains\", \"--no-contains\", \"--merged\", +\"--no-merged\" and \"--points-at\". If optional REV is nil, default to +the \"HEAD\" commit. Optional ARGS are additional arguments passed to +\"git branch\"." (seq-remove (##string-match-p "\\(\\`(HEAD\\|HEAD -> \\)" %) (mapcar (##substring % 2) (magit-git-lines "branch" args relation rev)))) (defun magit-list-containing-branches (&optional rev &rest args) + "Return list of branches containing REV. +If optional REV is nil, default to the \"HEAD\" commit. +Optional ARGS are additional arguments passed to \"git branch\"." (magit-list-related-branches "--contains" rev args)) (defun magit-list-publishing-branches (&optional rev) @@ -2100,17 +2155,25 @@ When nil, use `magit-list-refs-sortby'. If both are nil, use magit-published-branches)) (defun magit-list-merged-branches (&optional rev &rest args) + "Return list of branches merged into REV. +If optional REV is nil, default to the \"HEAD\" commit. +Optional ARGS are additional arguments passed to \"git branch\"." (magit-list-related-branches "--merged" rev args)) (defun magit-list-unmerged-branches (&optional rev &rest args) + "Return list of branches not merged into REV. +If optional REV is nil, default to the \"HEAD\" commit. +Optional ARGS are additional arguments passed to \"git branch\"." (magit-list-related-branches "--no-merged" rev args)) (defun magit-list-unmerged-to-upstream-branches () + "Return list of branches not merged into their respective upstreams." (seq-filter (##and-let ((upstream (magit-get-upstream-branch %))) (member % (magit-list-unmerged-branches upstream))) (magit-list-local-branch-names))) (defun magit-list-branches-pointing-at (rev) + "Return list of branches pointing at REV." (let ((re (format "\\`%s refs/\\(heads\\|remotes\\)/\\(.*\\)\\'" (magit-rev-verify rev)))) (seq-keep (##and (string-match re %) @@ -2277,36 +2340,73 @@ specified using `core.worktree'." (if (> (length line) 8) (substring line 9) t))))) (nreverse worktrees))) -(defun magit-symbolic-ref-p (name) - (magit-git-success "symbolic-ref" "--quiet" name)) +(defun magit-symbolic-ref-p (string) + "Return t if STRING is a reference, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (magit-git-success "symbolic-ref" "--quiet" string)) -(defun magit-ref-p (rev) - (or (car (member rev (magit-list-refs "refs/"))) - (car (member rev (magit-list-refnames "refs/"))))) +(defun magit-ref-p (string) + "Return t if STRING is a reference, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (and (magit-ref-fullname string) + (not (magit-symbolic-ref-p string)))) -(defun magit-branch-p (rev) - (or (car (member rev (magit-list-branches))) - (car (member rev (magit-list-branch-names))))) +(defun magit-branch-p (string) + "Return t if STRING is a local or remote-tracking branch, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (or (magit-local-branch-p string) + (magit-remote-branch-p string))) -(defun magit-local-branch-p (rev) - (or (car (member rev (magit-list-local-branches))) - (car (member rev (magit-list-local-branch-names))))) +(defun magit-local-branch-p (string) + "Return t if STRING is a local branch. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (magit-git-success "show-ref" "--quiet" "--branches" string)) -(defun magit-remote-branch-p (rev) - (or (car (member rev (magit-list-remote-branches))) - (car (member rev (magit-list-remote-branch-names))))) +(defun magit-remote-branch-p (string) + "Return t if STRING is a remote-tracking branch, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (seq-some (lambda (line) + (let ((ref (cadr (split-string line " ")))) + (or (equal string ref) + (equal string (substring ref 5)) + (equal string (substring ref 13))))) + (magit-git-lines "show-ref" string))) + +(defun magit-tag-p (string) + "Return t if STRING is an annotated or lightweight tag, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (magit-git-success "show-ref" "--quiet" "--tags" string)) + +(defun magit-annotated-tag-p (string) + "Return t if STRING is an annotated tag, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (equal (magit-object-type string) "tag")) + +(defun magit-lightweight-tag-p (string) + "Return t if STRING is a lightweight (un-annotated) tag, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (and (magit-tag-p string) + (not (magit-annotated-tag-p string)))) + +(defun magit-remote-p (string) + "Return t if STRING is a remote, nil otherwise. +Signal an error if STRING is not a string." + (cl-assert (stringp string)) + (and (member string (magit-list-remotes)) t)) (defun magit-branch-set-face (branch) (magit--propertize-face branch (if (magit-local-branch-p branch) 'magit-branch-local 'magit-branch-remote))) -(defun magit-tag-p (obj) - (equal (magit-object-type obj) "tag")) - -(defun magit-remote-p (string) - (car (member string (magit-list-remotes)))) - (defvar magit-main-branch-names '("main" "master" "trunk" "development") "Branch names reserved for use by the primary branch. @@ -2373,7 +2473,8 @@ If `first-parent' is set, traverse only first parents." (magit-rev-parse (magit-abbrev-arg "short") rev)) (defun magit--abbrev-if-oid (obj) - (cond ((or (magit-ref-p obj) (member obj '("{index}" "{worktree}"))) obj) + (cond ((member obj '("{index}" "{worktree}")) obj) + ((magit-ref-p obj) obj) ((magit-rev-parse (magit-abbrev-arg "short") obj)) (obj))) @@ -2399,7 +2500,7 @@ If `first-parent' is set, traverse only first parents." (car (split-string (buffer-string)))))) (defun magit-rev-format (format &optional rev args) - ;; Prefer `git log --no-walk' to `git show --no-patch' because it + ;; Prefer "git log --no-walk" to "git show --no-patch" because it ;; performs better in some scenarios. (let ((str (magit-git-string "log" "--no-walk" (concat "--format=" format) args @@ -2409,7 +2510,7 @@ If `first-parent' is set, traverse only first parents." str))) (defun magit-rev-insert-format (format &optional rev args) - ;; Prefer `git log --no-walk' to `git show --no-patch' because it + ;; Prefer "git log --no-walk" to "git show --no-patch" because it ;; performs better in some scenarios. (magit-git-insert "log" "--no-walk" (concat "--format=" format) args @@ -2987,8 +3088,17 @@ out. Only existing branches can be selected." (magit-get-remote)))) (defun magit-read-module-path (prompt &optional predicate) + ;; Predicates are evaluate with the minibuffer as the current buffer. + ;; Unlike for other completion frameworks, Helm does not ensure that + ;; the value of `default-directory' in that buffer is the same as in + ;; the buffer from which completion was invoked. (magit-completing-read prompt (magit-list-module-paths) - predicate t nil nil + (and predicate + (let ((dir default-directory)) + (lambda (module) + (let ((default-directory dir)) + (funcall predicate module))))) + t nil nil (magit-module-at-point predicate))) (defun magit-module-confirm (verb &optional predicate) @@ -3017,23 +3127,24 @@ out. Only existing branches can be selected." ;;; Git Hooks -(defun magit-run-git-hook (githook &rest args) - (dolist (githook (ensure-list githook)) - (let* ((githook (symbol-name githook)) - (hook (save-match-data - (if (string-match "\\`common-" githook) - (intern (format "magit-common-git-%s-functions" - (substring githook (match-end 0)))) - (intern (format "magit-git-%s-functions" githook)))))) - (when (and (boundp hook) - (symbol-value hook)) - (magit--client-message "Running %s..." hook) - (apply #'run-hook-with-args hook args) - (magit--client-message "Running %s...done" hook)))) - ;; Emacsclient prints the returned value to stdout. We cannot prevent - ;; that, but we can use something that looks like we actually *wanted* - ;; to print (which we don't). - '---) +(defun magit-run-git-hook (hook &rest args) + "Run the Lisp HOOK with the specified arguments ARGS. + +ARGS are the arguments (a possibly empty list of strings), which +the Git hook was called with. HOOK is a string naming a hook. + +This function is only intended to be called via \"emacsclient\", by +an executable by the same name, which in turn is only intended to +be called by Git hooks." + (setq hook (intern hook)) + (when (and (boundp hook) + (symbol-value hook)) + (magit--client-message "Running %s..." hook) + (advice-add 'message :override #'magit--client-message) + (unwind-protect (apply #'run-hook-with-args hook args) + (advice-remove 'message #'magit--client-message)) + (magit--client-message "Running %s...done" hook)) + '---) ; Emacsclient insists on printing the value. (defun magit--client-message (format-string &rest args) ;; See `server-process-filter'. @@ -3063,11 +3174,15 @@ Instead use `magit-commit-p' or `magit-commit-oid'.") ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-gitignore.el b/lisp/magit/magit-gitignore.el index 8fd239b6..85503b13 100644 --- a/lisp/magit/magit-gitignore.el +++ b/lisp/magit/magit-gitignore.el @@ -132,10 +132,9 @@ Rules that are defined in that file affect all local repositories." (defun magit--gitignore-patterns (&optional directory) (let* ((topdir (magit-toplevel)) (default-directory (or directory topdir)) - (files (magit--untracked-files directory t)) + (files (magit-untracked-files nil directory)) ;; Include directories that contain only untracked files. - (dirs (seq-filter (##equal (substring % -1) "/") - (magit--untracked-files directory))) + (dirs (magit--untracked-directories nil directory)) (globs nil) (dirglobs nil)) (when directory @@ -158,11 +157,10 @@ Rules that are defined in that file affect all local repositories." "Call \"git update-index --skip-worktree -- FILE\"." (interactive (list (magit-read-file-choice "Skip worktree for" - (magit-with-toplevel - (cl-set-difference - (magit-list-files) - (magit-skip-worktree-files) - :test #'equal))))) + (cl-set-difference + (magit-list-files) + (magit-skip-worktree-files) + :test #'equal)))) (magit-with-toplevel (magit-run-git "update-index" "--skip-worktree" "--" file))) @@ -171,8 +169,7 @@ Rules that are defined in that file affect all local repositories." "Call \"git update-index --no-skip-worktree -- FILE\"." (interactive (list (magit-read-file-choice "Do not skip worktree for" - (magit-with-toplevel - (magit-skip-worktree-files))))) + (magit-skip-worktree-files)))) (magit-with-toplevel (magit-run-git "update-index" "--no-skip-worktree" "--" file))) @@ -183,11 +180,10 @@ Rules that are defined in that file affect all local repositories." "Call \"git update-index --assume-unchanged -- FILE\"." (interactive (list (magit-read-file-choice "Assume file to be unchanged" - (magit-with-toplevel - (cl-set-difference - (magit-list-files) - (magit-assume-unchanged-files) - :test #'equal))))) + (cl-set-difference + (magit-list-files) + (magit-assume-unchanged-files) + :test #'equal)))) (magit-with-toplevel (magit-run-git "update-index" "--assume-unchanged" "--" file))) @@ -196,8 +192,7 @@ Rules that are defined in that file affect all local repositories." "Call \"git update-index --no-assume-unchanged -- FILE\"." (interactive (list (magit-read-file-choice "Do not assume file to be unchanged" - (magit-with-toplevel - (magit-assume-unchanged-files))))) + (magit-assume-unchanged-files)))) (magit-with-toplevel (magit-run-git "update-index" "--no-assume-unchanged" "--" file))) @@ -206,11 +201,15 @@ Rules that are defined in that file affect all local repositories." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-log.el b/lisp/magit/magit-log.el index 603a8533..0f8abf9b 100644 --- a/lisp/magit/magit-log.el +++ b/lisp/magit/magit-log.el @@ -79,10 +79,10 @@ :type 'hook) (defcustom magit-log-remove-graph-args '("--follow" "-G" "-S" "-L") - "The log arguments that cause the `--graph' argument to be dropped. + "The log arguments that cause the \"--graph\" argument to be dropped. The default value lists the arguments that are incompatible with -`--graph' and therefore must be dropped when that is used. You +\"--graph\" and therefore must be dropped when that is used. You can add additional arguments that are available in `magit-log', but I recommend that you don't do that. Nowadays I would define this as a constant, but I am preserving it as an option, in case @@ -209,7 +209,7 @@ because the latter may make use of Imenu's outdated cache." (defcustom magit-log-color-graph-limit 256 "Number of commits over which log graphs are not colored. -When showing more commits than specified, then the `--color' +When showing more commits than specified, then the \"--color\" argument is silently dropped. This is necessary because the `ansi-color' library, which is used to turn control sequences into faces, is just too slow." @@ -220,7 +220,7 @@ into faces, is just too slow." (defcustom magit-log-show-signatures-limit 256 "Number of commits over which signatures are not verified. When showing more commits than specified by this option, then the -`--show-signature' argument, if specified, is silently dropped. +\"--show-signature\" argument, if specified, is silently dropped. This is necessary because checking the signature of a large number of commits is just too slow." :package-version '(magit . "4.0.0") @@ -778,7 +778,7 @@ completion candidates." ;;;###autoload (defun magit-log-buffer-file (&optional follow beg end) "Show log for the blob or file visited in the current buffer. -With a prefix argument or when `--follow' is an active log +With a prefix argument or when \"--follow\" is an active log argument, then follow renames. When the region is active, restrict the log to the lines that the region touches." (interactive (cons current-prefix-arg (magit-file-region-line-numbers))) @@ -891,7 +891,7 @@ https://github.com/mhagger/git-when-merged." (defun magit-delete-shelved-branch (branch) "Delete the shelved BRANCH. Delete a ref created by `magit-branch-shelve'." - (interactive (list (magit-read-shelved-branch "Log shelved branch"))) + (interactive (list (magit-read-shelved-branch "Delete shelved branch"))) (magit-run-git "update-ref" "-d" (concat "refs/shelved/" branch))) ;;;; Limit Commands @@ -1416,7 +1416,7 @@ Do not add this to a hook variable." (cl-defun magit-log-wash-rev (style abbrev) (when (derived-mode-p 'magit-log-mode 'magit-reflog-mode) - (cl-incf magit-log-count)) + (incf magit-log-count)) (looking-at (pcase style ('log magit-log-heading-re) ('cherry magit-log-cherry-re) @@ -1546,17 +1546,17 @@ Do not add this to a hook variable." (delete-char (if (looking-at "\n") 1 4)) (magit-diff-wash-diffs (list "--stat") limit)) (when align - (setq align (make-string (1+ abbrev) ? ))) + (setq align (make-string (1+ abbrev) ?\s))) (when (and (not (eobp)) (not (looking-at non-graph-re))) (when align - (setq align (make-string (1+ abbrev) ? ))) + (setq align (make-string (1+ abbrev) ?\s))) (while (and (not (eobp)) (not (looking-at non-graph-re))) (when align (save-excursion (insert align))) (forward-line) (magit-make-margin-overlay)) - ;; When `--format' is used and its value isn't one of the - ;; predefined formats, then `git-log' does not insert a + ;; When "--format" is used and its value isn't one of the + ;; predefined formats, then "git log" does not insert a ;; separator line. (save-excursion (forward-line -1) @@ -1590,6 +1590,7 @@ exists mostly for backward compatibility reasons." (magit-section-forward))) (add-hook 'magit-section-movement-hook #'magit-log-maybe-show-more-commits) +(add-hook 'magit-mouse-set-point-hook #'magit-log-maybe-show-more-commits) (defvar magit--update-revision-buffer nil) @@ -1601,6 +1602,7 @@ See also info node `(magit)Section Movement'." (magit--maybe-update-revision-buffer))) (add-hook 'magit-section-movement-hook #'magit-log-maybe-update-revision-buffer) +(add-hook 'magit-mouse-set-point-hook #'magit-log-maybe-update-revision-buffer) (defun magit--maybe-update-revision-buffer () (when-let* ((commit (magit-section-value-if 'commit)) @@ -1916,13 +1918,8 @@ Type \\[magit-cherry-pick] to apply the commit at point. (defun magit-insert-cherry-headers () "Insert headers appropriate for `magit-cherry-mode' buffers." - (let ((branch (propertize magit-buffer-refname - 'font-lock-face 'magit-branch-local)) - (upstream (propertize - magit-buffer-cherry-upstream 'font-lock-face - (if (magit-local-branch-p magit-buffer-cherry-upstream) - 'magit-branch-local - 'magit-branch-remote)))) + (let ((branch (magit-branch-set-face magit-buffer-refname)) + (upstream (magit-branch-set-face magit-buffer-cherry-upstream))) (magit-insert-head-branch-header branch) (magit-insert-upstream-branch-header branch upstream "Upstream: ") (insert ?\n))) @@ -2121,11 +2118,15 @@ all others with \"-\"." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-margin.el b/lisp/magit/magit-margin.el index 50e0ce25..887d5855 100644 --- a/lisp/magit/magit-margin.el +++ b/lisp/magit/magit-margin.el @@ -260,11 +260,15 @@ English.") ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-merge.el b/lisp/magit/magit-merge.el index a60de226..0020e74a 100644 --- a/lisp/magit/magit-merge.el +++ b/lisp/magit/magit-merge.el @@ -322,11 +322,15 @@ If no merge is in progress, do nothing." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-mode.el b/lisp/magit/magit-mode.el index 00ee7769..c068e7f3 100644 --- a/lisp/magit/magit-mode.el +++ b/lisp/magit/magit-mode.el @@ -666,8 +666,7 @@ INITIAL-SECTION SELECT-SECTION &rest BINDINGS)" &key buffer directory initial-section select-section) (let* ((value (and locked (with-temp-buffer - (pcase-dolist (`(,var ,val) bindings) - (set (make-local-variable var) val)) + (mapc (##apply #'set-local %) bindings) (let ((major-mode mode)) (magit-buffer-value))))) (buffer (if buffer @@ -683,8 +682,7 @@ INITIAL-SECTION SELECT-SECTION &rest BINDINGS)" (setq default-directory directory)) (funcall mode) (magit-xref-setup #'magit-setup-buffer-internal bindings) - (pcase-dolist (`(,var ,val) bindings) - (set (make-local-variable var) val)) + (mapc (##apply #'set-local %) bindings) (when created (run-hooks 'magit-create-buffer-hook))) (magit-display-buffer buffer) @@ -1573,6 +1571,7 @@ mentioned caches completely." (cond (all (setq magit-repository-local-cache nil) (setq magit--host-git-version-cache nil) + (setq magit-githook-directory nil) (dolist (buffer (buffer-list)) (with-current-buffer buffer (when (derived-mode-p 'magit-mode) @@ -1636,11 +1635,15 @@ line. Avoid including the line after the end of the file." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-notes.el b/lisp/magit/magit-notes.el index 2f9f9d9a..b4f432dc 100644 --- a/lisp/magit/magit-notes.el +++ b/lisp/magit/magit-notes.el @@ -201,11 +201,15 @@ Also see `magit-notes-merge'." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-patch.el b/lisp/magit/magit-patch.el index ca0c799c..8ff52cee 100644 --- a/lisp/magit/magit-patch.el +++ b/lisp/magit/magit-patch.el @@ -330,11 +330,15 @@ is asked to pull. START has to be reachable from that commit." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-pkg.el b/lisp/magit/magit-pkg.el index e4b335c2..f0f1bb5b 100644 --- a/lisp/magit/magit-pkg.el +++ b/lisp/magit/magit-pkg.el @@ -1,17 +1,17 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "magit" "20260401.2251" +(define-package "magit" "20260621.1253" "A Git porcelain inside Emacs." '((emacs "28.1") - (compat "30.1") + (compat "31.0") (cond-let "0.2") (llama "1.0") (magit-section "4.5") (seq "2.24") - (transient "0.12") + (transient "0.13") (with-editor "3.4")) :url "https://github.com/magit/magit" - :commit "6db34dc77d10fc9b8c925e79b4e0e21d9f78ac5c" - :revdesc "6db34dc77d10" + :commit "20058311576bd47751681bdc2ae239da76dec2dc" + :revdesc "20058311576b" :keywords '("git" "tools" "vc") :authors '(("Marius Vollmer" . "marius.vollmer@gmail.com") ("Jonas Bernoulli" . "emacs.magit@jonas.bernoulli.dev")) diff --git a/lisp/magit/magit-process.el b/lisp/magit/magit-process.el index cdcea822..9943c6c7 100644 --- a/lisp/magit/magit-process.el +++ b/lisp/magit/magit-process.el @@ -515,44 +515,37 @@ eol conversion." (when magit-process-record-invocations (let ((messages-buffer-name magit-process-record-buffer-name) (inhibit-message t)) - (message "%s" - (format-spec - (format-time-string magit-process-record-entry-format) - `((?d . ,(abbreviate-file-name default-directory)) - (?a . ,(magit-process--format-arguments process args))))))) + (message + "%s" (format-spec + (format-time-string magit-process-record-entry-format) + `((?d . ,(abbreviate-file-name default-directory)) + (?a . ,(magit-process--format-arguments process args))))))) (let ((process-environment (magit-process-environment)) (default-process-coding-system (magit--process-coding-system))) (apply #'process-file process infile buffer display args))) -(defvar magit--shadowed-githook-directory nil) - -(defun magit--shadowed-githook-directory () - (or magit--shadowed-githook-directory - (setq magit--shadowed-githook-directory - (let ((magit-git-global-arguments nil)) - (cl-letf (((symbol-function 'magit-process-environment) - (lambda () process-environment))) - (or (magit-get "core.hooksPath") - (expand-file-name "hooks" (magit-gitdir)))))))) - (defun magit-process-environment () - ;; The various w32 hacks are only applicable when running on the local - ;; machine. A local binding of process-environment different from the - ;; top-level value affects the environment used by Tramp. - (let ((local (not (file-remote-p default-directory)))) - (append magit-git-environment - (and magit-overriding-githook-directory - (list (concat "SHADOWED_GITHOOK_DIRECTORY=" - (magit--shadowed-githook-directory)))) - (and local - (cdr (assoc magit-git-executable magit-git-w32-path-hack))) - (and local magit-need-cygwin-noglob - (mapcar (lambda (var) - (concat var "=" (if-let ((val (getenv var))) - (concat val " noglob") - "noglob"))) - '("CYGWIN" "MSYS"))) - process-environment))) + (cond + ((file-remote-p default-directory) + `(,@magit-git-environment + ,@process-environment)) + (`(,@magit-git-environment + ,@(and magit-githook-directory + (not (file-remote-p default-directory)) + (list (format "PATH=%s:%s" + magit-githook-directory + (getenv "PATH")))) + ;; The various w32 hacks are only applicable when running on the + ;; local machine. A local binding of process-environment different + ;; from the top-level value affects the environment used by Tramp. + ,@(cdr (assoc magit-git-executable magit--git-w32-path-hack)) + ,@(and magit-need-cygwin-noglob + (mapcar (lambda (var) + (concat var "=" (if-let ((val (getenv var))) + (concat val " noglob") + "noglob"))) + '("CYGWIN" "MSYS"))) + ,@process-environment)))) (defvar magit-this-process nil) @@ -610,7 +603,8 @@ See `magit-start-process' for more information." (let ((m (string-join (flatten-tree args) " "))) (remove-list-of-text-properties 0 (length m) '(face) m) m)) - (magit-start-git nil args)) + (with-editor* "MAGIT_HOOK_EDITOR" + (magit-start-git nil args))) (defun magit-run-git-with-editor (&rest args) "Export GIT_EDITOR and start Git. @@ -624,8 +618,16 @@ current when this function was called (if it is a Magit buffer and still alive), as well as the respective Magit status buffer. See `magit-start-process' and `with-editor' for more information." + (magit-msg "Running %s %s" (magit-git-executable) + (let ((m (string-join (flatten-tree args) " "))) + (remove-list-of-text-properties 0 (length m) '(face) m) + m)) (magit--record-separated-gitdir) - (magit-with-editor (magit-run-git-async args))) + (with-editor* "MAGIT_HOOK_EDITOR" + (with-environment-variables + ((magit-with-editor-envvar (getenv "MAGIT_HOOK_EDITOR"))) + (let ((magit-process-popup-time -1)) + (magit-start-git nil args))))) (defun magit-run-git-sequencer (&rest args) "Export GIT_EDITOR and start Git. @@ -807,16 +809,17 @@ Magit status buffer." (defun magit-process--format-arguments (program args) (cond ((and args (equal program (magit-git-executable))) - (let ((global (magit-process-git-arguments--length))) + (pcase-let ((`(,global ,local) + (magit-process-git-arguments--split program args))) (concat (propertize (file-name-nondirectory program) 'font-lock-face 'magit-section-heading) " " (propertize (magit--ellipsis) 'font-lock-face 'magit-section-heading - 'help-echo (string-join (seq-take args global) " ")) + 'help-echo (string-join global " ")) " " - (propertize (mapconcat #'shell-quote-argument (seq-drop args global) " ") + (propertize (mapconcat #'shell-quote-argument local " ") 'font-lock-face 'magit-section-heading)))) ((and args (equal program shell-file-name)) (propertize (cadr args) @@ -841,7 +844,7 @@ Magit status buffer." ((memq (process-status process) '(exit signal)) (delete-region (oref section start) (1+ (oref section end))) - (cl-decf count)) + (decf count)) ((push section head)))) (pop tail)) (oset magit-root-section children @@ -874,15 +877,15 @@ Magit status buffer." (status-buf (with-current-buffer process-buf (magit-get-mode-buffer 'magit-status-mode)))) (with-current-buffer status-buf - (when-let ((section - (magit-get-section - `((commit . ,(magit-rev-parse "HEAD")) - (,(pcase (car (seq-drop - (process-command process) - (1+ (magit-process-git-arguments--length)))) - ((or "rebase" "am") 'rebase-sequence) - ((or "cherry-pick" "revert") 'sequence))) - (status))))) + (when-let + ((section + (magit-get-section + `((commit . ,(magit-rev-parse "HEAD")) + (,(pcase-let ((`(,cmd . ,args) (process-command process))) + (pcase (cadr (magit-process-git-arguments--split cmd args)) + ((or "rebase" "am") 'rebase-sequence) + ((or "cherry-pick" "revert") 'sequence)))) + (status))))) (goto-char (oref section start)) (magit-section-update-highlight)))))) @@ -1116,7 +1119,7 @@ as argument." (defun magit-process-set-mode-line (program args) "Display the git command (sans arguments) in the mode line." (when (equal program (magit-git-executable)) - (setq args (nthcdr (magit-process-git-arguments--length) args))) + (setq args (cadr (magit-process-git-arguments--split program args)))) (let ((str (concat " " (propertize (concat (file-name-nondirectory program) (and args (concat " " (car args)))) @@ -1207,7 +1210,8 @@ If STR is supplied, it replaces the `mode-line-process' text." (define-error 'magit-git-error "Git error") (defun magit-process-error-summary (process-buf section) - "A one-line error summary from the given SECTION." + "Return one-line error summary from SECTION in PROCESS-BUF. +If PROCESS-BUF is no longer alive, return nil." (and (buffer-live-p process-buf) (with-current-buffer process-buf (and (oref section content) @@ -1221,9 +1225,9 @@ If STR is supplied, it replaces the `mode-line-process' text." (match-str 1)))))))))) (defun magit-process-error-tooltip (process-buf section) - "Returns the text from SECTION of the PROCESS-BUF buffer. - -Limited by `magit-process-error-tooltip-max-lines'." + "Return text from SECTION in PROCESS-BUF. +Option `magit-process-error-tooltip-max-lines' limits how many lines to +return. If that is nil, or PROCESS-BUF is no longer alive, return nil." (and (integerp magit-process-error-tooltip-max-lines) (> magit-process-error-tooltip-max-lines 0) (buffer-live-p process-buf) @@ -1356,11 +1360,15 @@ Limited by `magit-process-error-tooltip-max-lines'." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-pull.el b/lisp/magit/magit-pull.el index 7eb1ab01..2faf998d 100644 --- a/lisp/magit/magit-pull.el +++ b/lisp/magit/magit-pull.el @@ -163,11 +163,15 @@ the upstream." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-push.el b/lisp/magit/magit-push.el index eac346ca..d239a020 100644 --- a/lisp/magit/magit-push.el +++ b/lisp/magit/magit-push.el @@ -371,11 +371,15 @@ You can add this command as a suffix using something like: ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-reflog.el b/lisp/magit/magit-reflog.el index 5a529c70..d7556c40 100644 --- a/lisp/magit/magit-reflog.el +++ b/lisp/magit/magit-reflog.el @@ -208,11 +208,15 @@ Type \\[magit-reset] to reset `HEAD' to the commit at point. ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-refs.el b/lisp/magit/magit-refs.el index ce3dc01c..fc918e94 100644 --- a/lisp/magit/magit-refs.el +++ b/lisp/magit/magit-refs.el @@ -814,11 +814,15 @@ line is inserted at all." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-remote.el b/lisp/magit/magit-remote.el index 7f9f8c12..bc7d7258 100644 --- a/lisp/magit/magit-remote.el +++ b/lisp/magit/magit-remote.el @@ -419,11 +419,15 @@ using \"always\" for remotes named \"origin\". ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-repos.el b/lisp/magit/magit-repos.el index 1ad8d918..39a5da76 100644 --- a/lisp/magit/magit-repos.el +++ b/lisp/magit/magit-repos.el @@ -247,7 +247,7 @@ If it contains \"%s\" then the directory is substituted for that." (let ((default-directory (file-name-as-directory (expand-file-name repo base)))) (if msg - (let ((msg (concat (format "(%s/%s) " (cl-incf i) len) + (let ((msg (concat (format "(%s/%s) " (incf i) len) (format msg default-directory)))) (message msg) (funcall fn) @@ -556,11 +556,15 @@ instead." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-reset.el b/lisp/magit/magit-reset.el index 0ae1c57f..47358e99 100644 --- a/lisp/magit/magit-reset.el +++ b/lisp/magit/magit-reset.el @@ -137,11 +137,15 @@ or \"detached head\" will be substituted for %s." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-sequence.el b/lisp/magit/magit-sequence.el index 20c3383e..a461549b 100644 --- a/lisp/magit/magit-sequence.el +++ b/lisp/magit/magit-sequence.el @@ -718,7 +718,7 @@ START has to be selected from a list of recent commits." ;; merely to add new commits *after* it. Try not to ;; ask users whether they really want to edit public ;; commits, when they don't actually intend to do so. - (not (seq-every-p (##magit-rev-equal % commit) branches)))) + (not (all (##magit-rev-equal % commit) branches)))) (let ((m1 "Some of these commits have already been published to ") (m2 ".\nDo you really want to modify them")) (magit-confirm (or magit--rebase-published-symbol 'rebase-published) @@ -946,7 +946,7 @@ If no such sequence is in progress, do nothing." "pick" commit 'magit-sequence-pick)) ((magit-sequence-insert-am-patch "pick" patch 'magit-sequence-pick))) - (cl-decf i))) + (decf i))) (magit-sequence-insert-sequence nil "ORIG_HEAD") (insert ?\n)))) @@ -1142,11 +1142,15 @@ buffer (i.e., the reverse of how they will be applied)." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-sparse-checkout.el b/lisp/magit/magit-sparse-checkout.el index bd0b98e4..861478b0 100644 --- a/lisp/magit/magit-sparse-checkout.el +++ b/lisp/magit/magit-sparse-checkout.el @@ -22,7 +22,7 @@ ;;; Commentary: -;; This library provides an interface to the `git sparse-checkout' +;; This library provides an interface to the "git sparse-checkout" ;; command (operating in cone mode). ;;; Code: @@ -46,7 +46,7 @@ (defun magit-sparse-checkout-directories () "Return directories that are recursively included in the sparse checkout. -See the `git sparse-checkout' manpage for details about +See the \"git sparse-checkout\" manpage for details about \"recursive\" versus \"parent\" directories in cone mode." (and (magit-get-boolean "core.sparsecheckoutcone") (mapcar #'file-name-as-directory @@ -150,11 +150,15 @@ This header is not inserted by default. To enable it, add it to ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-stash.el b/lisp/magit/magit-stash.el index fa1d3663..3d79e949 100644 --- a/lisp/magit/magit-stash.el +++ b/lisp/magit/magit-stash.el @@ -124,8 +124,8 @@ AUTHOR-WIDTH has to be an integer. When the name of the author (defun magit-stash-both (message &optional include-untracked) "Create a stash of the index and working tree. Untracked files are included according to infix arguments. -One prefix argument is equivalent to `--include-untracked' -while two prefix arguments are equivalent to `--all'." +One prefix argument is equivalent to \"--include-untracked\" +while two prefix arguments are equivalent to \"--all\"." (interactive (progn (when (and (magit-merge-in-progress-p) (not (magit-y-or-n-p "\ @@ -150,8 +150,8 @@ Applying the resulting stash has the inverse effect." (defun magit-stash-worktree (message &optional include-untracked) "Create a stash of unstaged changes in the working tree. Untracked files are included according to infix arguments. -One prefix argument is equivalent to `--include-untracked' -while two prefix arguments are equivalent to `--all'." +One prefix argument is equivalent to \"--include-untracked\" +while two prefix arguments are equivalent to \"--all\"." (interactive (magit-stash-read-args)) (magit-stash-save message nil t include-untracked t 'index)) @@ -159,8 +159,8 @@ while two prefix arguments are equivalent to `--all'." (defun magit-stash-keep-index (message &optional include-untracked) "Create a stash of the index and working tree, keeping index intact. Untracked files are included according to infix arguments. -One prefix argument is equivalent to `--include-untracked' -while two prefix arguments are equivalent to `--all'." +One prefix argument is equivalent to \"--include-untracked\" +while two prefix arguments are equivalent to \"--all\"." (interactive (magit-stash-read-args)) (magit-stash-save message t t include-untracked t 'index)) @@ -206,8 +206,8 @@ The resulting message is what Git would have used." (defun magit-snapshot-both (&optional include-untracked) "Create a snapshot of the index and working tree. Untracked files are included according to infix arguments. -One prefix argument is equivalent to `--include-untracked' -while two prefix arguments are equivalent to `--all'." +One prefix argument is equivalent to \"--include-untracked\" +while two prefix arguments are equivalent to \"--all\"." (interactive (magit-snapshot-read-args)) (magit-snapshot-save t t include-untracked t)) @@ -222,8 +222,8 @@ Unstaged and untracked changes are not stashed." (defun magit-snapshot-worktree (&optional include-untracked) "Create a snapshot of unstaged changes in the working tree. Untracked files are included according to infix arguments. -One prefix argument is equivalent to `--include-untracked' -while two prefix arguments are equivalent to `--all'." +One prefix argument is equivalent to \"--include-untracked\" +while two prefix arguments are equivalent to \"--all\"." (interactive (magit-snapshot-read-args)) (magit-snapshot-save nil t include-untracked t)) @@ -320,10 +320,11 @@ want to fall back to using \"--3way\", without being prompted." (magit--run-git-stash "apply" stash) (let* ((range (format "%s^..%s" stash stash)) (stashed (magit-git-items "diff" "-z" "--name-only" range "--")) - (conflicts (cl-sort (cl-union (magit-unstaged-files t stashed) - (magit-untracked-files t stashed) - :test #'equal) - #'string<)) + (conflicts (compat-call + sort (cl-union (magit-unstaged-files t stashed) + (magit-untracked-files t stashed) + :test #'equal) + :lessp #'string<)) (arg (if (or (not conflicts) (memq 'stash-apply-3way magit-no-confirm)) "--3way" @@ -685,11 +686,15 @@ that make up the stash." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-status.el b/lisp/magit/magit-status.el index 19690357..97bc935a 100644 --- a/lisp/magit/magit-status.el +++ b/lisp/magit/magit-status.el @@ -271,7 +271,7 @@ Non-interactively DIRECTORY is (re-)initialized unconditionally." toplevel directory))) (user-error "Abort"))) (list directory))) - ;; `git init' does not understand the meaning of "~"! + ;; "git init" does not understand the meaning of "~"! (magit-call-git "init" (magit-convert-filename-for-git (expand-file-name directory))) (magit-status-setup-buffer directory)) @@ -804,7 +804,7 @@ Honor the buffer's file filter, which can be set using \"D - -\"." (let ((magit-section-insert-in-reverse t) (limit magit-status-file-list-limit)) (while (and files (> limit 0)) - (cl-decf limit) + (decf limit) (let ((file (pop files))) (magit-insert-section (file file) (insert (funcall magit-format-file-function @@ -823,11 +823,15 @@ Honor the buffer's file filter, which can be set using \"D - -\"." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-submodule.el b/lisp/magit/magit-submodule.el index db7385ec..042ae93b 100644 --- a/lisp/magit/magit-submodule.el +++ b/lisp/magit/magit-submodule.el @@ -190,7 +190,7 @@ and also setting this variable to t will lead to tears." ()) (cl-defmethod transient-format-description ((obj magit--git-submodule-suffix)) - (let ((value (delq nil (mapcar #'transient-infix-value transient--suffixes)))) + (let ((value (seq-filter #'transient-infix-value transient--suffixes))) (replace-regexp-in-string "\\[--[^]]+\\]" (lambda (match) @@ -463,7 +463,7 @@ whether they are wrapped in an additional section." ;;;###autoload (defun magit-insert-modules-overview () "Insert sections for all modules. -For each section insert the path and the output of `git describe --tags', +For each section insert the path and the output of \"git describe --tags\", or, failing that, the abbreviated HEAD commit hash." (when-let ((modules (magit-list-module-paths))) (magit-insert-section (modules nil t) @@ -717,11 +717,15 @@ These sections can be expanded to show the respective commits." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-subtree.el b/lisp/magit/magit-subtree.el index daf46016..e27a00af 100644 --- a/lisp/magit/magit-subtree.el +++ b/lisp/magit/magit-subtree.el @@ -187,11 +187,15 @@ ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-tag.el b/lisp/magit/magit-tag.el index 62212c8f..48af73c5 100644 --- a/lisp/magit/magit-tag.el +++ b/lisp/magit/magit-tag.el @@ -224,33 +224,36 @@ a tag qualifies as a release tag." (save-match-data (mapcar #'cdr - (nreverse - (cl-sort (seq-keep - (lambda (line) - (and-let* - ((_(string-match " +" line)) - (tag (substring line 0 (match-beginning 0))) - (msg (substring line (match-end 0))) - (_(string-match magit-release-tag-regexp tag)) - (ver (match-str 2 tag)) - (version-regexp-alist magit-tag-version-regexp-alist)) - (list (version-to-list ver) ver tag msg))) - ;; Cannot rely on "--sort=-version:refname" because - ;; that gets confused if the version prefix has changed. - (magit-git-lines "tag" "-n")) - ;; The inverse of this function does not exist. - #'version-list-< :key #'car))))) + (compat-call + sort (seq-keep + (lambda (line) + (and-let* + ((_(string-match " +" line)) + (tag (substring line 0 (match-beginning 0))) + (msg (substring line (match-end 0))) + (_(string-match magit-release-tag-regexp tag)) + (ver (match-str 2 tag)) + (version-regexp-alist magit-tag-version-regexp-alist)) + (list (version-to-list ver) ver tag msg))) + ;; Cannot rely on "--sort=-version:refname" because + ;; that gets confused if the version prefix has changed. + (magit-git-lines "tag" "-n")) + :lessp #'version-list-< :reverse t :key #'car)))) ;;; _ (provide 'magit-tag) ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-transient.el b/lisp/magit/magit-transient.el index acff0492..d89623b3 100644 --- a/lisp/magit/magit-transient.el +++ b/lisp/magit/magit-transient.el @@ -77,8 +77,9 @@ (arg (if (oref obj global) "--global" "--local"))) (oset obj variable variable) (oset obj value - (and (zerop (magit-process-git t "config" "--bool" arg variable)) - (buffer-substring (point-min) (1- (point-max))))))) + (with-temp-buffer + (and (zerop (magit-process-git t "config" "--bool" arg variable)) + (buffer-substring (point-min) (1- (point-max)))))))) ;;;; Read @@ -186,8 +187,7 @@ (pcase-let ((`(,fallback . ,choices) (magit--git-variable-list-choices obj))) (concat (propertize "[" 'face 'transient-inactive-value) - (mapconcat #'identity choices - (propertize "|" 'face 'transient-inactive-value)) + (string-join choices (propertize "|" 'face 'transient-inactive-value)) (and fallback (propertize "|" 'face 'transient-inactive-value)) fallback (propertize "]" 'face 'transient-inactive-value))))) @@ -240,11 +240,15 @@ ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-wip.el b/lisp/magit/magit-wip.el index 049243b4..09401568 100644 --- a/lisp/magit/magit-wip.el +++ b/lisp/magit/magit-wip.el @@ -49,38 +49,24 @@ (defcustom magit-wip-merge-branch nil "Whether to merge the current branch into its wip ref. -If non-nil and the current branch has new commits, then it is -merged into the wip ref before creating a new wip commit. This -makes it easier to inspect wip history and the wip commits are -never garbage collected. - If nil and the current branch has new commits, then the wip ref is reset to the tip of the branch before creating a new wip commit. With this setting wip commits are eventually garbage collected. This is currently the default. -If `immediately', then use `git-commit-post-finish-hook' to -create the merge commit. This is discouraged because it can -lead to a race condition, e.g., during rebases. +If non-nil and the current branch has new commits, then it is +merged into the wip ref before creating a new wip commit. This +makes it easier to inspect wip history and the wip commits are +never garbage collected. -If `githook', then use `magit-common-git-post-commit-hook' to -create the merge commit. This uses the experimental support for -calling Lisp hooks from Git hooks, which is disabled by default, -Customize `magit-overriding-githook-directory' to enable use of -Git hooks." +When this and `magit-run-hooks-from-githooks' are both enabled, +and the other conditions mention in the docstring of that option +are also met, then the wip merge commit is created right after +the user creates a regular commit. Otherwise the wip merge +commit is created right before the next regular commit." :package-version '(magit . "2.90.0") :group 'magit-wip - :set (lambda (symbol value) - (set-default-toplevel-value symbol value) - (when (bound-and-true-p magit-wip-mode) - (if (eq value 'immediately) - (add-hook 'git-commit-post-finish-hook #'magit-wip-commit) - (remove-hook 'git-commit-post-finish-hook #'magit-wip-commit)))) - :type '(choice - (const :tag "Yes (safely, just in time)" t) - (const :tag "Yes (immediately, with race condition)" immediately) - (const :tag "Yes (using experimental Git hook support)" githook) - (const :tag "No" nil))) + :type 'boolean) (defcustom magit-wip-namespace "refs/wip/" "Namespace used for work-in-progress refs. @@ -110,23 +96,21 @@ buffer." :package-version '(magit . "2.90.0") :lighter magit-wip-mode-lighter :global t - :set-after '(magit-wip-merge-branch) (cond (magit-wip-mode (add-hook 'after-save-hook #'magit-wip-commit-buffer-file) (add-hook 'magit-after-apply-functions #'magit-wip-commit) (add-hook 'magit-before-change-functions #'magit-wip-commit) (add-hook 'before-save-hook #'magit-wip-commit-initial-backup) - (add-hook 'magit-common-git-post-commit-functions #'magit-wip-post-commit) - (when (eq magit-wip-merge-branch 'immediately) - (add-hook 'git-commit-post-finish-hook #'magit-wip-commit))) + (add-hook 'magit-common-git-post-commit-functions + #'magit-wip-post-commit)) (t (remove-hook 'after-save-hook #'magit-wip-commit-buffer-file) (remove-hook 'magit-after-apply-functions #'magit-wip-commit) (remove-hook 'magit-before-change-functions #'magit-wip-commit) (remove-hook 'before-save-hook #'magit-wip-commit-initial-backup) - (remove-hook 'magit-common-git-post-commit-functions #'magit-wip-post-commit) - (remove-hook 'git-commit-post-finish-hook #'magit-wip-commit)))) + (remove-hook 'magit-common-git-post-commit-functions + #'magit-wip-post-commit)))) (defun magit-wip-commit-buffer-file (&optional msg) "Commit visited file to a worktree work-in-progress ref." @@ -158,8 +142,7 @@ buffer." (setq magit-wip-buffer-backed-up t))) (defun magit-wip-post-commit (&rest _) - (when (eq magit-wip-merge-branch 'githook) - (magit-wip-commit))) + (magit-wip-commit)) ;;; Core @@ -361,7 +344,7 @@ many \"branches\" of each wip ref are shown." (string-match "^[^ ]+ \\([^:]+\\)" (cadr reflog))) (push (match-str 1 (cadr reflog)) tips)) (setq reflog (cddr reflog)) - (cl-decf count)) + (decf count)) (cons wipref (nreverse tips))))) (defun magit-wip-purge () @@ -390,11 +373,15 @@ many \"branches\" of each wip ref are shown." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit-worktree.el b/lisp/magit/magit-worktree.el index c70f221a..e71a1233 100644 --- a/lisp/magit/magit-worktree.el +++ b/lisp/magit/magit-worktree.el @@ -185,8 +185,7 @@ Interactively, use `magit-read-worktree-directory-function'." (kill-buffer) (magit-diff-visit-directory (if preexisting-directory - (concat (file-name-as-directory directory) - (file-name-nondirectory worktree)) + (file-name-concat directory (file-name-nondirectory worktree)) directory))) (magit-refresh)))) @@ -313,11 +312,15 @@ with padding for alignment." ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit.el b/lisp/magit/magit.el index 6c2e53ae..2c0b25b8 100644 --- a/lisp/magit/magit.el +++ b/lisp/magit/magit.el @@ -17,16 +17,16 @@ ;; Homepage: https://github.com/magit/magit ;; Keywords: git tools vc -;; Package-Version: 20260401.2251 -;; Package-Revision: 6db34dc77d10 +;; Package-Version: 20260621.1253 +;; Package-Revision: 20058311576b ;; Package-Requires: ( ;; (emacs "28.1") -;; (compat "30.1") +;; (compat "31.0") ;; (cond-let "0.2") ;; (llama "1.0") ;; (magit-section "4.5") ;; (seq "2.24") -;; (transient "0.12") +;; (transient "0.13") ;; (with-editor "3.4")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -79,7 +79,7 @@ (defcustom magit-openpgp-default-signing-key nil "Fingerprint of your default Openpgp key used for signing. If the specified primary key has signing capacity then it is used -as the value of the `--gpg-sign' argument without prompting, even +as the value of the \"--gpg-sign\" argument without prompting, even when other such keys exist. To be able to select another key you must then use a prefix argument." :package-version '(magit . "4.0.0") @@ -551,9 +551,8 @@ is run in the top-level directory of the current working tree." (magit-read-gpg-secret-key prompt initial-input history (lambda (cert) - (cl-some (lambda (key) - (memq 'sign (epg-sub-key-capability key))) - (epg-key-sub-key-list cert))) + (seq-some (##memq 'sign (epg-sub-key-capability %)) + (epg-key-sub-key-list cert))) magit-openpgp-default-signing-key)) ;;; Font-Lock Keywords @@ -802,11 +801,15 @@ For X11 something like ~/.xinitrc should work.\n" ;; Local Variables: ;; read-symbol-shorthands: ( ;; ("and$" . "cond-let--and$") -;; ("and>" . "cond-let--and>") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") +;; ("thread$" . "cond-let--thread$") ;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") ;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") ;; ("while-let" . "cond-let--while-let") ;; ("match-string" . "match-string") ;; ("match-str" . "match-string-no-properties")) diff --git a/lisp/magit/magit.info b/lisp/magit/magit.info index 7792dac3..9600e457 100644 --- a/lisp/magit/magit.info +++ b/lisp/magit/magit.info @@ -2173,6 +2173,83 @@ where Emacs is searching for ‘git’. what you are doing. And think very hard before adding something; it will be used every time Magit runs Git for any purpose. +4.7.6 Mapping Git Hooks to Lisp Hooks +------------------------------------- + +Git hooks can be configured to call Lisp hooks, starting with Git +v2.54.0. The mechanism described here is only used when ‘git’ is +invoked by Magit, and ‘git’ is run asynchronously and on the local +machine. + +‘magit-run-hooks-from-githooks’ + This option controls whether Git hooks may run Lisp hooks. + + Magit only defines one such hook mapping, but users can add more. + +‘magit-user-githook-file’ + This option controls the location of a user-editable file + containing additional hook mappings. + + -- Variable: magit-githook-directory + The value of this variable is the directory containing files Magit + needs to map Git hooks to Lisp hooks. + + This directory must contains two files; ‘config’, which maps Git + hooks to the Lisp hook ‘magit-common-git-post-commit-functions’, + and an executable ‘magit-run-git-hook’, which is used in that, and + potentially other, hook mappings. + + The value of this variable is set by ‘magit-process-git-arguments’, + when it is first needed. Users can set it to another directory, + but that should rarely be necessary. Additional hook mappings + should instead be defined in ‘magit-user-githook-file’. + + -- Function: magit-run-git-hook (hook &rest args) + This function runs the Lisp hook HOOK with the specified arguments + ARGS. ARGS are the arguments (a possibly empty list of strings), + which the Git hook was called with. HOOK is a string naming a + hook. + + This function is only intended to be called via ‘emacsclient’, by + an executable by the same name, which in turn is only intended to + be called by Git hooks. + + -- Variable: magit-common-git-post-commit-functions + This Lisp hook is run by the Git hooks ‘post-commit’, ‘post-merge’ + and ‘post-rewrite’. There is not a single Git hook, which is + called after a commit is created; to achieve that, all of these + hooks have to be used. + + Git hooks are documented in *note (gitman)githooks::. Also see *note +(gitman)git-hook::. Using ‘magit-common-git-post-commit-functions’ as +an example, the following is how Magit gets Git to map Git hooks to Lisp +hooks. + + Iff Magit calls Git asynchronously and on the local machine, it +injects the global arguments ‘-c include.path=/path/to/githooks/config’. +That file contains: + + [hook "magit-common-post-commit"] + event = post-commit + event = post-merge + event = post-rewrite + command = magit-run-git-hook magit-common-git-post-commit-functions + + Consequently when one of the Git hooks ‘post-commit’, ‘post-merge’ or +‘post-rewrite’ is triggered, that causes the ‘magit-run-git-hook’ +executable to be called with ‘magit-common-git-post-commit-functions’ as +the first argument. + + That executable used the ‘emacsclient’ to call a Lisp function by the +same name, which takes the name of a Lisp hook as the first argument. +The executable may receive additional arguments from the Git hook, which +it passes on to the function. All arguments are strings. + + The function ‘magit-run-git-hook’ runs the Lisp hook using +‘run-hook-with-args’, passing along all arguments, starting with the +second. It also arranges for ‘message’ to output to standard output, so +that messages appear in Magit's process buffer. +  File: magit.info, Node: Inspecting, Next: Manipulating, Prev: Interface Concepts, Up: Top @@ -5512,6 +5589,9 @@ them. Those features are available from separate transient commands. the name of the branch that is to be created. The default is ‘t’, and I recommend you leave it at that. + -- User Option: magit-branch-name-suggestions + List of names and/or prefixes suggested when naming a new branch. + -- User Option: magit-branch-prefer-remote-upstream This option specifies whether remote upstreams are favored over local upstreams when creating new branches. @@ -7734,18 +7814,14 @@ files are being committed. This option controls whether the current branch is merged into the wip refs after a new commit was created on the branch. - If non-‘nil’ and the current branch has new commits, then it is - merged into the wip ref before creating a new wip commit. This - makes it easier to inspect wip history and the wip commits are - never garbage collected. - If ‘nil’ and the current branch has new commits, then the wip ref is reset to the tip of the branch before creating a new wip commit. With this setting wip commits are eventually garbage collected. - If ‘immediately’, then use ‘git-commit-post-finish-hook’ to create - the merge commit. This is discouraged because it can lead to a - race condition, e.g., during rebases. + If non-‘nil’ and the current branch has new commits, then it is + merged into the wip ref before creating a new wip commit. This + makes it easier to inspect wip history and the wip commits are + never garbage collected. When ‘magit-wip-merge-branch’ is ‘t’, then the history looks like this: @@ -9230,6 +9306,15 @@ being enabled. If you choose to disable VC anyway, then you can do so by changing the value of ‘vc-handled-backends’. +A.1.7 The diff/log is filtered to one file; how do I show all changes. +---------------------------------------------------------------------- + +The diff arguments used in the current buffer can be changed using the +‘magit-diff-refresh’ menu on ‘D’. + + The log arguments used in the current buffer can be changed using the +‘magit-log-refresh’ menu on ‘L’. +  File: magit.info, Node: FAQ - Issues and Errors, Prev: FAQ - How to ...?, Up: FAQ @@ -9899,6 +9984,8 @@ Appendix C Keystroke Index * M-p <2>: Rebasing. (line 158) * M-w: Blaming. (line 134) * M-w <1>: Common Commands. (line 39) +* magit-run-hooks-from-githooks: Running Git. (line 223) +* magit-user-githook-file: Running Git. (line 228) * MM: Rebasing. (line 248) * Mt: Rebasing. (line 254) * n: Sections. (line 38) @@ -10178,14 +10265,14 @@ Appendix D Function and Command Index * magit-branch-configure: Branching. (line 81) * magit-branch-create: Branching. (line 104) * magit-branch-delete: Branching. (line 188) -* magit-branch-or-checkout: Branching. (line 307) -* magit-branch-orphan: Branching. (line 303) +* magit-branch-or-checkout: Branching. (line 310) +* magit-branch-orphan: Branching. (line 306) * magit-branch-rename: Branching. (line 199) * magit-branch-reset: Branching. (line 173) -* magit-branch-shelve: Branching. (line 447) +* magit-branch-shelve: Branching. (line 450) * magit-branch-spinoff: Branching. (line 141) * magit-branch-spinout: Branching. (line 168) -* magit-branch-unshelve: Branching. (line 457) +* magit-branch-unshelve: Branching. (line 460) * magit-builtin-completing-read: Completion Confirmation and the Selection. (line 354) * magit-bundle: Bundle. (line 8) @@ -10608,6 +10695,7 @@ Appendix D Function and Command Index * magit-run-git: Calling Git. (line 144) * magit-run-git-async: Calling Git. (line 169) * magit-run-git-gui: Running Git. (line 131) +* magit-run-git-hook: Running Git. (line 245) * magit-run-git-with-editor: Calling Git. (line 181) * magit-run-git-with-input: Calling Git. (line 147) * magit-run-gitk: Running Git. (line 122) @@ -10783,13 +10871,13 @@ Appendix E Variable Index * auto-revert-stop-on-user-input: Modes and Buffers. (line 388) * auto-revert-use-notify: Modes and Buffers. (line 369) * auto-revert-verbose: Modes and Buffers. (line 417) -* branch.autoSetupMerge: Branching. (line 394) -* branch.autoSetupRebase: Branching. (line 408) -* branch.NAME.description: Branching. (line 365) -* branch.NAME.merge: Branching. (line 333) -* branch.NAME.pushRemote: Branching. (line 352) -* branch.NAME.rebase: Branching. (line 343) -* branch.NAME.remote: Branching. (line 338) +* branch.autoSetupMerge: Branching. (line 397) +* branch.autoSetupRebase: Branching. (line 411) +* branch.NAME.description: Branching. (line 368) +* branch.NAME.merge: Branching. (line 336) +* branch.NAME.pushRemote: Branching. (line 355) +* branch.NAME.rebase: Branching. (line 346) +* branch.NAME.remote: Branching. (line 341) * core.notesRef: Notes. (line 53) * git-commit-finish-query-functions: Committing. (line 518) * git-commit-known-pseudo-headers: Committing. (line 408) @@ -10812,9 +10900,10 @@ Appendix E Variable Index * magit-blame-read-only: Blaming. (line 161) * magit-blame-styles: Blaming. (line 147) * magit-blame-time-format: Blaming. (line 157) -* magit-branch-adjust-remote-upstream-alist: Branching. (line 252) +* magit-branch-adjust-remote-upstream-alist: Branching. (line 255) * magit-branch-direct-configure: Branching. (line 69) -* magit-branch-prefer-remote-upstream: Branching. (line 208) +* magit-branch-name-suggestions: Branching. (line 208) +* magit-branch-prefer-remote-upstream: Branching. (line 211) * magit-branch-read-upstream-first: Branching. (line 203) * magit-buffer-name-format: Modes and Buffers. (line 150) * magit-bury-buffer-function: Modes and Buffers. (line 215) @@ -10831,6 +10920,7 @@ Appendix E Variable Index * magit-commit-reword-override-date: Committing. (line 284) * magit-commit-show-diff: Committing. (line 221) * magit-commit-squash-confirm: Committing. (line 269) +* magit-common-git-post-commit-functions: Running Git. (line 255) * magit-completing-read-function: Completion Confirmation and the Selection. (line 340) * magit-define-global-key-bindings: Essential Settings. (line 239) @@ -10869,6 +10959,7 @@ Appendix E Variable Index * magit-git-debug: Calling Git. (line 95) * magit-git-executable: Running Git. (line 162) * magit-git-global-arguments: Running Git. (line 202) +* magit-githook-directory: Running Git. (line 231) * magit-keep-region-overlay: Completion Confirmation and the Selection. (line 294) * magit-list-refs-sortby: Completion Confirmation and the Selection. @@ -10893,7 +10984,7 @@ Appendix E Variable Index * magit-post-commit-hook: Committing. (line 229) * magit-post-display-buffer-hook: Modes and Buffers. (line 117) * magit-pre-display-buffer-hook: Modes and Buffers. (line 108) -* magit-prefer-remote-upstream: Branching. (line 431) +* magit-prefer-remote-upstream: Branching. (line 434) * magit-prefix-use-buffer-arguments: Transient Arguments and Buffer Variables. (line 65) * magit-process-raise-error: Calling Git. (line 235) @@ -10948,14 +11039,14 @@ Appendix E Variable Index * magit-wip-mode-lighter: Wip Modes. (line 93) * magit-wip-namespace: Wip Modes. (line 86) * notes.displayRef: Notes. (line 57) -* pull.rebase: Branching. (line 373) +* pull.rebase: Branching. (line 376) * remote.NAME.fetch: Remotes. (line 108) * remote.NAME.followRemoteHEAD: Remotes. (line 128) * remote.NAME.push: Remotes. (line 117) * remote.NAME.pushurl: Remotes. (line 112) * remote.NAME.tagOpts: Remotes. (line 121) * remote.NAME.url: Remotes. (line 104) -* remote.pushDefault: Branching. (line 385) +* remote.pushDefault: Branching. (line 388)  Tag Table: @@ -10997,145 +11088,147 @@ Ref: Git Process Status90163 Ref: Running Git Manually91261 Ref: Git Executable93834 Ref: Global Git Arguments96721 -Node: Inspecting97436 -Node: Status Buffer98583 -Ref: Status Sections102707 -Ref: Status File List Sections106169 -Ref: Status Log Sections108739 -Ref: Status Header Sections110089 -Ref: Status Module Sections112580 -Ref: Status Options114954 -Node: Repository List116229 -Node: Logging121016 -Ref: Refreshing Logs123747 -Ref: Log Buffer125091 -Ref: Log Margin129808 -Ref: Select from Log132871 -Ref: Reflog134993 -Ref: Cherries136546 -Node: Diffing138322 -Ref: Refreshing Diffs142267 -Ref: Commands Available in Diffs147545 -Ref: Diff Options149940 -Ref: Revision Buffer157441 -Node: Ediffing160695 -Node: References Buffer166739 -Ref: References Sections177302 -Node: Bisecting178087 -Node: Visiting Files and Blobs180394 -Ref: General-Purpose Visit Commands180836 -Ref: Visiting Files and Blobs from a Diff181654 -Node: Blaming186902 -Node: Manipulating193880 -Node: Creating Repository194222 -Node: Cloning Repository194759 -Node: Staging and Unstaging201198 -Ref: Staging from File-Visiting Buffers205126 -Node: Applying206141 -Node: Committing208214 -Ref: Initiating a Commit208839 -Ref: Creating a new commit209151 -Ref: Editing the last commit209261 -Ref: Editing any reachable commit211240 -Ref: Editing any reachable commit and rebasing immediately215653 -Ref: Options used by commit commands217301 -Ref: Used by all or most commit commands217373 -Ref: Used by all squash and fixup commands219602 -Ref: Used by specific commit commands220156 -Ref: Editing Commit Messages220480 -Ref: Using the Revision Stack223034 -Ref: Commit Pseudo Headers225994 -Ref: Commit Mode and Hooks227146 -Ref: Commit Message Conventions229855 -Node: Branching231743 -Ref: The Two Remotes231864 -Ref: Branch Commands234429 -Ref: Branch Git Variables247167 -Ref: Auxiliary Branch Commands252412 -Node: Merging253429 -Node: Resolving Conflicts257778 -Node: Rebasing263148 -Ref: Editing Rebase Sequences268345 -Ref: Information About In-Progress Rebase273574 -Ref: Rebasing-Footnote-1282566 -Node: Cherry Picking283162 -Ref: Reverting287470 -Node: Resetting288830 -Node: Stashing290654 -Node: Transferring296893 -Node: Remotes297115 -Ref: Remote Commands297212 -Ref: Remote Git Variables301156 -Node: Fetching303089 -Node: Pulling305572 -Node: Pushing306598 -Node: Plain Patches310885 -Node: Maildir Patches312356 -Node: Miscellaneous313835 -Node: Tagging314181 -Node: Notes316074 -Node: Submodules318409 -Ref: Listing Submodules318570 -Ref: Submodule Transient320631 -Node: Subtree322984 -Node: Worktree324915 -Node: Sparse checkouts326230 -Node: Bundle329002 -Node: Common Commands329377 -Node: Wip Modes332003 -Ref: Wip Graph336577 -Node: Commands for Buffers Visiting Files339016 -Node: Minor Mode for Buffers Visiting Blobs347331 -Node: Customizing348494 -Node: Per-Repository Configuration350086 -Node: Essential Settings352340 -Ref: Safety352629 -Ref: Performance354304 -Ref: Log Performance357095 -Ref: Diff Performance358394 -Ref: Refs Buffer Performance359735 -Ref: Committing Performance360308 -Ref: Microsoft Windows Performance361288 -Ref: MacOS Performance362375 -Ref: Global Bindings363258 -Ref: Essential Settings-Footnote-1365431 -Node: Plumbing365513 -Node: Calling Git366338 -Ref: Getting a Value from Git367797 -Ref: Calling Git for Effect371415 -Node: Section Plumbing377354 -Ref: Creating Sections377506 -Ref: Section Selection381316 -Ref: Matching Sections383013 -Node: Refreshing Buffers388915 -Node: Conventions392055 -Ref: Theming Faces392219 -Node: FAQ400252 -Node: FAQ - How to ...?400688 -Ref: How to pronounce Magit?400821 -Ref: How to show git's output?401513 -Ref: How to install the gitman info manual?402130 -Ref: How to show diffs for gpg-encrypted files?402935 -Ref: How does branching and pushing work?403344 -Ref: Should I disable VC?403508 -Node: FAQ - Issues and Errors403991 -Ref: Magit is slow404136 -Ref: I changed several thousand files at once and now Magit is unusable404282 -Ref: I am having problems committing404821 -Ref: I am using MS Windows and cannot push with Magit405086 -Ref: I am using macOS and SOMETHING works in shell but not in Magit405484 -Ref: Expanding a file to show the diff causes it to disappear406071 -Ref: Point is wrong in the COMMIT_EDITMSG buffer406421 -Ref: The mode-line information isn't always up-to-date407245 -Ref: A branch and tag sharing the same name breaks SOMETHING408076 -Ref: My Git hooks work on the command-line but not inside Magit408724 -Ref: git-commit-mode isn't used when committing from the command-line409316 -Ref: Point ends up inside invisible text when jumping to a file-visiting buffer411304 -Ref: I am no longer able to save popup defaults411896 -Node: Debugging Tools412689 -Node: Keystroke Index416624 -Node: Function and Command Index454793 -Node: Variable Index506438 +Ref: Mapping Git Hooks to Lisp Hooks97436 +Node: Inspecting100877 +Node: Status Buffer102024 +Ref: Status Sections106148 +Ref: Status File List Sections109610 +Ref: Status Log Sections112180 +Ref: Status Header Sections113530 +Ref: Status Module Sections116021 +Ref: Status Options118395 +Node: Repository List119670 +Node: Logging124457 +Ref: Refreshing Logs127188 +Ref: Log Buffer128532 +Ref: Log Margin133249 +Ref: Select from Log136312 +Ref: Reflog138434 +Ref: Cherries139987 +Node: Diffing141763 +Ref: Refreshing Diffs145708 +Ref: Commands Available in Diffs150986 +Ref: Diff Options153381 +Ref: Revision Buffer160882 +Node: Ediffing164136 +Node: References Buffer170180 +Ref: References Sections180743 +Node: Bisecting181528 +Node: Visiting Files and Blobs183835 +Ref: General-Purpose Visit Commands184277 +Ref: Visiting Files and Blobs from a Diff185095 +Node: Blaming190343 +Node: Manipulating197321 +Node: Creating Repository197663 +Node: Cloning Repository198200 +Node: Staging and Unstaging204639 +Ref: Staging from File-Visiting Buffers208567 +Node: Applying209582 +Node: Committing211655 +Ref: Initiating a Commit212280 +Ref: Creating a new commit212592 +Ref: Editing the last commit212702 +Ref: Editing any reachable commit214681 +Ref: Editing any reachable commit and rebasing immediately219094 +Ref: Options used by commit commands220742 +Ref: Used by all or most commit commands220814 +Ref: Used by all squash and fixup commands223043 +Ref: Used by specific commit commands223597 +Ref: Editing Commit Messages223921 +Ref: Using the Revision Stack226475 +Ref: Commit Pseudo Headers229435 +Ref: Commit Mode and Hooks230587 +Ref: Commit Message Conventions233296 +Node: Branching235184 +Ref: The Two Remotes235305 +Ref: Branch Commands237870 +Ref: Branch Git Variables250727 +Ref: Auxiliary Branch Commands255972 +Node: Merging256989 +Node: Resolving Conflicts261338 +Node: Rebasing266708 +Ref: Editing Rebase Sequences271905 +Ref: Information About In-Progress Rebase277134 +Ref: Rebasing-Footnote-1286126 +Node: Cherry Picking286722 +Ref: Reverting291030 +Node: Resetting292390 +Node: Stashing294214 +Node: Transferring300453 +Node: Remotes300675 +Ref: Remote Commands300772 +Ref: Remote Git Variables304716 +Node: Fetching306649 +Node: Pulling309132 +Node: Pushing310158 +Node: Plain Patches314445 +Node: Maildir Patches315916 +Node: Miscellaneous317395 +Node: Tagging317741 +Node: Notes319634 +Node: Submodules321969 +Ref: Listing Submodules322130 +Ref: Submodule Transient324191 +Node: Subtree326544 +Node: Worktree328475 +Node: Sparse checkouts329790 +Node: Bundle332562 +Node: Common Commands332937 +Node: Wip Modes335563 +Ref: Wip Graph340137 +Node: Commands for Buffers Visiting Files342383 +Node: Minor Mode for Buffers Visiting Blobs350698 +Node: Customizing351861 +Node: Per-Repository Configuration353453 +Node: Essential Settings355707 +Ref: Safety355996 +Ref: Performance357671 +Ref: Log Performance360462 +Ref: Diff Performance361761 +Ref: Refs Buffer Performance363102 +Ref: Committing Performance363675 +Ref: Microsoft Windows Performance364655 +Ref: MacOS Performance365742 +Ref: Global Bindings366625 +Ref: Essential Settings-Footnote-1368798 +Node: Plumbing368880 +Node: Calling Git369705 +Ref: Getting a Value from Git371164 +Ref: Calling Git for Effect374782 +Node: Section Plumbing380721 +Ref: Creating Sections380873 +Ref: Section Selection384683 +Ref: Matching Sections386380 +Node: Refreshing Buffers392282 +Node: Conventions395422 +Ref: Theming Faces395586 +Node: FAQ403619 +Node: FAQ - How to ...?404055 +Ref: How to pronounce Magit?404188 +Ref: How to show git's output?404880 +Ref: How to install the gitman info manual?405497 +Ref: How to show diffs for gpg-encrypted files?406302 +Ref: How does branching and pushing work?406711 +Ref: Should I disable VC?406875 +Ref: The diff/log is filtered to one file; how do I show all changes407358 +Node: FAQ - Issues and Errors407730 +Ref: Magit is slow407875 +Ref: I changed several thousand files at once and now Magit is unusable408021 +Ref: I am having problems committing408560 +Ref: I am using MS Windows and cannot push with Magit408825 +Ref: I am using macOS and SOMETHING works in shell but not in Magit409223 +Ref: Expanding a file to show the diff causes it to disappear409810 +Ref: Point is wrong in the COMMIT_EDITMSG buffer410160 +Ref: The mode-line information isn't always up-to-date410984 +Ref: A branch and tag sharing the same name breaks SOMETHING411815 +Ref: My Git hooks work on the command-line but not inside Magit412463 +Ref: git-commit-mode isn't used when committing from the command-line413055 +Ref: Point ends up inside invisible text when jumping to a file-visiting buffer415043 +Ref: I am no longer able to save popup defaults415635 +Node: Debugging Tools416428 +Node: Keystroke Index420363 +Node: Function and Command Index458678 +Node: Variable Index510396  End Tag Table diff --git a/lisp/markdown-mode/markdown-mode-pkg.el b/lisp/markdown-mode/markdown-mode-pkg.el index 2a5f71e9..5ebb5b90 100644 --- a/lisp/markdown-mode/markdown-mode-pkg.el +++ b/lisp/markdown-mode/markdown-mode-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "markdown-mode" "20260321.143" +(define-package "markdown-mode" "20260425.954" "Major mode for Markdown-formatted text." '((emacs "28.1")) :url "https://jblevins.org/projects/markdown-mode/" - :commit "182640f79c3ed66f82f0419f130dffc173ee9464" - :revdesc "182640f79c3e" + :commit "1f72cefa6a4b759f90e335e4908725a721b17ad9" + :revdesc "1f72cefa6a4b" :keywords '("markdown" "github flavored markdown" "itex") :authors '(("Jason R. Blevins" . "jblevins@xbeta.org")) :maintainers '(("Jason R. Blevins" . "jblevins@xbeta.org"))) diff --git a/lisp/markdown-mode/markdown-mode.el b/lisp/markdown-mode/markdown-mode.el index 2ad72802..4873a666 100644 --- a/lisp/markdown-mode/markdown-mode.el +++ b/lisp/markdown-mode/markdown-mode.el @@ -6,8 +6,8 @@ ;; Author: Jason R. Blevins ;; Maintainer: Jason R. Blevins ;; Created: May 24, 2007 -;; Package-Version: 20260321.143 -;; Package-Revision: 182640f79c3e +;; Package-Version: 20260425.954 +;; Package-Revision: 1f72cefa6a4b ;; Package-Requires: ((emacs "28.1")) ;; Keywords: Markdown, GitHub Flavored Markdown, itex ;; URL: https://jblevins.org/projects/markdown-mode/ @@ -2931,22 +2931,21 @@ This may be useful for tables and Pandoc's line_blocks extension." "Return t if PROP from BEGIN to END is equal to one of the given PROP-VALUES. Also returns t if PROP is a list containing one of the PROP-VALUES. Return nil otherwise." - (let (props) - (catch 'found - (dolist (loc (number-sequence begin end)) - (when (setq props (get-text-property loc prop)) - (cond ((listp props) - ;; props is a list, check for membership - (dolist (val prop-values) - (when (memq val props) (throw 'found loc)))) - (t - ;; props is a scalar, check for equality - (dolist (val prop-values) - (when (eq val props) (throw 'found loc)))))))))) + (catch 'found + (let ((loc begin)) + (while (<= loc end) + (when-let* ((props (get-text-property loc prop))) + (if (listp props) + ;; props is a list, check for membership + (dolist (val prop-values) + (when (memq val props) (throw 'found loc))) + (dolist (val prop-values) + (when (eq val props) (throw 'found loc))))) + (setq loc (next-single-property-change loc prop nil (1+ end))))))) (defun markdown-range-properties-exist (begin end props) (cl-loop - for loc in (number-sequence begin end) + for loc from begin to end with result = nil while (not (setq result diff --git a/lisp/multiple-cursors/mc-mark-more.el b/lisp/multiple-cursors/mc-mark-more.el index 2ba6e54a..8db16d7a 100644 --- a/lisp/multiple-cursors/mc-mark-more.el +++ b/lisp/multiple-cursors/mc-mark-more.el @@ -56,7 +56,7 @@ beg)) (defun mc/furthest-cursor-before-point () - (let ((beg (if mark-active (min (mark) (point)) (point))) + (let ((beg (or (use-region-beginning) (point))) furthest) (mc/for-each-fake-cursor (when (< (mc/cursor-beg cursor) beg) @@ -65,7 +65,7 @@ furthest)) (defun mc/furthest-cursor-after-point () - (let ((end (if mark-active (max (mark) (point)) (point))) + (let ((end (or (use-region-end) (point))) furthest) (mc/for-each-fake-cursor (when (> (mc/cursor-end cursor) end) diff --git a/lisp/multiple-cursors/multiple-cursors-pkg.el b/lisp/multiple-cursors/multiple-cursors-pkg.el index e295fc74..4326d343 100644 --- a/lisp/multiple-cursors/multiple-cursors-pkg.el +++ b/lisp/multiple-cursors/multiple-cursors-pkg.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "multiple-cursors" "20260117.1733" +(define-package "multiple-cursors" "20260419.931" "Multiple cursors for Emacs." '((emacs "24.4") (cl-lib "0.5")) :url "https://github.com/magnars/multiple-cursors.el" - :commit "ddd677091afc7d65ce56d11866e18aeded110ada" - :revdesc "ddd677091afc" + :commit "94b8b07a4bab87f803123723b68227565429dfa1" + :revdesc "94b8b07a4bab" :keywords '("editing" "cursors") :authors '(("Magnar Sveen" . "magnars@gmail.com")) :maintainers '(("Magnar Sveen" . "magnars@gmail.com"))) diff --git a/lisp/multiple-cursors/multiple-cursors.el b/lisp/multiple-cursors/multiple-cursors.el index 83972b27..d2291c17 100644 --- a/lisp/multiple-cursors/multiple-cursors.el +++ b/lisp/multiple-cursors/multiple-cursors.el @@ -3,8 +3,8 @@ ;; Copyright (C) 2012-2016 Magnar Sveen ;; Author: Magnar Sveen -;; Package-Version: 20260117.1733 -;; Package-Revision: ddd677091afc +;; Package-Version: 20260419.931 +;; Package-Revision: 94b8b07a4bab ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5")) ;; Keywords: editing cursors ;; Homepage: https://github.com/magnars/multiple-cursors.el diff --git a/lisp/my/my-autoloads.el b/lisp/my/my-autoloads.el index 7c0474bf..39b6c437 100644 --- a/lisp/my/my-autoloads.el +++ b/lisp/my/my-autoloads.el @@ -50,11 +50,13 @@ publishing directory. Return output file name. (fn PLIST FILENAME PUB-DIR)") -(put 'org-html-head 'safe-local-variable 'stringp) +(put 'my-org-article-html-head 'safe-local-variable 'stringp) (autoload 'my-org-article-html-export-to-html "my-org-article" "\ See `org-tufte-export-to-file' and `org-html-export-to-html'. (fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'my-org-article-help "my-org-article" "\ +Open the `my-org-article' manual." t) (register-definition-prefixes "my-org-article" '("my-")) diff --git a/lisp/my/my-org-article-autoloads.el b/lisp/my/my-org-article-autoloads.el new file mode 100644 index 00000000..98347e96 --- /dev/null +++ b/lisp/my/my-org-article-autoloads.el @@ -0,0 +1,107 @@ +;;; my-org-article-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from my.el + +(register-definition-prefixes "my" '("my-")) + + +;;; Generated autoloads from my-org-article.el + +(autoload 'my-org-article-latex-make-preamble "my-org-article" "\ +See `org-latex-make-preamble'. +Uses also `my-org-article-latex-template-value' with +`my-org-article-latex-template-plist' to inserts placeholders. + +(fn INFO &optional TEMPLATE SNIPPET?)") +(autoload 'my-org-article-latex-export-to-latex "my-org-article" "\ +See `org-latex-export-to-latex' + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'my-org-article-latex-export-to-pdf "my-org-article" "\ +See `org-latex-export-to-pdf' + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'my-org-article-latex-publish-to-latex "my-org-article" "\ +Publish an Org file to LaTeX. + +FILENAME is the filename of the Org file to be published. PLIST +is the property list for the given project. PUB-DIR is the +publishing directory. + +Return output file name. + +(fn PLIST FILENAME PUB-DIR)") +(autoload 'my-org-article-latex-publish-to-pdf "my-org-article" "\ +Publish an Org file to PDF (via LaTeX). + +FILENAME is the filename of the Org file to be published. PLIST +is the property list for the given project. PUB-DIR is the +publishing directory. + +Return output file name. + +(fn PLIST FILENAME PUB-DIR)") +(put 'my-org-article-html-head 'safe-local-variable 'stringp) +(autoload 'my-org-article-html-export-to-html "my-org-article" "\ +See `org-tufte-export-to-file' and `org-html-export-to-html'. + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'my-org-article-help "my-org-article" "\ +Open the `my-org-article' manual." t) +(register-definition-prefixes "my-org-article" '("my-")) + + +;;; Generated autoloads from my-org-letter.el + +(autoload 'my-org-letter-latex-make-preamble "my-org-letter" "\ +See `org-latex-make-preamble' + +(fn INFO &optional TEMPLATE SNIPPET?)") +(autoload 'my-org-letter-latex-export-to-latex "my-org-letter" "\ +See `org-latex-export-to-latex' + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(autoload 'my-org-letter-latex-export-to-pdf "my-org-letter" "\ +See `org-latex-export-to-pdf' + +(fn &optional ASYNC SUBTREEP VISIBLE-ONLY BODY-ONLY EXT-PLIST)" t) +(register-definition-prefixes "my-org-letter" '("my-org-letter-")) + + +;;; Generated autoloads from my-theme.el + +(when load-file-name (add-to-list 'custom-theme-load-path (file-name-as-directory (file-name-directory load-file-name)))) +(register-definition-prefixes "my-theme" 'nil) + + +;;; Generated autoloads from my-tool-bar.el + +(register-definition-prefixes "my-tool-bar" '("my-")) + + +;;; Generated autoloads from my-view.el + +(register-definition-prefixes "my-view" '("my-view-")) + +;;; End of scraped data + +(provide 'my-org-article-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; my-org-article-autoloads.el ends here diff --git a/lisp/nerd-icons-dired/nerd-icons-dired-pkg.el b/lisp/nerd-icons-dired/nerd-icons-dired-pkg.el index e657823d..6ca95f76 100644 --- a/lisp/nerd-icons-dired/nerd-icons-dired-pkg.el +++ b/lisp/nerd-icons-dired/nerd-icons-dired-pkg.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "nerd-icons-dired" "20260206.1554" +(define-package "nerd-icons-dired" "20260415.1746" "Shows icons for each file in dired mode." '((emacs "24.4") (nerd-icons "0.0.1")) :url "https://github.com/rainstormstudio/nerd-icons-dired" - :commit "929b62f01b93d30a3f42cc507fc45c84a2457b3f" - :revdesc "929b62f01b93" + :commit "104acd8879528b8115589f35f1bbcbe231ad732f" + :revdesc "104acd887952" :keywords '("lisp") :authors '(("Hongyu Ding" . "rainstormstudio@yahoo.com")) :maintainers '(("Hongyu Ding" . "rainstormstudio@yahoo.com"))) diff --git a/lisp/nerd-icons-dired/nerd-icons-dired.el b/lisp/nerd-icons-dired/nerd-icons-dired.el index 766fb519..bb3cbe5a 100644 --- a/lisp/nerd-icons-dired/nerd-icons-dired.el +++ b/lisp/nerd-icons-dired/nerd-icons-dired.el @@ -4,8 +4,8 @@ ;; Author: Hongyu Ding ;; Keywords: lisp -;; Package-Version: 20260206.1554 -;; Package-Revision: 929b62f01b93 +;; Package-Version: 20260415.1746 +;; Package-Revision: 104acd887952 ;; Package-Requires: ((emacs "24.4") (nerd-icons "0.0.1")) ;; URL: https://github.com/rainstormstudio/nerd-icons-dired ;; Keywords: files, icons, dired @@ -62,6 +62,11 @@ :group 'nerd-icons :type 'function) +(defcustom nerd-icons-dired-icon-size 1.0 + "The default icon size in Dired." + :group 'nerd-icons + :type 'float) + (defvar nerd-icons-dired-mode) (defun nerd-icons-dired--add-overlay (pos string) @@ -89,10 +94,11 @@ (when (dired-move-to-filename nil) (let ((file (dired-get-filename 'relative 'noerror))) (when file - (let ((icon (if (file-directory-p file) - (funcall nerd-icons-dired-dir-icon-function file) - (funcall nerd-icons-dired-file-icon-function file))) - (inhibit-read-only t)) + (let* ((func (if (file-directory-p file) + nerd-icons-dired-dir-icon-function + nerd-icons-dired-file-icon-function)) + (icon (funcall func file :height nerd-icons-dired-icon-size)) + (inhibit-read-only t)) (if (member file '("." "..")) (nerd-icons-dired--add-overlay (dired-move-to-filename) nerd-icons-dired-special-prefix-string) diff --git a/lisp/nerd-icons/nerd-icons-autoloads.el b/lisp/nerd-icons/nerd-icons-autoloads.el index e8751d48..8d8fba79 100644 --- a/lisp/nerd-icons/nerd-icons-autoloads.el +++ b/lisp/nerd-icons/nerd-icons-autoloads.el @@ -14,7 +14,8 @@ (autoload 'nerd-icons-install-fonts "nerd-icons" "\ Helper function to download and install the latests fonts based on OS. The provided Nerd Font is Symbols Nerd Font Mono. -When PFX is non-nil, ignore the prompt and just install +When PFX is non-nil, ignore the prompt and just install. +On Windows only, when PFX is a path, ignore the prompt and download there. (fn &optional PFX)" t) (autoload 'nerd-icons-insert "nerd-icons" "\ diff --git a/lisp/nerd-icons/nerd-icons-pkg.el b/lisp/nerd-icons/nerd-icons-pkg.el index 4faf0666..dc3b96a2 100644 --- a/lisp/nerd-icons/nerd-icons-pkg.el +++ b/lisp/nerd-icons/nerd-icons-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "nerd-icons" "20260325.346" +(define-package "nerd-icons" "20260619.455" "Emacs Nerd Font Icons Library." '((emacs "25.1")) :url "https://github.com/rainstormstudio/nerd-icons.el" - :commit "1db0b0b9203cf293b38ac278273efcfc3581a05f" - :revdesc "1db0b0b9203c" + :commit "a9a9177e135dd407d508609ac4d9915eb8608b4f" + :revdesc "a9a9177e135d" :keywords '("lisp") :authors '(("Hongyu Ding" . "rainstormstudio@yahoo.com") ("Vincent Zhang" . "seagle0128@gmail.com")) diff --git a/lisp/nerd-icons/nerd-icons.el b/lisp/nerd-icons/nerd-icons.el index d8de0c47..9fd11284 100644 --- a/lisp/nerd-icons/nerd-icons.el +++ b/lisp/nerd-icons/nerd-icons.el @@ -4,8 +4,8 @@ ;; Author: Hongyu Ding , Vincent Zhang ;; Keywords: lisp -;; Package-Version: 20260325.346 -;; Package-Revision: 1db0b0b9203c +;; Package-Version: 20260619.455 +;; Package-Revision: a9a9177e135d ;; Package-Requires: ((emacs "25.1")) ;; URL: https://github.com/rainstormstudio/nerd-icons.el ;; Keywords: convenient, lisp @@ -90,6 +90,7 @@ ;; Shell ("fish" nerd-icons-devicon "nf-dev-terminal" :face nerd-icons-lpink) ("zsh" nerd-icons-devicon "nf-dev-terminal" :face nerd-icons-lcyan) + ("bash" nerd-icons-devicon "nf-dev-terminal" :face nerd-icons-purple) ("sh" nerd-icons-devicon "nf-dev-terminal" :face nerd-icons-purple) ("nu" nerd-icons-devicon "nf-dev-terminal" :face nerd-icons-green) ("bat" nerd-icons-codicon "nf-cod-terminal_cmd" :face nerd-icons-lsilver) @@ -235,6 +236,7 @@ ;; ("hy" nerd-icons-sucicon "nf-custom-hy" :face nerd-icons-blue) ("el" nerd-icons-sucicon "nf-custom-emacs" :face nerd-icons-purple) ("eld" nerd-icons-sucicon "nf-custom-emacs" :face nerd-icons-purple) + ("fnl" nerd-icons-sucicon "nf-custom-fennel" :face nerd-icons-green) ("clj" nerd-icons-devicon "nf-dev-clojure" :face nerd-icons-blue) ("cljc" nerd-icons-devicon "nf-dev-clojure" :face nerd-icons-blue) ("cljd" nerd-icons-devicon "nf-dev-clojure" :face nerd-icons-green) @@ -431,6 +433,7 @@ ("webm" nerd-icons-faicon "nf-fa-film" :face nerd-icons-blue) ;; Fonts ("ttf" nerd-icons-faicon "nf-fa-font" :face nerd-icons-dcyan) + ("otf" nerd-icons-faicon "nf-fa-font" :face nerd-icons-dcyan) ("woff" nerd-icons-faicon "nf-fa-font" :face nerd-icons-cyan) ("woff2" nerd-icons-faicon "nf-fa-font" :face nerd-icons-cyan) ;; Archives @@ -554,7 +557,7 @@ ("^\\.env\\.test\\.local$" nerd-icons-codicon "nf-cod-settings" :face nerd-icons-yellow) ("^\\.env\\.uat$" nerd-icons-codicon "nf-cod-settings" :face nerd-icons-yellow) ("^\\.env\\.cat$" nerd-icons-codicon "nf-cod-settings" :face nerd-icons-yellow) - ("^\\.envrc$" nerd-icons-codicon "nf-cod-settings" :face nerd-icons-yellow) + ("^\\.envrc$" nerd-icons-codicon "nf-cod-settings" :face nerd-icons-yellow) ;; Config ("nginx$" nerd-icons-devicon "nf-dev-nginx" :face nerd-icons-dgreen) @@ -562,8 +565,8 @@ ;; C ("^\\(GNU\\|\\)Makefile.*" nerd-icons-sucicon "nf-seti-makefile" :face nerd-icons-dorange) - ("^CMakeLists.txt$" nerd-icons-devicon "nf-dev-cmake" :face nerd-icons-red) - ("^CMakeCache.txt$" nerd-icons-devicon "nf-dev-cmake" :face nerd-icons-blue) + ("^CMakeLists.txt$" nerd-icons-devicon "nf-dev-cmake" :face nerd-icons-red) + ("^CMakeCache.txt$" nerd-icons-devicon "nf-dev-cmake" :face nerd-icons-blue) ("^meson.build$" nerd-icons-sucicon "nf-seti-makefile" :face nerd-icons-purple) ;; TODO: meson ("^meson_options.txt$" nerd-icons-sucicon "nf-seti-makefile" :face nerd-icons-purple) ;; TODO: meson @@ -671,30 +674,36 @@ (defvar nerd-icons-dir-icon-alist '( - ("trash" nerd-icons-faicon "nf-fa-trash_o") - ("dropbox" nerd-icons-faicon "nf-fa-dropbox") + ("[Tt]rash" nerd-icons-faicon "nf-fa-trash_o") + ("[Dd]ropbox" nerd-icons-faicon "nf-fa-dropbox") ("google[ _-]drive" nerd-icons-mdicon "nf-md-folder_google_drive") ("github" nerd-icons-sucicon "nf-custom-folder_github") ("^atom$" nerd-icons-devicon "nf-dev-atom") - ("documents" nerd-icons-mdicon "nf-md-folder_file") - ("download" nerd-icons-mdicon "nf-md-folder_download") - ("desktop" nerd-icons-octicon "nf-oct-device_desktop") - ("pictures" nerd-icons-mdicon "nf-md-folder_image") - ("photos" nerd-icons-faicon "nf-fa-camera_retro") - ("music" nerd-icons-mdicon "nf-md-folder_music") - ("movies" nerd-icons-faicon "nf-fa-film") - ("code" nerd-icons-octicon "nf-oct-code") - ("workspace" nerd-icons-octicon "nf-oct-code") - ;; ("test" nerd-icons-devicon "test-dir") + ("^src$" nerd-icons-octicon "nf-oct-code") + ("^tests?$" nerd-icons-octicon "nf-oct-beaker") + ("Applications" nerd-icons-mdicon "nf-md-application") + ("[Dd]ocuments" nerd-icons-mdicon "nf-md-folder_file") + ("[Dd]ownload" nerd-icons-mdicon "nf-md-folder_download") + ("[Dd]esktop" nerd-icons-octicon "nf-oct-device_desktop") + ("[Pp]ictures" nerd-icons-mdicon "nf-md-folder_image") + ("[Pp]hotos" nerd-icons-faicon "nf-fa-camera_retro") + ("[Mm]usic" nerd-icons-mdicon "nf-md-folder_music") + ("[Mm]ovies" nerd-icons-faicon "nf-fa-film") + ("[Cc]ode" nerd-icons-octicon "nf-oct-code") + ("[Ww]orkspace" nerd-icons-octicon "nf-oct-code") + ("\\.?cache" nerd-icons-octicon "nf-oct-cache") + ("\\.?config" nerd-icons-sucicon "nf-custom-folder_config") ("\\.git" nerd-icons-sucicon "nf-custom-folder_git") - ("\\.config" nerd-icons-sucicon "nf-custom-folder_config") + ("\\.npm" nerd-icons-sucicon "nf-custom-folder_npm") + ("\\.gem" nerd-icons-faicon "nf-fa-gem") + ("\\.ssh" nerd-icons-mdicon "nf-md-folder_key") ("hypr" nerd-icons-flicon "nf-linux-hyprland") ("kitty" nerd-icons-devicon "nf-dev-terminal") ("^gtk-.*" nerd-icons-flicon "nf-linux-gtk") ("inkscape" nerd-icons-devicon "nf-dev-inkscape") ("vlc" nerd-icons-mdicon "nf-md-vlc") ("discord" nerd-icons-faicon "nf-fa-discord") - ("JetBrains" nerd-icons-devicon "nf-dev-jetbrains") + ("[Jj]et[Bb]rains" nerd-icons-devicon "nf-dev-jetbrains") ("^go$" nerd-icons-devicon "nf-dev-go") ("mpv" nerd-icons-flicon "nf-linux-mpv") ("electron" nerd-icons-devicon "nf-dev-electron") @@ -1155,7 +1164,8 @@ NOTE: The mode-setting function may not be the same as the mode itself." (defun nerd-icons-match-to-alist (string alist) "Match STRING against an entry in ALIST using `string-match'." - (cdr (assoc string alist #'string-match))) + (let ((case-fold-search nil)) + (cdr (assoc string alist #'string-match)))) (defun nerd-icons-dir-is-submodule (dir) "Checker whether or not DIR is a git submodule." @@ -1195,7 +1205,8 @@ string." (defun nerd-icons-install-fonts (&optional pfx) "Helper function to download and install the latests fonts based on OS. The provided Nerd Font is Symbols Nerd Font Mono. -When PFX is non-nil, ignore the prompt and just install" +When PFX is non-nil, ignore the prompt and just install. +On Windows only, when PFX is a path, ignore the prompt and download there." (interactive "P") (when (or pfx (yes-or-no-p "This will download and install fonts, are you sure you want to do this?")) (let* ((url-format "https://raw.githubusercontent.com/rainstormstudio/nerd-icons.el/main/fonts/%s") @@ -1212,7 +1223,9 @@ When PFX is non-nil, ignore the prompt and just install" "/Library/Fonts/" nerd-icons-fonts-subdirectory)))) (known-dest? (stringp font-dest)) - (font-dest (or font-dest (read-directory-name "Font installation directory: " "~/")))) + (font-dest (or font-dest + (and (stringp pfx) pfx) + (read-directory-name "Font installation directory: " "~/")))) (unless (file-directory-p font-dest) (mkdir font-dest t)) diff --git a/lisp/notmuch/notmuch-pkg.el b/lisp/notmuch/notmuch-pkg.el index 2a2816fd..c30920a1 100644 --- a/lisp/notmuch/notmuch-pkg.el +++ b/lisp/notmuch/notmuch-pkg.el @@ -4,4 +4,6 @@ () :url "https://notmuchmail.org" :commit "094744b3f6f5c6831b6555d1fe75709abf1d1279" - :revdesc "094744b3f6f5") + :revdesc "094744b3f6f5" + :authors '(("Carl Worth" . "cworth@cworth.org")) + :maintainers '(("Carl Worth" . "cworth@cworth.org"))) diff --git a/lisp/ol-notmuch/ol-notmuch-pkg.el b/lisp/ol-notmuch/ol-notmuch-pkg.el index 4c7b1e0c..51922428 100644 --- a/lisp/ol-notmuch/ol-notmuch-pkg.el +++ b/lisp/ol-notmuch/ol-notmuch-pkg.el @@ -1,13 +1,13 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "ol-notmuch" "20260101.1844" +(define-package "ol-notmuch" "20260601.1532" "Links to notmuch messages." '((emacs "29.1") - (compat "30.1") + (compat "31.0") (notmuch "0.39") - (org "9.7")) + (org "9.8")) :url "https://github.com/tarsius/ol-notmuch" - :commit "8f717329388935538fe433b9a15f1599edd9fcd5" - :revdesc "8f7173293889" + :commit "fba0b5790a4c9aafeab69fd329776d0b4afa9aac" + :revdesc "fba0b5790a4c" :keywords '("hypermedia" "mail") :authors '(("Matthieu Lemerre" . "racin@free.fr")) :maintainers '(("Jonas Bernoulli" . "emacs.ol-notmuch@jonas.bernoulli.dev"))) diff --git a/lisp/ol-notmuch/ol-notmuch.el b/lisp/ol-notmuch/ol-notmuch.el index 7f1cf873..ca6cd698 100644 --- a/lisp/ol-notmuch/ol-notmuch.el +++ b/lisp/ol-notmuch/ol-notmuch.el @@ -9,13 +9,13 @@ ;; Homepage: https://github.com/tarsius/ol-notmuch ;; Keywords: hypermedia mail -;; Package-Version: 20260101.1844 -;; Package-Revision: 8f7173293889 +;; Package-Version: 20260601.1532 +;; Package-Revision: fba0b5790a4c ;; Package-Requires: ( ;; (emacs "29.1") -;; (compat "30.1") +;; (compat "31.0") ;; (notmuch "0.39") -;; (org "9.7")) +;; (org "9.8")) ;; SPDX-License-Identifier: GPL-3.0-or-later diff --git a/lisp/olivetti/olivetti-pkg.el b/lisp/olivetti/olivetti-pkg.el index 9d4a65fa..2220ce32 100644 --- a/lisp/olivetti/olivetti-pkg.el +++ b/lisp/olivetti/olivetti-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "olivetti" "20241030.542" +(define-package "olivetti" "20260524.213" "Minor mode to automatically balance window margins." '((emacs "24.4")) :url "https://github.com/rnkn/olivetti" - :commit "845eb7a95a3ca3325f1120c654d761b91683f598" - :revdesc "845eb7a95a3c" + :commit "d2ccae56b442d9c5b06dd2481057abbd7eb82551" + :revdesc "d2ccae56b442" :keywords '("wp" "text") :authors '(("Paul W. Rankin" . "rnkn@rnkn.xyz")) :maintainers '(("Paul W. Rankin" . "rnkn@rnkn.xyz"))) diff --git a/lisp/olivetti/olivetti.el b/lisp/olivetti/olivetti.el index 3301760d..671b8590 100644 --- a/lisp/olivetti/olivetti.el +++ b/lisp/olivetti/olivetti.el @@ -4,8 +4,8 @@ ;; Author: Paul W. Rankin ;; Keywords: wp, text -;; Package-Version: 20241030.542 -;; Package-Revision: 845eb7a95a3c +;; Package-Version: 20260524.213 +;; Package-Revision: d2ccae56b442 ;; Package-Requires: ((emacs "24.4")) ;; URL: https://github.com/rnkn/olivetti @@ -176,7 +176,7 @@ If an integer, set text body width to that integer in columns; if a floating point between 0.0 and 1.0, set text body width to that fraction of the total window width. If nil (the default), use the value of `fill-column' + 2. The extra 2 columns are to prevent -text files at `fill-colum ' from wrapping in the body widith. +text files at `fill-column' from wrapping in the body width. An integer is best if you want text body width to remain constant, while a floating point is best if you want text body @@ -264,18 +264,23 @@ if it is an integer, and otherwise return WIDTH." (setq height (/ height 100.0))) (round (* width (or height 1))))) +(defun olivetti-window-width (window) + "Return the pixel width of WINDOW, including default margins." + (+ (window-body-width window t) + (* (frame-char-width (window-frame window)) + (+ left-margin-width right-margin-width)))) + (defun olivetti-normalize-width (width window) "Parse WIDTH to a safe pixel value for `olivetti-body-width' for WINDOW." - (let ((char-width (frame-char-width (window-frame window))) - (window-width-pix (window-body-width window t)) - min-width-pix) - (setq min-width-pix (* char-width + (let* ((char-width (frame-char-width (window-frame window))) + (window-width-pix (olivetti-window-width window)) + (min-width-pix (* char-width (+ olivetti-minimum-body-width - (% olivetti-minimum-body-width 2)))) - (if (floatp width) - (floor (max min-width-pix (* window-width-pix (min width 1.0)))) - (olivetti-scale-width - (max min-width-pix (min (* width char-width) window-width-pix)))))) + (% olivetti-minimum-body-width 2))))) + (if (floatp width) + (floor (max min-width-pix (* window-width-pix (min width 1.0)))) + (olivetti-scale-width + (max min-width-pix (min (* width char-width) window-width-pix)))))) (defun olivetti-reset-window (window) "Remove Olivetti's parameters and margins from WINDOW." @@ -284,7 +289,7 @@ if it is an integer, and otherwise return WIDTH." (if (consp fringe-mode) (set-window-fringes window (car fringe-mode) (cdr fringe-mode)) (set-window-fringes window fringe-mode fringe-mode)) - (set-window-margins window nil)) + (set-window-margins window left-margin-width right-margin-width)) (defun olivetti-reset-all-windows () "Call `olivetti-reset-window' on all windows." @@ -325,10 +330,10 @@ window." ;; `fill-column' (when (null olivetti-body-width) (setq olivetti-body-width (+ fill-column 2))) - (let ((char-width-pix (frame-char-width (window-frame window-or-frame))) - (window-width-pix (window-body-width window-or-frame t)) - (safe-width-pix (olivetti-normalize-width - olivetti-body-width window-or-frame))) + (let* ((char-width-pix (frame-char-width (window-frame window-or-frame))) + (window-width-pix (olivetti-window-width window-or-frame)) + (safe-width-pix (olivetti-normalize-width + olivetti-body-width window-or-frame))) ;; Handle possible display of fringes (when (and window-system olivetti-style) (let ((fringe-total (- (window-pixel-width window-or-frame) @@ -351,11 +356,11 @@ window." (setq left-margin (max (round (/ (- margin-total-pix (car fringes)) char-width-pix)) - 0) + left-margin-width) right-margin (max (round (/ (- margin-total-pix (cadr fringes)) char-width-pix)) - 0)) + right-margin-width)) ;; Finally set the margins (set-window-margins window-or-frame left-margin right-margin))) ;; Set remaining window parameters diff --git a/lisp/org-roam/org-roam-capture.el b/lisp/org-roam/org-roam-capture.el index 5a501f55..a4d5978a 100644 --- a/lisp/org-roam/org-roam-capture.el +++ b/lisp/org-roam/org-roam-capture.el @@ -567,7 +567,7 @@ Return the ID of the location." ;; caller. (save-excursion (goto-char p) - (if-let ((id (org-entry-get p "ID"))) + (if-let* ((id (org-entry-get p "ID"))) (setf (org-roam-node-id org-roam-capture--node) id) (org-entry-put p "ID" (org-roam-node-id org-roam-capture--node))) (prog1 diff --git a/lisp/org-roam/org-roam-dailies.el b/lisp/org-roam/org-roam-dailies.el index d0652013..a11f30d1 100644 --- a/lisp/org-roam/org-roam-dailies.el +++ b/lisp/org-roam/org-roam-dailies.el @@ -1,13 +1,11 @@ ;;; org-roam-dailies.el --- Daily-notes for Org-roam -*- coding: utf-8; lexical-binding: t; -*- -;;; + ;; Copyright © 2020-2025 Jethro Kuan ;; Copyright © 2020 Leo Vivier ;; Author: Jethro Kuan ;; Leo Vivier ;; URL: https://github.com/org-roam/org-roam -;; Keywords: org-mode, roam, convenience -;; Package-Requires: ((emacs "26.1") (org-roam "2.1")) ;; This file is NOT part of GNU Emacs. diff --git a/lisp/org-roam/org-roam-export.el b/lisp/org-roam/org-roam-export.el index b552bea8..03dd8415 100644 --- a/lisp/org-roam/org-roam-export.el +++ b/lisp/org-roam/org-roam-export.el @@ -4,8 +4,6 @@ ;; Author: Jethro Kuan ;; URL: https://github.com/org-roam/org-roam -;; Keywords: org-mode, roam, convenience -;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; This file is NOT part of GNU Emacs. diff --git a/lisp/org-roam/org-roam-graph.el b/lisp/org-roam/org-roam-graph.el index a04d86d4..038fb578 100644 --- a/lisp/org-roam/org-roam-graph.el +++ b/lisp/org-roam/org-roam-graph.el @@ -4,8 +4,6 @@ ;; Author: Jethro Kuan ;; URL: https://github.com/org-roam/org-roam -;; Keywords: org-mode, roam, convenience -;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; This file is NOT part of GNU Emacs. diff --git a/lisp/org-roam/org-roam-node.el b/lisp/org-roam/org-roam-node.el index 8a44c85a..6bb35f7e 100644 --- a/lisp/org-roam/org-roam-node.el +++ b/lisp/org-roam/org-roam-node.el @@ -28,6 +28,7 @@ ;;; Code: (require 'crm) (require 'subr-x) +(eval-when-compile (require 'rx)) (require 'org-roam) ;;; Options @@ -793,27 +794,41 @@ Assumes that the cursor was put where the link is." ;;;;;; Completion-at-point interface (defconst org-roam-bracket-completion-re - "\\[\\[\\(\\(?:roam:\\)?\\)\\([^z-a]*?\\)]]" - "Regex for completion within link brackets. -We use this as a substitute for `org-link-bracket-re', because -`org-link-bracket-re' requires content within the brackets for a match.") + (rx "[[" + (group (opt "roam:")) + ;; Change from ‘org-link-bracket-re’: allow empty URI part + (group (zero-or-more + (or (not (any "[]\\")) + (and "\\" (zero-or-more "\\\\") (any "[]")) + (and (one-or-more "\\") (not (any "[]")))))) + "]]") + "Regexp for completion within link brackets. +Intended for ‘org-roam-complete-link-at-point’, which see.") (defun org-roam-complete-link-at-point () - "Complete \"roam:\" link at point to an existing Org-roam node." - (let (roam-p start end) - (when (org-in-regexp org-roam-bracket-completion-re 1) - (setq roam-p (not (or (org-in-src-block-p) - (string-blank-p (match-string 1)))) - start (match-beginning 2) - end (match-end 2)) - (list start end - (org-roam--get-titles) - :exit-function - (lambda (str &rest _) - (delete-char (- 0 (length str))) - (insert (concat (unless roam-p "roam:") - str)) - (forward-char 2)))))) + "Complete inside link brackets to an existing Org-roam node. +Targets [[$title]] and [[roam:$title]] links. [[id:$id][$description]] +links are not targeted to allow for changing link descriptions without +changing the target node." + (when-let* ((_ (org-in-regexp org-roam-bracket-completion-re 1)) + (uri-start (match-beginning 1)) + (title-start (match-beginning 2)) + (end (match-end 2)) + ;; don’t try to complete if point is in the delimiting brackets + (_ (<= title-start (point) end)) + (_ (not (org-in-src-block-p)))) + (list title-start end + (org-roam--get-titles) + :exit-function + (lambda (str &rest _) + "Replace title inserted by completion with ID and title." + (delete-region uri-start (point)) + (insert "id:" + (org-roam-node-id (org-roam-node-from-title-or-alias + (substring-no-properties str))) + "][" str) + ;; Move point after closing brackets + (forward-char 2))))) (defun org-roam-complete-everywhere () "Complete symbol at point as a link completion to an Org-roam node. @@ -821,7 +836,7 @@ This is a `completion-at-point' function, and is active when `org-roam-completion-everywhere' is non-nil. Unlike `org-roam-complete-link-at-point' this will complete even -outside of the bracket syntax for links (i.e. \"[[roam:|]]\"), +outside of the bracket syntax for links (i.e. \"[[|]]\"), hence \"everywhere\"." (when (and org-roam-completion-everywhere (thing-at-point 'word) @@ -833,7 +848,10 @@ hence \"everywhere\"." :exit-function (lambda (str _status) (delete-char (- (length str))) - (insert "[[roam:" str "]]")) + (insert "[[id:" + (org-roam-node-id (org-roam-node-from-title-or-alias + (substring-no-properties str))) + "][" str "]]")) ;; Proceed with the next completion function if the returned titles ;; do not match. This allows the default Org capfs or custom capfs ;; of lower priority to run. @@ -1095,13 +1113,7 @@ and when nil is returned the node will be filtered out." (defun org-roam-tag-completions () "Return list of tags for completions within Org-roam." (let ((roam-tags (mapcar #'car (org-roam-db-query [:select :distinct [tag] :from tags]))) - (org-tags (cl-loop for tagg in org-tag-alist - nconc (pcase tagg - ('(:newline) - nil) - (`(,tag . ,_) - (list tag)) - (_ nil))))) + (org-tags (seq-filter #'stringp (mapcar #'car org-tag-alist)))) (seq-uniq (append roam-tags org-tags)))) ;;;; Editing diff --git a/lisp/org-roam/org-roam-overlay.el b/lisp/org-roam/org-roam-overlay.el index 3f36190f..460e12ab 100644 --- a/lisp/org-roam/org-roam-overlay.el +++ b/lisp/org-roam/org-roam-overlay.el @@ -4,8 +4,6 @@ ;; Author: Jethro Kuan ;; URL: https://github.com/org-roam/org-roam -;; Keywords: org-mode, roam, convenience -;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; This file is NOT part of GNU Emacs. diff --git a/lisp/org-roam/org-roam-pkg.el b/lisp/org-roam/org-roam-pkg.el index fdcd28fb..d28532e2 100644 --- a/lisp/org-roam/org-roam-pkg.el +++ b/lisp/org-roam/org-roam-pkg.el @@ -1,14 +1,14 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "org-roam" "20260224.1637" +(define-package "org-roam" "20260425.1623" "A database abstraction layer for Org-mode." - '((emacs "26.1") + '((emacs "27.1") (compat "30.1") (org "9.6") - (emacsql "4.1.0") - (magit-section "3.0.0")) + (emacsql "4.3.3") + (magit-section "4.4.2")) :url "https://github.com/org-roam/org-roam" - :commit "20934cfb5a2e7ae037ec10bbc81ca97478738178" - :revdesc "20934cfb5a2e" + :commit "c54c523dec175695645399705606ea19056a3053" + :revdesc "c54c523dec17" :keywords '("org-mode" "roam" "convenience") :authors '(("Jethro Kuan" . "jethrokuan95@gmail.com")) :maintainers '(("Jethro Kuan" . "jethrokuan95@gmail.com"))) diff --git a/lisp/org-roam/org-roam-protocol.el b/lisp/org-roam/org-roam-protocol.el index 4174a56c..557bb811 100644 --- a/lisp/org-roam/org-roam-protocol.el +++ b/lisp/org-roam/org-roam-protocol.el @@ -1,10 +1,9 @@ ;;; org-roam-protocol.el --- Protocol handler for roam:// links -*- coding: utf-8; lexical-binding: t; -*- ;; Copyright © 2020-2025 Jethro Kuan + ;; Author: Jethro Kuan ;; URL: https://github.com/org-roam/org-roam -;; Keywords: org-mode, roam, convenience -;; Package-Requires: ((emacs "26.1") (org "9.6") (org-roam "2.1")) ;; This file is NOT part of GNU Emacs. diff --git a/lisp/org-roam/org-roam.el b/lisp/org-roam/org-roam.el index ab9a4e9e..cb681a32 100644 --- a/lisp/org-roam/org-roam.el +++ b/lisp/org-roam/org-roam.el @@ -5,9 +5,9 @@ ;; Author: Jethro Kuan ;; URL: https://github.com/org-roam/org-roam ;; Keywords: org-mode, roam, convenience -;; Package-Version: 20260224.1637 -;; Package-Revision: 20934cfb5a2e -;; Package-Requires: ((emacs "26.1") (compat "30.1") (org "9.6") (emacsql "4.1.0") (magit-section "3.0.0")) +;; Package-Version: 20260425.1623 +;; Package-Revision: c54c523dec17 +;; Package-Requires: ((emacs "27.1") (compat "30.1") (org "9.6") (emacsql "4.3.3") (magit-section "4.4.2")) ;; This file is NOT part of GNU Emacs. diff --git a/lisp/org-roam/org-roam.info b/lisp/org-roam/org-roam.info index 2605414b..a05a192e 100644 --- a/lisp/org-roam/org-roam.info +++ b/lisp/org-roam/org-roam.info @@ -1095,8 +1095,7 @@ Completions within link brackets are provided by ‘org-roam-complete-link-at-point’. The completion candidates are the titles and aliases for all Org-roam -nodes. Upon choosing a candidate, a ‘roam:Title’ link will be inserted, -linking to node of choice. +nodes. Choosing a candidate inserts an ID link to the chosen node.  File: org-roam.info, Node: Completing anywhere, Prev: Completing within Link Brackets, Up: Completion @@ -1107,9 +1106,8 @@ File: org-roam.info, Node: Completing anywhere, Prev: Completing within Link B The same completions can be triggered anywhere for the symbol at point if not within a bracketed link. This is provided by ‘org-roam-complete-everywhere’. Similarly, the completion candidates -are the titles and aliases for all Org-roam nodes, and upon choosing a -candidate a ‘roam:Title’ link will be inserted linking to the node of -choice. +are the titles and aliases for all Org-roam nodes, and choosing a +candidate inserts an ID link to the chosen node. This is disabled by default. To enable it, set ‘org-roam-completion-everywhere’ to ‘t’: @@ -2393,7 +2391,7 @@ Appendix D Variable Index [index] * Menu: -* org-roam-completion-everywhere: Completing anywhere. (line 18) +* org-roam-completion-everywhere: Completing anywhere. (line 17) * org-roam-dailies-capture-templates: Configuration. (line 12) * org-roam-dailies-directory: Configuration. (line 8) * org-roam-db-extra-links-elements: What to cache. (line 34) @@ -2444,61 +2442,61 @@ Node: Citations35304 Node: Using the Cached Information35870 Node: Completion37013 Node: Completing within Link Brackets37808 -Node: Completing anywhere38258 -Node: Encryption39034 -Node: The Templating System39790 -Node: Template Walkthrough40505 -Node: Org-roam Template Expansion42413 -Node: Extensions44279 -Node: org-roam-protocol44515 -Node: Installation (1)44977 -Node: Linux45812 -Node: Mac OS47334 -Ref: Testing org-protocol50101 -Node: Windows51110 -Node: The roam-node protocol51853 -Node: The roam-ref protocol52240 -Node: org-roam-graph53418 -Node: Graph Options55307 -Node: org-roam-dailies56313 -Node: Configuration56600 -Node: Usage57395 -Node: org-roam-export59124 -Node: Performance Optimization59642 -Node: Garbage Collection59848 -Node: The Org-mode Ecosystem60642 -Node: Browsing History with winner-mode61139 -Node: Versioning Notes62010 -Node: Full-text search with Deft62801 -Node: Org-journal63552 -Node: Org-download64364 -Node: mathpixel64881 -Node: Org-noter / Interleave65458 -Node: Bibliography65850 -Node: Spaced Repetition66607 -Node: FAQ67263 -Node: How do I have more than one Org-roam directory?67731 -Node: How do I create a note whose title already matches one of the candidates?69302 -Node: How can I stop Org-roam from creating IDs everywhere?70203 -Node: How do I migrate from Roam Research?70895 -Node: How to migrate from Org-roam v1?71392 -Node: How do I publish my notes with an Internet-friendly graph?72782 -Node: Configure org-mode for publishing74137 -Node: Overriding the default link creation function75615 -Node: Copying the generated file to the export directory76287 -Node: Developer's Guide to Org-roam77258 -Node: Org-roam's Design Principle77530 -Node: Building Extensions and Advanced Customization of Org-roam79512 -Node: Accessing the Database80766 -Node: Accessing and Modifying Nodes81495 -Node: Extending the Capture System83360 -Node: Appendix84887 -Node: Note-taking Workflows85074 -Node: Ecosystem86319 -Node: Keystroke Index86436 -Node: Command Index86587 -Node: Function Index86740 -Node: Variable Index88518 +Node: Completing anywhere38222 +Node: Encryption38960 +Node: The Templating System39716 +Node: Template Walkthrough40431 +Node: Org-roam Template Expansion42339 +Node: Extensions44205 +Node: org-roam-protocol44441 +Node: Installation (1)44903 +Node: Linux45738 +Node: Mac OS47260 +Ref: Testing org-protocol50027 +Node: Windows51036 +Node: The roam-node protocol51779 +Node: The roam-ref protocol52166 +Node: org-roam-graph53344 +Node: Graph Options55233 +Node: org-roam-dailies56239 +Node: Configuration56526 +Node: Usage57321 +Node: org-roam-export59050 +Node: Performance Optimization59568 +Node: Garbage Collection59774 +Node: The Org-mode Ecosystem60568 +Node: Browsing History with winner-mode61065 +Node: Versioning Notes61936 +Node: Full-text search with Deft62727 +Node: Org-journal63478 +Node: Org-download64290 +Node: mathpixel64807 +Node: Org-noter / Interleave65384 +Node: Bibliography65776 +Node: Spaced Repetition66533 +Node: FAQ67189 +Node: How do I have more than one Org-roam directory?67657 +Node: How do I create a note whose title already matches one of the candidates?69228 +Node: How can I stop Org-roam from creating IDs everywhere?70129 +Node: How do I migrate from Roam Research?70821 +Node: How to migrate from Org-roam v1?71318 +Node: How do I publish my notes with an Internet-friendly graph?72708 +Node: Configure org-mode for publishing74063 +Node: Overriding the default link creation function75541 +Node: Copying the generated file to the export directory76213 +Node: Developer's Guide to Org-roam77184 +Node: Org-roam's Design Principle77456 +Node: Building Extensions and Advanced Customization of Org-roam79438 +Node: Accessing the Database80692 +Node: Accessing and Modifying Nodes81421 +Node: Extending the Capture System83286 +Node: Appendix84813 +Node: Note-taking Workflows85000 +Node: Ecosystem86245 +Node: Keystroke Index86362 +Node: Command Index86513 +Node: Function Index86666 +Node: Variable Index88444  End Tag Table diff --git a/lisp/org-wc/org-wc-autoloads.el b/lisp/org-wc/org-wc-autoloads.el new file mode 100644 index 00000000..cb901ebf --- /dev/null +++ b/lisp/org-wc/org-wc-autoloads.el @@ -0,0 +1,59 @@ +;;; org-wc-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from org-wc.el + +(autoload 'org-word-count "org-wc" "\ +Report the number of words in the Org mode buffer or selected region. + +Ignores heading lines, blocks, comments, drawers, and links +depending on customizable variables in customization group org-wc. + +LaTeX macros are counted as 1 word. + +(fn BEG END)" t) +(autoload 'org-wc-count-subtrees "org-wc" "\ +Count words in each subtree, putting result as the property :org-wc on that heading." t) +(autoload 'org-wc-display "org-wc" "\ +Show subtree word counts in the entire buffer. +With prefix argument, only show the total wordcount for the buffer or region +in the echo area. + +Use \\[org-wc-remove-overlays] to remove the subtree times. + +Ignores: heading lines, + blocks, + comments, + drawers. +LaTeX macros are counted as 1 word. + +(fn TOTAL-ONLY)" t) +(autoload 'org-wc-remove-overlays "org-wc" "\ +Remove the occur highlights from the buffer. +BEG and END are ignored. If NOREMOVE is nil, remove this function +from the `before-change-functions' in the current buffer. + +(fn &optional BEG END NOREMOVE)" t) +(register-definition-prefixes "org-wc" '("org-w")) + +;;; End of scraped data + +(provide 'org-wc-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; org-wc-autoloads.el ends here diff --git a/lisp/org-wc/org-wc-pkg.el b/lisp/org-wc/org-wc-pkg.el new file mode 100644 index 00000000..bc7c578f --- /dev/null +++ b/lisp/org-wc/org-wc-pkg.el @@ -0,0 +1,7 @@ +;; -*- no-byte-compile: t; lexical-binding: nil -*- +(define-package "org-wc" "20251023.1922" + "Count words in org mode trees." + () + :url "https://github.com/tesujimath/org-wc" + :commit "65e2caaeaeea01b4ab3ab3cd964c911297885a0f" + :revdesc "65e2caaeaeea") diff --git a/lisp/org-wc/org-wc.el b/lisp/org-wc/org-wc.el new file mode 100644 index 00000000..605df15b --- /dev/null +++ b/lisp/org-wc/org-wc.el @@ -0,0 +1,326 @@ +;;; org-wc.el --- Count words in org mode trees. -*- lexical-binding: t -*- + +;; This file 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, or (at your option) +;; any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;; Author: Simon Guest +;; Created: 2011-04-23 + +;; Package-Version: 20251023.1922 +;; Package-Revision: 65e2caaeaeea + +;;; Commentary: + +;; Shows word count per heading line, summed over sub-headings. +;; Aims to be fast, so doesn't check carefully what it's counting. ;-) +;; +;; Implementation based on: +;; - Paul Sexton's word count posted on org-mode mailing list 21/2/11. +;; - clock overlays +;; +;; v2 +;; 29/4/11 +;; Don't modify buffer, and fixed handling of empty sections. +;; +;; v3 +;; 29/4/11 +;; Handle narrowing correctly, so partial word count works on narrowed regions. + +;;; Code: + +(require 'org) +(require 'cl-lib) + +(defun org-wc--downcase-list-of-strings-set-default (var val) + (set-default var (mapcar #'downcase val))) + +(defgroup org-wc nil + "Options for configuring org-mode wordcount" + :group 'org) + +(defcustom org-wc-ignored-tags '("nowc" "noexport" "ARCHIVE") + "List of tags for which subtrees will be ignored in word counts" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p) + +(defcustom org-wc-ignore-commented-trees t + "Ignore trees with COMMENT-prefix if non-nil." + :type 'boolean + :safe #'booleanp) + +(defcustom org-wc-default-link-count 'description-or-path + "Default way of counting words in links. +This is applied to any link type not specified in any of +‘org-wc-ignored-link-types’,‘org-wc-one-word-link-types’, or +‘org-wc-only-description-link-types’ " + :type '(choice + (const :tag "Count words in description or else path part of links" description-or-path) + (const :tag "Count words only in description part of links" description) + (const :tag "Count links as 0 words" ignore) + (const :tag "Count links as 1 word" oneword) + (const :tag "Count words only in path part of links" path)) + :safe 'symbolp) + +(defcustom org-wc-ignored-link-types nil + "Link types which won't be counted as a word" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p) + +(defcustom org-wc-one-word-link-types '("zotero") + "Link types which will be counted as one word" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p) + +(defcustom org-wc-description-or-path-link-types '() + "Link types for which the description or the path should be counted" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p) + +(defcustom org-wc-only-description-link-types '("note") + "Link types for which only the description should be counted" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p) + +(defcustom org-wc-only-path-link-types '() + "Link types for which only the path should be counted" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p) + +(defcustom org-wc-blocks-to-count '("quote" "verse") + "List of blocks which should be included in word count. + +Use lower case block names" + :type '(repeat string) + :safe #'org-wc-list-of-strings-p + :set #'org-wc--downcase-list-of-strings-set-default) + +(defun org-wc-list-of-strings-p (arg) + (cl-every #'stringp arg)) + +;;;###autoload +(defun org-word-count (beg end) + "Report the number of words in the Org mode buffer or selected region. + +Ignores heading lines, blocks, comments, drawers, and links +depending on customizable variables in customization group org-wc. + +LaTeX macros are counted as 1 word. " + (interactive + (if (use-region-p) + (list (region-beginning) (region-end)) + (list (point-min) (point-max)))) + (message (format "%d words in %s." + (org-word-count-aux beg end) + (if (use-region-p) "region" "buffer")))) + +(defun org-word-count-aux (beg end) + "Return the number of words between BEG and END." + (let ((wc 0) + subtreecount + (latex-macro-regexp "\\\\[A-Za-z]+\\(\\[[^]]*\\]\\|\\){\\([^}]*\\)}") + (drawer-end-regexp "^[ \t]*:END:[ \t]*$")) + (save-excursion + (goto-char beg) + (while (< (point) end) + (cond + ;; Handle headlines and subtrees + ((org-at-heading-p) + (cond + ;; Ignore commented and org-wc-ignored-tags trees + ((or (and org-wc-ignore-commented-trees (org-in-commented-heading-p)) + (cl-intersection org-wc-ignored-tags (org-get-tags) :test #'string=)) + (org-end-of-subtree t t)) + ;; Re-use count for subtrees already counted + ((setq subtreecount (get-text-property (point) :org-wc)) + (cl-incf wc subtreecount) + (org-end-of-subtree t t)) + ;; Skip counting words in headline + (t (org-wc--goto-char (point-at-eol) end)))) + ;; Ignore most blocks. + ((when (save-excursion + (beginning-of-line 1) + (looking-at org-block-regexp)) + (if (member (downcase (match-string 1)) org-wc-blocks-to-count) + (progn ;; go inside block and subtract count of end line + (org-wc--goto-char (match-beginning 4) end) + (cl-decf wc)) + (org-wc--goto-char (match-end 0) end)))) + ;; Ignore comments. + ((org-at-comment-p) + (org-wc--goto-char (point-at-eol) end)) + ;; Ignore drawers. + ((org-at-drawer-p) + (beginning-of-line 1) + (cond + ((looking-at drawer-end-regexp) ; If BEG was inside drawer, + (setq wc 0) ; discard words + (forward-line)) ; and exit drawer, + ((re-search-forward drawer-end-regexp + end t) ; else find end of drawer + (forward-line)) ; and exit drawer, or signal error + (t (user-error "Unclosed drawer at line %d" + (line-number-at-pos nil t))))) + ;; Ignore all other #+ lines + ((looking-at "#+") + (org-wc--goto-char (point-at-eol) end)) + ;; Handle links + ((save-excursion + (when (< (1+ (point-min)) (point)) (backward-char 2)) + (looking-at org-link-bracket-re)) + (let* ((type (car (save-match-data (split-string (match-string 1) ":")))) + (pathstart (+ 1 (length type) (match-beginning 1)))) + (cl-case (cond ((member type org-wc-ignored-link-types) 'ignore) + ((member type org-wc-one-word-link-types) 'oneword) + ((member type org-wc-only-description-link-types) + 'description) + ((member type org-wc-only-path-link-types) 'path) + ((member type org-wc-description-or-path-link-types) + 'description-or-path) + (t org-wc-default-link-count)) + (ignore (org-wc--goto-char (match-end 0) end)) + (oneword (org-wc--goto-char (match-end 0) end) + (cl-incf wc)) + (description (if (match-beginning 2) + (goto-char (match-beginning 2)) + (org-wc--goto-char + (match-end 0) end))) + (path (cl-incf wc (count-words-region pathstart + (match-end 1))) + (org-wc--goto-char (match-end 0) end)) + (description-or-path + (if (match-beginning 2) + (goto-char (match-beginning 2)) + (cl-incf wc (count-words-region pathstart + (match-end 1))) + (org-wc--goto-char (match-end 0) end))) + (t (user-error "Error in org-wc link configuration"))))) + ;; Count latex macros as 1 word, ignoring their arguments. + ((save-excursion + (when (< (point-min) (point)) (backward-char)) + (looking-at latex-macro-regexp)) + (org-wc--goto-char (match-end 0) end) + (cl-incf wc)) + (t + (and (re-search-forward "\\w+\\W*" end 'skip) + (cl-incf wc)))))) + wc)) + +;;;###autoload +(defun org-wc-count-subtrees () + "Count words in each subtree, putting result as the property :org-wc on that heading." + (interactive) + (remove-text-properties (point-min) (point-max) + '(:org-wc t)) + (save-excursion + (goto-char (point-max)) + (while (outline-previous-heading) + (save-restriction + (org-narrow-to-subtree) + (let ((wc (org-word-count-aux (point-min) (point-max)))) + (put-text-property (point) (point-at-eol) :org-wc wc) + (goto-char (point-min))))))) + +;;;###autoload +(defun org-wc-display (total-only) + "Show subtree word counts in the entire buffer. +With prefix argument, only show the total wordcount for the buffer or region +in the echo area. + +Use \\[org-wc-remove-overlays] to remove the subtree times. + +Ignores: heading lines, + blocks, + comments, + drawers. +LaTeX macros are counted as 1 word." + (interactive "P") + (let ((beg (if (region-active-p) (region-beginning) (point-min))) + (end (if (region-active-p) (region-end) (point-max)))) + (org-wc-remove-overlays) + (unless total-only + (let ((bmp (buffer-modified-p)) + wc + p) + (org-wc-count-subtrees) + (save-excursion + (goto-char (point-min)) + (while (or (and (equal (setq p (point)) (point-min)) + (get-text-property p :org-wc)) + (setq p (next-single-property-change + (point) :org-wc))) + (goto-char p) + (when (setq wc (get-text-property p :org-wc)) + (org-wc-put-overlay wc))) + ;; Arrange to remove the overlays upon next change. + (when org-remove-highlights-with-change + (add-hook 'before-change-functions 'org-wc-remove-overlays + nil 'local))) + (set-buffer-modified-p bmp))) + (org-word-count beg end))) + +(defvar org-wc-overlays nil) +(make-variable-buffer-local 'org-wc-overlays) + +(defface org-wc-overlay + '((t (:weight bold))) + "Face for displaying org-wc overlays.") + +(defun org-wc-put-overlay (wc) + "Put an overlay on the current line, displaying word count. +If LEVEL is given, prefix word count with a corresponding number of stars. +This creates a new overlay and stores it in `org-wc-overlays', so that it +will be easy to remove." + (let* ((c 60) + (off 0) + ov tx) + (org-move-to-column c) + (unless (eolp) (skip-chars-backward "^ \t")) + (skip-chars-backward " \t") + (setq ov (make-overlay (1- (point)) (point-at-eol)) + tx (concat (buffer-substring (1- (point)) (point)) + (make-string (+ off (max 0 (- c (current-column)))) ?.) + (org-add-props (format "%s" (number-to-string wc)) + (list 'face 'org-wc-overlay)) + "")) + (if (not (featurep 'xemacs)) + (overlay-put ov 'display tx) + (overlay-put ov 'invisible t) + (overlay-put ov 'end-glyph (make-glyph tx))) + (push ov org-wc-overlays))) + +;;;###autoload +(defun org-wc-remove-overlays (&optional _beg _end noremove) + "Remove the occur highlights from the buffer. +BEG and END are ignored. If NOREMOVE is nil, remove this function +from the `before-change-functions' in the current buffer." + (interactive) + (unless org-inhibit-highlight-removal + (mapc 'delete-overlay org-wc-overlays) + (setq org-wc-overlays nil) + (unless noremove + (remove-hook 'before-change-functions + 'org-wc-remove-overlays 'local)))) + +(defun org-wc--goto-char (char end) + "Moves point to CHAR and from there passes 0+ non-word characters. +Searchers to end as a maximum. + +This ensures that we are in an expected state (at the first word +character after some non-word characters) after moving beyond +headlines, links etc." + (goto-char char) + (re-search-forward "\\W*" end 'skip)) + + +(provide 'org-wc) +;;; org-wc.el ends here diff --git a/lisp/org/doc/org-manual.org b/lisp/org/doc/org-manual.org index 79cd30e8..5ca1c8cc 100644 --- a/lisp/org/doc/org-manual.org +++ b/lisp/org/doc/org-manual.org @@ -125,7 +125,7 @@ To avoid interference with the built-in Org mode, you can use the command line (you need Emacs 30 or later): #+begin_src sh -emacs -Q -batch -eval "(progn (require 'package) (package-initialize) (package-refresh-contents) (package-upgrade 'org))" +emacs -Q -batch -eval "(progn (package-refresh-contents) (package-upgrade 'org))" #+end_src This approach has the advantage of isolating the upgrade process from @@ -2252,7 +2252,7 @@ instead of letting Calc handle the formatting. A few examples: | =exp($2)+exp($1)= | Math functions can be used | | =$0;%.1f= | Reformat current cell to 1 decimal | | =($3-32)*5/9= | Fahrenheit to Celsius conversion | -| =$c/$1/$cm= | Herz to centimeter conversion, using =constants.el= | +| =$c/$1/$cm= | Hertz to centimeter conversion using =constants.el= | | =tan($1);Dp3s1= | Compute in degrees, precision 3, display SCI 1 | | =sin($1);Dp3%.1e= | Same, but use ~format~ specifier for display | | =vmean($2..$7)= | Compute column range mean, using vector function | diff --git a/lisp/org/doc/org-version.inc b/lisp/org/doc/org-version.inc index 59bc2253..b0c36522 100644 --- a/lisp/org/doc/org-version.inc +++ b/lisp/org/doc/org-version.inc @@ -1,3 +1,3 @@ @c automatically generated, do not edit -@set VERSION 9.8.1 (9.8.1-c1bb5a) -@set DATE 2026-04-05 +@set VERSION 9.8.6 (9.8.6-dcb8f0) +@set DATE 2026-06-27 diff --git a/lisp/org/doc/org.texi b/lisp/org/doc/org.texi index 78a45c9b..bab4768a 100644 --- a/lisp/org/doc/org.texi +++ b/lisp/org/doc/org.texi @@ -653,7 +653,7 @@ To avoid interference with the built-in Org mode@comma{} you can use the command line (you need Emacs 30 or later): @example -emacs -Q -batch -eval "(progn (require 'package) (package-initialize) (package-refresh-contents) (package-upgrade 'org))" +emacs -Q -batch -eval "(progn (package-refresh-contents) (package-upgrade 'org))" @end example This approach has the advantage of isolating the upgrade process from @@ -987,7 +987,7 @@ recorded. @node Conventions @section Typesetting Conventions Used in this Manual -@anchor{TODO keywords@comma{} tags@comma{} properties@comma{} etc} +@anchor{TODO keywords tags properties etc} @subheading TODO keywords@comma{} tags@comma{} properties@comma{} etc. Org uses various syntactic elements: TODO keywords@comma{} tags@comma{} property @@ -2775,7 +2775,7 @@ instead of letting Calc handle the formatting. A few examples: @item @samp{($3-32)*5/9} @tab Fahrenheit to Celsius conversion @item @samp{$c/$1/$cm} -@tab Herz to centimeter conversion@comma{} using @samp{constants.el} +@tab Hertz to centimeter conversion using @samp{constants.el} @item @samp{tan($1);Dp3s1} @tab Compute in degrees@comma{} precision 3@comma{} display SCI 1 @item @samp{sin($1);Dp3%.1e} diff --git a/lisp/org/doc/orgguide.texi b/lisp/org/doc/orgguide.texi index 616f9d5b..5f64d778 100644 --- a/lisp/org/doc/orgguide.texi +++ b/lisp/org/doc/orgguide.texi @@ -65,7 +65,7 @@ modify this GNU manual.'' * Tags:: Tagging headlines and matching sets of tags. * Properties:: Storing information about an entry. * Dates and Times:: Making items useful for planning. -* Capture@comma{} Refile@comma{} Archive:: The ins and outs for projects. +* Capture@comma{} Refile@comma{} Archive: Capture Refile Archive. The ins and outs for projects. * Agenda Views:: Collecting information into views. * Markup:: Compose beautiful documents. * Exporting:: Sharing and publishing notes. @@ -1416,7 +1416,7 @@ of recently clocked tasks. The @kbd{l} key may be used in the agenda (see @ref{Built-in Agenda Views}) to show which tasks have been worked on or closed during a day. -@node Capture@comma{} Refile@comma{} Archive +@node Capture Refile Archive @chapter Capture@comma{} Refile@comma{} Archive An important part of any organization system is the ability to quickly diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index a8ca1ccd..13fa3a2c 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -2976,6 +2976,7 @@ used as a string to be appended to #+begin_example line." (forward-line 0) (insert ": ") (forward-line 1))) (t (goto-char beg) + (unless (bolp) (insert "\n")) (insert (if results-switches (format "%s%s\n" (funcall maybe-cap "#+begin_example") @@ -2984,7 +2985,9 @@ used as a string to be appended to #+begin_example line." (let ((p (point))) (if (markerp end) (goto-char end) (forward-char (- end beg))) (org-escape-code-in-region p (point))) - (insert (funcall maybe-cap "#+end_example\n"))))))))) + (unless (bolp) (insert "\n")) + (insert (funcall maybe-cap "#+end_example")) + (unless (eolp) (insert "\n"))))))))) (defun org-babel-update-block-body (new-body) "Update the body of the current code block to NEW-BODY." diff --git a/lisp/org/ob-ditaa.el b/lisp/org/ob-ditaa.el index 55d375a8..82995d47 100644 --- a/lisp/org/ob-ditaa.el +++ b/lisp/org/ob-ditaa.el @@ -2,7 +2,8 @@ ;; Copyright (C) 2009-2026 Free Software Foundation, Inc. -;; Authors: Eric Schulte, Jarmo Hurri +;; Authors: Eric Schulte +;; Jarmo Hurri ;; Keywords: literate programming, reproducible research ;; URL: https://orgmode.org diff --git a/lisp/org/ob-tangle.el b/lisp/org/ob-tangle.el index 9d4c914c..a21ab3f1 100644 --- a/lisp/org/ob-tangle.el +++ b/lisp/org/ob-tangle.el @@ -42,6 +42,7 @@ (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-begin "org-element" (node)) +(declare-function org-element-end "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" ()) @@ -720,8 +721,12 @@ of the current buffer." (forward-line 1) ;; Try to preserve location of point within the source code in ;; tangled code file. - (let ((offset (- mid body-start))) - (when (> end (+ offset (point))) + (let ((offset (- mid body-start)) + (block-ends-here (org-with-point-at (org-element-end (org-element-at-point)) + (skip-chars-backward " \t\n\r") + (forward-line 0) + (point)))) + (when (> block-ends-here (+ offset (point))) (forward-char offset))) (setq target-char (point))) (org-src-switch-to-buffer target-buffer t) diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index dd86a716..481eba50 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el @@ -7875,7 +7875,7 @@ in the agenda." "Rebuild possibly ALL agenda view(s) in the current buffer." (interactive "P") (defvar org-agenda-tag-filter-while-redo) ;FIXME: Where is this var used? - (let* ((p (or (and (looking-at "\\'") (1- (point))) (point))) + (let* ((p (or (and (/= 1 (point)) (looking-at "\\'") (1- (point))) (point))) (cpa (unless (eq all t) current-prefix-arg)) (org-agenda-doing-sticky-redo org-agenda-sticky) (org-agenda-sticky nil) diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el index ce2d23a9..b803d0fe 100644 --- a/lisp/org/org-clock.el +++ b/lisp/org/org-clock.el @@ -1991,13 +1991,15 @@ Optional argument N tells to change by that many units." (user-error "No active clock")) (save-excursion ; Do not replace this with `with-current-buffer'. (with-no-warnings (set-buffer (org-clocking-buffer))) - (goto-char org-clock-marker) - (if (looking-back (concat "^[ \t]*" org-clock-string ".*") - (line-beginning-position)) - (progn (delete-region (1- (line-beginning-position)) (line-end-position)) - (org-remove-empty-drawer-at (point))) - (message "Clock gone, cancel the timer anyway") - (sit-for 2))) + (save-restriction + (widen) + (goto-char org-clock-marker) + (if (looking-back (concat "^[ \t]*" org-clock-string ".*") + (line-beginning-position)) + (progn (delete-region (1- (line-beginning-position)) (line-end-position)) + (org-remove-empty-drawer-at (point))) + (message "Clock gone, cancel the timer anyway") + (sit-for 2)))) (move-marker org-clock-marker nil) (move-marker org-clock-hd-marker nil) (setq org-clock-current-task nil) diff --git a/lisp/org/org-colview.el b/lisp/org/org-colview.el index 8b97aa2a..6eed2351 100644 --- a/lisp/org/org-colview.el +++ b/lisp/org/org-colview.el @@ -3,6 +3,7 @@ ;; Copyright (C) 2004-2026 Free Software Foundation, Inc. ;; Author: Carsten Dominik +;; Maintainer: Slawomir Grochowski ;; Keywords: outlines, hypermedia, calendar, text ;; URL: https://orgmode.org ;; diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el index 5f64cc1c..32edc154 100644 --- a/lisp/org/org-element.el +++ b/lisp/org/org-element.el @@ -183,7 +183,7 @@ Drawer's name is located in match group 1.") (defconst org-element-dynamic-block-open-re (rx line-start (0+ (any ?\s ?\t)) "#+BEGIN:" (0+ (any ?\s ?\t)) - (group (1+ word)) + (group (1+ (not space))) (opt (1+ (any ?\s ?\t)) (group (1+ nonl)))) @@ -193,7 +193,7 @@ Parameters are in match group 2.") (defconst org-element-dynamic-block-open-re-nogroup (rx line-start (0+ (any ?\s ?\t)) - "#+BEGIN:" (0+ (any ?\s ?\t)) word) + "#+BEGIN:" (0+ (any ?\s ?\t)) (not space)) "Regexp matching the opening line of a dynamic block.") (defconst org-element-headline-re diff --git a/lisp/org/org-list.el b/lisp/org/org-list.el index 687098c1..c13ec193 100644 --- a/lisp/org/org-list.el +++ b/lisp/org/org-list.el @@ -1860,7 +1860,7 @@ Initial position of cursor is restored after the changes." ;; Shift the indentation between END and BEG by DELTA. ;; Start from the line before END. ;; Take care not to shift to or before IND, which is the - ;; containg list item indentation. (otherwise, we are going + ;; containing list item indentation. (otherwise, we are going ;; to break the list structure) (lambda (end beg delta ind) (goto-char end) diff --git a/lisp/org/org-pkg.el b/lisp/org/org-pkg.el index 199ad9d0..578151fa 100644 --- a/lisp/org/org-pkg.el +++ b/lisp/org/org-pkg.el @@ -1,2 +1,2 @@ ;; Generated package description from org.el -*- mode: lisp-data; no-byte-compile: t -*- -(define-package "org" "9.8.1" "Outline-based notes management and organizer" '((emacs "28.2")) :commit "c1bb5ae59101181708fc061666b6f2e402911c7b" :authors '(("Carsten Dominik" . "carsten.dominik@gmail.com")) :maintainer '("Ihor Radchenko" . "yantar92@posteo.net") :keywords '("outlines" "hypermedia" "calendar" "text") :url "https://orgmode.org") +(define-package "org" "9.8.6" "Outline-based notes management and organizer" '((emacs "28.2")) :commit "dcb8f00ec7605327dcd32562c2a51ad278f7af28" :authors '(("Carsten Dominik" . "carsten.dominik@gmail.com")) :maintainer '("Ihor Radchenko" . "yantar92@posteo.net") :keywords '("outlines" "hypermedia" "calendar" "text") :url "https://orgmode.org") diff --git a/lisp/org/org-table.el b/lisp/org/org-table.el index ba33f672..32cea3e4 100644 --- a/lisp/org/org-table.el +++ b/lisp/org/org-table.el @@ -2953,6 +2953,8 @@ known that the table will be realigned a little later anyway." (log-first-time (current-time)) (log-last-time log-first-time) (cnt 0) + (table-beg org-table-current-begin-pos) + (table-end (org-table-end)) beg end eqlcol eqlfield) ;; Insert constants in all formulas. (when eqlist @@ -2989,8 +2991,8 @@ existing formula for column %s" ;; Get the correct line range to process. (if all (progn - (setq end (copy-marker (org-table-end))) - (goto-char (setq beg org-table-current-begin-pos)) + (setq end (copy-marker table-end)) + (goto-char (setq beg table-beg)) (cond ((re-search-forward org-table-calculate-mark-regexp end t) ;; This is a table with marked lines, compute selected @@ -3005,7 +3007,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 + (org-combine-change-calls table-beg table-end (goto-char beg) ;; Mark named fields untouchable. Also check if several ;; field/range formulas try to set the same field. diff --git a/lisp/org/org-timer.el b/lisp/org/org-timer.el index 8c9df9d3..d6d7cfaa 100644 --- a/lisp/org/org-timer.el +++ b/lisp/org/org-timer.el @@ -436,7 +436,7 @@ using three \\[universal-argument] prefix arguments." (and (not (equal opt '(64))) effort-minutes (number-to-string effort-minutes)) - (and (consp opt) default-timer) + (and (consp opt) (not (equal opt '(64))) default-timer) (and (stringp opt) opt) (read-from-minibuffer "How much time left? (minutes or h:mm:ss) " diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el index 9e05bd9b..f913efed 100644 --- a/lisp/org/org-version.el +++ b/lisp/org/org-version.el @@ -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.8.1")) + (let ((org-release "9.8.6")) 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 "9.8.1-c1bb5a")) + (let ((org-git-version "9.8.6-dcb8f0")) org-git-version)) (provide 'org-version) diff --git a/lisp/org/org.el b/lisp/org/org.el index d711687f..be9f31ec 100644 --- a/lisp/org/org.el +++ b/lisp/org/org.el @@ -9,7 +9,7 @@ ;; URL: https://orgmode.org ;; Package-Requires: ((emacs "28.2")) -;; Version: 9.8.1 +;; Version: 9.8.6 ;; This file is part of GNU Emacs. ;; @@ -9551,6 +9551,7 @@ When foo is written as FOO, upcase the #+BEGIN/END as well." (goto-char region-end) ;; Ignore empty lines at the end of the region. (skip-chars-backward " \r\t\n") + (unless (eolp) (insert "\n") (forward-line -1)) (end-of-line)) (unless (bolp) (insert "\n")) (indent-to column) @@ -9877,7 +9878,8 @@ When called through Elisp, arg is also interpreted in the following way: (org-update-parent-todo-statistics)) (when (bound-and-true-p org-clock-out-when-done) (org-clock-out-if-current)) - (run-hooks 'org-after-todo-state-change-hook) + (save-excursion + (run-hooks 'org-after-todo-state-change-hook)) (when (and arg (not (member org-state org-done-keywords))) (setq head (org-get-todo-sequence-head org-state))) (put-text-property (line-beginning-position) @@ -10583,7 +10585,8 @@ enough to shift date past today. Continue? " (org-timestamp-change n (cdr (assoc what whata)) nil t)) (setq msg (concat msg type " " org-last-changed-timestamp " "))))))) - (run-hooks 'org-todo-repeat-hook) + (save-excursion + (run-hooks 'org-todo-repeat-hook)) (setq org-log-post-message msg) (message msg)))) diff --git a/lisp/org/org.info b/lisp/org/org.info index 7c1fa591..567ac674 100644 --- a/lisp/org/org.info +++ b/lisp/org/org.info @@ -633,7 +633,7 @@ with ‘M-x list-packages’. See *note Package Menu: (emacs)Package Menu. To avoid interference with the built-in Org mode, you can use the command line (you need Emacs 30 or later): - emacs -Q -batch -eval "(progn (require 'package) (package-initialize) (package-refresh-contents) (package-upgrade 'org))" + emacs -Q -batch -eval "(progn (package-refresh-contents) (package-upgrade 'org))" This approach has the advantage of isolating the upgrade process from a running Emacs session, ensuring that version conflicts cannot arise. @@ -2327,7 +2327,7 @@ letting Calc handle the formatting. A few examples: ‘exp($2)+exp($1)’ Math functions can be used ‘$0;%.1f’ Reformat current cell to 1 decimal ‘($3-32)*5/9’ Fahrenheit to Celsius conversion -‘$c/$1/$cm’ Herz to centimeter conversion, using ‘constants.el’ +‘$c/$1/$cm’ Hertz to centimeter conversion using ‘constants.el’ ‘tan($1);Dp3s1’ Compute in degrees, precision 3, display SCI 1 ‘sin($1);Dp3%.1e’ Same, but use ‘format’ specifier for display ‘vmean($2..$7)’ Compute column range mean, using vector function @@ -24850,602 +24850,602 @@ Node: Introduction23267 Node: Summary23729 Node: Installation26884 Ref: Using Emacs packaging system27807 -Ref: Using Org's Git repository28701 -Ref: Installing Org's contributed packages29747 -Node: Activation30372 -Ref: Activation-Footnote-132044 -Node: Feedback32176 -Ref: How to create a useful backtrace35412 -Ref: How to profile Org performance36526 -Ref: Feedback-Footnote-138718 -Node: Conventions38844 -Ref: TODO keywords, tags, properties, etc39015 -Ref: Key bindings and commands39910 -Node: Document Structure40539 -Node: Headlines41705 -Ref: Headlines-Footnote-143021 -Node: Visibility Cycling43300 -Node: Global and local cycling43690 -Ref: Global and local cycling-Footnote-146415 -Ref: Global and local cycling-Footnote-246477 -Node: Initial visibility46781 -Ref: Initial visibility-Footnote-147971 -Node: Catching invisible edits48164 -Node: Motion48910 -Node: Structure Editing50375 -Node: Sparse Trees56920 -Ref: Sparse Trees-Footnote-159516 -Ref: Sparse Trees-Footnote-259636 -Node: Plain Lists59712 -Ref: Plain Lists-Footnote-167324 -Ref: Plain Lists-Footnote-267688 -Ref: Plain Lists-Footnote-367788 -Ref: Plain Lists-Footnote-468045 -Ref: Plain Lists-Footnote-568220 -Ref: Plain Lists-Footnote-668324 -Ref: Plain Lists-Footnote-768430 -Node: Drawers68500 -Ref: Drawers-Footnote-170189 -Node: Blocks70301 -Node: Tables70895 -Node: Built-in Table Editor71578 -Ref: Creation and conversion73312 -Ref: Re-aligning and field motion74250 -Ref: Column and row editing75183 -Ref: Regions77614 -Ref: Calculations79009 -Ref: Miscellaneous (1)79852 -Ref: Built-in Table Editor-Footnote-182329 -Node: Column Width and Alignment82437 -Node: Column Groups85775 -Node: Orgtbl Mode87343 -Node: The Spreadsheet88158 -Node: References89630 -Ref: Field references90089 -Ref: Range references92523 -Ref: Field coordinates in formulas93782 -Ref: Named references94769 -Ref: Remote references95736 -Ref: References-Footnote-196657 -Ref: References-Footnote-296885 -Ref: References-Footnote-396988 -Node: Formula syntax for Calc97313 -Ref: Formula syntax for Calc-Footnote-1103091 -Node: Formula syntax for Lisp103329 -Ref: Formula syntax for Lisp-Footnote-1105925 -Node: Durations and time values106163 -Node: Field and range formulas107550 -Node: Column formulas110018 -Node: Lookup functions112130 -Node: Editing and debugging formulas114098 -Ref: Using multiple TBLFM lines118614 -Ref: Debugging formulas119457 -Node: Updating the table119874 -Node: Advanced features121246 -Ref: Advanced features-Footnote-1125697 -Node: Org Plot125805 -Ref: Graphical plots using Gnuplot126006 -Ref: Plot options128527 -Ref: ASCII bar plots131432 -Node: Hyperlinks132680 -Node: Link Format133544 -Ref: Link Format-Footnote-1135701 -Ref: Link Format-Footnote-2135933 -Node: Internal Links136064 -Ref: Internal Links-Footnote-1138623 -Ref: Internal Links-Footnote-2138863 -Node: Radio Targets139004 -Node: External Links139721 -Ref: External Links-Footnote-1145723 -Ref: External Links-Footnote-2145816 -Node: Handling Links146240 -Ref: Handling Links-Footnote-1154771 -Ref: Handling Links-Footnote-2154967 -Ref: Handling Links-Footnote-3155101 -Ref: Handling Links-Footnote-4155397 -Ref: Handling Links-Footnote-5155659 -Ref: Handling Links-Footnote-6155781 -Node: Using Links Outside Org155856 -Node: Link Abbreviations156331 -Node: Search Options159295 -Ref: Search Options-Footnote-1161441 -Node: Custom Searches161522 -Node: TODO Items162556 -Ref: TODO Items-Footnote-1163681 -Node: TODO Basics163795 -Node: TODO Extensions166424 -Node: Workflow states167477 -Ref: Workflow states-Footnote-1168875 -Node: TODO types168991 -Ref: TODO types-Footnote-1170798 -Node: Multiple sets in one file170870 -Node: Fast access to TODO states172819 -Ref: Fast access to TODO states-Footnote-1173699 -Node: Per-file keywords173806 -Ref: Per-file keywords-Footnote-1175274 -Node: Faces for TODO keywords175478 -Node: TODO dependencies176551 -Node: Progress Logging179040 -Node: Closing items180175 -Ref: Closing items-Footnote-1181176 -Ref: Closing items-Footnote-2181250 -Node: Tracking TODO state changes181328 -Ref: Tracking TODO state changes-Footnote-1184466 -Ref: Tracking TODO state changes-Footnote-2184528 -Ref: Tracking TODO state changes-Footnote-3184685 -Node: Tracking your habits184963 -Node: Priorities189478 -Ref: Priorities-Footnote-1192133 -Node: Breaking Down Tasks192206 -Ref: Breaking Down Tasks-Footnote-1194236 -Node: Checkboxes194343 -Ref: Checkboxes-Footnote-1200061 -Ref: Checkboxes-Footnote-2200178 -Ref: Checkboxes-Footnote-3200358 -Node: Tags200472 -Node: Tag Inheritance201583 -Ref: Tag Inheritance-Footnote-1203104 -Node: Setting Tags203208 -Ref: Setting Tags-Footnote-1209979 -Ref: Setting Tags-Footnote-2210157 -Node: Tag Hierarchy210235 -Node: Tag Searches213833 -Node: Properties and Columns215056 -Node: Property Syntax216445 -Node: Special Properties221268 -Ref: Special Properties-Footnote-1223017 -Node: Property Searches223313 -Node: Property Inheritance224699 -Node: Column View226571 -Node: Defining columns227810 -Node: Scope of column definitions228195 -Node: Column attributes229254 -Ref: Summaries in deeply nested hierarchy234213 -Ref: Column attributes-Footnote-1235407 -Ref: Column attributes-Footnote-2235805 -Ref: Column attributes-Footnote-3236003 -Node: Using column view236142 -Ref: Turning column view on or off236308 -Ref: Editing values237349 -Ref: Modifying column view on-the-fly238753 -Node: Capturing column view239448 -Ref: Capturing column view-Footnote-1243423 -Node: Dates and Times243559 -Node: Timestamps244728 -Ref: Timestamps-Footnote-1247692 -Ref: Timestamps-Footnote-2247988 -Node: Creating Timestamps248776 -Node: The date/time prompt251790 -Ref: The date/time prompt-Footnote-1256741 -Ref: The date/time prompt-Footnote-2256912 -Ref: The date/time prompt-Footnote-3257023 -Ref: The date/time prompt-Footnote-4257274 -Node: Custom time format257372 -Node: Deadlines and Scheduling259140 -Ref: Deadlines and Scheduling-Footnote-1262708 -Node: Inserting deadline/schedule262871 -Ref: Inserting deadline/schedule-Footnote-1264862 -Ref: Inserting deadline/schedule-Footnote-2265023 -Ref: Inserting deadline/schedule-Footnote-3265149 -Node: Repeated tasks265275 -Ref: Repeated tasks-Footnote-1270003 -Ref: Repeated tasks-Footnote-2270086 -Ref: Repeated tasks-Footnote-3270369 -Node: Clocking Work Time270591 -Ref: Clocking Work Time-Footnote-1271794 -Ref: Clocking Work Time-Footnote-2271947 -Node: Clocking commands272089 -Ref: Clocking commands-Footnote-1277328 -Ref: Clocking commands-Footnote-2277439 -Ref: Clocking commands-Footnote-3277521 -Ref: Clocking commands-Footnote-4277584 -Node: The clock table277667 -Ref: The clock table-Footnote-1285780 -Ref: The clock table-Footnote-2285889 -Ref: The clock table-Footnote-3285987 -Node: Resolving idle time286112 -Ref: Resolving idle time (1)286308 -Ref: Continuous clocking289488 -Ref: Clocking out automatically after some idle time289994 -Ref: Resolving idle time-Footnote-1290625 -Node: Effort Estimates291072 -Ref: Effort Estimates-Footnote-1294027 -Node: Timers294138 -Node: Refiling and Archiving296351 -Node: Refile and Copy296914 -Ref: Refile and Copy-Footnote-1299744 -Node: Archiving299858 -Node: Moving subtrees300571 -Node: Internal archiving302705 -Node: Capture and Attachments305438 -Node: Capture306243 -Node: Setting up capture306763 -Node: Using capture307126 -Node: Capture templates309464 -Node: Template elements311505 -Ref: Template elements-Footnote-1320832 -Ref: Template elements-Footnote-2321165 -Ref: Template elements-Footnote-3321510 -Node: Template expansion321605 -Ref: Template expansion-Footnote-1326083 -Ref: Template expansion-Footnote-2326174 -Ref: Template expansion-Footnote-3326364 -Node: Templates in contexts326463 -Node: Attachments327302 -Node: Attachment defaults and dispatcher328346 -Ref: Attachment defaults and dispatcher-Footnote-1331688 -Node: Attachment options331839 -Node: Attachment links335845 -Node: Automatic version-control with Git336492 -Node: Attach from Dired337001 -Node: RSS Feeds338365 -Node: Agenda Views339816 -Node: Agenda Files342223 -Ref: Agenda Files-Footnote-1345132 -Ref: Agenda Files-Footnote-2345276 -Node: Agenda Dispatcher345474 -Ref: Agenda Dispatcher-Footnote-1348350 -Node: Built-in Agenda Views348448 -Node: Weekly/daily agenda349045 -Ref: Calendar/Diary integration350500 -Ref: Anniversaries from BBDB352574 -Ref: Appointment reminders354363 -Ref: Weekly/daily agenda-Footnote-1354913 -Ref: Weekly/daily agenda-Footnote-2355157 -Node: Global TODO list355377 -Node: Matching tags and properties358228 -Ref: Matching tags and properties-Footnote-1366711 -Node: Search view366816 -Node: Stuck projects368463 -Node: Presentation and Sorting370590 -Node: Categories371567 -Node: Time-of-day specifications372313 -Ref: Time-of-day specifications-Footnote-1374302 -Node: Sorting of agenda items374425 -Node: Filtering/limiting agenda items376162 -Ref: Filtering in the agenda376867 -Ref: Computed tag filtering381001 -Ref: Setting limits for the agenda382493 -Ref: Filtering/limiting agenda items-Footnote-1384042 -Node: Agenda Commands384593 -Ref: Motion (1)385322 -Ref: View/Go to Org file385525 -Ref: Change display387031 -Ref: Remote editing394797 -Ref: Bulk remote editing selected entries400155 -Ref: Calendar commands403243 -Ref: Quit and exit405123 -Ref: Agenda Commands-Footnote-1405485 -Ref: Agenda Commands-Footnote-2405561 -Ref: Agenda Commands-Footnote-3405665 -Node: Custom Agenda Views405752 -Node: Storing searches406404 -Ref: Storing searches-Footnote-1409355 -Ref: Storing searches-Footnote-2409472 -Node: Block agenda409718 -Node: Setting options411047 -Node: Exporting Agenda Views414659 -Ref: Exporting Agenda Views-Footnote-1419197 -Ref: Exporting Agenda Views-Footnote-2419414 -Ref: Exporting Agenda Views-Footnote-3419564 -Ref: Exporting Agenda Views-Footnote-4419751 -Node: Agenda Column View419833 -Node: Markup for Rich Contents423174 -Node: Paragraphs424481 -Node: Emphasis and Monospace425616 -Ref: Emphasis and Monospace-Footnote-1427197 -Node: Subscripts and Superscripts427283 -Ref: Subscripts and Superscripts-Footnote-1429037 -Node: Special Symbols429154 -Ref: Special Symbols-Footnote-1431064 -Ref: Special Symbols-Footnote-2431230 -Node: Embedded LaTeX431327 -Ref: Embedded LaTeX-Footnote-1432175 -Node: LaTeX fragments432365 -Ref: LaTeX fragments-Footnote-1434821 -Node: Previewing LaTeX fragments435014 -Ref: Previewing LaTeX fragments-Footnote-1436496 -Node: CDLaTeX mode436745 -Ref: CDLaTeX mode-Footnote-1439483 -Node: Literal Examples439630 -Ref: Literal Examples-Footnote-1444752 -Ref: Literal Examples-Footnote-2445208 -Ref: Literal Examples-Footnote-3445386 -Ref: Literal Examples-Footnote-4445540 -Ref: Literal Examples-Footnote-5445768 -Ref: Literal Examples-Footnote-6445953 -Node: Images and link previews446051 -Ref: Images and link previews-Footnote-1448776 -Node: Images448943 -Ref: Images-Footnote-1452447 -Ref: Images-Footnote-2452570 -Node: Captions452656 -Node: Horizontal Rules453372 -Node: Creating Footnotes453628 -Ref: Creating Footnotes-Footnote-1456755 -Ref: Creating Footnotes-Footnote-2456861 -Node: Exporting456968 -Node: The Export Dispatcher459736 -Node: Export Settings463183 -Ref: Export Settings-Footnote-1471959 -Ref: Export Settings-Footnote-2472071 -Ref: Export Settings-Footnote-3472171 -Node: Table of Contents472367 -Ref: Table of Contents-Footnote-1475026 -Node: Include Files475347 -Ref: Include Files-Footnote-1478437 -Node: Macro Replacement478615 -Ref: Macro Replacement-Footnote-1482495 -Ref: Macro Replacement-Footnote-2482607 -Node: Comment Lines482824 -Ref: Comment Lines-Footnote-1483690 -Ref: Comment Lines-Footnote-2483825 -Node: ASCII/Latin-1/UTF-8 export483941 -Ref: ASCII export commands484795 -Ref: ASCII specific export settings485332 -Ref: Header and sectioning structure485775 -Ref: Quoting ASCII text486049 -Ref: ASCII specific attributes486419 -Ref: ASCII special blocks486695 -Node: Beamer Export487041 -Node: Beamer export commands487981 -Node: Beamer specific export settings488686 -Node: Frames and Blocks in Beamer490588 -Ref: Frames and Blocks in Beamer-Footnote-1494124 -Node: Beamer specific syntax494283 -Node: Editing support496256 -Node: A Beamer example496727 -Node: HTML Export498004 -Node: HTML export commands499192 -Node: HTML specific export settings499734 -Node: HTML doctypes501944 -Node: HTML preamble and postamble504114 -Node: Bare HTML505167 -Node: Quoting HTML tags505755 -Node: Headlines in HTML export506436 -Node: Links in HTML export507239 -Node: Tables in HTML export508696 -Node: Images in HTML export510397 -Node: Math formatting in HTML export511951 -Ref: Math formatting in HTML export-Footnote-1513444 -Ref: Math formatting in HTML export-Footnote-2513636 -Node: Text areas in HTML export513919 -Node: CSS support515060 -Ref: CSS support-Footnote-1518690 -Node: JavaScript support518870 -Node: LaTeX Export522049 -Ref: LaTeX Export-Footnote-1524332 -Node: LaTeX/PDF export commands524480 -Ref: LaTeX/PDF export commands-Footnote-1525978 -Node: LaTeX specific export settings526176 -Node: LaTeX header and sectioning529871 -Node: Quoting LaTeX code534377 -Node: Tables in LaTeX export535261 -Node: Images in LaTeX export540513 -Node: Plain lists in LaTeX export543162 -Node: Source blocks in LaTeX export544181 -Ref: Source blocks in LaTeX export-Footnote-1546307 -Node: Example blocks in LaTeX export546465 -Node: Special blocks in LaTeX export547190 -Node: Horizontal rules in LaTeX export548449 -Node: Verse blocks in LaTeX export548885 -Node: Quote blocks in LaTeX export551608 -Node: Controlling the way the table of contents is generated552789 -Node: Markdown Export553862 -Ref: Markdown export commands554591 -Ref: Header and sectioning structure (1)555028 -Node: OpenDocument Text Export555572 -Ref: OpenDocument Text Export-Footnote-1556677 -Node: Pre-requisites for ODT export556827 -Node: ODT export commands557204 -Node: ODT specific export settings558384 -Node: Extending ODT export559429 -Ref: Automatically exporting to other formats560237 -Ref: Converting between document formats560662 -Node: Applying custom styles561202 -Ref: Applying custom styles the easy way561733 -Ref: Using third-party styles and templates562687 -Node: Links in ODT export562983 -Node: Tables in ODT export563645 -Node: Images in ODT export565651 -Ref: Embedding images565855 -Ref: Embedding clickable images566171 -Ref: Sizing and scaling of embedded images566505 -Ref: Anchoring of images568183 -Node: Math formatting in ODT export568504 -Node: LaTeX math snippets568933 -Ref: LaTeX math snippets-Footnote-1571182 -Ref: LaTeX math snippets-Footnote-2571241 -Node: MathML and OpenDocument formula files571287 -Node: Labels and captions in ODT export571795 -Node: Literal examples in ODT export573063 -Node: Advanced topics in ODT export573902 -Ref: Configuring a document converter574211 -Ref: Working with OpenDocument style files575149 -Ref: x-orgodtstyles-xml575615 -Ref: x-orgodtcontenttemplate-xml575957 -Ref: x-overriding-factory-styles576601 -Ref: Creating one-off styles577845 -Ref: Customizing tables in ODT export579846 -Ref: Validating OpenDocument XML584706 -Ref: Advanced topics in ODT export-Footnote-1585500 -Ref: Advanced topics in ODT export-Footnote-2585605 -Ref: Advanced topics in ODT export-Footnote-3585698 -Node: Org Export586046 -Ref: Org export commands586400 -Node: Texinfo Export586706 -Node: Texinfo export commands587746 -Node: Texinfo specific export settings588371 -Node: Texinfo file header590079 -Node: Texinfo title and copyright page591059 -Node: Info directory file592422 -Node: Headings and sectioning structure593136 -Node: Indices595157 -Node: Quoting Texinfo code596183 -Node: Plain lists in Texinfo export596681 -Node: Tables in Texinfo export601409 -Node: Images in Texinfo export601904 -Node: Quotations in Texinfo export602541 -Node: Key bindings in Texinfo export603493 -Node: Special blocks in Texinfo export604294 -Node: A Texinfo example605127 -Node: iCalendar Export607218 -Ref: iCalendar Export-Footnote-1612677 -Node: Other Built-in Backends612860 -Node: Advanced Export Configuration613486 -Ref: Export hooks613685 -Ref: Filters614905 -Ref: Defining filters for individual files617405 -Ref: Summary of the export process618210 -Ref: Extending an existing backend623611 -Ref: Advanced Export Configuration-Footnote-1625946 -Ref: Advanced Export Configuration-Footnote-2626302 -Ref: Advanced Export Configuration-Footnote-3626451 -Node: Export Region626537 -Node: Publishing627595 -Node: Configuration628469 -Node: Project alist629251 -Node: Sources and destinations630389 -Node: Selecting files631708 -Node: Publishing action632661 -Ref: Publishing action-Footnote-1634512 -Node: Publishing options634675 -Ref: Generic properties635427 -Ref: ASCII specific properties638115 -Ref: Beamer specific properties639715 -Ref: HTML specific properties640347 -Ref: LaTeX specific properties645583 -Ref: Markdown specific properties649161 -Ref: ODT specific properties649537 -Ref: Texinfo specific properties650609 -Node: Publishing links652037 -Node: Site map653487 -Node: Generating an index656313 -Node: Uploading Files657105 -Node: Sample Configuration658886 -Node: Simple example659384 -Node: Complex example660100 -Node: Triggering Publication662188 -Node: Citation handling663214 -Node: Citations666257 -Node: Citation export processors668619 -Node: Bibliography printing672095 -Node: Bibliography options in the biblatex and csl export processors673243 -Node: Working with Source Code675360 -Node: Features Overview677665 -Node: Structure of Code Blocks680410 -Node: Using Header Arguments683353 -Ref: System-wide header arguments684078 -Ref: Header arguments in Org mode properties685749 -Ref: Code block specific header arguments687573 -Ref: Header arguments in function calls689072 -Node: Environment of a Code Block689746 -Ref: Passing arguments689960 -Ref: Using sessions698499 -Ref: Choosing a working directory699898 -Ref: Inserting headers and footers702198 -Node: Evaluating Code Blocks702707 -Ref: How to evaluate source code703153 -Ref: Limit code block evaluation706099 -Ref: Cache results of evaluation707085 -Ref: Evaluating Code Blocks-Footnote-1709596 -Ref: Evaluating Code Blocks-Footnote-2709730 -Node: Results of Evaluation709888 -Ref: Collection710695 -Ref: Type712154 -Ref: Format716176 -Ref: Handling718815 -Ref: Post-processing720206 -Ref: Results of Evaluation-Footnote-1721956 -Node: Exporting Code Blocks722114 -Node: Extracting Source Code726136 -Ref: Header arguments727117 -Ref: Functions731810 -Ref: Tangle hooks732061 -Ref: Jumping between code and Org732837 -Node: Languages733346 -Node: Editing Source Code734347 -Node: Noweb Reference Syntax737323 -Ref: Noweb Reference Syntax-Footnote-1744050 -Ref: Noweb Reference Syntax-Footnote-2744141 -Ref: Noweb Reference Syntax-Footnote-3744754 -Node: Library of Babel744874 -Node: Key bindings and Useful Functions745588 -Node: Batch Execution748013 -Node: Miscellaneous748794 -Node: Completion750318 -Node: Structure Templates752241 -Ref: Structure Templates-Footnote-1753991 -Node: Speed Keys754083 -Node: Clean View755250 -Node: Org Indent Mode756460 -Ref: Org Indent Mode-Footnote-1757666 -Node: Hard indentation757890 -Ref: Hard indentation-Footnote-1759371 -Ref: Hard indentation-Footnote-2759477 -Node: Execute commands in the active region759621 -Node: Dynamic Headline Numbering760633 -Node: The Very Busy C-c C-c Key761799 -Node: In-buffer Settings763788 -Ref: In-buffer Settings-Footnote-1773727 -Node: Regular Expressions773925 -Node: Org Syntax774547 -Node: Documentation Access776164 -Node: Escape Character776581 -Node: Code Evaluation Security777441 -Node: Interaction780306 -Node: Cooperation780727 -Node: Conflicts783793 -Node: TTY Keys788376 -Node: Protocols790013 -Node: The store-link protocol791474 -Node: The capture protocol792633 -Node: The open-source protocol794287 -Node: Org Crypt797508 -Node: Org Mobile799381 -Node: Setting up the staging area800766 -Ref: Setting up the staging area-Footnote-1802006 -Ref: Setting up the staging area-Footnote-2802213 -Node: Pushing to the mobile application802385 -Ref: Pushing to the mobile application-Footnote-1803396 -Ref: Pushing to the mobile application-Footnote-2803487 -Ref: Pushing to the mobile application-Footnote-3803854 -Node: Pulling from the mobile application803930 -Ref: Pulling from the mobile application-Footnote-1806243 -Node: Drag and Drop & yank-media806296 -Ref: Drag and Drop & yank-media-Footnote-1808113 -Node: Repeating commands808259 -Node: Hacking810401 -Node: Hooks811424 -Node: Add-on Packages811699 -Node: Adding Hyperlink Types812155 -Node: Adding Hyperlink preview815969 -Node: Adding Export Backends818711 -Node: Tables in Arbitrary Syntax819956 -Node: Radio tables821220 -Node: A LaTeX example823350 -Ref: A LaTeX example-Footnote-1827201 -Ref: A LaTeX example-Footnote-2827244 -Ref: A LaTeX example-Footnote-3827405 -Node: Translator functions827857 -Node: Dynamic Blocks830039 -Node: Special Agenda Views832283 -Ref: Special Agenda Views-Footnote-1835994 -Ref: Special Agenda Views-Footnote-2836200 -Node: Speeding Up Your Agendas836332 -Node: Extracting Agenda Information837555 -Node: Using the Property API841362 -Node: Using the Mapping API844711 -Node: History and Acknowledgments848741 -Ref: From Carsten848939 -Ref: From Bastien852367 -Ref: List of Contributions854890 -Node: GNU Free Documentation License863553 -Ref: ADDENDUM How to use this License for your documents887314 -Node: Main Index888703 -Node: Key Index990623 -Node: Command and Function Index1048053 -Node: Variable Index1097828 +Ref: Using Org's Git repository28661 +Ref: Installing Org's contributed packages29707 +Node: Activation30332 +Ref: Activation-Footnote-132004 +Node: Feedback32136 +Ref: How to create a useful backtrace35372 +Ref: How to profile Org performance36486 +Ref: Feedback-Footnote-138678 +Node: Conventions38804 +Ref: TODO keywords tags properties etc38975 +Ref: Key bindings and commands39870 +Node: Document Structure40499 +Node: Headlines41665 +Ref: Headlines-Footnote-142981 +Node: Visibility Cycling43260 +Node: Global and local cycling43650 +Ref: Global and local cycling-Footnote-146375 +Ref: Global and local cycling-Footnote-246437 +Node: Initial visibility46741 +Ref: Initial visibility-Footnote-147931 +Node: Catching invisible edits48124 +Node: Motion48870 +Node: Structure Editing50335 +Node: Sparse Trees56880 +Ref: Sparse Trees-Footnote-159476 +Ref: Sparse Trees-Footnote-259596 +Node: Plain Lists59672 +Ref: Plain Lists-Footnote-167284 +Ref: Plain Lists-Footnote-267648 +Ref: Plain Lists-Footnote-367748 +Ref: Plain Lists-Footnote-468005 +Ref: Plain Lists-Footnote-568180 +Ref: Plain Lists-Footnote-668284 +Ref: Plain Lists-Footnote-768390 +Node: Drawers68460 +Ref: Drawers-Footnote-170149 +Node: Blocks70261 +Node: Tables70855 +Node: Built-in Table Editor71538 +Ref: Creation and conversion73272 +Ref: Re-aligning and field motion74210 +Ref: Column and row editing75143 +Ref: Regions77574 +Ref: Calculations78969 +Ref: Miscellaneous (1)79812 +Ref: Built-in Table Editor-Footnote-182289 +Node: Column Width and Alignment82397 +Node: Column Groups85735 +Node: Orgtbl Mode87303 +Node: The Spreadsheet88118 +Node: References89590 +Ref: Field references90049 +Ref: Range references92483 +Ref: Field coordinates in formulas93742 +Ref: Named references94729 +Ref: Remote references95696 +Ref: References-Footnote-196617 +Ref: References-Footnote-296845 +Ref: References-Footnote-396948 +Node: Formula syntax for Calc97273 +Ref: Formula syntax for Calc-Footnote-1103051 +Node: Formula syntax for Lisp103289 +Ref: Formula syntax for Lisp-Footnote-1105885 +Node: Durations and time values106123 +Node: Field and range formulas107510 +Node: Column formulas109978 +Node: Lookup functions112090 +Node: Editing and debugging formulas114058 +Ref: Using multiple TBLFM lines118574 +Ref: Debugging formulas119417 +Node: Updating the table119834 +Node: Advanced features121206 +Ref: Advanced features-Footnote-1125657 +Node: Org Plot125765 +Ref: Graphical plots using Gnuplot125966 +Ref: Plot options128487 +Ref: ASCII bar plots131392 +Node: Hyperlinks132640 +Node: Link Format133504 +Ref: Link Format-Footnote-1135661 +Ref: Link Format-Footnote-2135893 +Node: Internal Links136024 +Ref: Internal Links-Footnote-1138583 +Ref: Internal Links-Footnote-2138823 +Node: Radio Targets138964 +Node: External Links139681 +Ref: External Links-Footnote-1145683 +Ref: External Links-Footnote-2145776 +Node: Handling Links146200 +Ref: Handling Links-Footnote-1154731 +Ref: Handling Links-Footnote-2154927 +Ref: Handling Links-Footnote-3155061 +Ref: Handling Links-Footnote-4155357 +Ref: Handling Links-Footnote-5155619 +Ref: Handling Links-Footnote-6155741 +Node: Using Links Outside Org155816 +Node: Link Abbreviations156291 +Node: Search Options159255 +Ref: Search Options-Footnote-1161401 +Node: Custom Searches161482 +Node: TODO Items162516 +Ref: TODO Items-Footnote-1163641 +Node: TODO Basics163755 +Node: TODO Extensions166384 +Node: Workflow states167437 +Ref: Workflow states-Footnote-1168835 +Node: TODO types168951 +Ref: TODO types-Footnote-1170758 +Node: Multiple sets in one file170830 +Node: Fast access to TODO states172779 +Ref: Fast access to TODO states-Footnote-1173659 +Node: Per-file keywords173766 +Ref: Per-file keywords-Footnote-1175234 +Node: Faces for TODO keywords175438 +Node: TODO dependencies176511 +Node: Progress Logging179000 +Node: Closing items180135 +Ref: Closing items-Footnote-1181136 +Ref: Closing items-Footnote-2181210 +Node: Tracking TODO state changes181288 +Ref: Tracking TODO state changes-Footnote-1184426 +Ref: Tracking TODO state changes-Footnote-2184488 +Ref: Tracking TODO state changes-Footnote-3184645 +Node: Tracking your habits184923 +Node: Priorities189438 +Ref: Priorities-Footnote-1192093 +Node: Breaking Down Tasks192166 +Ref: Breaking Down Tasks-Footnote-1194196 +Node: Checkboxes194303 +Ref: Checkboxes-Footnote-1200021 +Ref: Checkboxes-Footnote-2200138 +Ref: Checkboxes-Footnote-3200318 +Node: Tags200432 +Node: Tag Inheritance201543 +Ref: Tag Inheritance-Footnote-1203064 +Node: Setting Tags203168 +Ref: Setting Tags-Footnote-1209939 +Ref: Setting Tags-Footnote-2210117 +Node: Tag Hierarchy210195 +Node: Tag Searches213793 +Node: Properties and Columns215016 +Node: Property Syntax216405 +Node: Special Properties221228 +Ref: Special Properties-Footnote-1222977 +Node: Property Searches223273 +Node: Property Inheritance224659 +Node: Column View226531 +Node: Defining columns227770 +Node: Scope of column definitions228155 +Node: Column attributes229214 +Ref: Summaries in deeply nested hierarchy234173 +Ref: Column attributes-Footnote-1235367 +Ref: Column attributes-Footnote-2235765 +Ref: Column attributes-Footnote-3235963 +Node: Using column view236102 +Ref: Turning column view on or off236268 +Ref: Editing values237309 +Ref: Modifying column view on-the-fly238713 +Node: Capturing column view239408 +Ref: Capturing column view-Footnote-1243383 +Node: Dates and Times243519 +Node: Timestamps244688 +Ref: Timestamps-Footnote-1247652 +Ref: Timestamps-Footnote-2247948 +Node: Creating Timestamps248736 +Node: The date/time prompt251750 +Ref: The date/time prompt-Footnote-1256701 +Ref: The date/time prompt-Footnote-2256872 +Ref: The date/time prompt-Footnote-3256983 +Ref: The date/time prompt-Footnote-4257234 +Node: Custom time format257332 +Node: Deadlines and Scheduling259100 +Ref: Deadlines and Scheduling-Footnote-1262668 +Node: Inserting deadline/schedule262831 +Ref: Inserting deadline/schedule-Footnote-1264822 +Ref: Inserting deadline/schedule-Footnote-2264983 +Ref: Inserting deadline/schedule-Footnote-3265109 +Node: Repeated tasks265235 +Ref: Repeated tasks-Footnote-1269963 +Ref: Repeated tasks-Footnote-2270046 +Ref: Repeated tasks-Footnote-3270329 +Node: Clocking Work Time270551 +Ref: Clocking Work Time-Footnote-1271754 +Ref: Clocking Work Time-Footnote-2271907 +Node: Clocking commands272049 +Ref: Clocking commands-Footnote-1277288 +Ref: Clocking commands-Footnote-2277399 +Ref: Clocking commands-Footnote-3277481 +Ref: Clocking commands-Footnote-4277544 +Node: The clock table277627 +Ref: The clock table-Footnote-1285740 +Ref: The clock table-Footnote-2285849 +Ref: The clock table-Footnote-3285947 +Node: Resolving idle time286072 +Ref: Resolving idle time (1)286268 +Ref: Continuous clocking289448 +Ref: Clocking out automatically after some idle time289954 +Ref: Resolving idle time-Footnote-1290585 +Node: Effort Estimates291032 +Ref: Effort Estimates-Footnote-1293987 +Node: Timers294098 +Node: Refiling and Archiving296311 +Node: Refile and Copy296874 +Ref: Refile and Copy-Footnote-1299704 +Node: Archiving299818 +Node: Moving subtrees300531 +Node: Internal archiving302665 +Node: Capture and Attachments305398 +Node: Capture306203 +Node: Setting up capture306723 +Node: Using capture307086 +Node: Capture templates309424 +Node: Template elements311465 +Ref: Template elements-Footnote-1320792 +Ref: Template elements-Footnote-2321125 +Ref: Template elements-Footnote-3321470 +Node: Template expansion321565 +Ref: Template expansion-Footnote-1326043 +Ref: Template expansion-Footnote-2326134 +Ref: Template expansion-Footnote-3326324 +Node: Templates in contexts326423 +Node: Attachments327262 +Node: Attachment defaults and dispatcher328306 +Ref: Attachment defaults and dispatcher-Footnote-1331648 +Node: Attachment options331799 +Node: Attachment links335805 +Node: Automatic version-control with Git336452 +Node: Attach from Dired336961 +Node: RSS Feeds338325 +Node: Agenda Views339776 +Node: Agenda Files342183 +Ref: Agenda Files-Footnote-1345092 +Ref: Agenda Files-Footnote-2345236 +Node: Agenda Dispatcher345434 +Ref: Agenda Dispatcher-Footnote-1348310 +Node: Built-in Agenda Views348408 +Node: Weekly/daily agenda349005 +Ref: Calendar/Diary integration350460 +Ref: Anniversaries from BBDB352534 +Ref: Appointment reminders354323 +Ref: Weekly/daily agenda-Footnote-1354873 +Ref: Weekly/daily agenda-Footnote-2355117 +Node: Global TODO list355337 +Node: Matching tags and properties358188 +Ref: Matching tags and properties-Footnote-1366671 +Node: Search view366776 +Node: Stuck projects368423 +Node: Presentation and Sorting370550 +Node: Categories371527 +Node: Time-of-day specifications372273 +Ref: Time-of-day specifications-Footnote-1374262 +Node: Sorting of agenda items374385 +Node: Filtering/limiting agenda items376122 +Ref: Filtering in the agenda376827 +Ref: Computed tag filtering380961 +Ref: Setting limits for the agenda382453 +Ref: Filtering/limiting agenda items-Footnote-1384002 +Node: Agenda Commands384553 +Ref: Motion (1)385282 +Ref: View/Go to Org file385485 +Ref: Change display386991 +Ref: Remote editing394757 +Ref: Bulk remote editing selected entries400115 +Ref: Calendar commands403203 +Ref: Quit and exit405083 +Ref: Agenda Commands-Footnote-1405445 +Ref: Agenda Commands-Footnote-2405521 +Ref: Agenda Commands-Footnote-3405625 +Node: Custom Agenda Views405712 +Node: Storing searches406364 +Ref: Storing searches-Footnote-1409315 +Ref: Storing searches-Footnote-2409432 +Node: Block agenda409678 +Node: Setting options411007 +Node: Exporting Agenda Views414619 +Ref: Exporting Agenda Views-Footnote-1419157 +Ref: Exporting Agenda Views-Footnote-2419374 +Ref: Exporting Agenda Views-Footnote-3419524 +Ref: Exporting Agenda Views-Footnote-4419711 +Node: Agenda Column View419793 +Node: Markup for Rich Contents423134 +Node: Paragraphs424441 +Node: Emphasis and Monospace425576 +Ref: Emphasis and Monospace-Footnote-1427157 +Node: Subscripts and Superscripts427243 +Ref: Subscripts and Superscripts-Footnote-1428997 +Node: Special Symbols429114 +Ref: Special Symbols-Footnote-1431024 +Ref: Special Symbols-Footnote-2431190 +Node: Embedded LaTeX431287 +Ref: Embedded LaTeX-Footnote-1432135 +Node: LaTeX fragments432325 +Ref: LaTeX fragments-Footnote-1434781 +Node: Previewing LaTeX fragments434974 +Ref: Previewing LaTeX fragments-Footnote-1436456 +Node: CDLaTeX mode436705 +Ref: CDLaTeX mode-Footnote-1439443 +Node: Literal Examples439590 +Ref: Literal Examples-Footnote-1444712 +Ref: Literal Examples-Footnote-2445168 +Ref: Literal Examples-Footnote-3445346 +Ref: Literal Examples-Footnote-4445500 +Ref: Literal Examples-Footnote-5445728 +Ref: Literal Examples-Footnote-6445913 +Node: Images and link previews446011 +Ref: Images and link previews-Footnote-1448736 +Node: Images448903 +Ref: Images-Footnote-1452407 +Ref: Images-Footnote-2452530 +Node: Captions452616 +Node: Horizontal Rules453332 +Node: Creating Footnotes453588 +Ref: Creating Footnotes-Footnote-1456715 +Ref: Creating Footnotes-Footnote-2456821 +Node: Exporting456928 +Node: The Export Dispatcher459696 +Node: Export Settings463143 +Ref: Export Settings-Footnote-1471919 +Ref: Export Settings-Footnote-2472031 +Ref: Export Settings-Footnote-3472131 +Node: Table of Contents472327 +Ref: Table of Contents-Footnote-1474986 +Node: Include Files475307 +Ref: Include Files-Footnote-1478397 +Node: Macro Replacement478575 +Ref: Macro Replacement-Footnote-1482455 +Ref: Macro Replacement-Footnote-2482567 +Node: Comment Lines482784 +Ref: Comment Lines-Footnote-1483650 +Ref: Comment Lines-Footnote-2483785 +Node: ASCII/Latin-1/UTF-8 export483901 +Ref: ASCII export commands484755 +Ref: ASCII specific export settings485292 +Ref: Header and sectioning structure485735 +Ref: Quoting ASCII text486009 +Ref: ASCII specific attributes486379 +Ref: ASCII special blocks486655 +Node: Beamer Export487001 +Node: Beamer export commands487941 +Node: Beamer specific export settings488646 +Node: Frames and Blocks in Beamer490548 +Ref: Frames and Blocks in Beamer-Footnote-1494084 +Node: Beamer specific syntax494243 +Node: Editing support496216 +Node: A Beamer example496687 +Node: HTML Export497964 +Node: HTML export commands499152 +Node: HTML specific export settings499694 +Node: HTML doctypes501904 +Node: HTML preamble and postamble504074 +Node: Bare HTML505127 +Node: Quoting HTML tags505715 +Node: Headlines in HTML export506396 +Node: Links in HTML export507199 +Node: Tables in HTML export508656 +Node: Images in HTML export510357 +Node: Math formatting in HTML export511911 +Ref: Math formatting in HTML export-Footnote-1513404 +Ref: Math formatting in HTML export-Footnote-2513596 +Node: Text areas in HTML export513879 +Node: CSS support515020 +Ref: CSS support-Footnote-1518650 +Node: JavaScript support518830 +Node: LaTeX Export522009 +Ref: LaTeX Export-Footnote-1524292 +Node: LaTeX/PDF export commands524440 +Ref: LaTeX/PDF export commands-Footnote-1525938 +Node: LaTeX specific export settings526136 +Node: LaTeX header and sectioning529831 +Node: Quoting LaTeX code534337 +Node: Tables in LaTeX export535221 +Node: Images in LaTeX export540473 +Node: Plain lists in LaTeX export543122 +Node: Source blocks in LaTeX export544141 +Ref: Source blocks in LaTeX export-Footnote-1546267 +Node: Example blocks in LaTeX export546425 +Node: Special blocks in LaTeX export547150 +Node: Horizontal rules in LaTeX export548409 +Node: Verse blocks in LaTeX export548845 +Node: Quote blocks in LaTeX export551568 +Node: Controlling the way the table of contents is generated552749 +Node: Markdown Export553822 +Ref: Markdown export commands554551 +Ref: Header and sectioning structure (1)554988 +Node: OpenDocument Text Export555532 +Ref: OpenDocument Text Export-Footnote-1556637 +Node: Pre-requisites for ODT export556787 +Node: ODT export commands557164 +Node: ODT specific export settings558344 +Node: Extending ODT export559389 +Ref: Automatically exporting to other formats560197 +Ref: Converting between document formats560622 +Node: Applying custom styles561162 +Ref: Applying custom styles the easy way561693 +Ref: Using third-party styles and templates562647 +Node: Links in ODT export562943 +Node: Tables in ODT export563605 +Node: Images in ODT export565611 +Ref: Embedding images565815 +Ref: Embedding clickable images566131 +Ref: Sizing and scaling of embedded images566465 +Ref: Anchoring of images568143 +Node: Math formatting in ODT export568464 +Node: LaTeX math snippets568893 +Ref: LaTeX math snippets-Footnote-1571142 +Ref: LaTeX math snippets-Footnote-2571201 +Node: MathML and OpenDocument formula files571247 +Node: Labels and captions in ODT export571755 +Node: Literal examples in ODT export573023 +Node: Advanced topics in ODT export573862 +Ref: Configuring a document converter574171 +Ref: Working with OpenDocument style files575109 +Ref: x-orgodtstyles-xml575575 +Ref: x-orgodtcontenttemplate-xml575917 +Ref: x-overriding-factory-styles576561 +Ref: Creating one-off styles577805 +Ref: Customizing tables in ODT export579806 +Ref: Validating OpenDocument XML584666 +Ref: Advanced topics in ODT export-Footnote-1585460 +Ref: Advanced topics in ODT export-Footnote-2585565 +Ref: Advanced topics in ODT export-Footnote-3585658 +Node: Org Export586006 +Ref: Org export commands586360 +Node: Texinfo Export586666 +Node: Texinfo export commands587706 +Node: Texinfo specific export settings588331 +Node: Texinfo file header590039 +Node: Texinfo title and copyright page591019 +Node: Info directory file592382 +Node: Headings and sectioning structure593096 +Node: Indices595117 +Node: Quoting Texinfo code596143 +Node: Plain lists in Texinfo export596641 +Node: Tables in Texinfo export601369 +Node: Images in Texinfo export601864 +Node: Quotations in Texinfo export602501 +Node: Key bindings in Texinfo export603453 +Node: Special blocks in Texinfo export604254 +Node: A Texinfo example605087 +Node: iCalendar Export607178 +Ref: iCalendar Export-Footnote-1612637 +Node: Other Built-in Backends612820 +Node: Advanced Export Configuration613446 +Ref: Export hooks613645 +Ref: Filters614865 +Ref: Defining filters for individual files617365 +Ref: Summary of the export process618170 +Ref: Extending an existing backend623571 +Ref: Advanced Export Configuration-Footnote-1625906 +Ref: Advanced Export Configuration-Footnote-2626262 +Ref: Advanced Export Configuration-Footnote-3626411 +Node: Export Region626497 +Node: Publishing627555 +Node: Configuration628429 +Node: Project alist629211 +Node: Sources and destinations630349 +Node: Selecting files631668 +Node: Publishing action632621 +Ref: Publishing action-Footnote-1634472 +Node: Publishing options634635 +Ref: Generic properties635387 +Ref: ASCII specific properties638075 +Ref: Beamer specific properties639675 +Ref: HTML specific properties640307 +Ref: LaTeX specific properties645543 +Ref: Markdown specific properties649121 +Ref: ODT specific properties649497 +Ref: Texinfo specific properties650569 +Node: Publishing links651997 +Node: Site map653447 +Node: Generating an index656273 +Node: Uploading Files657065 +Node: Sample Configuration658846 +Node: Simple example659344 +Node: Complex example660060 +Node: Triggering Publication662148 +Node: Citation handling663174 +Node: Citations666217 +Node: Citation export processors668579 +Node: Bibliography printing672055 +Node: Bibliography options in the biblatex and csl export processors673203 +Node: Working with Source Code675320 +Node: Features Overview677625 +Node: Structure of Code Blocks680370 +Node: Using Header Arguments683313 +Ref: System-wide header arguments684038 +Ref: Header arguments in Org mode properties685709 +Ref: Code block specific header arguments687533 +Ref: Header arguments in function calls689032 +Node: Environment of a Code Block689706 +Ref: Passing arguments689920 +Ref: Using sessions698459 +Ref: Choosing a working directory699858 +Ref: Inserting headers and footers702158 +Node: Evaluating Code Blocks702667 +Ref: How to evaluate source code703113 +Ref: Limit code block evaluation706059 +Ref: Cache results of evaluation707045 +Ref: Evaluating Code Blocks-Footnote-1709556 +Ref: Evaluating Code Blocks-Footnote-2709690 +Node: Results of Evaluation709848 +Ref: Collection710655 +Ref: Type712114 +Ref: Format716136 +Ref: Handling718775 +Ref: Post-processing720166 +Ref: Results of Evaluation-Footnote-1721916 +Node: Exporting Code Blocks722074 +Node: Extracting Source Code726096 +Ref: Header arguments727077 +Ref: Functions731770 +Ref: Tangle hooks732021 +Ref: Jumping between code and Org732797 +Node: Languages733306 +Node: Editing Source Code734307 +Node: Noweb Reference Syntax737283 +Ref: Noweb Reference Syntax-Footnote-1744010 +Ref: Noweb Reference Syntax-Footnote-2744101 +Ref: Noweb Reference Syntax-Footnote-3744714 +Node: Library of Babel744834 +Node: Key bindings and Useful Functions745548 +Node: Batch Execution747973 +Node: Miscellaneous748754 +Node: Completion750278 +Node: Structure Templates752201 +Ref: Structure Templates-Footnote-1753951 +Node: Speed Keys754043 +Node: Clean View755210 +Node: Org Indent Mode756420 +Ref: Org Indent Mode-Footnote-1757626 +Node: Hard indentation757850 +Ref: Hard indentation-Footnote-1759331 +Ref: Hard indentation-Footnote-2759437 +Node: Execute commands in the active region759581 +Node: Dynamic Headline Numbering760593 +Node: The Very Busy C-c C-c Key761759 +Node: In-buffer Settings763748 +Ref: In-buffer Settings-Footnote-1773687 +Node: Regular Expressions773885 +Node: Org Syntax774507 +Node: Documentation Access776124 +Node: Escape Character776541 +Node: Code Evaluation Security777401 +Node: Interaction780266 +Node: Cooperation780687 +Node: Conflicts783753 +Node: TTY Keys788336 +Node: Protocols789973 +Node: The store-link protocol791434 +Node: The capture protocol792593 +Node: The open-source protocol794247 +Node: Org Crypt797468 +Node: Org Mobile799341 +Node: Setting up the staging area800726 +Ref: Setting up the staging area-Footnote-1801966 +Ref: Setting up the staging area-Footnote-2802173 +Node: Pushing to the mobile application802345 +Ref: Pushing to the mobile application-Footnote-1803356 +Ref: Pushing to the mobile application-Footnote-2803447 +Ref: Pushing to the mobile application-Footnote-3803814 +Node: Pulling from the mobile application803890 +Ref: Pulling from the mobile application-Footnote-1806203 +Node: Drag and Drop & yank-media806256 +Ref: Drag and Drop & yank-media-Footnote-1808073 +Node: Repeating commands808219 +Node: Hacking810361 +Node: Hooks811384 +Node: Add-on Packages811659 +Node: Adding Hyperlink Types812115 +Node: Adding Hyperlink preview815929 +Node: Adding Export Backends818671 +Node: Tables in Arbitrary Syntax819916 +Node: Radio tables821180 +Node: A LaTeX example823310 +Ref: A LaTeX example-Footnote-1827161 +Ref: A LaTeX example-Footnote-2827204 +Ref: A LaTeX example-Footnote-3827365 +Node: Translator functions827817 +Node: Dynamic Blocks829999 +Node: Special Agenda Views832243 +Ref: Special Agenda Views-Footnote-1835954 +Ref: Special Agenda Views-Footnote-2836160 +Node: Speeding Up Your Agendas836292 +Node: Extracting Agenda Information837515 +Node: Using the Property API841322 +Node: Using the Mapping API844671 +Node: History and Acknowledgments848701 +Ref: From Carsten848899 +Ref: From Bastien852327 +Ref: List of Contributions854850 +Node: GNU Free Documentation License863513 +Ref: ADDENDUM How to use this License for your documents887274 +Node: Main Index888663 +Node: Key Index990583 +Node: Command and Function Index1048013 +Node: Variable Index1097788  End Tag Table diff --git a/lisp/org/orgguide.info b/lisp/org/orgguide.info index f1f63f68..a8a57759 100644 --- a/lisp/org/orgguide.info +++ b/lisp/org/orgguide.info @@ -50,7 +50,7 @@ Copyright © 2004-2026 Free Software Foundation, Inc. * Tags:: Tagging headlines and matching sets of tags. * Properties:: Storing information about an entry. * Dates and Times:: Making items useful for planning. -* Capture, Refile, Archive:: The ins and outs for projects. +* Capture, Refile, Archive: Capture Refile Archive. The ins and outs for projects. * Agenda Views:: Collecting information into views. * Markup:: Compose beautiful documents. * Exporting:: Sharing and publishing notes. @@ -1095,7 +1095,7 @@ Tags::). The syntax for the search string is described in *note Matching Tags and Properties::.  -File: orgguide.info, Node: Dates and Times, Next: Capture, Refile, Archive, Prev: Properties, Up: Top +File: orgguide.info, Node: Dates and Times, Next: Capture Refile Archive, Prev: Properties, Up: Top 8 Dates and Times ***************** @@ -1326,7 +1326,7 @@ project. Views::) to show which tasks have been worked on or closed during a day.  -File: orgguide.info, Node: Capture, Refile, Archive, Next: Agenda Views, Prev: Dates and Times, Up: Top +File: orgguide.info, Node: Capture Refile Archive, Next: Agenda Views, Prev: Dates and Times, Up: Top 9 Capture, Refile, Archive ************************** @@ -1346,7 +1346,7 @@ fast. * Archiving:: What to do with finished products.  -File: orgguide.info, Node: Capture, Next: Refile and Copy, Up: Capture, Refile, Archive +File: orgguide.info, Node: Capture, Next: Refile and Copy, Up: Capture Refile Archive 9.1 Capture =========== @@ -1426,7 +1426,7 @@ locations. See *note Capture templates::. a backslash.  -File: orgguide.info, Node: Refile and Copy, Next: Archiving, Prev: Capture, Up: Capture, Refile, Archive +File: orgguide.info, Node: Refile and Copy, Next: Archiving, Prev: Capture, Up: Capture Refile Archive 9.2 Refile and Copy =================== @@ -1459,7 +1459,7 @@ command: deleted.  -File: orgguide.info, Node: Archiving, Prev: Refile and Copy, Up: Capture, Refile, Archive +File: orgguide.info, Node: Archiving, Prev: Refile and Copy, Up: Capture Refile Archive 9.3 Archiving ============= @@ -1493,7 +1493,7 @@ example: #+ARCHIVE: %s_done::  -File: orgguide.info, Node: Agenda Views, Next: Markup, Prev: Capture, Refile, Archive, Up: Top +File: orgguide.info, Node: Agenda Views, Next: Markup, Prev: Capture Refile Archive, Up: Top 10 Agenda Views *************** @@ -2539,63 +2539,63 @@ file, use  Tag Table: Node: Top907 -Node: Introduction5611 -Ref: Installation6298 -Ref: Activation7010 -Ref: Feedback7453 -Node: Document Structure7710 -Node: Headlines8784 -Ref: Headlines-Footnote-19682 -Node: Visibility Cycling9805 -Node: Motion11205 -Node: Structure Editing11799 -Ref: Structure Editing-Footnote-113235 -Node: Sparse Trees13339 -Ref: Sparse Trees-Footnote-114449 -Node: Plain Lists14569 -Node: Tables17361 -Ref: Creation and conversion19054 -Ref: Re-aligning and field motion19654 -Ref: Column and row editing20417 -Node: Hyperlinks21768 -Ref: Internal links22526 -Ref: External Links22979 -Ref: Handling Links24808 -Node: TODO Items26298 -Node: TODO Basics27275 -Node: Multi-state Workflow28808 -Node: Progress Logging30598 -Ref: Closing items31360 -Ref: Tracking TODO state changes31915 -Ref: Progress Logging-Footnote-132914 -Ref: Progress Logging-Footnote-232987 -Node: Priorities33065 -Node: Breaking Down Tasks33982 -Node: Checkboxes34785 -Node: Tags35922 -Ref: Tag inheritance36565 -Ref: Setting tags37303 -Ref: Tag groups39081 -Ref: Tag searches39875 -Ref: Tags-Footnote-140955 -Node: Properties41059 -Node: Dates and Times42871 -Node: Timestamps43912 -Node: Creating Timestamps46666 -Node: Deadlines and Scheduling48511 -Ref: Deadlines and Scheduling-Footnote-150575 -Ref: Deadlines and Scheduling-Footnote-250736 -Node: Clocking Work Time50899 -Node: Capture, Refile, Archive52334 -Node: Capture53146 -Ref: Setting up capture53452 -Ref: Using capture53723 -Ref: Capture templates54419 -Ref: Capture-Footnote-155852 -Ref: Capture-Footnote-255963 -Node: Refile and Copy56054 -Node: Archiving57368 -Node: Agenda Views58748 +Node: Introduction5635 +Ref: Installation6322 +Ref: Activation7034 +Ref: Feedback7477 +Node: Document Structure7734 +Node: Headlines8808 +Ref: Headlines-Footnote-19706 +Node: Visibility Cycling9829 +Node: Motion11229 +Node: Structure Editing11823 +Ref: Structure Editing-Footnote-113259 +Node: Sparse Trees13363 +Ref: Sparse Trees-Footnote-114473 +Node: Plain Lists14593 +Node: Tables17385 +Ref: Creation and conversion19078 +Ref: Re-aligning and field motion19678 +Ref: Column and row editing20441 +Node: Hyperlinks21792 +Ref: Internal links22550 +Ref: External Links23003 +Ref: Handling Links24832 +Node: TODO Items26322 +Node: TODO Basics27299 +Node: Multi-state Workflow28832 +Node: Progress Logging30622 +Ref: Closing items31384 +Ref: Tracking TODO state changes31939 +Ref: Progress Logging-Footnote-132938 +Ref: Progress Logging-Footnote-233011 +Node: Priorities33089 +Node: Breaking Down Tasks34006 +Node: Checkboxes34809 +Node: Tags35946 +Ref: Tag inheritance36589 +Ref: Setting tags37327 +Ref: Tag groups39105 +Ref: Tag searches39899 +Ref: Tags-Footnote-140979 +Node: Properties41083 +Node: Dates and Times42895 +Node: Timestamps43932 +Node: Creating Timestamps46686 +Node: Deadlines and Scheduling48531 +Ref: Deadlines and Scheduling-Footnote-150595 +Ref: Deadlines and Scheduling-Footnote-250756 +Node: Clocking Work Time50919 +Node: Capture Refile Archive52354 +Node: Capture53162 +Ref: Setting up capture53464 +Ref: Using capture53735 +Ref: Capture templates54431 +Ref: Capture-Footnote-155864 +Ref: Capture-Footnote-255975 +Node: Refile and Copy56066 +Node: Archiving57376 +Node: Agenda Views58752 Node: Agenda Files60207 Node: Agenda Dispatcher60955 Node: Built-in Agenda Views61877 diff --git a/lisp/org/ox-html.el b/lisp/org/ox-html.el index 6916cc21..8e6f3415 100644 --- a/lisp/org/ox-html.el +++ b/lisp/org/ox-html.el @@ -3103,7 +3103,8 @@ 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 (plist-get info :output-file))) + cache-dir (file-name-directory + (or (plist-get info :output-file) bfn))) ;; Re-create LaTeX environment from original buffer in ;; temporary buffer so that dvipng/imagemagick can properly ;; turn the fragment into an image. diff --git a/lisp/org/ox-koma-letter.el b/lisp/org/ox-koma-letter.el index 61b624e8..3a17dceb 100644 --- a/lisp/org/ox-koma-letter.el +++ b/lisp/org/ox-koma-letter.el @@ -6,7 +6,6 @@ ;; Alan Schmitt ;; Viktor Rosenfeld ;; Rasmus Pank Roulund -;; Maintainer: Marco Wahl ;; Keywords: org, text, tex ;; This file is part of GNU Emacs. diff --git a/lisp/org/ox-latex.el b/lisp/org/ox-latex.el index b4d1ada8..d07af9e6 100644 --- a/lisp/org/ox-latex.el +++ b/lisp/org/ox-latex.el @@ -3,7 +3,7 @@ ;; Copyright (C) 2011-2026 Free Software Foundation, Inc. ;; Author: Nicolas Goaziou -;; Maintainer: Daniel Fleischer +;; Maintainer: Pedro A. Aranda ;; Keywords: outlines, hypermedia, calendar, text ;; This file is part of GNU Emacs. @@ -1712,7 +1712,7 @@ Return the new header." ;; exclusively through ini files, return HEADER as-is. (header (if (or language-ini-only (not (stringp language-code)) - (not (string-match "\\\\usepackage\\[\\(.*\\)\\]{babel}" header))) + (not (string-match "\\\\usepackage\\[\\([^]]*\\)\\]{babel}" header))) header (let ((options (save-match-data (org-split-string (match-string 1 header) ",[ \t]*")))) diff --git a/lisp/org/ox-odt.el b/lisp/org/ox-odt.el index 232a6437..03a47c86 100644 --- a/lisp/org/ox-odt.el +++ b/lisp/org/ox-odt.el @@ -2869,7 +2869,7 @@ Style is a symbol among `quoted', `centered' and nil." (org-element-lineage paragraph '(center-block quote-block section))) - (center-block 'center) + (center-block 'centered) (quote-block 'quoted))) (defun org-odt--format-paragraph (paragraph contents info default center quote) diff --git a/lisp/org/ox-texinfo.el b/lisp/org/ox-texinfo.el index a5417773..0aaa4902 100644 --- a/lisp/org/ox-texinfo.el +++ b/lisp/org/ox-texinfo.el @@ -573,12 +573,14 @@ Trim string and collapse multiple whitespace characters as they are not significant. Replace leading left parenthesis, when followed by a right parenthesis, with a square bracket. Remove periods, commas and colons." - (org-trim - (replace-regexp-in-string - "[ \t]+" " " - (replace-regexp-in-string - "[:,.]" "" - (replace-regexp-in-string "\\`(\\(.*?)\\)" "[\\1" title))))) + (thread-last + title + (replace-regexp-in-string "\\`(\\(.*?)\\)" "[\\1") + ;; See https://lists.gnu.org/archive/html/bug-texinfo/2026-03/msg00000.html + (replace-regexp-in-string (rx "@comma" (optional "{}")) "") + (replace-regexp-in-string "[:,.]" "") + (replace-regexp-in-string "[ \t]+" " ") + org-trim)) (defun org-texinfo--sanitize-title (title info) "Make TITLE suitable as a section name. diff --git a/lisp/orgit/orgit-pkg.el b/lisp/orgit/orgit-pkg.el index 1c7a87a8..ffb98791 100644 --- a/lisp/orgit/orgit-pkg.el +++ b/lisp/orgit/orgit-pkg.el @@ -1,14 +1,15 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "orgit" "20260301.1255" +(define-package "orgit" "20260623.2148" "Support for Org links to Magit buffers." - '((emacs "28.1") - (compat "30.1") - (cond-let "0.2") + '((emacs "29.1") + (compat "31.0") + (cond-let "1.1") + (llama "1.0") (magit "4.5") - (org "9.7")) + (org "9.8")) :url "https://github.com/magit/orgit" - :commit "4fb91faff3bf32dac5f6f932654c280cd1f190f7" - :revdesc "4fb91faff3bf" + :commit "1aa3a27b1cb6aec913f7365caebdd6ffbe178443" + :revdesc "1aa3a27b1cb6" :keywords '("hypermedia" "vc") :authors '(("Jonas Bernoulli" . "emacs.orgit@jonas.bernoulli.dev")) :maintainers '(("Jonas Bernoulli" . "emacs.orgit@jonas.bernoulli.dev"))) diff --git a/lisp/orgit/orgit.el b/lisp/orgit/orgit.el index c731d087..7dc17bc0 100644 --- a/lisp/orgit/orgit.el +++ b/lisp/orgit/orgit.el @@ -6,14 +6,15 @@ ;; Homepage: https://github.com/magit/orgit ;; Keywords: hypermedia vc -;; Package-Version: 20260301.1255 -;; Package-Revision: 4fb91faff3bf +;; Package-Version: 20260623.2148 +;; Package-Revision: 1aa3a27b1cb6 ;; Package-Requires: ( -;; (emacs "28.1") -;; (compat "30.1") -;; (cond-let "0.2") +;; (emacs "29.1") +;; (compat "31.0") +;; (cond-let "1.1") +;; (llama "1.0") ;; (magit "4.5") -;; (org "9.7")) +;; (org "9.8")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -50,11 +51,14 @@ ;; Format ;; ------ -;; The three link types defined here take these forms: +;; The four link types defined here take these forms: ;; ;; orgit:/path/to/repo/ links to a `magit-status' buffer ;; orgit-rev:/path/to/repo/::REV links to a `magit-revision' buffer ;; orgit-log:/path/to/repo/::ARGS links to a `magit-log' buffer +;; orgit-blob:/path/to/repo/::REV:path/to/file[#N[-M|,C]] +;; links to a file- or blob-visiting buffer, optionally at +;; a certain line and column (#N,C), or span of lines (#N-M) ;; Before v1.3.0 only the first revision was stored in `orgit-log' ;; links, and all other revisions were discarded. All other arguments @@ -87,23 +91,20 @@ ;; To explicitly define the web urls, use something like: ;; -;; git config orgit.status http://example.com/repo/overview -;; git config orgit.rev http://example.com/repo/revision/%r -;; git config orgit.log http://example.com/repo/history/%r +;; git config orgit.status https://example.com/repo/overview +;; git config orgit.rev https://example.com/repo/revision/%r +;; git config orgit.log https://example.com/repo/history/%r +;; git config orgit.blob https://example.com/repo/history/%r ;;; Code: -(require 'cl-lib) (require 'compat) (require 'cond-let) (require 'format-spec) +(require 'llama) (require 'magit) (require 'org) - -(unless (fboundp 'org-link-store-props) - (defalias 'org-link-store-props 'org-store-link-props)) - -(eval-when-compile (require 'subr-x)) +(require 'seq) ;;; Options @@ -116,39 +117,45 @@ `(("github.com[:/]\\(.+?\\)\\(?:\\.git\\)?$" "https://github.com/%n" "https://github.com/%n/commits/%r" - "https://github.com/%n/commit/%r") + "https://github.com/%n/commit/%r" + "https://github.com/%n/blob/%r/%p%C") ("gitlab.com[:/]\\(.+?\\)\\(?:\\.git\\)?$" "https://gitlab.com/%n" "https://gitlab.com/%n/commits/%r" - "https://gitlab.com/%n/commit/%r") - ("codeberg.org\\(/\\|:git@\\)\\(.+?\\)\\(?:\\.git\\)?$" + "https://gitlab.com/%n/commit/%r" + "https://gitlab.com/%n/-/blob/%r/%p%l") + ("\\(?:git@\\)?codeberg.org/\\(.+?\\)\\(?:\\.git\\)?$" "https://codeberg.org/%n" ;; Redirects to commits/branch/%r. "https://codeberg.org/%n/commits/%r" - "https://codeberg.org/%n/commit/%r") + "https://codeberg.org/%n/commit/%r" + "https://codeberg.org/%n/src/commit/%r/%p%L") ("git.sr.ht[:/]\\(.+?\\)\\(?:\\.git\\)?$" "https://git.sr.ht/%n" "https://git.sr.ht/%n/log/%r" - "https://git.sr.ht/%n/commit/%r") + "https://git.sr.ht/%n/commit/%r" + "https://git.sr.ht/%n/tree/%r/%p%l") ("bitbucket.org[:/]\\(.+?\\)\\(?:\\.git\\)?$" "https://bitbucket.org/%n" "https://bitbucket.org/%n/commits/branch/%r" - "https://bitbucket.org/%n/commits/%r") - ("git.kernel.org/pub/scm[:/]\\(.+\\)$" - "https://git.kernel.org/cgit/%n" - "https://git.kernel.org/cgit/%n/log/?h=%r" - "https://git.kernel.org/cgit/%n/commit/?id=%r")) + "https://bitbucket.org/%n/commits/%r" + "https://bitbucket.org/%n/src/%r/%p%X") + ("git.kernel.org/pub/scm[:/]\\(.+?\\)\\.git$" + "https://git.kernel.org/pub/scm/%n.git" + "https://git.kernel.org/pub/scm/%n.git/log/?h=%r" + "https://git.kernel.org/pub/scm/%n.git/commit/?id=%r" + "https://git.kernel.org/pub/scm/%n.git/tree/%p?id=%r%x")) "Alist used to translate Git urls to web urls when exporting links. -Each entry has the form (REMOTE-REGEXP STATUS LOG REVISION). If -a REMOTE-REGEXP matches the url of the chosen remote then one of -the corresponding format strings STATUS, LOG or REVISION is used -according to the major mode of the buffer being linked to. +Each entry has the form (REMOTE-REGEXP STATUS LOG REVISION BLOB). +If a REMOTE-REGEXP matches the url of the chosen remote then one +of the corresponding format strings STATUS, LOG, REVISION or BLOB +is used according to the major mode of the buffer being linked to. The first submatch of REMOTE-REGEXP has to match the repository identifier (which usually consists of the username and repository name). The %n in the format string is replaced with that match. -LOG and REVISION additionally have to contain %r which is +LOG, REVISION and BLOB additionally have to contain %r which is replaced with the appropriate revision. This can be overwritten in individual repositories using the Git @@ -161,7 +168,8 @@ are defined then `orgit-remote' and `orgit.remote' have no effect." (regexp :tag "Remote regexp") (string :tag "Status format") (string :tag "Log format" :format "%{%t%}: %v") - (string :tag "Revision format")))) + (string :tag "Revision format") + (string :tag "Blob format")))) (defcustom orgit-remote "origin" "Default remote used when exporting links. @@ -284,8 +292,8 @@ In that case `orgit-rev-store' stores one or more links instead." (magit-status-setup-buffer (orgit--repository-directory repo))) ;;;###autoload -(defun orgit-status-export (path desc format) - (orgit-export path desc format "status" 1)) +(defun orgit-status-export (path desc backend _info) + (orgit-export path desc backend 'status)) ;;;###autoload (defun orgit-status-complete-link (&optional arg) @@ -337,7 +345,7 @@ In that case `orgit-rev-store' stores one or more links instead." (magit-log-setup-buffer revs args files))) ;;;###autoload -(defun orgit-log-export (path desc format) +(defun orgit-log-export (path desc backend _info) (pcase-let* ((`(,repo ,args) (split-string path "::")) (first-branch (cond ((string-prefix-p "((" args) (caar (read args))) @@ -346,8 +354,7 @@ In that case `orgit-rev-store' stores one or more links instead." (args)))) (when (string-prefix-p "--" first-branch) (setq first-branch nil)) - (orgit-export (concat repo "::" first-branch) - desc format "log" 2))) + (orgit-export (concat repo "::" first-branch) desc backend 'log))) ;;;###autoload (defun orgit-log-complete-link (&optional arg) @@ -427,8 +434,8 @@ store links to the Magit-Revision mode buffers for these commits." rev (car (magit-diff-arguments 'magit-revision-mode)) nil))) ;;;###autoload -(defun orgit-rev-export (path desc format) - (orgit-export path desc format "rev" 3)) +(defun orgit-rev-export (path desc backend _info) + (orgit-export path desc backend 'rev)) ;;;###autoload (defun orgit-rev-complete-link (&optional arg) @@ -437,11 +444,79 @@ store links to the Magit-Revision mode buffers for these commits." (orgit--current-repository) (magit-read-branch-or-commit "Revision")))) +;;; Blob + +;;;###autoload +(with-eval-after-load 'org + (with-eval-after-load 'magit + (org-link-set-parameters "orgit-blob" + :store 'orgit-blob-store + :follow 'orgit-blob-open + :export 'orgit-blob-export + :complete 'orgit-blob-complete-link))) + +;;;###autoload +(defun orgit-blob-store () + (and-let* ((file magit-buffer-file-name) + (oid (or magit-buffer-revision-oid magit-buffer-blob-oid)) + (repo (orgit--current-repository)) + (file (file-relative-name file repo))) + (org-link-store-props + :type "orgit-blob" + :link (cond ((region-active-p) + (format "orgit-blob:%s::%s/%s#%s-%s" repo oid file + (line-number-at-pos (region-beginning)) + (line-number-at-pos (region-end)))) + ((format "orgit-blob:%s::%s/%s#%s,%s" repo oid file + (line-number-at-pos) + (current-column)))) + :description (format "%s:%s:%s" repo + (magit-rev-abbrev oid) + file)))) + +;;;###autoload +(defun orgit-blob-open (path) + (pcase-let* ((`(,repo ,value) (split-string path "::")) + (`(,rev ,path ,beg ,end ,column) (orgit-blob--split value)) + (default-directory (orgit--repository-directory repo))) + (with-current-buffer (magit-find-file rev path) + (when beg + (goto-char (point-min)) + (forward-line (1- beg)) + (cond (end (set-mark (point)) + (forward-line (- end beg))) + (column (forward-char column))))))) + +;;;###autoload +(defun orgit-blob-export (path desc backend _info) + (orgit-export path desc backend 'blob)) + +;;;###autoload +(defun orgit-blob-complete-link (&optional arg) + (let ((default-directory (magit-read-repository arg))) + (apply #'format "orgit-blob:%s::%s" + (orgit--current-repository) + (magit-find-file-read-args "Find file")))) + +(defun orgit-blob--split (value) + (if (string-match "\\`\\([^/]+\\)/\\([^#]+\\)\ +\\(?:#\\([0-9]+\\)\\(?:\\([-,]\\)\\([0-9]+\\)\\)?\\)?\\'" value) + (list (match-string 1 value) + (match-string 2 value) + (and (match-string 3 value) + (string-to-number (match-string 3 value))) + (and (equal (match-string 4 value) "-") + (string-to-number (match-string 5 value))) + (and (equal (match-string 4 value) ",") + (string-to-number (match-string 5 value)))) + (list value))) + ;;; Export -(defun orgit-export (path desc format gitvar idx) +(defun orgit-export (path _desc backend type) (pcase-let* ((`(,dir ,rev) (split-string path "::")) - (dir (orgit--repository-directory dir))) + (dir (orgit--repository-directory dir)) + (format-n (pcase type ('status 0) ('log 1) ('rev 2) ('blob 3)))) (cond-let* ((not (file-exists-p dir)) (signal 'org-link-broken @@ -457,38 +532,62 @@ store links to the Magit-Revision mode buffers for these commits." (signal 'org-link-broken (list (format "Cannot determine public remote for %s" default-directory)))) - ([url (magit-get "orgit" gitvar)] - (orgit--format-export (format-spec url `((?r . ,rev))) desc format)) - ([url (magit-get "remote" remote "url")] - [format (cl-find-if (lambda (elt) - (string-match (car elt) url)) - orgit-export-alist)] - (orgit--format-export (format-spec (nth idx format) - `((?n . ,(match-string 1 url)) - (?r . ,rev))) - desc format)) + ([format (magit-get "orgit" (symbol-name type))] + (orgit--format-export backend (orgit--format-url type format rev))) + ([git-url (magit-get "remote" remote "url")] + [format:name (seq-some (pcase-lambda (`(,regexp . ,formats)) + (and (string-match regexp git-url) + (cons (nth format-n formats) + (match-string 1 git-url)))) + orgit-export-alist)] + (orgit--format-export backend (orgit--format-url type format:name rev))) ((signal 'org-link-broken (list (format "Cannot determine public url for %s" path))))))) -(defun orgit--format-export (link desc format) - (pcase format - ('html (format "%s" link desc)) - ('latex (format "\\href{%s}{%s}" link desc)) +(defun orgit--format-export (backend link &optional desc) + (pcase backend + ('html (format "%s" link (or desc link))) + ('latex (format "\\href{%s}{%s}" link (or desc link))) ('ascii link) (_ link))) +(defun orgit--format-url (type format value) + (pcase-let ((`(,format . ,name) + (if (consp format) format (list format))) + (`(,rev ,path ,beg ,end) + (if (eq type 'blob) (orgit-blob--split value) (list value)))) + (format-spec + format `((?n . ,name) + (?r . ,rev) + (?p . ,path) + (?l . ,(##cond (end (format "#L%s-%s" beg end)) + (beg (format "#L%s" beg)) + (""))) + (?L . ,(##cond (end (format "#L%s-L%s" beg end)) + (beg (format "#L%s" beg)) + (""))) + (?C . ,(##cond (end (format "?plain=1#L%s-L%s" beg end)) + (beg (format "?plain=1#L%s" beg)) + (""))) + (?x . ,(##cond (beg (format "#n%s" beg)) + (""))) + (?X . ,(##cond (end (format "#lines%s:%s" beg end)) + (beg (format "#lines%s" beg)) + (""))))))) + ;;; Utilities (defun orgit--current-repository () - (or (and orgit-store-repository-id - (car (rassoc default-directory (magit-repos-alist)))) - (abbreviate-file-name default-directory))) + (abbreviate-file-name + (or (and orgit-store-repository-id + (car (rassoc default-directory (magit-repos-alist)))) + (magit-toplevel)))) (defun orgit--repository-directory (repo) (let ((dir (or (cdr (assoc repo (magit-repos-alist))) (file-name-as-directory (expand-file-name repo))))) (cond ((file-exists-p dir) dir) - ((string-match-p "\\`[./]" repo) + ((string-prefix-p "./" repo) (error "Cannot open link; %S does not exist" dir)) ((error "Cannot open link; no entry for %S in `%s'" repo 'magit-repository-directories))))) @@ -501,8 +600,16 @@ store links to the Magit-Revision mode buffers for these commits." ;; (cond . 0) ;; (interactive . 0)) ;; read-symbol-shorthands: ( -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") -;; ("when-let" . "cond-let--when-let")) +;; ("and$" . "cond-let--and$") +;; ("thread$" . "cond-let--thread$") +;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") +;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") +;; ("while-let" . "cond-let--while-let")) ;; End: ;;; orgit.el ends here diff --git a/lisp/plantuml-mode/plantuml-mode-pkg.el b/lisp/plantuml-mode/plantuml-mode-pkg.el index aed1de79..c311216e 100644 --- a/lisp/plantuml-mode/plantuml-mode-pkg.el +++ b/lisp/plantuml-mode/plantuml-mode-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "plantuml-mode" "20250705.1148" +(define-package "plantuml-mode" "20260514.1745" "Major mode for PlantUML." '((dash "2.0.0") (emacs "25.1") (deflate "0.0.3")) :url "https://github.com/skuro/plantuml-mode" - :commit "0a19d9988879c57b176dd4c03f59003644f9c9b0" - :revdesc "0a19d9988879" + :commit "a4a63efa4a3980bfbd825bfb3a263c6664401e79" + :revdesc "a4a63efa4a39" :keywords '("files" "text" "processes" "tools")) diff --git a/lisp/plantuml-mode/plantuml-mode.el b/lisp/plantuml-mode/plantuml-mode.el index dc38caf9..96f07f21 100644 --- a/lisp/plantuml-mode/plantuml-mode.el +++ b/lisp/plantuml-mode/plantuml-mode.el @@ -6,8 +6,8 @@ ;; Author: Zhang Weize (zwz) ;; Maintainer: Carlo Sciolla (skuro) ;; Keywords: files text processes tools -;; Package-Version: 20250705.1148 -;; Package-Revision: 0a19d9988879 +;; Package-Version: 20260514.1745 +;; Package-Revision: a4a63efa4a39 ;; Package-Requires: ((dash "2.0.0") (emacs "25.1") (deflate "0.0.3")) ;; Homepage: https://github.com/skuro/plantuml-mode @@ -38,6 +38,7 @@ ;;; Change log: ;; +;; version 1.9.0, 2026-05-14 Moved regexes to `rx', support for tab indents ;; version 1.8.0, 2025-07-04 Support for `'hex' and `'deflate' modes for server URL encoding ;; version 1.7.0, 2025-05-24 Support for `completion-at-point' ;; version 1.6.0, 2025-05-15 Fix server exec mode; various indentation enhancements and bug fixes; better preview buffer management @@ -654,6 +655,311 @@ Uses prefix (as PREFIX) to choose where to display it: You should move your configuration to use `plantuml-mode'. \ See more at https://github.com/skuro/puml-mode/issues/26"))) +;; Below are the regexps for indentation. +;; +;; Notes: +;; - You can override any of the X-start / X-end regexps below. `defvar' +;; respects pre-existing bindings, so a `(setq ...)' before this file is +;; loaded keeps your value; a `(setq ...)' after loading also overrides +;; the default. E.g., to disable indentation on activate: +;; +;; (setq plantuml-indent-regexp-activate-start "NEVER MATCH THIS EXPRESSION") +;; (setq plantuml-indent-regexp-activate-end "NEVER MATCH THIS EXPRESSION") +;; +;; - Due to the nature of using (context-insensitive) regexps, indentation +;; has the following limitations: +;; - commands commented out by /' ... '/ will _not_ be ignored, and may +;; lead to mis-indentation. +;; - You can correct mis-indentation by adding '-comment lines containing +;; PLANTUML_MODE_INDENT_INCREASE and/or PLANTUML_MODE_INDENT_DECREASE. +;; (Note: the comment line should not contain any text matching other +;; indent regexps, or this user-control instruction will be ignored. +;; At most one will count per line.) +(defvar plantuml-indent-regexp-block-start + (rx line-start + (zero-or-more not-newline) + "{" + (zero-or-more " ") + line-end) + "Indentation regex for all plantuml elements that might define a {} block. +Plantuml elements like skinparam, rectangle, sprite, package, etc. +The opening { has to be the last visible character in the line (whitespace +might follow).") + +(defvar plantuml-indent-regexp-note-start + (rx line-start + (zero-or-more blank) + (optional (group "floating" (one-or-more blank))) + (optional (any "hr")) + "note" + (one-or-more " ") + (group (or "right" "left" "top" "bottom" "over" "as")) + (zero-or-more (not (any ":"))) + (optional (group "::" (one-or-more (not (any ":"))))) + line-end) + "Simplified regex. Note syntax is especially inconsistent across diagrams.") + +(defvar plantuml-indent-regexp-group-start + (rx line-start + (zero-or-more blank) + (or + ;; keywords that open blocks with or without following tokens on the same line + (seq (group (or "alt" "else" "opt" "loop" "par" "critical" "group")) + (or (seq (one-or-more blank) (one-or-more not-newline)) + line-end)) + ;; keywords that open blocks only when followed by a label + (seq (group "break") + (one-or-more blank) + (one-or-more not-newline)))) + "Indentation regex for plantuml group elements defined for sequence diagrams. +Two variants for groups: keyword is either followed by whitespace and some text +or it is followed by line end.") + +(cl-defun plantuml-indent-regexp-meta-block-start (start-form + &key (before '(zero-or-more blank)) + (after '(seq (one-or-more blank) + (one-or-more not-newline)))) + "Regex for block-starting form START-FORM. + +Allows to optionally specify a BEFORE form (default: +`(zero-or-more blank). + +Allows to optionally specify an AFTER form (default: +`(seq (one-or-more blank) (one-or-more not-newline))" + (rx-to-string + `(seq line-start + ,before + ,start-form + ,after + line-end))) + +(cl-defun plantuml-indent-regexp-meta-block-end (end-form + &key (before '(zero-or-more blank)) + (after '(seq (zero-or-more blank) + (optional (group "'" (zero-or-more not-newline)))))) + "Regex matching a block-ending construct described by END-FORM. +END-FORM is an `rx' form — typically a string token like \"endif\" +or a composite form like `(or \"}\" \"end\")'. + +Allows to optionally specify a BEFORE form (default: +`(zero-or-more blank). + +Allows to optionally specify an AFTER form (default: +`(seq (one-or-more blank) (one-or-more not-newline))" + (rx-to-string + `(seq line-start + ,before + ,end-form + ,after + line-end))) + +(defvar plantuml-indent-regexp-activate-start + (plantuml-indent-regexp-meta-block-start "activate")) + +(defvar plantuml-indent-regexp-box-start + (plantuml-indent-regexp-meta-block-start "box")) + +(defvar plantuml-indent-regexp-ref-start + (plantuml-indent-regexp-meta-block-start '(seq "ref" + (one-or-more blank) + "over") + :after '(seq (one-or-more blank) + (+? (not (any ":")))))) + +(defvar plantuml-indent-regexp-title-start + (plantuml-indent-regexp-meta-block-start "title" + :after '(seq (zero-or-more blank) + (optional (group "'" (zero-or-more not-newline)))))) + +(defvar plantuml-indent-regexp-header-start + (plantuml-indent-regexp-meta-block-start '(or (seq (or "center" "left" "right") + (one-or-more blank) + "header") + "header") + :after '(seq (zero-or-more blank) + (optional (group "'" (zero-or-more not-newline)))))) + +(defvar plantuml-indent-regexp-footer-start + (plantuml-indent-regexp-meta-block-start '(or (seq (or "center" "left" "right") + (one-or-more blank) + "footer") + "footer") + :after '(seq (zero-or-more blank) + (optional (group "'" (zero-or-more not-newline)))))) + +;; NOTE: this breaks if replacing `" "' with `blank' +(defvar plantuml-indent-regexp-legend-start + (plantuml-indent-regexp-meta-block-start '(or "legend" + (seq "legend" (one-or-more " ") (or "bottom" "top")) + (seq "legend" (one-or-more " ") (or "center" "left" "right")) + (seq "legend" + (one-or-more " ") (or "bottom" "top") + (one-or-more " ") (or "center" "left" "right"))) + :after '(seq (zero-or-more " ") + (optional (group "'" (zero-or-more not-newline)))))) + +(defvar plantuml-indent-regexp-oldif-start + (plantuml-indent-regexp-meta-block-start '(seq "if" + (one-or-more blank) + "\"" (zero-or-more not-newline) "\"" + (one-or-more blank) + "then") + :before '(zero-or-more not-newline) + :after '(seq (zero-or-more blank) + (optional (group "'" (zero-or-more not-newline))))) + "Used in current activity diagram but sometimes already mentioned as deprecated.") + +(defvar plantuml-indent-regexp-newif-start + (plantuml-indent-regexp-meta-block-start '(seq (optional "else") + "if" + (one-or-more blank) + "(" (zero-or-more not-newline) ")" + (one-or-more blank) + "then") + :after '(seq (zero-or-more blank) + (zero-or-more not-newline)))) + +(defvar plantuml-indent-regexp-loop-start + (plantuml-indent-regexp-meta-block-start '(or (seq "repeat" (zero-or-more blank)) + (seq "while" + (one-or-more blank) + "(" (zero-or-more not-newline) ")" + (zero-or-more not-newline))) + :after '(seq))) + +(defvar plantuml-indent-regexp-fork-start + (plantuml-indent-regexp-meta-block-start '(seq (or "fork" "split") + (optional (one-or-more blank) "again")) + :after '(zero-or-more blank))) + +(defvar plantuml-indent-regexp-case-start + (plantuml-indent-regexp-meta-block-start '(seq (or "switch" "case") + (zero-or-more (syntax whitespace)) + "(" (zero-or-more not-newline) ")") + :after '(zero-or-more blank))) + +(defvar plantuml-indent-regexp-macro-start + (plantuml-indent-regexp-meta-block-start "!definelong" + :after '(zero-or-more not-newline))) + +(defvar plantuml-indent-regexp-user-control-start + (plantuml-indent-regexp-meta-block-start '(seq "'" + (zero-or-more not-newline) + (zero-or-more " ") + "PLANTUML_MODE_INDENT_INCREASE") + :after '(seq (zero-or-more blank) + (zero-or-more not-newline)))) + +(defvar plantuml-indent-regexp-start (list plantuml-indent-regexp-block-start + plantuml-indent-regexp-group-start + plantuml-indent-regexp-activate-start + plantuml-indent-regexp-box-start + plantuml-indent-regexp-ref-start + plantuml-indent-regexp-legend-start + plantuml-indent-regexp-note-start + plantuml-indent-regexp-newif-start + plantuml-indent-regexp-loop-start + plantuml-indent-regexp-fork-start + plantuml-indent-regexp-case-start + plantuml-indent-regexp-title-start + plantuml-indent-regexp-header-start + plantuml-indent-regexp-footer-start + plantuml-indent-regexp-macro-start + plantuml-indent-regexp-oldif-start + plantuml-indent-regexp-user-control-start)) + +(defvar plantuml-indent-regexp-block-end + (plantuml-indent-regexp-meta-block-end '(or "}" + "endif" + (seq "else" (zero-or-more " ") (zero-or-more not-newline)) + "end"))) + +(defvar plantuml-indent-regexp-note-end + (plantuml-indent-regexp-meta-block-end '(group (or (seq "end" (one-or-more " ") "note") + (seq "end" (any "rh") "note"))))) + +(defvar plantuml-indent-regexp-group-end + (plantuml-indent-regexp-meta-block-end "end")) + +(defvar plantuml-indent-regexp-activate-end + (plantuml-indent-regexp-meta-block-end "deactivate" + :after '(seq (one-or-more blank) + (one-or-more not-newline)))) + +(defvar plantuml-indent-regexp-box-end + (plantuml-indent-regexp-meta-block-end '(seq "end" (one-or-more " ") "box"))) + +(defvar plantuml-indent-regexp-ref-end + (plantuml-indent-regexp-meta-block-end '(seq "end" (one-or-more " ") "ref"))) + +(defvar plantuml-indent-regexp-title-end + (plantuml-indent-regexp-meta-block-end '(seq "end" (one-or-more blank) "title"))) + +(defvar plantuml-indent-regexp-header-end + (plantuml-indent-regexp-meta-block-end "endheader")) + +(defvar plantuml-indent-regexp-footer-end + (plantuml-indent-regexp-meta-block-end "endfooter")) + +(defvar plantuml-indent-regexp-legend-end + (plantuml-indent-regexp-meta-block-end "endlegend")) + +(defvar plantuml-indent-regexp-oldif-end + (plantuml-indent-regexp-meta-block-end '(group (or "endif" "else")))) + +(defvar plantuml-indent-regexp-newif-end + (plantuml-indent-regexp-meta-block-end '(group (or "endif" "elseif" "else")))) + +(defvar plantuml-indent-regexp-loop-end + (plantuml-indent-regexp-meta-block-end '(group (or (seq "repeat" (zero-or-more " ") "while") + "endwhile")) + :after '(seq (zero-or-more blank) + (zero-or-more not-newline)))) + +(defvar plantuml-indent-regexp-fork-end + (plantuml-indent-regexp-meta-block-end '(group (or (seq (group (or "fork" "split")) + (one-or-more blank) "again") + (seq "end" (zero-or-more blank) + (group (or "fork" "split"))))) + :after '(seq (zero-or-more " ") + (optional (group "{" (zero-or-more not-newline) "}")) + (zero-or-more " ")))) + +;; NOTE: original used `\s-*` — see comment on the -case-start regex. +(defvar plantuml-indent-regexp-case-end + (plantuml-indent-regexp-meta-block-end '(group (or (seq "case" (zero-or-more blank) + "(" (zero-or-more not-newline) ")") + "endswitch")))) + +(defvar plantuml-indent-regexp-macro-end + (plantuml-indent-regexp-meta-block-end "!enddefinelong")) + +(defvar plantuml-indent-regexp-user-control-end + (plantuml-indent-regexp-meta-block-end '(seq "'" + (zero-or-more not-newline) + (zero-or-more " ") + "PLANTUML_MODE_INDENT_DECREASE") + :after '(seq (zero-or-more blank) + (zero-or-more not-newline)))) + +(defvar plantuml-indent-regexp-end (list plantuml-indent-regexp-block-end + plantuml-indent-regexp-group-end + plantuml-indent-regexp-activate-end + plantuml-indent-regexp-box-end + plantuml-indent-regexp-ref-end + plantuml-indent-regexp-legend-end + plantuml-indent-regexp-note-end + plantuml-indent-regexp-newif-end + plantuml-indent-regexp-loop-end + plantuml-indent-regexp-fork-end + plantuml-indent-regexp-case-end + plantuml-indent-regexp-title-end + plantuml-indent-regexp-header-end + plantuml-indent-regexp-footer-end + plantuml-indent-regexp-macro-end + plantuml-indent-regexp-oldif-end + plantuml-indent-regexp-user-control-end)) + (defun plantuml-init-once (&optional mode) "Ensure initialization only happens once. Use exec mode MODE to load the language details @@ -662,106 +968,59 @@ or by first querying `plantuml-get-exec-mode'." (let ((mode (or mode (plantuml-get-exec-mode)))) (unless plantuml-kwdList (plantuml-init mode) - (defvar plantuml-types-regexp (concat "^\\s *\\(" (regexp-opt plantuml-types 'words) "\\|\\<\\(note\\s +over\\|note\\s +\\(left\\|right\\|bottom\\|top\\)\\s +\\(of\\)?\\)\\>\\|\\<\\(\\(left\\|center\\|right\\)\\s +\\(header\\|footer\\)\\)\\>\\)")) - (defvar plantuml-keywords-regexp (concat "^\\s *" (regexp-opt plantuml-keywords 'words) "\\|\\(<\\|<|\\|\\*\\|o\\)\\(\\.+\\|-+\\)\\|\\(\\.+\\|-+\\)\\(>\\||>\\|\\*\\|o\\)\\|\\.\\{2,\\}\\|-\\{2,\\}")) - (defvar plantuml-builtins-regexp (regexp-opt plantuml-builtins 'words)) - (defvar plantuml-preprocessors-regexp (concat "^\\s *" (regexp-opt plantuml-preprocessors 'words))) - ;; Below are the regexp's for indentation. - ;; Notes: - ;; - there is some control on what it is indented by overriding some of below - ;; X-start and X-end regexp before plantuml-mode is loaded. E.g., to disable - ;; indentation on activate, you might define in your .emacs something like - ;; (setq plantuml-indent-regexp-activate-start - ;; "NEVER MATCH THIS EXPRESSION"); define _before_ load plantuml-mode! - ;; (setq plantuml-indent-regexp-activate-end - ;; "NEVER MATCH THIS EXPRESSION"); define _before_ load plantuml-mode! - ;; - due to the nature of using (context-insensitive) regexp, indentation have - ;; following limitations - ;; - commands commented out by /' ... '/ will _not_ be ignored - ;; and potentially lead to miss-indentation - ;; - you can though somewhat correct mis-indentation by adding in '-comment lines - ;; PLANTUML_MODE_INDENT_INCREASE and/or PLANTUML_MODE_INDENT_DECREASE - ;; to increase and/or decrease the level of indentation - ;; (Note: the line with the comment should not contain any text matching other indent - ;; regexp or this user-control instruction will be ignored; also at most will count - ;; per line ...) - (defvar plantuml-indent-regexp-block-start "^.*{\s*$" - "Indentation regex for all plantuml elements that might define a {} block. -Plantuml elements like skinparam, rectangle, sprite, package, etc. -The opening { has to be the last visible character in the line (whitespace -might follow).") - (defvar plantuml-indent-regexp-note-start "^\s*\\(floating\s+\\)?[hr]?note\s+\\(right\\|left\\|top\\|bottom\\|over\\|as\\)[^:]*\\(\\:\\:[^:]+\\)?$" "simplyfied regex; note syntax is especially inconsistent across diagrams") - (defvar plantuml-indent-regexp-group-start "^\s*\\(alt\\|else\\|opt\\|loop\\|par\\|break\\|critical\\|group\\)\\(?:\s+.+\\|$\\)" - "Indentation regex for plantuml group elements defined for sequence diagrams. -Two variants for groups: keyword is either followed by whitespace and some text -or it is followed by line end.") - (defvar plantuml-indent-regexp-activate-start "^\s*activate\s+.+$") - (defvar plantuml-indent-regexp-box-start "^\s*box\s+.+$") - (defvar plantuml-indent-regexp-ref-start "^\s*ref\s+over\s+[^:]+?$") - (defvar plantuml-indent-regexp-title-start "^\s*title\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-header-start "^\s*\\(?:\\(?:center\\|left\\|right\\)\s+header\\|header\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-footer-start "^\s*\\(?:\\(?:center\\|left\\|right\\)\s+footer\\|footer\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-legend-start "^\s*\\(?:legend\\|legend\s+\\(?:bottom\\|top\\)\\|legend\s+\\(?:center\\|left\\|right\\)\\|legend\s+\\(?:bottom\\|top\\)\s+\\(?:center\\|left\\|right\\)\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-oldif-start "^.*if\s+\".*\"\s+then\s*\\('.*\\)?$" - "used in current activity diagram, sometimes already mentioned as deprecated") - (defvar plantuml-indent-regexp-newif-start "^\s*\\(?:else\\)?if\s+(.*)\s+then\s*.*$") - (defvar plantuml-indent-regexp-loop-start "^\s*\\(?:repeat\s*\\|while\s+(.*).*\\)$") - (defvar plantuml-indent-regexp-fork-start "^\s*\\(?:fork\\|split\\)\\(?:\s+again\\)?\s*$") - (defvar plantuml-indent-regexp-case-start "^\s*\\(?:switch\\|case\\)\s-*(.*)\s*$") - (defvar plantuml-indent-regexp-macro-start "^\s*!definelong.*$") - (defvar plantuml-indent-regexp-user-control-start "^.*'.*\s*PLANTUML_MODE_INDENT_INCREASE\s*.*$") - (defvar plantuml-indent-regexp-start (list plantuml-indent-regexp-block-start - plantuml-indent-regexp-group-start - plantuml-indent-regexp-activate-start - plantuml-indent-regexp-box-start - plantuml-indent-regexp-ref-start - plantuml-indent-regexp-legend-start - plantuml-indent-regexp-note-start - plantuml-indent-regexp-newif-start - plantuml-indent-regexp-loop-start - plantuml-indent-regexp-fork-start - plantuml-indent-regexp-case-start - plantuml-indent-regexp-title-start - plantuml-indent-regexp-header-start - plantuml-indent-regexp-footer-start - plantuml-indent-regexp-macro-start - plantuml-indent-regexp-oldif-start - plantuml-indent-regexp-user-control-start)) - (defvar plantuml-indent-regexp-block-end "^\s*\\(?:}\\|endif\\|else\s*.*\\|end\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-note-end "^\s*\\(end\s+note\\|end[rh]note\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-group-end "^\s*end\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-activate-end "^\s*deactivate\s+.+$") - (defvar plantuml-indent-regexp-box-end "^\s*end\s+box\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-ref-end "^\s*end\s+ref\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-title-end "^\s*end\s+title\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-header-end "^\s*endheader\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-footer-end "^\s*endfooter\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-legend-end "^\s*endlegend\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-oldif-end "^\s*\\(endif\\|else\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-newif-end "^\s*\\(endif\\|elseif\\|else\\)\s*.*$") - (defvar plantuml-indent-regexp-loop-end "^\s*\\(repeat\s*while\\|endwhile\\)\s*.*$") - (defvar plantuml-indent-regexp-fork-end "^\s*\\(\\(fork\\|split\\)\s+again\\|end\s+\\(fork\\|split\\)\\)\s*\\(\{.*\}\\)?\s*$") - (defvar plantuml-indent-regexp-case-end "^\s*\\(case\s-*(.*)\\|endswitch\\)\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-macro-end "^\s*!enddefinelong\s*\\('.*\\)?$") - (defvar plantuml-indent-regexp-user-control-end "^.*'.*\s*PLANTUML_MODE_INDENT_DECREASE\s*.*$") - (defvar plantuml-indent-regexp-end (list plantuml-indent-regexp-block-end - plantuml-indent-regexp-group-end - plantuml-indent-regexp-activate-end - plantuml-indent-regexp-box-end - plantuml-indent-regexp-ref-end - plantuml-indent-regexp-legend-end - plantuml-indent-regexp-note-end - plantuml-indent-regexp-newif-end - plantuml-indent-regexp-loop-end - plantuml-indent-regexp-fork-end - plantuml-indent-regexp-case-end - plantuml-indent-regexp-title-end - plantuml-indent-regexp-header-end - plantuml-indent-regexp-footer-end - plantuml-indent-regexp-macro-end - plantuml-indent-regexp-oldif-end - plantuml-indent-regexp-user-control-end)) + ;; Font-lock regexes are built at init time because the keyword lists + ;; come from `plantuml.jar -language' output. `rx-to-string' is used + ;; instead of the `rx' macro so that `regexp-opt' results can be + ;; spliced in via the `(regexp ,…)' form. + (defvar plantuml-types-regexp + (rx-to-string + `(seq line-start + (zero-or-more blank) + (group + (or (regexp ,(regexp-opt plantuml-types 'words)) + (seq word-start + (group + (or (seq "note" (one-or-more blank) "over") + (seq "note" (one-or-more blank) + (group (or "left" "right" "bottom" "top")) + (one-or-more blank) + (optional (group "of"))))) + word-end) + (seq word-start + (group (group (or "left" "center" "right")) + (one-or-more blank) + (group (or "header" "footer"))) + word-end)))) + t)) + + (defvar plantuml-keywords-regexp + (rx-to-string + `(or (seq line-start + (zero-or-more blank) + (regexp ,(regexp-opt plantuml-keywords 'words))) + (seq (group (or "<" "<|" "*" "o")) + (group (or (one-or-more ".") (one-or-more "-")))) + (seq (group (or (one-or-more ".") (one-or-more "-"))) + (group (or ">" "|>" "*" "o"))) + (>= 2 ".") + (>= 2 "-")) + t)) + + ;; NOTE: kept as a plain `regexp-opt' call — wrapping a single + ;; `regexp-opt' in `rx-to-string' adds noise without aiding readability. + (defvar plantuml-builtins-regexp (regexp-opt plantuml-builtins 'words)) + + (defvar plantuml-preprocessors-regexp + (rx-to-string + `(seq line-start + (zero-or-more (syntax whitespace)) + (regexp ,(regexp-opt plantuml-preprocessors 'words))) + t)) + + ;; Indent regexps and their compounding lists are defined at the top + ;; level; see them above this function. + (setq plantuml-font-lock-keywords `( (,plantuml-types-regexp . font-lock-type-face) diff --git a/lisp/polymode/polymode-pkg.el b/lisp/polymode/polymode-pkg.el index d9cf757d..db429012 100644 --- a/lisp/polymode/polymode-pkg.el +++ b/lisp/polymode/polymode-pkg.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "polymode" "20260302.1043" +(define-package "polymode" "20260505.1803" "Extensible framework for multiple major modes." '((emacs "25")) :url "https://github.com/polymode/polymode" - :commit "4604f55cc020c75562526fb76b723e5e242c97c0" - :revdesc "4604f55cc020" + :commit "8cb72fa5dcc0d98746c680043dc121edc7621e3a" + :revdesc "8cb72fa5dcc0" :keywords '("languages" "multi-modes" "processes") :maintainers '(("Vitalie Spinu" . "spinuvit@gmail.com"))) diff --git a/lisp/polymode/polymode.el b/lisp/polymode/polymode.el index 9507b4af..a6ba964e 100644 --- a/lisp/polymode/polymode.el +++ b/lisp/polymode/polymode.el @@ -3,8 +3,8 @@ ;; Author: Vitalie Spinu ;; Maintainer: Vitalie Spinu ;; Copyright (C) 2013-2022 Free Software Foundation, Inc. -;; Package-Version: 20260302.1043 -;; Package-Revision: 4604f55cc020 +;; Package-Version: 20260505.1803 +;; Package-Revision: 8cb72fa5dcc0 ;; Package-Requires: ((emacs "25")) ;; URL: https://github.com/polymode/polymode ;; Keywords: languages, multi-modes, processes @@ -620,7 +620,7 @@ most frequently used slots are: (eieio-oref parent-conf '-minor-mode)))) ;; 3. nil (t polymode-minor-mode-map))))) - (apply #'define-keymap :parent parent-map keymap))) + (easy-mmode-define-keymap keymap nil nil (list :inherit parent-map)))) ,(format "Keymap for %s." mode-name)) diff --git a/lisp/posframe/posframe-pkg.el b/lisp/posframe/posframe-pkg.el index 9d5503b8..37b1aa45 100644 --- a/lisp/posframe/posframe-pkg.el +++ b/lisp/posframe/posframe-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "posframe" "20260302.251" +(define-package "posframe" "20260527.857" "Pop a posframe (just a frame) at point." '((emacs "26.1")) :url "https://github.com/tumashu/posframe" - :commit "3a80911b2f45ce6926196930bb7d5cc662c7b3c8" - :revdesc "3a80911b2f45" + :commit "74c8c56131ed866db47ae4191364b72dd4852456" + :revdesc "74c8c56131ed" :keywords '("convenience" "tooltip") :authors '(("Feng Shu" . "tumashu@163.com")) :maintainers '(("Feng Shu" . "tumashu@163.com"))) diff --git a/lisp/posframe/posframe.el b/lisp/posframe/posframe.el index 530d734a..4b2e98dc 100644 --- a/lisp/posframe/posframe.el +++ b/lisp/posframe/posframe.el @@ -5,8 +5,8 @@ ;; Author: Feng Shu ;; Maintainer: Feng Shu ;; URL: https://github.com/tumashu/posframe -;; Package-Version: 20260302.251 -;; Package-Revision: 3a80911b2f45 +;; Package-Version: 20260527.857 +;; Package-Revision: 74c8c56131ed ;; Keywords: convenience, tooltip ;; Package-Requires: ((emacs "26.1")) @@ -115,7 +115,7 @@ frame.") (defvar posframe-gtk-resize-child-frames (when (and - (> emacs-major-version 26) + (< emacs-major-version 29) (string-match-p "GTK3" system-configuration-features) (let ((value (or (getenv "XDG_CURRENT_DESKTOP") (getenv "DESKTOP_SESSION")))) (and (stringp value) @@ -433,6 +433,7 @@ You can use `posframe-delete-all' to delete all posframes." 0)) (mouse-position (cdr (mouse-pixel-position))) (frame-resize-pixelwise t) + pixel-position posframe) (with-current-buffer buffer @@ -474,63 +475,76 @@ You can use `posframe-delete-all' to delete all posframes." ;; Insert string into the posframe buffer (posframe--insert-string string no-properties) - (let ((size-info - (list :posframe posframe - :width width - :height height - :max-width max-width - :max-height max-height - :min-width min-width - :min-height min-height))) - ;; Set posframe's size - (posframe--set-frame-size size-info) + (let* ((size-info + (list :posframe posframe + :width width + :height height + :max-width max-width + :max-height max-height + :min-width min-width + :min-height min-height)) + (position-info + (list :position position + :poshandler poshandler + :font-height font-height + :font-width font-width + :posframe posframe + :posframe-buffer buffer + :parent-frame parent-frame + :parent-frame-width parent-frame-width + :parent-frame-height parent-frame-height + :ref-position ref-position + :parent-window parent-window + :parent-window-start parent-window-start + :parent-window-end parent-window-end + :parent-window-top parent-window-top + :parent-window-left parent-window-left + :parent-window-width parent-window-width + :parent-window-height parent-window-height + :mouse-x (car mouse-position) + :mouse-y (cdr mouse-position) + :mode-line-height mode-line-height + :minibuffer-height minibuffer-height + :header-line-height header-line-height + :tab-line-height tab-line-height + :x-pixel-offset x-pixel-offset + :y-pixel-offset y-pixel-offset + :parent-text-scale-mode-amount parent-text-scale-mode-amount))) + (setq pixel-position (and + width + height + (posframe-run-poshandler + `(,@poshandler-extra-info + ,@position-info + :posframe-width ,(* (default-font-width) width) + :posframe-height ,(* (default-line-height) height))))) + (if (and width height + (fboundp 'set-frame-size-and-position-pixelwise) + (frame-visible-p posframe) + (not (equal + pixel-position + posframe--last-posframe-pixel-position))) + (posframe--set-frame-size-and-position + size-info + pixel-position + parent-frame-width parent-frame-height) + ;; Set posframe's size + (posframe--set-frame-size size-info) + ;; Get new position of posframe. + (unless pixel-position + (setq pixel-position + (posframe-run-poshandler + ;; All poshandlers will get info from this plist. + `(,@poshandler-extra-info + ,@position-info + :posframe-width ,(frame-pixel-width posframe) + :posframe-height ,(frame-pixel-height posframe))))) + ;; Move posframe + (posframe--set-frame-position + posframe pixel-position parent-frame-width parent-frame-height)) ;; Re-adjust posframe's size when buffer's content has changed. (posframe--run-refresh-timer refresh size-info)) - ;; Get new position of posframe. - (setq position - (posframe-run-poshandler - ;; All poshandlers will get info from this plist. - `(,@poshandler-extra-info - ,@(list :position position - :poshandler poshandler - :font-height font-height - :font-width font-width - :posframe posframe - :posframe-width - (if width - (* (default-font-width) width) - (frame-pixel-width posframe)) - :posframe-height - (if height - (* (default-line-height) height) - (frame-pixel-height posframe)) - :posframe-buffer buffer - :parent-frame parent-frame - :parent-frame-width parent-frame-width - :parent-frame-height parent-frame-height - :ref-position ref-position - :parent-window parent-window - :parent-window-start parent-window-start - :parent-window-end parent-window-end - :parent-window-top parent-window-top - :parent-window-left parent-window-left - :parent-window-width parent-window-width - :parent-window-height parent-window-height - :mouse-x (car mouse-position) - :mouse-y (cdr mouse-position) - :mode-line-height mode-line-height - :minibuffer-height minibuffer-height - :header-line-height header-line-height - :tab-line-height tab-line-height - :x-pixel-offset x-pixel-offset - :y-pixel-offset y-pixel-offset - :parent-text-scale-mode-amount parent-text-scale-mode-amount)))) - - ;; Move posframe - (posframe--set-frame-position - posframe position parent-frame-width parent-frame-height) - ;; Delay hide posframe when timeout is a number. (posframe--run-timeout-timer posframe timeout) @@ -557,13 +571,13 @@ You can use `posframe-delete-all' to delete all posframes." (+ (or (cdr ref-position) 0) (cdr mouse-position))) :posframe-x - (if (>= (car position) 0) - (car position) + (if (>= (car pixel-position) 0) + (car pixel-position) (- (frame-pixel-width parent-frame) (frame-pixel-width posframe))) :posframe-y - (if (>= (cdr position) 0) - (cdr position) + (if (>= (cdr pixel-position) 0) + (cdr pixel-position) (- (frame-pixel-height parent-frame) (frame-pixel-height posframe))) :posframe-width (frame-pixel-width posframe) @@ -628,6 +642,13 @@ ACCEPT-FOCUS." ;; http://git.savannah.gnu.org/cgit/emacs.git/commit/?id=ff7b1a133bfa7f2614650f8551824ffaef13fadc (border-width (or border-width internal-border-width 0)) (border-color (or border-color internal-border-color)) + (border-face + ;; NOTE: when use refposhander feature, parent-frame will be + ;; nil, we should use internal-border instead. + (if (and (facep 'child-frame-border) + parent-frame) + 'child-frame-border + 'internal-border)) (buffer (get-buffer-create buffer-or-name)) (after-make-frame-functions nil) (x-gtk-resize-child-frames posframe-gtk-resize-child-frames) @@ -745,24 +766,6 @@ ACCEPT-FOCUS." (set-frame-parameter posframe--frame 'font (or font (face-attribute 'default :font parent-frame))) - (when border-color - (if parent-frame - (set-face-background - (if (facep 'child-frame-border) - 'child-frame-border - 'internal-border) - border-color posframe--frame) - ;; NOTE: when use refposhander feature, parent-frame will be - ;; nil, we should use internal-border instead. - (set-face-background - 'internal-border - border-color posframe--frame)) - ;; HACK: Set face background after border color, otherwise the - ;; border is not updated (BUG!). - (when (version< emacs-version "28.0") - (set-frame-parameter - posframe--frame 'background-color - (or background-color (face-attribute 'default :background))))) (let ((posframe-window (frame-root-window posframe--frame))) ;; This method is more stable than 'setq mode/header-line-format nil' (unless respect-mode-line @@ -774,6 +777,18 @@ ACCEPT-FOCUS." ;; this posframe will be deleted too. (set-window-dedicated-p posframe-window t))) + (when (and border-color + ;; Work around https://debbugs.gnu.org/80871 + (not (equal (face-background border-face posframe--frame) + border-color))) + (set-face-background border-face border-color posframe--frame) + ;; HACK: Set face background after border color, otherwise the + ;; border is not updated (BUG!). + (when (version< emacs-version "28.0") + (set-frame-parameter + posframe--frame 'background-color + (or background-color (face-attribute 'default :background))))) + ;; Remove tab-bar always. ;; NOTE: if we do not test the value of frame parameter ;; 'tab-bar-lines before set it, posframe will flicker when @@ -869,6 +884,22 @@ will be removed." (height 'horizontally)))) (setq-local posframe--last-posframe-size size-info))) +(defun posframe--set-frame-size-and-position (size-info position parent-frame-width parent-frame-height) + (let ((posframe (plist-get size-info :posframe)) + (width (plist-get size-info :width)) + (height (plist-get size-info :height))) + (unless (and width height) + (user-error "Width and height must be specified together")) + (set-frame-size-and-position-pixelwise + posframe + (* (default-font-width) width) + (* (default-line-height) height) + (car position) (cdr position)) + (posframe--save-new-posframe-position posframe position + parent-frame-width + parent-frame-height) + (posframe--make-frame-visible posframe))) + (defun posframe--fit-frame-to-buffer (posframe max-height min-height max-width min-width only) "POSFRAME version of function `fit-frame-to-buffer'. Arguments HEIGHT, MAX-HEIGHT, MIN-HEIGHT, WIDTH, MAX-WIDTH, @@ -959,19 +990,28 @@ This need PARENT-FRAME-WIDTH and PARENT-FRAME-HEIGHT" ;; the posframe. (equal posframe--last-parent-frame-size (cons parent-frame-width parent-frame-height)) - (equal posframe--last-posframe-displayed-size - (cons (frame-pixel-width posframe) - (frame-pixel-height posframe)))) - (unless (equal (frame-position posframe) position) - (set-frame-position posframe (car position) (cdr position))) - (setq-local posframe--last-posframe-pixel-position position) - (setq-local posframe--last-parent-frame-size - (cons parent-frame-width parent-frame-height)) - (setq-local posframe--last-posframe-displayed-size - (cons (frame-pixel-width posframe) - (frame-pixel-height posframe)))) + (or (and (>= (car position) 0) + (>= (cdr position) 0)) + (equal posframe--last-posframe-displayed-size + (cons (frame-pixel-width posframe) + (frame-pixel-height posframe))))) + (set-frame-position posframe (car position) (cdr position)) + (posframe--save-new-posframe-position posframe position + parent-frame-width + parent-frame-height)) (posframe--make-frame-visible posframe)) +(defun posframe--save-new-posframe-position (posframe + position + parent-frame-width + parent-frame-height) + (setq-local posframe--last-posframe-pixel-position position) + (setq-local posframe--last-parent-frame-size + (cons parent-frame-width parent-frame-height)) + (setq-local posframe--last-posframe-displayed-size + (cons (frame-pixel-width posframe) + (frame-pixel-height posframe)))) + (defun posframe--make-frame-visible (posframe) "Let POSFRAME visible and redraw it when needed." (unless (frame-visible-p posframe) diff --git a/lisp/simple-httpd/simple-httpd-pkg.el b/lisp/simple-httpd/simple-httpd-pkg.el index 5c76bd0e..5d7bb4f2 100644 --- a/lisp/simple-httpd/simple-httpd-pkg.el +++ b/lisp/simple-httpd/simple-httpd-pkg.el @@ -1,9 +1,12 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "simple-httpd" "20230821.1458" - "Pure elisp HTTP server." - '((cl-lib "0.3")) +(define-package "simple-httpd" "20260623.1110" + "Pure Elisp HTTP server." + '((emacs "29.1") + (compat "31")) :url "https://github.com/skeeto/emacs-http-server" - :commit "347c30494d3bcfc79de35e54538f92f4e4a46ecd" - :revdesc "347c30494d3b" + :commit "ceb208f96601be09397fc9e64fa96014ac1c8739" + :revdesc "ceb208f96601" + :keywords '("network" "comm") :authors '(("Christopher Wellons" . "wellons@nullprogram.com")) - :maintainers '(("Christopher Wellons" . "wellons@nullprogram.com"))) + :maintainers '(("Philip Kaludercic" . "philipk@posteo.net") + ("Daniel Mendler" . "mail@daniel-mendler.de"))) diff --git a/lisp/simple-httpd/simple-httpd.el b/lisp/simple-httpd/simple-httpd.el index de541525..7cd2eef7 100644 --- a/lisp/simple-httpd/simple-httpd.el +++ b/lisp/simple-httpd/simple-httpd.el @@ -1,73 +1,75 @@ -;;; simple-httpd.el --- pure elisp HTTP server +;;; simple-httpd.el --- Pure Elisp HTTP server -*- lexical-binding: t -*- ;; This is free and unencumbered software released into the public domain. ;; Author: Christopher Wellons +;; Maintainer: Philip Kaludercic , Daniel Mendler ;; URL: https://github.com/skeeto/emacs-http-server -;; Package-Version: 20230821.1458 -;; Package-Revision: 347c30494d3b -;; Package-Requires: ((cl-lib "0.3")) +;; Package-Version: 20260623.1110 +;; Package-Revision: ceb208f96601 +;; Package-Requires: ((emacs "29.1") (compat "31")) +;; Keywords: network, comm ;;; Commentary: -;; Use `httpd-start' to start the web server. Files are served from +;; Use `httpd-start' to start the web server. Files are served from ;; `httpd-root' on port `httpd-port' using `httpd-ip-family' at host -;; `httpd-host'. While the root can be changed at any time, the server +;; `httpd-host'. While the root can be changed at any time, the server ;; needs to be restarted in order for a port change to take effect. ;; Everything is performed by servlets, including serving -;; files. Servlets are enabled by setting `httpd-servlets' to true -;; (default). Servlets are four-parameter functions that begin with +;; files. Servlets are enabled by setting `httpd-servlets' to true +;; (default). Servlets are four-parameter functions that begin with ;; "httpd/" where the trailing component specifies the initial path on -;; the server. For example, the function `httpd/hello-world' will be +;; the server. For example, the function `httpd/hello-world' will be ;; called for the request "/hello-world" and "/hello-world/foo". ;; The default servlet `httpd/' is the one that serves files from ;; `httpd-root' and can be turned off through redefinition or setting -;; `httpd-serve-files' to nil. It is used even when `httpd-servlets' +;; `httpd-serve-files' to nil. It is used even when `httpd-servlets' ;; is nil. ;; The four parameters for a servlet are process, URI path, GET/POST ;; arguments (alist), and the full request object (header -;; alist). These are ordered by general importance so that some can be -;; ignored. Two macros are provided to help with writing servlets. +;; alist). These are ordered by general importance so that some can be +;; ignored. Two macros are provided to help with writing servlets. -;; * `with-httpd-buffer' -- Creates a temporary buffer that is +;; * `httpd-with-buffer' -- Creates a temporary buffer that is ;; automatically served to the client at the end of the body. ;; Additionally, `standard-output' is set to this output -;; buffer. For example, this servlet says hello, +;; buffer. For example, this servlet says hello, ;; (defun httpd/hello-world (proc path &rest args) -;; (with-httpd-buffer proc "text/plain" +;; (httpd-with-buffer proc "text/plain" ;; (insert "hello, " (file-name-nondirectory path)))) ;; This servlet be viewed at http://localhost:8080/hello-world/Emacs -;; * `defservlet' -- Similar to the above macro but totally hides the -;; process object from the servlet itself. The above servlet can be +;; * `httpd-servlet' -- Similar to the above macro but totally hides the +;; process object from the servlet itself. The above servlet can be ;; re-written identically like so, -;; (defservlet hello-world text/plain (path) +;; (httpd-servlet hello-world text/plain (path) ;; (insert "hello, " (file-name-nondirectory path))) -;; Note that `defservlet' automatically sets `httpd-current-proc'. See -;; below. +;; Note that `httpd-servlet' automatically sets `httpd-current-proc'. +;; See below. ;; The "function parameters" part can be left empty or contain up to ;; three parameters corresponding to the final three servlet -;; parameters. For example, a servlet that shows *scratch* and doesn't +;; parameters. For example, a servlet that shows *scratch* and doesn't ;; need parameters, -;; (defservlet scratch text/plain () +;; (httpd-servlet scratch text/plain () ;; (insert-buffer-substring (get-buffer-create "*scratch*"))) -;; A higher level macro `defservlet*' wraps this lower-level -;; `defservlet' macro, automatically binding variables to components -;; of the request. For example, this binds parts of the request path -;; and one query parameter. Request components not provided by the +;; A higher level macro `httpd-servlet*' wraps this lower-level +;; `httpd-servlet' macro, automatically binding variables to components +;; of the request. For example, this binds parts of the request path +;; and one query parameter. Request components not provided by the ;; client are bound to nil. -;; (defservlet* packages/:package/:version text/plain (verbose) +;; (httpd-servlet* packages/:package/:version text/plain (verbose) ;; (insert (format "%s\n%s\n" package version)) ;; (princ (get-description package version)) ;; (when verbose @@ -84,118 +86,85 @@ ;; * `httpd-redirect' -- redirect the browser to another url ;; * `httpd-send-header' -- send custom headers ;; * `httpd-error' -- report an error to the client -;; * `httpd-log' -- log an object to *httpd* +;; * `httpd-log' -- log an object to the `httpd-log-buffer' ;; Some of these functions require a process object, which isn't -;; passed to `defservlet' servlets. Use t in place of the process +;; passed to `httpd-servlet' servlets. Use t in place of the process ;; argument to use `httpd-current-proc' (like `standard-output'). ;; If you just need to serve static from some location under some -;; route on the server, use `httpd-def-file-servlet'. It expands into -;; a `defservlet' that serves files. - -;;; History: - -;; Version 1.5.1: improvements -;; * Add `httpd-running-p' -;; * Properly handle "Connection: close" and HTTP/1.0 -;; Version 1.5.0: improvements -;; * Drastically improved performance for large requests -;; * More HTTP status codes -;; Version 1.4.6: fixes -;; * Added httpd-serve-directory -;; * Fix some encoding issues -;; Version 1.4.5: fixes -;; * Update to cl-lib from cl -;; Version 1.4.4: features -;; * Common Lisp &key-like defservlet* argument support -;; * Fix up some defservlet* usage warnings. -;; Version 1.4.3: features -;; * Add `httpd-discard-buffer' -;; * Add `httpd-def-file-servlet' -;; * Be more careful about not sending extra headers -;; Version 1.4.2: features, fixes -;; * `defservlet*' macro -;; Version 1.4.1: small bug fixes, one feature -;; * All mime-type parameters now accept string designators -;; * Documentation update -;; Version 1.4.0: features, API change, and fixes -;; * Removed httpd-send-buffer; httpd-send-header now does this implicitly -;; * httpd-send-header now accepts keywords instead -;; * Fix httpd-clean-path in Windows -;; * Fix a content-length bug -;; * defservlet fontification -;; Version 1.3.1: features and fixes -;; * Set `standard-output' in `with-httpd-buffer' -;; Version 1.3.0: security fix -;; * Fix path expansion security issue -;; * Fix coding system (don't default) -;; Version 1.2.4: fixes -;; * Handle large POSTs -;; * Fix date strings +;; route on the server, use `httpd-file-servlet'. It expands into +;; a `httpd-servlet' that serves files. ;;; Code: +(eval-when-compile (require 'subr-x)) (require 'cl-lib) (require 'pp) (require 'url-util) +(require 'compat) (defgroup simple-httpd nil "A simple web server." - :group 'comm) + :link '(url-link :tag "Website" "https://github.com/skeeto/emacs-http-server") + :link '(emacs-library-link :tag "Library Source" "simple-httpd.el") + :group 'network + :group 'comm + :group 'web + :prefix "httpd-") (defcustom httpd-ip-family 'ipv4 "Web server IP family used by `make-network-process'." - :group 'simple-httpd :type 'symbol) (defcustom httpd-host nil "Web server host name used by `make-network-process'." - :group 'simple-httpd :type '(choice (const nil) (const local) string)) (defcustom httpd-port 8080 "Web server port." - :group 'simple-httpd - :type 'integer) + :type 'natnum) (defcustom httpd-root "~/public_html" "Web server file root." - :group 'simple-httpd - :type 'directory) + :type '(choice (const nil) directory)) (defcustom httpd-serve-files t "Enable serving files from `httpd-root'." - :group 'simple-httpd :type 'boolean) (defcustom httpd-listings t "If true, serve directory listings." - :group 'simple-httpd :type 'boolean) (defcustom httpd-servlets t "Enable servlets." - :group 'simple-httpd :type 'boolean) +(defcustom httpd-log-buffer "*httpd*" + "Buffer for log messages. +Set to nil to disable logging." + :type '(choice (const nil) string)) + (defcustom httpd-show-backtrace-when-error nil "If true, show backtrace on error page." - :group 'simple-httpd :type 'boolean) (defcustom httpd-start-hook nil "Hook to run when the server has started." - :group 'simple-httpd :type 'hook) (defcustom httpd-stop-hook nil "Hook to run when the server has stopped." - :group 'simple-httpd :type 'hook) -(defvar httpd-server-name (format "simple-httpd (Emacs %s)" emacs-version) - "String to use in the Server header.") +(defcustom httpd-filter-functions nil + "Functions called with request as argument, should return modified request." + :type 'hook) + +(defcustom httpd-server-name (format "simple-httpd (Emacs %s)" emacs-version) + "String to use in the Server header." + :type '(choice (const nil) string)) (defvar httpd-mime-types '(("png" . "image/png") @@ -204,18 +173,20 @@ ("jpeg" . "image/jpeg") ("tif" . "image/tif") ("tiff" . "image/tiff") + ("webp" . "image/webp") ("ico" . "image/x-icon") ("svg" . "image/svg+xml") - ("css" . "text/css; charset=utf-8") - ("htm" . "text/html; charset=utf-8") - ("html" . "text/html; charset=utf-8") - ("xml" . "text/xml; charset=utf-8") - ("rss" . "text/xml; charset=utf-8") - ("atom" . "text/xml; charset=utf-8") - ("txt" . "text/plain; charset=utf-8") - ("el" . "text/plain; charset=utf-8") - ("js" . "text/javascript; charset=utf-8") - ("md" . "text/x-markdown; charset=utf-8") + ("css" . "text/css") + ("htm" . "text/html") + ("html" . "text/html") + ("xml" . "text/xml") + ("rss" . "text/xml") + ("atom" . "text/xml") + ("txt" . "text/plain") + ("el" . "text/plain") + ("js" . "text/javascript") + ("md" . "text/markdown") + ("org" . "text/org") ("gz" . "application/octet-stream") ("ps" . "application/postscript") ("eps" . "application/postscript") @@ -308,266 +279,364 @@ "HTTP status codes.") (defvar httpd-html - '((403 . " - -403 Forbidden - -

Forbidden

-

The requested URL is forbidden.

-
%s
-") - (404 . " - -404 Not Found - -

Not Found

+ '((404 . " +%2$s %3$s +

%2$s %3$s

The requested URL was not found on this server.

-
%s
-") - (500 . " - -500 Internal Error - -

500 Internal Error

-

Internal error when handling this request.

-
%s
+
%1$s
") + (t . " +%2$s %3$s +

%2$s %3$s

+

An error occurred.

+
%1$s
")) "HTML for various errors.") +(defvar httpd--server nil + "Server process.") + +(defvar httpd--clients nil + "Client processes.") + ;; User interface ;;;###autoload (defun httpd-start () - "Start the web server process. If the server is already -running, this will restart the server. There is only one server -instance per Emacs instance." + "Start the web server process. +If the server is already running, this will restart the server. There +is only one server instance per Emacs instance." (interactive) (httpd-stop) - (httpd-log `(start ,(current-time-string))) - (make-network-process - :name "httpd" - :service httpd-port - :server t - :host httpd-host - :family httpd-ip-family - :filter 'httpd--filter - :coding 'binary - :log 'httpd--log) + (httpd-log `(start ,(httpd-date-string))) + (setq httpd--server + (make-network-process + :name "httpd" + :service httpd-port + :server t + :host httpd-host + :family httpd-ip-family + :filter #'httpd--filter + :coding 'binary + :log #'httpd--accept)) (run-hooks 'httpd-start-hook)) ;;;###autoload (defun httpd-stop () "Stop the web server if it is currently running, otherwise do nothing." (interactive) - (when (process-status "httpd") - (delete-process "httpd") - (httpd-log `(stop ,(current-time-string))) + (when (httpd-running-p) + (mapc #'delete-process httpd--clients) + (delete-process httpd--server) + (setq httpd--server nil + httpd--clients nil) + (httpd-log `(stop ,(httpd-date-string))) (run-hooks 'httpd-stop-hook))) ;;;###autoload (defun httpd-running-p () "Return non-nil if the simple-httpd server is running." - (not (null (process-status "httpd")))) + (process-live-p httpd--server)) ;;;###autoload -(defun httpd-serve-directory (directory) - "Start the web server with given `directory' as `httpd-root'." +(defun httpd-serve-directory (&optional directory) + "Start the web server with given DIRECTORY as `httpd-root'. +If DIRECTORY is nil use the current `default-directory'." (interactive "DServe directory: \n") - (setf httpd-root directory) + (setq httpd-root (or directory default-directory)) (httpd-start) (message "Started simple-httpd on %s:%d, serving: %s" (cl-case httpd-host - ((nil) "0.0.0.0") - ((local) "localhost") - (otherwise httpd-host)) httpd-port directory)) + ((nil local) "localhost") + (otherwise httpd-host)) + httpd-port directory)) (defun httpd-batch-start () "Never returns, holding the server open indefinitely for batch mode. -Logs are redirected to stdout. To use, invoke Emacs like this: -emacs -Q -batch -l simple-httpd.elc -f httpd-batch-start" - (if (not noninteractive) - (error "Only use `httpd-batch-start' in batch mode!") - (httpd-start) - (defalias 'httpd-log 'pp) - (while t (sleep-for 60)))) +Logs are redirected to stdout. To use, invoke Emacs like this: + \"emacs -Q -batch -l simple-httpd.elc -f httpd-batch-start\"" + (unless noninteractive + (error "Only use `httpd-batch-start' in batch mode")) + (httpd-start) + (fset #'httpd-log #'pp) + (while t (sleep-for 60))) ;; Utility (defun httpd-date-string (&optional date) - "Return an HTTP date string (RFC 1123)." - (format-time-string "%a, %e %b %Y %T GMT" date t)) + "Return DATE as HTTP date string (RFC 1123)." + (format-time-string "%a, %d %b %Y %T GMT" date t)) (defun httpd-etag (file) "Compute the ETag for FILE." (concat "\"" (substring (sha1 (prin1-to-string (file-attributes file))) -16) "\"")) -(defun httpd--stringify (designator) - "Turn a string designator into a string." - (let ((string (format "%s" designator))) - (if (keywordp designator) - (substring string 1) - string))) +(defun httpd--stringify (obj) + "Turn OBJ into a string, e.g., symbols, keywords or strings." + (cond + ((stringp obj) obj) + ((keywordp obj) (substring (symbol-name obj) 1)) + ((symbolp obj) (symbol-name obj)) + (t (format "%s" obj)))) ;; Networking code (defun httpd--connection-close-p (request) - "Return non-nil if the client requested \"connection: close\"." - (or (equal '("close") (cdr (assoc "Connection" request))) - (equal '("HTTP/1.0") (cddr (assoc "GET" request))))) + "Return non-nil if the REQUEST has \"connection: close\"." + (let ((conn (cadr (assoc "Connection" request)))) + (or (and (stringp conn) (string-equal-ignore-case conn "close")) + (equal "HTTP/1.0" (caddar request))))) + +(defun httpd--parse-content-args (request) + "Parse arguments in content string of REQUEST." + (when-let* ((content-type (cadr (assoc "Content-Type" request))) + ((string-prefix-p "application/x-www-form-urlencoded" + content-type))) + (httpd-parse-args (cadr (assoc "Content" request))))) + +(defun httpd--handle-request (proc request) + "Handle REQUEST from client PROC." + (condition-case err + (let* ((_ (run-hook-wrapped + 'httpd-filter-functions + (lambda (fun) + (setq request (funcall fun request)) + nil))) + (uri (cadar request)) + (parsed-uri (httpd-parse-uri (concat uri))) + (uri-path (car parsed-uri)) + (uri-query (nconc (cadr parsed-uri) + (httpd--parse-content-args request))) + (servlet (httpd-get-servlet uri-path))) + (httpd-log `(request + (date ,(httpd-date-string)) + (address ,(car (process-contact proc))) + (path ,uri-path) + (query ,uri-query) + (servlet ,servlet) + (headers . ,request))) + (process-put proc :request-active request) + (funcall servlet proc uri-path uri-query request)) + (error + (httpd--error-safe proc 500 err)))) + +(defun httpd--content-string (len) + "Return request content of length LEN if it is fully transmitted. +Return nil if transmission is incomplete." + (if (> len 0) + (when (>= (buffer-size) len) + (prog1 (buffer-substring (point-min) (setq len (+ (point-min) len))) + (delete-region (point-min) len))) + "")) + +(defun httpd--content-length (request) + "Return content length of REQUEST." + (let* ((te (cadr (assoc "Transfer-Encoding" request))) + (len (cadr (assoc "Content-Length" request)))) + (when (and (or (not te) (string-equal-ignore-case te "identity")) + (or (not len) (string-match-p "\\`[0-9]+\\'" len))) + (if len (string-to-number len) 0)))) (defun httpd--filter (proc chunk) - "Runs each time client makes a request." + "Process called each time client makes a request. +PROC is the client process and CHUNK is part of the request as string." (with-current-buffer (process-get proc :request-buffer) (goto-char (point-max)) (insert chunk) - (let ((request (process-get proc :request))) - (unless request - (when (setf request (httpd-parse)) - (delete-region (point-min) (point)) - (process-put proc :request request))) - (when request - (let ((content-length (cadr (assoc "Content-Length" request)))) - (when (or (null content-length) - (= (buffer-size) (string-to-number content-length))) - (let* ((content (buffer-string)) - (uri (cl-cadar request)) - (parsed-uri (httpd-parse-uri (concat uri))) - (uri-path (httpd-unhex (nth 0 parsed-uri))) - (uri-query (append (nth 1 parsed-uri) - (httpd-parse-args content))) - (servlet (httpd-get-servlet uri-path))) - (erase-buffer) - (process-put proc :request nil) - (setf request (nreverse (cons (list "Content" content) - (nreverse request)))) - (httpd-log `(request (date ,(httpd-date-string)) - (address ,(car (process-contact proc))) - (get ,uri-path) - ,(cons 'headers request))) - (if (null servlet) - (httpd--error-safe proc 404) - (condition-case error-case - (funcall servlet proc uri-path uri-query request) - (error (httpd--error-safe proc 500 error-case)))) - (when (httpd--connection-close-p request) - (process-send-eof proc))))))))) + (let ((continue t) (request nil)) + (while continue + (setq continue nil + request (process-get proc :request-pending)) + (when (and (not request) (setq request (httpd-parse))) + (process-put proc :request-pending request) + (delete-region (point-min) (point))) + (cond + (request + (if-let* ((len (httpd--content-length request))) + (when-let* ((content (httpd--content-string len))) + (process-put proc :request-pending nil) + (httpd--push-request proc (nconc request `(("Content" ,content)))) + (setq continue t)) + (httpd--push-request proc `(("GET" "/error?status=411" "HTTP/1.1") + ("Connection" "close"))))) + ((looking-at-p "[^\r\n]*[\r\n]") + (httpd--push-request proc '(("GET" "/error?status=400" "HTTP/1.1") + ("Connection" "close")))))))) + (httpd--pop-request proc)) -(defun httpd--log (server proc message) - "Runs each time a new client connects." - (with-current-buffer (generate-new-buffer " *httpd-client*") - (process-put proc :request-buffer (current-buffer))) +(defun httpd--push-request (proc request) + "Push REQUEST to client PROC queue." + (cl-callf (lambda (q) (nconc q (list request))) + (process-get proc :request-queue))) + +(defun httpd--pop-request (proc) + "Pop request from client PROC queue and handle it." + (when-let* (((not (process-get proc :request-active))) + (request (pop (process-get proc :request-queue)))) + (run-at-time 0 nil #'httpd--handle-request proc request))) + +(defun httpd--accept (_server proc _message) + "Runs each time a new client PROC connects to the server." + (push proc httpd--clients) + (process-put proc :request-buffer (generate-new-buffer " *httpd-client*" t)) (set-process-sentinel proc #'httpd--sentinel) - (httpd-log (list 'connection (car (process-contact proc))))) + (httpd-log `(connection ,(car (process-contact proc))))) (defun httpd--sentinel (proc message) - "Runs when a client closes the connection." - (unless (string-match-p "^open " message) - (let ((buffer (process-get proc :request-buffer))) - (when buffer - (kill-buffer buffer))))) + "Runs when a client PROC closes the connection. +MESSAGE describes the state change." + (unless (string-prefix-p "open " message) + (httpd-log `(close ,(car (process-contact proc)))) + (cl-callf2 delq proc httpd--clients) + (when-let* ((buffer (process-get proc :request-buffer))) + (kill-buffer buffer)))) ;; Logging +(defun httpd--log (item) + "Pretty print ITEM to the log." + (with-current-buffer (get-buffer-create httpd-log-buffer) + (setq buffer-read-only t + truncate-lines t) + (with-silent-modifications + (let* ((win (get-buffer-window)) + (follow (and win (= (window-point win) (point-max))))) + (save-excursion + (goto-char (point-max)) + (pp item (current-buffer))) + (when follow + (set-window-point win (point-max))))))) + (defun httpd-log (item) - "Pretty print a lisp object to the log." - (with-current-buffer (get-buffer-create "*httpd*") - (setf buffer-read-only nil) - (let ((follow (= (point) (point-max)))) - (save-excursion - (goto-char (point-max)) - (pp item (current-buffer))) - (if follow (goto-char (point-max)))) - (setf truncate-lines t - buffer-read-only t) - (set-buffer-modified-p nil))) + "Pretty print ITEM to the log. +If `httpd-log-buffer' is nil, ITEM may not be evaluated." + (declare (compiler-macro + (lambda (_) + `(when httpd-log-buffer + (httpd--log ,item))))) + (when httpd-log-buffer + (httpd--log item))) ;; Servlets (defvar httpd-current-proc nil "The process object currently in use.") -(defvar httpd--header-sent nil +(defvar-local httpd--header-sent nil "Buffer-local variable indicating if the header has been sent.") -(make-variable-buffer-local 'httpd--header-sent) -(defun httpd-resolve-proc (proc) - "Return the correct process to use. This handles `httpd-current-proc'." +(defsubst httpd--resolve-proc (proc) + "Return the correct process to use. +Return `httpd-current-proc' if PROC is t." (if (eq t proc) httpd-current-proc proc)) -(defmacro with-httpd-buffer (proc mime &rest body) - "Create a temporary buffer, set it as the current buffer, and, -at the end of body, automatically serve it to an HTTP client with -an HTTP header indicating the specified MIME type. Additionally, -`standard-output' is set to this output buffer and -`httpd-current-proc' is set to PROC." +(defmacro httpd--ensure-buffer (&rest body) + "Ensure that BODY is executed in a temporary httpd buffer. +Reuse the current buffer if it is a temporary httpd buffer." + (declare (indent 0) (debug t)) + (cl-with-gensyms (temp) + `(let (,temp) + (with-current-buffer + (if (eq major-mode 'httpd-buffer) + (current-buffer) + (setq ,temp (generate-new-buffer " *httpd-temp*" t))) + (unwind-protect + (progn + (setq major-mode 'httpd-buffer) + ,@body) + (when (buffer-live-p ,temp) + (kill-buffer ,temp))))))) + +(defmacro httpd-with-buffer (proc mime &rest body) + "Create temporary buffer and serve it to the client. +Create a temporary buffer, set it as the current buffer, and, at the end +of body, automatically serve it to an HTTP client with an HTTP header +indicating the specified MIME type. Additionally, `standard-output' is +set to this output buffer and `httpd-current-proc' is set to PROC." (declare (indent defun)) - (let ((proc-sym (make-symbol "--proc--"))) - `(let ((,proc-sym ,proc)) - (with-temp-buffer - (setf major-mode 'httpd-buffer) - (let ((standard-output (current-buffer)) - (httpd-current-proc ,proc-sym)) - ,@body) - (unless httpd--header-sent - (httpd-send-header ,proc-sym ,mime 200)))))) + (cl-once-only (proc) + `(httpd--ensure-buffer + (let ((standard-output (current-buffer)) + (httpd-current-proc ,proc)) + ,@body) + (unless httpd--header-sent + (httpd-send-header ,proc ,mime 200))))) (defun httpd-discard-buffer () - "Don't respond using current server buffer (`with-httpd-buffer'). -Returns a process for future response." - (when (eq major-mode 'httpd-buffer) (setf httpd--header-sent t)) + "Don't respond using current server buffer (`httpd-with-buffer'). +Returns a process for future response. + + (httpd-servlet slow text/plain () + (let ((proc (httpd-discard-buffer))) + (run-at-time 1 0 + (lambda () + (ignore-errors + (httpd-with-buffer proc \"text/plain\" + (insert \"Slow response\")))))))" + (when (eq major-mode 'httpd-buffer) (setq httpd--header-sent t)) httpd-current-proc) -(defmacro defservlet (name mime path-query-request &rest body) - "Defines a simple httpd servelet. The servlet runs in a -temporary buffer which is automatically served to the client -along with a header. +(defmacro httpd-servlet (name mime path-query-request &rest body) + "Defines a simple httpd servlet. + +NAME is the servlet name as symbol. +MIME the mime-type as symbol. +PATH-QUERY-REQUEST is the argument list. +BODY is the function body. + +The servlet runs in a temporary buffer which is automatically served to +the client along with a header. A servlet that serves the contents of *scratch*, - (defservlet scratch text/plain () + (httpd-servlet scratch text/plain () (insert-buffer-substring (get-buffer-create \"*scratch*\"))) A servlet that says hello, - (defservlet hello-world text/plain (path) + (httpd-servlet hello-world text/plain (path) (insert \"hello, \" (file-name-nondirectory path))))" (declare (indent defun)) - (let ((proc-sym (make-symbol "proc")) - (fname (intern (concat "httpd/" (symbol-name name))))) - `(defun ,fname (,proc-sym ,@path-query-request &rest ,(cl-gensym)) - (with-httpd-buffer ,proc-sym ,(httpd--stringify mime) - ,@body)))) + (cl-with-gensyms (proc-sym rest-sym) + (let ((fname (intern (concat "httpd/" (symbol-name name))))) + `(defun ,fname (,proc-sym ,@path-query-request &rest ,rest-sym) + (httpd-with-buffer ,proc-sym ,(httpd--stringify mime) + ,@body))))) (defun httpd-parse-endpoint (symbol) - "Parse an endpoint definition template for use with `defservlet*'." + "Parse an endpoint template SYMBOL for use with `httpd-servlet*'." (cl-loop for item in (split-string (symbol-name symbol) "/") for n upfrom 0 when (and (> (length item) 0) (eql (aref item 0) ?:)) collect (cons (intern (substring item 1)) n) into vars else collect item into path - finally - (cl-return - (cl-values (intern (mapconcat #'identity path "/")) vars)))) + finally return + (cl-values (intern (string-join path "/")) vars))) (defvar httpd-path nil - "Anaphoric variable for `defservlet*'.") + "Dynamic variable bound by `httpd-servlet*'.") (defvar httpd-query nil - "Anaphoric variable for `defservlet*'.") + "Dynamic variable bound by `httpd-servlet*'.") (defvar httpd-request nil - "Anaphoric variable for `defservlet*'.") + "Dynamic variable bound by `httpd-servlet*'.") (defvar httpd-split-path nil - "Anaphoric variable for `defservlet*'.") + "Dynamic variable bound by `httpd-servlet*'.") -(defmacro defservlet* (endpoint mime args &rest body) - "Like `defservlet', but automatically bind variables/arguments -to the request. Trailing components of the ENDPOINT can be bound -by prefixing these components with a colon, acting like a template. +(defmacro httpd-servlet* (endpoint mime args &rest body) + "Like `httpd-servlet', but bind variables/arguments to the request. - (defservlet* packages/:package/:version text/plain (verbose) +ENDPOINT is the path as symbol. +MIME the mime-type as symbol. +ARGS is the argument list. +BODY is the function body. + +Trailing components of the ENDPOINT can be bound by prefixing these +components with a colon, acting like a template. + + (httpd-servlet* packages/:package/:version text/plain (verbose) (insert (format \"%s\\n%s\\n\" package version)) (princ (get-description package version)) (when verbose @@ -578,93 +647,80 @@ When accessed from this URL, http://example.com/packages/foobar/1.0?verbose=1 the variables package, version, and verbose will be bound to the -associated components of the URL. Components not provided are -bound to nil. The query arguments can use the Common Lisp &key +associated components of the URL. Components not provided are +bound to nil. The query arguments can use the Common Lisp &key form (variable default provided-p). - (defservlet* greeting/:name text/plain ((greeting \"hi\" greeting-p)) + (httpd-servlet* greeting/:name text/plain ((greeting \"hi\" greeting-p)) (princ (format \"%s, %s (provided: %s)\" greeting name greeting-p))) -The original path, query, and request can be accessed by the -anaphoric special variables `httpd-path', `httpd-query', and -`httpd-request'." +The original path, query, and request can be accessed by the dynamically +bound variables `httpd-path', `httpd-query', and `httpd-request'." (declare (indent defun)) - (let ((path-lexical (cl-gensym)) - (query-lexical (cl-gensym)) - (request-lexical (cl-gensym))) + (cl-with-gensyms (path-lexical query-lexical request-lexical) (cl-multiple-value-bind (path vars) (httpd-parse-endpoint endpoint) - `(defservlet ,path ,mime (,path-lexical ,query-lexical ,request-lexical) + `(httpd-servlet ,path ,mime (,path-lexical ,query-lexical ,request-lexical) (let ((httpd-path ,path-lexical) (httpd-query ,query-lexical) (httpd-request ,request-lexical) (httpd-split-path (split-string (substring ,path-lexical 1) "/"))) (let ,(cl-loop for (var . pos) in vars - for extract = - `(httpd-unhex (nth ,pos httpd-split-path)) + for extract = `(nth ,pos httpd-split-path) collect (list var extract)) (let ,(cl-loop for arg in args for has-default = (listp arg) for has-default-p = (and has-default (= 3 (length arg))) for arg-name = (symbol-name - (if has-default (cl-first arg) arg)) + (if has-default (car arg) arg)) when has-default collect - (list (cl-first arg) + (list (car arg) `(let ((value (assoc ,arg-name httpd-query))) (if value - (cl-second value) - ,(cl-second arg)))) + (cadr value) + ,(cadr arg)))) else collect - (list arg `(cl-second + (list arg `(cadr (assoc ,arg-name httpd-query))) when has-default-p collect - (list (cl-third arg) + (list (caddr arg) `(not (null (assoc ,arg-name httpd-query))))) ,@body))))))) -(font-lock-add-keywords - 'emacs-lisp-mode - '(("(\\<\\(defservlet\\*?\\)\\> +\\([^ ()]+\\) +\\([^ ()]+\\)" - (1 'font-lock-keyword-face) - (2 'font-lock-function-name-face) - (3 'font-lock-type-face)))) - -(defmacro httpd-def-file-servlet (name root) +(defmacro httpd-file-servlet (name root) "Defines a servlet that serves files from ROOT under the route NAME. - (httpd-def-file-servlet my/www \"/var/www/\") + (httpd-file-servlet my/www \"/var/www/\") Automatically handles redirects and uses `httpd-serve-root' to actually serve up files." (let* ((short-root (directory-file-name (symbol-name name))) (path-root (concat short-root "/")) (chop (length path-root))) - `(defservlet ,name nil (uri-path query request) - (setf httpd--header-sent t) ; Don't actually use this temp buffer + `(httpd-servlet ,name nil (uri-path query request) (if (= (length uri-path) ,chop) (httpd-redirect t ,path-root) - (let ((path (substring uri-path ,chop))) - (httpd-serve-root t ,root path request)))))) + (httpd-serve-root t ,root (substring uri-path ,chop) request))))) ;; Request parsing -(defun httpd--normalize-header (header) - "Destructively capitalize the components of HEADER." - (mapconcat #'capitalize (split-string header "-") "-")) +(defsubst httpd--normalize-header (header) + "Capitalize the components of HEADER." + (replace-regexp-in-string "[^-]+" #'capitalize header t)) (defun httpd-parse () "Parse HTTP header in current buffer into association list. -Leaves the point at the start of the request content. Returns nil +Leaves the point at the start of the request content. Returns nil if it failed to parse a complete HTTP header." (goto-char (point-min)) - (when (looking-at "\\([^ ]+\\) +\\([^ ]+\\) +\\([^\r]+\\)\r\n") + (when (looking-at "\\([^ \r\n]+\\) +\\([^ \r\n]+\\) +\\([^\r\n]+\\)\r\n") (let ((method (match-string 1)) (path (decode-coding-string (match-string 2) 'iso-8859-1)) (version (match-string 3)) - (headers ())) + (headers nil)) (goto-char (match-end 0)) - (while (looking-at "\\([-!#-'*+.0-9A-Z^_`a-z|~]+\\): *\\([^\r]+\\)\r\n") + (while (looking-at "\\([-!#-'*+.0-9A-Z^_`a-z|~]+\\): *\\([^\r\n]+\\)\r\n") (goto-char (match-end 0)) (let ((name (match-string 1)) (value (match-string 2))) @@ -674,47 +730,59 @@ if it failed to parse a complete HTTP header." (goto-char (match-end 0)) (cons (list method path version) (nreverse headers)))))) -(defun httpd-unhex (str) - "Fully decode the URL encoding in STR (including +'s)." - (when str - (let ((nonplussed (replace-regexp-in-string (regexp-quote "+") " " str))) - (decode-coding-string (url-unhex-string nonplussed t) 'utf-8)))) +(defsubst httpd-unhex (str) + "Fully decode the URL encoding in STR." + (decode-coding-string (url-unhex-string str t) 'utf-8)) -(defun httpd-parse-args (argstr) - "Parse a string containing URL encoded arguments." - (unless (zerop (length argstr)) - (mapcar (lambda (str) - (mapcar 'httpd-unhex (split-string str "="))) - (split-string argstr "&")))) +(defsubst httpd-unhex-plus (str) + "Fully decode URL/form encoding in STR, treating `+' as space." + (httpd-unhex (string-replace "+" " " str))) + +(defun httpd-parse-args (str) + "Parse STR containing URL/form encoded arguments." + (unless (equal str "") + (mapcar + (lambda (s) + (if-let* ((i (string-search "=" s))) + (list (httpd-unhex-plus (substring s 0 i)) + (httpd-unhex-plus (substring s (1+ i)))) + (list (httpd-unhex-plus s)))) + (split-string str "&" t)))) (defun httpd-parse-uri (uri) "Split a URI into its components. The first element of the return value is the script path, the second element is an alist of variable/value pairs, and the third element is the fragment." - (let ((p1 (string-match (regexp-quote "?") uri)) - (p2 (string-match (regexp-quote "#") uri)) - retval) - (push (if p2 (httpd-unhex (substring uri (1+ p2)))) retval) - (push (if p1 (httpd-parse-args (substring uri (1+ p1) p2))) retval) - (push (substring uri 0 (or p1 p2)) retval))) + (let ((q (string-search "?" uri)) + (h (string-search "#" uri))) + (when (and q h (> q h)) + (setq q nil)) + (list (httpd-unhex (substring uri 0 (or q h))) + (and q (httpd-parse-args (substring uri (1+ q) h))) + (and h (httpd-unhex (substring uri (1+ h))))))) + +(defconst httpd--html-entities + '((?& . "&") + (?' . "'") + (?\" . """) + (?< . "<") + (?> . ">")) + "Alist of HTML entities escaped by `httpd-escape-html-buffer'.") (defun httpd-escape-html-buffer () "Escape current buffer contents to be safe for inserting into HTML." (goto-char (point-min)) - (while (search-forward-regexp "[<>&]" nil t) + (while (re-search-forward "[<>&'\"]" nil t) (replace-match - (cl-case (aref (match-string 0) 0) - (?< "<") - (?> ">") - (?& "&"))))) + (alist-get (char-after (match-beginning 0)) httpd--html-entities)))) -(defun httpd-escape-html (string) - "Escape STRING so that it's safe to insert into an HTML document." - (with-temp-buffer - (insert string) - (httpd-escape-html-buffer) - (buffer-string))) +(defun httpd-escape-html (str) + "Escape STR so that it's safe to insert into an HTML document." + (replace-regexp-in-string + "[<>&'\"]" + (lambda (c) (alist-get (aref c 0) httpd--html-entities)) + str)) ;; Path handling @@ -728,35 +796,35 @@ element is the fragment." (defun httpd-clean-path (path) "Clean dangerous .. from PATH and remove the leading slash." - (let* ((sep (if (member system-type '(windows-nt ms-dos)) "[/\\]" "/")) - (split (delete ".." (split-string path sep))) - (unsplit (mapconcat 'identity (delete "" split) "/"))) - (concat "./" unsplit))) + (let ((sep (if (memq system-type '(windows-nt ms-dos)) "[/\\]" "/"))) + (concat "./" (string-join (delete ".." (split-string path sep t)) "/")))) (defun httpd-gen-path (path &optional root) - "Translate GET to secure path in ROOT (`httpd-root')." - (let ((clean (expand-file-name (httpd-clean-path path) (or root httpd-root)))) + "Generate secure path in ROOT from request PATH." + (let ((clean (expand-file-name (httpd-clean-path path) root))) (if (file-directory-p clean) (let* ((dir (file-name-as-directory clean)) - (indexes (cl-mapcar (apply-partially 'concat dir) httpd-indexes)) - (existing (cl-remove-if-not 'file-exists-p indexes))) + (indexes (mapcar (apply-partially #'concat dir) httpd-indexes)) + (existing (cl-remove-if-not #'file-exists-p indexes))) (or (car existing) dir)) clean))) (defun httpd-get-servlet (uri-path) "Determine the servlet to be executed for URI-PATH." - (if (not httpd-servlets) - 'httpd/ - (cl-labels ((cat (x) - (concat "httpd/" (mapconcat 'identity (reverse x) "/")))) - (let ((parts (cdr (split-string (directory-file-name uri-path) "/")))) - (or - (cl-find-if 'fboundp (mapcar 'intern-soft - (cl-maplist #'cat (reverse parts)))) - 'httpd/))))) + (or + (and httpd-servlets + (cl-find-if + #'fboundp + (cl-maplist + (lambda (x) + (intern-soft (string-join (cons "httpd" (reverse x)) "/"))) + (nreverse (cdr (split-string (directory-file-name uri-path) "/")))))) + 'httpd/)) (defun httpd-serve-root (proc root uri-path &optional request) - "Securely serve a file from ROOT from under PATH." + "Securely serve a file from ROOT. +PROC is the client process, URI-PATH the request path and REQUEST the +request header as alist." (let* ((path (httpd-gen-path uri-path root)) (status (httpd-status path))) (cond @@ -765,140 +833,164 @@ element is the fragment." (t (httpd-send-file proc path request))))) (defun httpd/ (proc uri-path query request) - "Default root servlet which serves files when httpd-serve-files is T." - (if (and httpd-serve-files httpd-root) - (httpd-serve-root proc httpd-root uri-path request) - (httpd-error proc 403))) + "Default root servlet which serves files when `httpd-serve-files' is t. +PROC is the client process, URI-PATH the request path, QUERY the query +arguments and REQUEST the request header as alist." + (cond + ((equal uri-path "/error") + (let ((status (string-to-number (or (cadr (assoc "status" query)) "")))) + (unless (and (assq status httpd-status-codes) (>= status 400)) + (setq status 400)) + (httpd-error proc status))) + ((and httpd-serve-files httpd-root) + (httpd-serve-root proc httpd-root uri-path request)) + ((httpd-error proc 403)))) (defun httpd-get-mime (ext) - "Fetch MIME type given the file extention." + "Fetch MIME type given the file extension EXT." (or (and ext (cdr (assoc (downcase ext) httpd-mime-types))) "application/octet-stream")) ;; Data sending functions (defun httpd-send-header (proc mime status &rest header-keys) - "Send an HTTP header with given MIME type and STATUS, followed -by the current buffer. If PROC is T use the `httpd-current-proc' -as the process. + "Send an HTTP header followed by the current buffer. +MIME is the mime type and STATUS the HTTP status code. If PROC is t use +the `httpd-current-proc' as the process. Extra headers can be sent by supplying them like keywords, i.e. (httpd-send-header t \"text/plain\" 200 :X-Powered-By \"simple-httpd\")" - (let ((status-str (cdr (assq status httpd-status-codes))) - (headers `(("Server" . ,httpd-server-name) - ("Date" . ,(httpd-date-string)) - ("Connection" . "keep-alive") - ("Content-Type" . ,(httpd--stringify mime)) - ("Content-Length" . ,(httpd--buffer-size))))) - (unless httpd--header-sent - (setf httpd--header-sent t) - (with-temp-buffer - (insert (format "HTTP/1.1 %d %s\r\n" status status-str)) - (cl-loop for (header value) on header-keys by #'cddr - for header-name = (substring (symbol-name header) 1) - for value-name = (format "%s" value) - collect (cons header-name value-name) into extras - finally (setf headers (nconc headers extras))) - (dolist (header headers) - (insert (format "%s: %s\r\n" (car header) (cdr header)))) - (insert "\r\n") - (process-send-region (httpd-resolve-proc proc) - (point-min) (point-max))) - (process-send-region (httpd-resolve-proc proc) - (point-min) (point-max))))) + (when httpd--header-sent + (error "Header already sent")) + (setq httpd--header-sent t) + (let* ((proc (httpd--resolve-proc proc)) + (request (or (process-get proc :request-active) + (error "No active request"))) + (status-str (alist-get status httpd-status-codes)) + (mime-str (httpd--stringify mime)) + (mime-str (if (and (string-prefix-p "text/" mime-str) + (not (string-search "charset=" mime-str))) + (concat mime-str "; charset=utf-8") + mime-str)) + (close (httpd--connection-close-p request)) + (headers `(("Date" . ,(httpd-date-string)) + ("Content-Type" . ,mime-str) + ("Content-Length" . ,(httpd--buffer-size)) + ("Connection" . ,(if close "close" "keep-alive")) + ,@(and httpd-server-name `(("Server" . ,httpd-server-name))))) + (header-list `(,(format "%s %d %s\r\n" (caddar request) status status-str) + ,@(cl-loop for (header . value) in headers collect + (format "%s: %s\r\n" header value)) + ,@(cl-loop for (header value) on header-keys by #'cddr collect + (format "%s: %s\r\n" (httpd--stringify header) value)) + "\r\n"))) + (process-put proc :request-active nil) + (process-send-string proc (apply #'concat header-list)) + (unless (or (= (point-min) (point-max)) (equal "HEAD" (caar request))) + (process-send-region proc (point-min) (point-max))) + (if close + (delete-process proc) + (httpd--pop-request proc)))) (defun httpd-redirect (proc path &optional code) - "Redirect the client to PATH (default 301). If PROC is T use -the `httpd-current-proc' as the process." - (httpd-log (list 'redirect path)) - (httpd-discard-buffer) - (with-temp-buffer + "Redirect the client to PATH (default 301). +If PROC is t use the `httpd-current-proc' as the process." + (httpd-log `(redirect ,path)) + (httpd--ensure-buffer (httpd-send-header proc "text/plain" (or code 301) :Location path))) (defun httpd-send-file (proc path &optional req) - "Serve file to the given client. If PROC is T use the -`httpd-current-proc' as the process." - (httpd-discard-buffer) - (let ((req-etag (cadr (assoc "If-None-Match" req))) - (etag (httpd-etag path)) - (mtime (httpd-date-string (nth 4 (file-attributes path))))) - (if (equal req-etag etag) - (with-temp-buffer - (httpd-log `(file ,path not-modified)) - (httpd-send-header proc "text/plain" 304)) - (httpd-log `(file ,path)) - (with-temp-buffer - (set-buffer-multibyte nil) - (insert-file-contents-literally path) - (httpd-send-header proc (httpd-get-mime (file-name-extension path)) - 200 :Last-Modified mtime :ETag etag))))) + "Serve file at PATH to the given client PROC. +REQ is the request. If PROC is t use the `httpd-current-proc' as the +process." + (httpd--ensure-buffer + (let ((etag (httpd-etag path))) + (if (not (equal (cadr (assoc "If-None-Match" req)) etag)) + (let ((mime (httpd-get-mime (file-name-extension path))) + (mtime (httpd-date-string + (file-attribute-modification-time + (file-attributes path))))) + (httpd-log `(file ,path)) + (set-buffer-multibyte nil) + (insert-file-contents-literally path) + (httpd-send-header proc mime 200 + :ETag etag :Last-Modified mtime)) + (httpd-log `(file ,path not-modified)) + (httpd-send-header proc "text/plain" 304))))) (defun httpd-send-directory (proc path uri-path) - "Serve a file listing to the client. If PROC is T use the -`httpd-current-proc' as the process." - (httpd-discard-buffer) - (let ((title (concat "Directory listing for " - (url-insert-entities-in-string uri-path)))) - (if (equal "/" (substring uri-path -1)) - (with-temp-buffer + "Serve a file listing to the client. +PROC is the client process, PATH the directory PATH, URI-PATH the +request path and REQUEST the request header as alist. If PROC is t use +the `httpd-current-proc' as the process." + (if (string-suffix-p "/" uri-path) + (let ((title (concat "Directory listing for " + (httpd-escape-html uri-path)))) + (httpd--ensure-buffer (httpd-log `(directory ,path)) - (insert "\n") - (insert "\n" title "\n") - (insert "\n

" title "

\n
\n
    ") + (insert "\n" + "\n" title "\n" + "\n

    " title "

    \n
    \n
      ") (dolist (file (directory-files path)) (unless (eq ?. (aref file 0)) (let* ((full (expand-file-name file path)) (tail (if (file-directory-p full) "/" "")) - (f (url-insert-entities-in-string file)) + (f (httpd-escape-html file)) (l (url-hexify-string file))) (insert (format "
    • %s%s
    • \n" l tail f tail))))) (insert "
    \n
    \n\n") - (httpd-send-header proc "text/html; charset=utf-8" 200)) - (httpd-redirect proc (concat uri-path "/"))))) + (httpd-send-header proc "text/html" 200))) + (httpd-redirect proc (concat uri-path "/")))) -(defun httpd--buffer-size (&optional buffer) - "Get the buffer size in bytes." - (let ((orig enable-multibyte-characters) - (size 0)) - (with-current-buffer (or buffer (current-buffer)) - (set-buffer-multibyte nil) - (setf size (buffer-size)) - (if orig (set-buffer-multibyte orig))) - size)) +(defun httpd--buffer-size () + "Get size of current buffer in bytes." + (let ((orig enable-multibyte-characters)) + (set-buffer-multibyte nil) + (prog1 (buffer-size) + (when orig (set-buffer-multibyte orig))))) (defun httpd-error (proc status &optional info) - "Send an error page appropriate for STATUS to the client, -optionally inserting object INFO into page. If PROC is T use the + "Send an error page appropriate for STATUS to the client. +The INFO object is optionally inserted into page. If PROC is t use the `httpd-current-proc' as the process." - (httpd-discard-buffer) (httpd-log `(error ,status ,info)) - (with-temp-buffer - (let ((html (or (cdr (assq status httpd-html)) "")) - (contents - (if (not info) - "" - (with-temp-buffer - (let ((standard-output (current-buffer))) - (insert "error: ") - (princ info) - (insert "\n") - (when httpd-show-backtrace-when-error - (insert "backtrace: ") - (princ (backtrace)) - (insert "\n")) - (httpd-escape-html-buffer) - (buffer-string)))))) - (insert (format html contents))) + (httpd--ensure-buffer + (let ((contents + (if (or info httpd-show-backtrace-when-error) + (with-temp-buffer + (let ((standard-output (current-buffer))) + (when info + (insert "error: ") + (princ info) + (insert ?\n)) + (when httpd-show-backtrace-when-error + (insert "backtrace:\n") + (backtrace) + (insert ?\n)) + (httpd-escape-html-buffer) + (buffer-string))) + ""))) + (insert (format + (or (alist-get status httpd-html) + (alist-get t httpd-html)) + contents status + (alist-get status httpd-status-codes)) ?\n)) (httpd-send-header proc "text/html" status))) (defun httpd--error-safe (&rest args) - "Call httpd-error and report failures to *httpd*." - (condition-case error-case + "Call `httpd-error' with ARGS and log failures." + (condition-case err (apply #'httpd-error args) - (error (httpd-log `(hard-error ,error-case))))) + (error (httpd-log `(hard-error ,err))))) + +;; Old names. Not deprecated to avoid churn. +(defalias 'defservlet #'httpd-servlet) +(defalias 'defservlet* #'httpd-servlet*) +(defalias 'httpd-def-file-servlet #'httpd-file-servlet) +(defalias 'with-httpd-buffer #'httpd-with-buffer) +(defalias 'httpd-resolve-proc #'httpd--resolve-proc) (provide 'simple-httpd) - ;;; simple-httpd.el ends here diff --git a/lisp/spacemacs-theme/spacemacs-theme-pkg.el b/lisp/spacemacs-theme/spacemacs-theme-pkg.el index ee8acf27..ed0e8fb5 100644 --- a/lisp/spacemacs-theme/spacemacs-theme-pkg.el +++ b/lisp/spacemacs-theme/spacemacs-theme-pkg.el @@ -1,8 +1,8 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "spacemacs-theme" "20251221.1656" +(define-package "spacemacs-theme" "20260523.1256" "Color theme with a dark and light versions." () :url "https://github.com/nashamri/spacemacs-theme" - :commit "5635d6bbc76e6f06b99fa5dac6e6fd6675459ca6" - :revdesc "5635d6bbc76e" + :commit "cbd290dfde96f53a7b41730c7840850a8a7b8a02" + :revdesc "cbd290dfde96" :keywords '("color" "theme")) diff --git a/lisp/spacemacs-theme/spacemacs-theme.el b/lisp/spacemacs-theme/spacemacs-theme.el index 2d95aceb..1f8f83b6 100644 --- a/lisp/spacemacs-theme/spacemacs-theme.el +++ b/lisp/spacemacs-theme/spacemacs-theme.el @@ -5,8 +5,8 @@ ;; Author: Nasser Alshammari ;; URL: ;; -;; Package-Version: 20251221.1656 -;; Package-Revision: 5635d6bbc76e +;; Package-Version: 20260523.1256 +;; Package-Revision: cbd290dfde96 ;; Keywords: color, theme ;; Package-Requires: ((emacs "24")) @@ -725,6 +725,13 @@ to `auto', tags may not be properly aligned. " `(lsp-ui-doc-background ((,class (:background ,bg2)))) `(lsp-ui-doc-header ((,class (:foreground ,head1 :background ,head1-bg)))) `(lsp-ui-sideline-code-action ((,class (:foreground ,comp)))) + `(lsp-ui-peek-header ((,class (:foreground ,bg1 :background ,base)))) + `(lsp-ui-peek-peek ((,class (:background ,bg2)))) + `(lsp-ui-peek-list ((,class (:background ,bg2)))) + `(lsp-ui-peek-highlight ((,class (:background ,highlight)))) + `(lsp-ui-peek-line-number ((,class (:foreground ,base)))) + `(lsp-ui-peek-selection ((,class (:background ,bg3)))) + `(lsp-ui-peek-filename ((,class (:foreground ,base)))) ;;;;; magit `(magit-blame-culprit ((,class :background ,yellow-bg :foreground ,yellow))) @@ -1068,6 +1075,13 @@ to `auto', tags may not be properly aligned. " `(web-mode-type-face ((,class (:inherit ,font-lock-type-face)))) `(web-mode-warning-face ((,class (:inherit ,font-lock-warning-face)))) +;;;;; wgrep + `(wgrep-face ((,class (:foreground ,green)))) + `(wgrep-delete-face ((,class (:foreground ,red)))) + `(wgrep-reject-face ((,class (:foreground ,red)))) + `(wgrep-file-face ((,class (:foreground ,green)))) + `(wgrep-done-face ((,class (:foreground ,blue)))) + ;;;;; which-key `(which-key-command-description-face ((,class (:foreground ,base)))) `(which-key-group-description-face ((,class (:foreground ,keyword)))) diff --git a/lisp/tablist/tablist-pkg.el b/lisp/tablist/tablist-pkg.el index 9ea7ff06..7593e162 100644 --- a/lisp/tablist/tablist-pkg.el +++ b/lisp/tablist/tablist-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "tablist" "20231019.1126" +(define-package "tablist" "20260623.1855" "Extended tabulated-list-mode." - '((emacs "24.3")) + '((emacs "25.1")) :url "https://github.com/emacsorphanage/tablist" - :commit "fcd37147121fabdf003a70279cf86fbe08cfac6f" - :revdesc "fcd37147121f" + :commit "01f065e387ffe6b7a41f180f257cd12551c7a9c2" + :revdesc "01f065e387ff" :keywords '("extensions" "lisp") :authors '(("Andreas Politz" . "politza@fh-trier.de")) :maintainers '(("Andreas Politz" . "politza@fh-trier.de"))) diff --git a/lisp/tablist/tablist.el b/lisp/tablist/tablist.el index d2f9ae80..a6f88ad1 100644 --- a/lisp/tablist/tablist.el +++ b/lisp/tablist/tablist.el @@ -5,9 +5,9 @@ ;; Author: Andreas Politz ;; Keywords: extensions, lisp ;; Package: tablist -;; Package-Version: 20231019.1126 -;; Package-Revision: fcd37147121f -;; Package-Requires: ((emacs "24.3")) +;; Package-Version: 20260623.1855 +;; Package-Revision: 01f065e387ff +;; Package-Requires: ((emacs "25.1")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -315,13 +315,12 @@ as argument for the function `completion-in-region'.") (cond ((not disable) (set (make-local-variable 'mode-line-misc-info) - (append - (list - (list 'tablist-current-filter - '(:eval (format " [%s]" - (if tablist-filter-suspended - "suspended" - "filtered"))))))) + (cons '(tablist-current-filter + (:eval (format " [%s]" + (if tablist-filter-suspended + "suspended" + "filtered")))) + cleaned-misc)) (add-hook 'post-command-hook 'tablist-selection-changed-handler nil t) (add-hook 'tablist-selection-changed-functions @@ -437,18 +436,18 @@ a face on." (tabulated-list-put-tag (string tablist-marker-char)) (put-text-property - (point-at-bol) - (1+ (point-at-bol)) + (line-beginning-position) + (1+ (line-beginning-position)) 'face tablist-marker-face) (let ((columns (tablist-column-offsets))) (dolist (c (tablist-major-columns)) (when (and (>= c 0) (< c (length columns))) - (let ((beg (+ (point-at-bol) + (let ((beg (+ (line-beginning-position) (nth c columns))) (end (if (= c (1- (length columns))) - (point-at-eol) - (+ (point-at-bol) + (line-end-position) + (+ (line-beginning-position) (nth (1+ c) columns))))) (cond ((and tablist-marked-face @@ -841,7 +840,7 @@ STATE is a return value of `tablist-get-mark-state'." (forward-char)) ;; before any columns (when (< current 0) - (goto-char (+ (point-at-bol) (if (> n 0) + (goto-char (+ (line-beginning-position) (if (> n 0) (car columns) (car (last columns))))) (setq n (* (cl-signum n) (1- (abs n))))) @@ -1011,11 +1010,11 @@ Optional REVERT-P means, revert the display afterwards." (unless n (setq n (tablist-current-column))) (tablist-assert-column-editable n) (let* ((offsets (append (tablist-column-offsets) - (list (- (point-at-eol) - (point-at-bol))))) - (beg (+ (point-at-bol) + (list (- (line-end-position) + (line-beginning-position))))) + (beg (+ (line-beginning-position) (nth n offsets))) - (end (+ (point-at-bol) + (end (+ (line-beginning-position) (nth (1+ n) offsets))) (entry (tabulated-list-get-entry beg)) (inhibit-read-only t) @@ -1027,9 +1026,9 @@ Optional REVERT-P means, revert the display afterwards." (goto-char beg) (delete-region beg end) (add-text-properties - (point-at-bol) (point-at-eol) + (line-beginning-position) (line-end-position) '(read-only t field t)) - (unless (= beg (point-at-bol)) + (unless (= beg (line-beginning-position)) (put-text-property (1- beg) beg 'rear-nonsticky t)) (save-excursion ;; Keep one read-only space at the end for keeping text @@ -1039,7 +1038,7 @@ Optional REVERT-P means, revert the display afterwards." (concat (tablist-nth-entry n entry) (propertize " " - 'display `(space :align-to ,(- end (point-at-bol))))) + 'display `(space :align-to ,(- end (line-beginning-position))))) 'field nil 'front-sticky '(tablist-edit) 'rear-nonsticky '(read-only field) @@ -1086,7 +1085,7 @@ Optional REVERT-P means, revert the display afterwards." (tablist-edit-column-minor-mode -1) (remove-overlays beg end 'tablist-edit t) (put-text-property beg end 'tablist-edit nil) - (delete-region (point-at-bol) (1+ (point-at-eol))) + (delete-region (line-beginning-position) (1+ (line-end-position))) (save-excursion (tabulated-list-print-entry id entry)) (forward-char (nth column (tablist-column-offsets)))))) @@ -1518,7 +1517,8 @@ FILTER defaults to `tablist-current-filter'." (tablist-filter-negate filter)))) (force-mode-line-update)) -(defadvice tabulated-list-print (after tabulated-list activate) +(define-advice tabulated-list-print + (:after (&rest _) tablist-reapply-filter) "Reapply the filter." (when (or tablist-minor-mode (derived-mode-p 'tablist-mode)) @@ -1864,8 +1864,8 @@ visibility." (beginning-of-line) (let ((inhibit-read-only t)) (add-text-properties - (point-at-bol) - (1+ (point-at-eol)) + (line-beginning-position) + (1+ (line-end-position)) `(invisible ,flag))))) (defun tablist-filter-hide-entry (&optional pos) diff --git a/lisp/transient/transient-pkg.el b/lisp/transient/transient-pkg.el index c8c841ff..e24d0efa 100644 --- a/lisp/transient/transient-pkg.el +++ b/lisp/transient/transient-pkg.el @@ -1,13 +1,14 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "transient" "20260401.2145" +(define-package "transient" "20260617.1137" "Transient commands." '((emacs "28.1") - (compat "30.1") - (cond-let "0.2") + (compat "31.0") + (cond-let "1.1") + (llama "1.0") (seq "2.24")) :url "https://github.com/magit/transient" - :commit "8b14203107950d6eba0e17d14867e05547725219" - :revdesc "8b1420310795" + :commit "9d103f338fb9fd4506a0f1651c483209ab838f49" + :revdesc "9d103f338fb9" :keywords '("extensions") :authors '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev")) :maintainers '(("Jonas Bernoulli" . "emacs.transient@jonas.bernoulli.dev"))) diff --git a/lisp/transient/transient.el b/lisp/transient/transient.el index 58410add..a98370d5 100644 --- a/lisp/transient/transient.el +++ b/lisp/transient/transient.el @@ -6,12 +6,13 @@ ;; Homepage: https://github.com/magit/transient ;; Keywords: extensions -;; Package-Version: 20260401.2145 -;; Package-Revision: 8b1420310795 +;; Package-Version: 20260617.1137 +;; Package-Revision: 9d103f338fb9 ;; Package-Requires: ( ;; (emacs "28.1") -;; (compat "30.1") -;; (cond-let "0.2") +;; (compat "31.0") +;; (cond-let "1.1") +;; (llama "1.0") ;; (seq "2.24")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -45,7 +46,7 @@ ;;; Code: -(defconst transient-version "0.12.0") +(defconst transient-version "0.13.4") (require 'cl-lib) (require 'compat) @@ -53,6 +54,7 @@ (require 'eieio) (require 'edmacro) (require 'format-spec) +(require 'llama) (require 'pcase) (require 'pp) @@ -101,19 +103,6 @@ similar defect.") :emergency)) (defvar Man-notify-method) (defvar pp-default-function) ; since Emacs 29.1 -(static-if (< emacs-major-version 30) - (progn - (defun internal--build-binding@backport-e680827e814 (fn binding prev-var) - "Backport not warning about `_' not being left unused. -Backport fix for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69108, -from Emacs commit e680827e814e155cf79175d87ff7c6ee3a08b69a." - (let ((binding (funcall fn binding prev-var))) - (if (eq (car binding) '_) - (cons (make-symbol "s") (cdr binding)) - binding))) - (advice-add 'internal--build-binding :around - #'internal--build-binding@backport-e680827e814))) - (define-obsolete-variable-alias 'transient-show-popup 'transient-show-menu @@ -136,9 +125,12 @@ from Emacs commit e680827e814e155cf79175d87ff7c6ee3a08b69a." ,(macroexp-progn body)) ((debug error) (transient--emergency-exit ,id) - (static-if (fboundp 'error-type-p) ; since Emacs 31.1 - (signal err) - (signal (car err) (cdr err)))))) + (static-if (version< emacs-version "31.0.50") + (signal (car err) (cdr err)) + (condition-case nil + (signal err) + (wrong-number-of-arguments + (signal (car err) (cdr err)))))))) (defun transient--exit-and-debug (&rest args) (transient--emergency-exit :debugger) @@ -871,7 +863,8 @@ See also option `transient-highlight-mismatched-keys'." (defun transient--pp-to-file (value file) (when (or value (file-exists-p file)) (make-directory (file-name-directory file) t) - (setq value (cl-sort (copy-sequence value) #'string< :key #'car)) + (setq value (compat-call sort (copy-sequence value) + :lessp #'string< :key #'car)) (with-temp-file file (let ((print-level nil) (print-length nil) @@ -906,11 +899,12 @@ should not change it manually.") (defun transient-save-history () (setq transient-history - (cl-sort (mapcar (pcase-lambda (`(,key . ,val)) - (cons key (seq-take (delete-dups val) - transient-history-limit))) - transient-history) - #'string< :key #'car)) + (compat-call sort + (mapcar (pcase-lambda (`(,key . ,val)) + (cons key (take transient-history-limit + (delete-dups val)))) + transient-history) + :lessp #'string< :key #'car)) (transient--pp-to-file transient-history transient-history-file)) (defun transient-maybe-save-history () @@ -1250,7 +1244,7 @@ to the setup function: (,(or class 'transient-prefix) :command ',name ,@slots)) (transient--set-layout ',name - (list ,@(mapcan (lambda (s) (transient--parse-child name s)) groups)))))) + (list ,@(mapcan (##transient--parse-child name %) groups)))))) (put 'transient-define-prefix 'autoload-macro 'expand) (defmacro transient-define-group (name &rest groups) @@ -1264,7 +1258,7 @@ form as for `transient-define-prefix'." (indent defun)) `(transient--set-layout ',name - (list ,@(mapcan (lambda (s) (transient--parse-child name s)) groups)))) + (list ,@(mapcan (##transient--parse-child name %) groups)))) (defmacro transient-define-suffix (name arglist &rest args) "Define NAME as a transient suffix command. @@ -1503,7 +1497,7 @@ commands are aliases for." ('transient-column))) (and args (cons 'list args)) (cons 'list - (mapcan (lambda (s) (transient--parse-child prefix s)) spec))))) + (mapcan (##transient--parse-child prefix %) spec))))) (defun transient--parse-suffix (prefix spec) (let (class args) @@ -1540,8 +1534,8 @@ commands are aliases for." (sym (intern (format "transient:%s:%s:%d" prefix - (replace-regexp-in-string (plist-get args :key) " " "") - (prog1 gensym-counter (cl-incf gensym-counter)))))) + (string-replace (plist-get args :key) " " "") + (prog1 gensym-counter (incf gensym-counter)))))) (use :command `(prog1 ',sym (put ',sym 'interactive-only t) @@ -1666,7 +1660,12 @@ symbol property.") (transient--set-layout prefix (named-let upgrade ((spec layout)) - (cond ((vectorp spec) + (cond ((and (vectorp spec) + (length= spec 3)) + ;; This format is used by emoji.el from Emacs <= 29.4. + (pcase-let ((`[,class ,args ,children] spec)) + (vector class args (mapcar #'upgrade children)))) + ((vectorp spec) (pcase-let ((`[,level ,class ,args ,children] spec)) (when level (setq args (plist-put args :level level))) @@ -1812,9 +1811,9 @@ layout of PREFIX." (let* ((siblings (aref parent 2)) (pos (cl-position group siblings))) (aset parent 2 - (nconc (seq-take siblings pos) + (nconc (take pos siblings) (transient--get-children group) - (seq-drop siblings (1+ pos)))))))) + (drop (1+ pos) siblings))))))) ;;;###autoload (defun transient-remove-suffix (prefix loc) @@ -2171,7 +2170,7 @@ probably use this instead: (defun transient--suffix-prototype (command) (or (get command 'transient--suffix) - (seq-some (lambda (cmd) (get cmd 'transient--suffix)) + (seq-some (##get % 'transient--suffix) (function-alias-p command)))) ;;; Keymaps @@ -2508,14 +2507,12 @@ of the corresponding object." (pcase this-command ('transient-update (setq transient--showp t) - (let ((keys (listify-key-sequence (this-single-command-raw-keys)))) - (setq unread-command-events (mapcar (lambda (key) (cons t key)) keys)) + (let ((keys (listify-key-sequence (this-single-command-keys)))) + (setq unread-command-events (mapcar (##cons t %) keys)) keys)) ('transient-quit-seq (setq unread-command-events - (butlast (listify-key-sequence - (this-single-command-raw-keys)) - 2)) + (butlast (listify-key-sequence (this-single-command-keys)) 2)) (butlast transient--redisplay-key)) (_ nil))) (let ((topmap (make-sparse-keymap)) @@ -2642,7 +2639,7 @@ value. Otherwise return CHILDREN as is.") (defun transient--init-suffixes (name) (let ((levels (alist-get name transient-levels))) - (mapcan (lambda (c) (transient--init-child levels c nil)) + (mapcan (##transient--init-child levels % nil) (append (transient--get-children name) (and (not transient--editp) (transient--get-children @@ -2661,7 +2658,7 @@ value. Otherwise return CHILDREN as is.") (defun transient--init-child (levels spec parent) (cl-etypecase spec - (symbol (mapcan (lambda (c) (transient--init-child levels c parent)) + (symbol (mapcan (##transient--init-child levels % parent) (transient--get-children spec))) (vector (transient--init-group levels spec parent)) (list (transient--init-suffix levels spec parent)) @@ -2677,7 +2674,7 @@ value. Otherwise return CHILDREN as is.") (_(prog1 t (when (transient--inapt-suffix-p obj) (oset obj inapt t)))) - (suffixes (mapcan (lambda (c) (transient--init-child levels c obj)) + (suffixes (mapcan (##transient--init-child levels % obj) (transient-setup-children obj children)))) (progn (oset obj suffixes suffixes) @@ -3638,8 +3635,7 @@ transient is active." (cond (interactivep (setq transient--helpp t)) - ((lookup-key transient--transient-map - (this-single-command-raw-keys)) + ((lookup-key transient--transient-map (this-single-command-keys)) (setq transient--helpp nil) (with-demoted-errors "transient-help: %S" (transient--display-help #'transient-show-help @@ -3680,7 +3676,7 @@ For example: (and transient--editp (setq command prefix))) (list command - (let ((keys (this-single-command-raw-keys))) + (let ((keys (this-single-command-keys))) (and (lookup-key transient--transient-map keys) (progn (transient--show) @@ -3701,8 +3697,7 @@ For example: (setq akey t)) (t (oset (transient-suffix-object command) level level) - (when (cdr (cl-remove-if-not (lambda (obj) - (eq (oref obj command) command)) + (when (cdr (cl-remove-if-not (##eq (oref % command) command) transient--suffixes)) (setq akey (cons command (this-command-keys)))))) (setf (alist-get akey alist) level) @@ -3833,7 +3828,7 @@ such as when suggesting a new feature or reporting an issue." :description "Echo arguments" :key "x" (interactive (list (transient-args transient-current-command))) - (if (seq-every-p #'stringp arguments) + (if (all #'stringp arguments) (message "%s: %s" (key-description (this-command-keys)) (mapconcat (lambda (arg) (propertize (if (string-match-p " " arg) @@ -3910,8 +3905,8 @@ Call `transient-default-value' but because that is a noop for (string-match regexp v) (match-string 1 v))))) (if multi-value - (delq nil (mapcar match value)) - (cl-some match value))))))) + (seq-filter match value) + (seq-some match value))))))) (cl-defmethod transient-init-value ((obj transient-switch)) "Extract OBJ's value from the value of the prefix object." @@ -4112,19 +4107,19 @@ stand-alone command." (when (fboundp 'org-read-date) (org-read-date 'with-time nil nil prompt default-time))) -(static-if (fboundp 'string-edit) ; since Emacs 29.1 - (defun transient-read-string-from-buffer (prompt value _) - "Switch to a new buffer to edit STRING in a recursive edit. +(static-when (fboundp 'string-edit) ; since Emacs 29.1 + (defun transient-read-string-from-buffer (prompt value _) + "Switch to a new buffer to edit STRING in a recursive edit. Like `read-string-from-buffer' but accept an additional argument as provided by `transient-infix-read' (but ignore it). Only available when using Emacs 29.1 or greater." - (string-edit prompt (or value "") - (lambda (edited) - (setq value edited) - (exit-recursive-edit)) - :abort-callback #'exit-recursive-edit) - (recursive-edit) - value)) + (string-edit prompt (or value "") + (lambda (edited) + (setq value edited) + (exit-recursive-edit)) + :abort-callback #'exit-recursive-edit) + (recursive-edit) + value)) ;;;; Prompt @@ -4419,7 +4414,7 @@ does nothing." nil) (pcase-exhaustive (oref obj multi-value) ('nil (concat arg value)) ((or 't 'rest) (cons arg value)) - ('repeat (mapcar (lambda (v) (concat arg v)) value)))))) + ('repeat (mapcar (##concat arg %) value)))))) (cl-defmethod transient-infix-value ((_ transient-variable)) "Return nil, which means \"no value\". @@ -4446,9 +4441,8 @@ Append \"=\ to ARG to indicate that it is an option." [match (let ((case-fold-search nil) (re (format "\\`%s\\(?:=\\(.+\\)\\)?\\'" (substring arg 0 -1)))) - (cl-find-if (lambda (a) - (and (stringp a) - (string-match re a))) + (cl-find-if (##and (stringp %) + (string-match re %)) args))] (match-string 1 match))))) @@ -4458,9 +4452,10 @@ Append \"=\ to ARG to indicate that it is an option." (when-let* ((_ transient--stack) (command (oref obj command)) (suffix-obj (transient-suffix-object command)) - (_(memq (if (slot-boundp suffix-obj 'transient) - (oref suffix-obj transient) - (oref transient-current-prefix transient-suffix)) + (_(memq (cond ((slot-boundp suffix-obj 'transient) + (oref suffix-obj transient)) + (transient-current-prefix + (oref transient-current-prefix transient-suffix))) (list t 'recurse #'transient--do-recurse)))) (oset obj return t))) @@ -4691,27 +4686,25 @@ have a history of their own.") "%s- [%s] %s" (key-description (this-command-keys)) (oref transient--prefix command) - (mapconcat - #'identity - (sort - (mapcan - (lambda (suffix) - (let ((key (kbd (oref suffix key)))) - ;; Don't list any common commands. - (and (not (memq (oref suffix command) - `(,(lookup-key transient-map key) - ,(lookup-key transient-sticky-map key) - ;; From transient-common-commands: - transient-set - transient-save - transient-history-prev - transient-history-next - transient-quit-one - transient-toggle-common - transient-set-level))) - (list (propertize (oref suffix key) 'face 'transient-key))))) - transient--suffixes) - #'string<) + (string-join + (sort (seq-keep + (lambda (suffix) + (let ((key (kbd (oref suffix key)))) + ;; Don't list any common commands. + (and (not (memq (oref suffix command) + `(,(lookup-key transient-map key) + ,(lookup-key transient-sticky-map key) + ;; From transient-common-commands: + transient-set + transient-save + transient-history-prev + transient-history-next + transient-quit-one + transient-toggle-common + transient-set-level))) + (propertize (oref suffix key) 'face 'transient-key)))) + transient--suffixes) + #'string<) (propertize "|" 'face 'transient-delimiter))))) (defun transient--insert-menu (setup) @@ -4938,14 +4931,14 @@ as a button." (let ((len (length transient--redisplay-key)) (seq (cl-coerce (edmacro-parse-keys key t) 'list))) (cond - ((member (seq-take seq len) + ((member (take len seq) (list transient--redisplay-key (thread-last transient--redisplay-key (cl-substitute ?- 'kp-subtract) (cl-substitute ?= 'kp-equal) (cl-substitute ?+ 'kp-add)))) - (let ((pre (key-description (vconcat (seq-take seq len)))) - (suf (key-description (vconcat (seq-drop seq len))))) + (let ((pre (key-description (vconcat (take len seq)))) + (suf (key-description (vconcat (drop len seq))))) (setq pre (string-replace "RET" "C-m" pre)) (setq pre (string-replace "TAB" "C-i" pre)) (setq suf (string-replace "RET" "C-m" suf)) @@ -5181,8 +5174,8 @@ apply the face `transient-unreachable' to the complete string." (defun transient--key-unreachable-p (obj) (and transient--redisplay-key (let ((key (oref obj key))) - (not (or (equal (seq-take (cl-coerce (edmacro-parse-keys key t) 'list) - (length transient--redisplay-key)) + (not (or (equal (take (length transient--redisplay-key) + (cl-coerce (edmacro-parse-keys key t) 'list)) transient--redisplay-key) (transient--lookup-key transient-sticky-map (kbd key))))))) @@ -5202,26 +5195,32 @@ apply the face `transient-unreachable' to the complete string." (length (oref suffix key)))) (oref group suffixes)))))) -(defun transient--pixel-width (string) - (save-window-excursion +(static-if (fboundp 'string-pixel-width) ; since Emacs 29.1 + (progn ; See https://github.com/magit/magit/issues/5557. + (defalias 'transient--string-pixel-width #'string-pixel-width)) + ;; c22b735f0c6 and 61c254cafc9 cannot be backported. Some later + ;; commits could be ported, but users should instead update Emacs. + (defun transient--string-pixel-width (string) (with-temp-buffer (insert string) - (set-window-dedicated-p nil nil) - (set-window-buffer nil (current-buffer)) - (car (window-text-pixel-size - nil (line-beginning-position) (point)))))) + (save-window-excursion + (set-window-dedicated-p nil nil) + (set-window-buffer nil (current-buffer)) + (car (window-text-pixel-size + nil (line-beginning-position) (point))))))) (defun transient--column-stops (columns) (let* ((var-pitch (or transient-align-variable-pitch (oref transient--prefix variable-pitch))) - (char-width (and var-pitch (transient--pixel-width " ")))) + (char-width (and var-pitch (transient--string-pixel-width " ")))) (transient--seq-reductions-from (apply-partially #'+ (* 2 (if var-pitch char-width 1))) (transient--mapn (lambda (cells min) (apply #'max (if min (if var-pitch (* min char-width) min) 0) - (mapcar (if var-pitch #'transient--pixel-width #'length) cells))) + (mapcar (if var-pitch #'transient--string-pixel-width #'length) + cells))) columns (oref transient--prefix column-widths)) 0))) @@ -5632,14 +5631,16 @@ search instead." lisp-imenu-generic-expression :test #'equal) (defun transient--suspend-text-conversion-style () - (static-if (boundp 'overriding-text-conversion-style) ; since Emacs 30.1 - (when text-conversion-style - (letrec ((suspended overriding-text-conversion-style) - (fn (lambda () - (setq overriding-text-conversion-style nil) - (remove-hook 'transient-exit-hook fn)))) - (setq overriding-text-conversion-style suspended) - (add-hook 'transient-exit-hook fn))))) + (when (and (bound-and-true-p text-conversion-style) + (bound-and-true-p overriding-text-conversion-style) + ;; Somehow the above does not silence the compiler. + (boundp 'overriding-text-conversion-style)) + (letrec ((suspended overriding-text-conversion-style) + (fn (lambda () + (setq overriding-text-conversion-style nil) + (remove-hook 'transient-exit-hook fn)))) + (setq overriding-text-conversion-style suspended) + (add-hook 'transient-exit-hook fn)))) (declare-function which-key-mode "ext:which-key" (&optional arg)) @@ -5700,7 +5701,7 @@ Like `cl-mapcar' but while that stops when the shortest list is exhausted, continue until the longest list is, using nil as stand-in for elements of exhausted lists." (let (result) - (while (catch 'more (mapc (lambda (l) (and l (throw 'more t))) lists) nil) + (while (catch 'more (mapc (##and % (throw 'more t)) lists) nil) (push (apply function (mapcar #'car-safe lists)) result) (setq lists (mapcar #'cdr lists))) (nreverse result))) @@ -5792,11 +5793,16 @@ as stand-in for elements of exhausted lists." ;; (cond . 0) ;; (interactive . 0)) ;; read-symbol-shorthands: ( -;; ("and$" . "cond-let--and$") -;; ("and-let" . "cond-let--and-let") -;; ("if-let" . "cond-let--if-let") -;; ("when$" . "cond-let--when$") -;; ("when-let" . "cond-let--when-let") -;; ("while-let" . "cond-let--while-let")) +;; ("and$" . "cond-let--and$") +;; ("thread$" . "cond-let--thread$") +;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") +;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") +;; ("while-let" . "cond-let--while-let")) ;; End: ;;; transient.el ends here diff --git a/lisp/transient/transient.info b/lisp/transient/transient.info index a563c4cc..717ed96b 100644 --- a/lisp/transient/transient.info +++ b/lisp/transient/transient.info @@ -32,7 +32,7 @@ used to implement similar menus in other packages. resource to get over that hurdle is Psionic K's interactive tutorial, available at . -This manual is for Transient version 0.12.0. +This manual is for Transient version 0.13.4. Copyright (C) 2018-2026 Free Software Foundation, Inc. diff --git a/lisp/update-autoloads.el b/lisp/update-autoloads.el index 17e90ba6..b0830fb0 100644 --- a/lisp/update-autoloads.el +++ b/lisp/update-autoloads.el @@ -63,6 +63,7 @@ ;; (package-generate-autoloads "org" (concat config-dir "lisp/org")) ;; already org-loaddefs.el (package-generate-autoloads "org-appear" (concat config-dir "lisp/org-appear")) (package-generate-autoloads "org-contrib" (concat config-dir "lisp/org-contrib")) + (package-generate-autoloads "org-wc" (concat config-dir "lisp/org-wc")) (package-generate-autoloads "ox-pandoc" (concat config-dir "lisp/ox-pandoc")) (package-generate-autoloads "ox-rst" (concat config-dir "lisp/ox-rst")) (package-generate-autoloads "rainbow-mode" (concat config-dir "lisp/rainbow-mode")) diff --git a/lisp/vterm/CMakeLists.txt b/lisp/vterm/CMakeLists.txt index 4917264c..65aa4e37 100644 --- a/lisp/vterm/CMakeLists.txt +++ b/lisp/vterm/CMakeLists.txt @@ -51,6 +51,10 @@ if (USE_SYSTEM_LIBVTERM) if (${VTermSBClearExists} EQUAL "0") add_definitions(-DVTermSBClearNotExists) endif() + execute_process(COMMAND grep -c "vterm_screen_enable_reflow" "${LIBVTERM_INCLUDE_DIR}/vterm.h" OUTPUT_VARIABLE VTermScreenEnableReflowExists) + if (${VTermScreenEnableReflowExists} EQUAL "0") + add_definitions(-DVTermScreenEnableReflowNotExists) + endif() else() message(STATUS "System libvterm not found: libvterm will be downloaded and compiled as part of the build process") endif() diff --git a/lisp/vterm/vterm-module.c b/lisp/vterm/vterm-module.c index 72ed7e90..f781c95e 100644 --- a/lisp/vterm/vterm-module.c +++ b/lisp/vterm/vterm-module.c @@ -28,6 +28,9 @@ void free_lineinfo(LineInfo *line) { } static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { Term *term = (Term *)data; + bool pushed_by_height_decr = + term->height_resize < 0 && + term->sb_pending_by_height_decr < -term->height_resize; if (!term->sb_size) { return 0; @@ -69,8 +72,9 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { sbrow->info = term->lines[0]; memmove(term->lines, term->lines + 1, sizeof(term->lines[0]) * (term->lines_len - 1)); - if (term->resizing) { - /* pushed by window height decr */ + if (pushed_by_height_decr) { + /* Only shrink line metadata for rows lost to a height decrease. + Reflow can also push lines during width changes. */ if (term->lines[term->lines_len - 1] != NULL) { /* do not need free here ,it is reused ,we just need set null */ term->lines[term->lines_len - 1] = NULL; @@ -97,8 +101,7 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { if (term->sb_pending < term->sb_size) { term->sb_pending++; /* when window height decreased */ - if (term->height_resize < 0 && - term->sb_pending_by_height_decr < -term->height_resize) { + if (pushed_by_height_decr) { term->sb_pending_by_height_decr++; } } @@ -114,6 +117,9 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) { /// @param data Term static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) { Term *term = (Term *)data; + bool popped_by_height_incr = + term->height_resize > 0 && + term->lines_len < term->height + term->height_resize; if (!term->sb_current) { return 0; @@ -142,14 +148,23 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) { cells[col].width = 1; } - LineInfo **lines = malloc(sizeof(LineInfo *) * (term->lines_len + 1)); - - memmove(lines + 1, term->lines, sizeof(term->lines[0]) * term->lines_len); - lines[0] = sbrow->info; + if (popped_by_height_incr) { + LineInfo **lines = malloc(sizeof(LineInfo *) * (term->lines_len + 1)); + memmove(lines + 1, term->lines, sizeof(term->lines[0]) * term->lines_len); + lines[0] = sbrow->info; + term->lines_len += 1; + free(term->lines); + term->lines = lines; + } else if (term->lines_len > 0) { + LineInfo *lastline = term->lines[term->lines_len - 1]; + memmove(term->lines + 1, term->lines, + sizeof(term->lines[0]) * (term->lines_len - 1)); + term->lines[0] = sbrow->info; + free_lineinfo(lastline); + } else { + free_lineinfo(sbrow->info); + } free(sbrow); - term->lines_len += 1; - free(term->lines); - term->lines = lines; return 1; } @@ -421,8 +436,9 @@ static int term_resize(int rows, int cols, void *user_data) { term->invalid_start = 0; term->invalid_end = rows; - /* if rows=term->lines_len, that means term_sb_pop already resize term->lines - */ + /* term_sb_pop grows term->lines only for rows gained by height increases. + * Reflow can also pop lines during width changes, but those pops keep the + * metadata length stable. */ /* if rowslines_len, term_sb_push would resize term->lines there */ /* we only need to take care of rows>term->height */ @@ -456,7 +472,6 @@ static int term_resize(int rows, int cols, void *user_data) { term->height = rows; invalidate_terminal(term, -1, -1); - term->resizing = false; return 1; } @@ -1253,6 +1268,9 @@ emacs_value Fvterm_new(emacs_env *env, ptrdiff_t nargs, emacs_value args[], vterm_screen_set_callbacks(term->vts, &vterm_screen_callbacks, term); vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL); vterm_screen_enable_altscreen(term->vts, true); +#ifndef VTermScreenEnableReflowNotExists + vterm_screen_enable_reflow(term->vts, true); +#endif term->sb_size = MIN(SB_MAX, sb_size); term->sb_current = 0; term->sb_pending = 0; @@ -1275,7 +1293,6 @@ emacs_value Fvterm_new(emacs_env *env, ptrdiff_t nargs, emacs_value args[], } term->linenum = term->height; term->linenum_added = 0; - term->resizing = false; term->pty_fd = -1; @@ -1373,7 +1390,6 @@ emacs_value Fvterm_set_size(emacs_env *env, ptrdiff_t nargs, emacs_value args[], term->linenum_added = rows - term->height - term->sb_current; } } - term->resizing = true; vterm_set_size(term->vt, rows, cols); vterm_screen_flush_damage(term->vts); diff --git a/lisp/vterm/vterm-module.h b/lisp/vterm/vterm-module.h index ebf36249..f387ae64 100644 --- a/lisp/vterm/vterm-module.h +++ b/lisp/vterm/vterm-module.h @@ -113,7 +113,6 @@ typedef struct Term { int width, height; int height_resize; - bool resizing; bool disable_bold_font; bool disable_underline; bool disable_inverse_video; diff --git a/lisp/vterm/vterm-pkg.el b/lisp/vterm/vterm-pkg.el index 8e5f2d11..3703c9b4 100644 --- a/lisp/vterm/vterm-pkg.el +++ b/lisp/vterm/vterm-pkg.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "vterm" "20251119.1653" +(define-package "vterm" "20260626.1906" "Fully-featured terminal emulator." '((emacs "25.1")) :url "https://github.com/akermu/emacs-libvterm" - :commit "a01a2894a1c1e81a39527835a9169e35b7ec5dec" - :revdesc "a01a2894a1c1" + :commit "9495966d9124ac32c307aee5c0aeb4a06be37519" + :revdesc "9495966d9124" :keywords '("terminals") :authors '(("Lukas Fürmetz" . "fuermetz@mailbox.org")) :maintainers '(("Lukas Fürmetz" . "fuermetz@mailbox.org"))) diff --git a/lisp/vterm/vterm.el b/lisp/vterm/vterm.el index d6e4e779..2bf6348c 100644 --- a/lisp/vterm/vterm.el +++ b/lisp/vterm/vterm.el @@ -3,8 +3,8 @@ ;; Copyright (C) 2017-2020 by Lukas Fürmetz & Contributors ;; ;; Author: Lukas Fürmetz -;; Package-Version: 20251119.1653 -;; Package-Revision: a01a2894a1c1 +;; Package-Version: 20260626.1906 +;; Package-Revision: 9495966d9124 ;; URL: https://github.com/akermu/emacs-libvterm ;; Keywords: terminals ;; Package-Requires: ((emacs "25.1")) @@ -844,7 +844,10 @@ Exceptions are defined by `vterm-keymap-exceptions'." ;; Support to compilation-shell-minor-mode ;; Is this necessary? See vterm--compilation-setup (setq next-error-function 'vterm-next-error-function) - (setq-local bookmark-make-record-function 'vterm--bookmark-make-record)) + (setq-local bookmark-make-record-function 'vterm--bookmark-make-record) + + ;; Support to display directory in buffer listings. + (setq list-buffers-directory (expand-file-name default-directory))) (defun vterm--tramp-get-shell (method) "Get the shell for a remote location as specified in `vterm-tramp-shells'. @@ -1244,12 +1247,9 @@ Argument ARG is passed to `yank'" But when clicking to the unused area below the last prompt, move the cursor to the prompt area." (interactive "e\np") - (let ((pt (mouse-set-point event promote-to-region))) - (if (= (count-words pt (point-max)) 0) - (vterm-reset-cursor-point) - pt)) - ;; Otherwise it selects text for every other click - (keyboard-quit)) + (if (> (count-words (posn-point (event-end event)) (point-max)) 0) + (mouse-set-point event promote-to-region) + (vterm-reset-cursor-point))) (defun vterm-send-string (string &optional paste-p) "Send the string STRING to vterm. @@ -1703,7 +1703,9 @@ If N is negative backward-line from end of buffer." (defun vterm--set-directory (path) "Set `default-directory' to PATH." (let ((dir (vterm--get-directory path))) - (when dir (setq default-directory dir)))) + (when dir + (setq default-directory dir) + (setq list-buffers-directory dir)))) (defun vterm--get-directory (path) "Get normalized directory to PATH." @@ -1719,7 +1721,10 @@ If N is negative backward-line from end of buffer." (progn (when (file-directory-p dir) (setq directory (file-name-as-directory dir)))) - (setq directory (file-name-as-directory (concat "/-:" path)))))) + (let ((method (if (tramp-tramp-file-p default-directory) + (tramp-file-name-method (tramp-dissect-file-name default-directory)) + tramp-default-method-marker))) + (setq directory (file-name-as-directory (concat tramp-prefix-format method tramp-postfix-method-format path))))))) (when (file-directory-p path) (setq directory (file-name-as-directory path)))) directory))) diff --git a/lisp/web-mode/web-mode-pkg.el b/lisp/web-mode/web-mode-pkg.el index 60c677be..b220acdf 100644 --- a/lisp/web-mode/web-mode-pkg.el +++ b/lisp/web-mode/web-mode-pkg.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "web-mode" "20260331.1441" +(define-package "web-mode" "20260623.932" "Major mode for editing web templates." '((emacs "24.3.1")) :url "https://web-mode.org" - :commit "e93b3fb89fd6345a5ff59795bed712abd486200a" - :revdesc "e93b3fb89fd6" + :commit "aeee2d4c82a791ff69657c1413873bf9265544df" + :revdesc "aeee2d4c82a7" :keywords '("languages") :maintainers '(("François-Xavier Bois" . "fxbois@gmail.com"))) diff --git a/lisp/web-mode/web-mode.el b/lisp/web-mode/web-mode.el index e9465c39..a42955f4 100644 --- a/lisp/web-mode/web-mode.el +++ b/lisp/web-mode/web-mode.el @@ -1,9 +1,9 @@ ;;; web-mode.el --- major mode for editing web templates -*- coding: utf-8; lexical-binding: t; -*- -;; Copyright 2011-2025 François-Xavier Bois +;; Copyright 2011-2026 François-Xavier Bois -;; Package-Version: 20260331.1441 -;; Package-Revision: e93b3fb89fd6 +;; Package-Version: 20260623.932 +;; Package-Revision: aeee2d4c82a7 ;; Author: François-Xavier Bois ;; Maintainer: François-Xavier Bois ;; Package-Requires: ((emacs "24.3.1")) @@ -24,7 +24,7 @@ ;;---- CONSTS ------------------------------------------------------------------ -(defconst web-mode-version "17.3.22" +(defconst web-mode-version "17.3.24" "Web Mode version.") ;;---- GROUPS ------------------------------------------------------------------ @@ -9591,7 +9591,7 @@ Also return non-nil if it is the command `self-insert-command' is remapped to." ((and is-js (member ?\, chars)) (when debug (message "I400(%S) part-args" pos)) (cond - ((not (web-mode-part-args-beginning pos reg-beg)) + ((not (web-mode-javascript-args-beginning pos reg-beg)) ;; #1337 ;;(message "ici") ) ((cdr (assoc "lineup-args" web-mode-indentation-params)) diff --git a/lisp/with-editor/with-editor-pkg.el b/lisp/with-editor/with-editor-pkg.el index 977b5f53..29e1b5c2 100644 --- a/lisp/with-editor/with-editor-pkg.el +++ b/lisp/with-editor/with-editor-pkg.el @@ -1,11 +1,13 @@ ;; -*- no-byte-compile: t; lexical-binding: nil -*- -(define-package "with-editor" "20260301.1317" +(define-package "with-editor" "20260625.855" "Use the Emacsclient as $EDITOR." - '((emacs "26.1") - (compat "30.1")) + '((emacs "28.1") + (compat "31.0") + (cond-let "1.1") + (llama "1.0")) :url "https://github.com/magit/with-editor" - :commit "64211dcb815f2533ac3d2a7e56ff36ae804d8338" - :revdesc "64211dcb815f" + :commit "36c34610b6b700b4d1f39ccabd2b8b8c9642292d" + :revdesc "36c34610b6b7" :keywords '("processes" "terminals") :authors '(("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev")) :maintainers '(("Jonas Bernoulli" . "emacs.with-editor@jonas.bernoulli.dev"))) diff --git a/lisp/with-editor/with-editor.el b/lisp/with-editor/with-editor.el index 519296cd..e1da9838 100644 --- a/lisp/with-editor/with-editor.el +++ b/lisp/with-editor/with-editor.el @@ -6,9 +6,13 @@ ;; Homepage: https://github.com/magit/with-editor ;; Keywords: processes terminals -;; Package-Version: 20260301.1317 -;; Package-Revision: 64211dcb815f -;; Package-Requires: ((emacs "26.1") (compat "30.1")) +;; Package-Version: 20260625.855 +;; Package-Revision: 36c34610b6b7 +;; Package-Requires: ( +;; (emacs "28.1") +;; (compat "31.0") +;; (cond-let "1.1") +;; (llama "1.0")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -80,6 +84,8 @@ (require 'cl-lib) (require 'compat) +(require 'cond-let) +(require 'llama) (require 'server) (require 'shell) (eval-when-compile (require 'subr-x)) @@ -125,14 +131,14 @@ please see https://github.com/magit/magit/wiki/Emacsclient.")))) ((bound-and-true-p emacsclient-program-name)) ("emacsclient")) path - (mapcan (lambda (v) (cl-mapcar (lambda (e) (concat v e)) exec-suffixes)) + (mapcan (lambda (suffix) (mapcar (##concat suffix %) exec-suffixes)) (nconc (and (boundp 'debian-emacs-flavor) (list (format ".%s" debian-emacs-flavor))) - (cl-mapcon (lambda (v) - (setq v (string-join (reverse v) ".")) - (list v - (concat "-" v) - (concat ".emacs" v))) + (cl-mapcon (lambda (ver) + (setq ver (string-join (reverse ver) ".")) + (list ver + (concat "-" ver) + (concat ".emacs" ver))) (reverse version-lst)) (cons "" with-editor-emacsclient-program-suffixes))) (lambda (exec) @@ -383,7 +389,7 @@ And some tools that do not handle $EDITOR properly also break." (dolist (client clients) (message "client %S" client) (ignore-errors - (server-send-string client "-error Canceled by user")) + (server-send-string client "-error Canceled by user\n")) (delete-process client)) (when (buffer-live-p buf) (kill-buffer buf))) @@ -546,12 +552,14 @@ at run-time. process-environment)) ;; As last resort fallback to the sleeping editor. (push (concat "ALTERNATE_EDITOR=" with-editor-sleeping-editor) - process-environment))) + process-environment) + ;; Work around bug in server.el of Emacs < 31.1. #139 + (when (member (getenv "TERM") '(nil "")) + (setenv "TERM" "dumb")))) (defun with-editor-server-window () (or (and buffer-file-name - (cdr (cl-find-if (lambda (cons) - (string-match-p (car cons) buffer-file-name)) + (cdr (cl-find-if (##string-match-p (car %) buffer-file-name) with-editor-server-window-alist))) server-window)) @@ -732,8 +740,7 @@ OPEN \\([^]+?\\)\ Files matching a regexp in `with-editor-file-name-history-exclude' are prevented from being added to that list." (pcase-dolist (`(,file . ,_) files) - (when (cl-find-if (lambda (regexp) - (string-match-p regexp file)) + (when (cl-find-if (##string-match-p % file) with-editor-file-name-history-exclude) (setq file-name-history (delete (abbreviate-file-name file) file-name-history))))) @@ -775,11 +782,11 @@ This works in `shell-mode', `term-mode', `eshell-mode' and (process-environment process-environment)) (with-editor--setup) (while (accept-process-output vterm--process 1 nil t)) - (when-let ((v (getenv envvar))) - (vterm-send-string (format " export %s=%S" envvar v)) + (when$ (getenv envvar) + (vterm-send-string (format " export %s=%S" envvar $)) (vterm-send-return)) - (when-let ((v (getenv "EMACS_SERVER_FILE"))) - (vterm-send-string (format " export EMACS_SERVER_FILE=%S" v)) + (when$ (getenv "EMACS_SERVER_FILE") + (vterm-send-string (format " export EMACS_SERVER_FILE=%S" $)) (vterm-send-return)) (vterm-send-string " clear") (vterm-send-return)) @@ -915,14 +922,13 @@ Also take care of that for `with-editor-[async-]shell-command'." (funcall fn command output-buffer error-buffer) (with-editor (funcall fn command output-buffer error-buffer))) ;; The comint filter was overridden with our filter. Use both. - (and-let* ((process (get-buffer-process - (or output-buffer - (get-buffer "*Async Shell Command*"))))) + (and-let ((process (get-buffer-process + (or output-buffer + (get-buffer "*Async Shell Command*"))))) (prog1 process - (set-process-filter process - (lambda (proc str) - (comint-output-filter proc str) - (with-editor-process-filter proc str t)))))) + (add-function :after (process-filter process) + (lambda (proc str) + (with-editor-process-filter proc str t)))))) ((funcall fn command output-buffer error-buffer))))) ;;; _ @@ -995,5 +1001,19 @@ See info node `(with-editor)Debugging' for instructions." ;; byte-compile-warnings: (not docstrings-control-chars) ;; indent-tabs-mode: nil ;; lisp-indent-local-overrides: ((cond . 0) (interactive . 0)) +;; read-symbol-shorthands: ( +;; ("and$" . "cond-let--and$") +;; ("thread$" . "cond-let--thread$") +;; ("when$" . "cond-let--when$") +;; ("and-let*" . "cond-let--and-let*") +;; ("and-let" . "cond-let--and-let") +;; ("if-let*" . "cond-let--if-let*") +;; ("if-let" . "cond-let--if-let") +;; ("when-let*" . "cond-let--when-let*") +;; ("when-let" . "cond-let--when-let") +;; ("while-let*" . "cond-let--while-let*") +;; ("while-let" . "cond-let--while-let") +;; ("match-string" . "match-string") +;; ("match-str" . "match-string-no-properties")) ;; End: ;;; with-editor.el ends here diff --git a/lisp/with-editor/with-editor.info b/lisp/with-editor/with-editor.info index a95ab4ec..963a14b3 100644 --- a/lisp/with-editor/with-editor.info +++ b/lisp/with-editor/with-editor.info @@ -40,7 +40,7 @@ library is made available as a separate package. It also defines some additional functionality which makes it useful even for end-users, who don't use Magit or another package which uses it internally. -This manual is for With-Editor version 3.4.9. +This manual is for With-Editor version 3.5.1. Copyright (C) 2015-2026 Jonas Bernoulli diff --git a/lisp/yasnippet/doc/faq.org b/lisp/yasnippet/doc/faq.org new file mode 100644 index 00000000..6cff4d88 --- /dev/null +++ b/lisp/yasnippet/doc/faq.org @@ -0,0 +1,87 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Frequently Asked Questions + +- *Note*: In addition to the questions and answers presented here, + you might also with to visit the list of [[https://github.com/joaotavora/yasnippet/issues?q=label%3Asupport][solved support issues]] in + the Github issue tracker. It might be more up-to-date than this + list. + +* Why are my snippet abbrev keys triggering when I don't want them too? +Expansion of abbrev keys is controlled by [[sym:yas-key-syntaxes][=yas-key-syntaxes=]]. Try +removing entries which correspond to the abbrev key character syntax. +For example, if you have a snippet with abbrev key "bar", that you +don't want to trigger when point follows the text =foo_bar=, remove +the ="w"= entry (since "bar" has only word syntax characters). + +* Why aren't my snippet abbrev keys triggering when I want them too? +See previous question, but in reverse. + +* Why is there an extra newline? + +If there is a newline at the end of a snippet definition file, +YASnippet will add a newline when expanding that snippet. When editing +or saving a snippet file, please be careful not to accidentally add a +terminal newline. + +Note that some editors will automatically add a newline for you. In +Emacs, if you set =require-final-newline= to =t=, it will add the +final newline automatically. + +* Why doesn't TAB navigation work with flyspell + +This is [[https://debbugs.gnu.org/26672][Emacs Bug#26672]], so you should upgrade to version 25.3 or +better. Otherwise, a workaround is to inhibit flyspell overlays while +the snippet is active: + +#+BEGIN_SRC emacs-lisp + (add-hook 'flyspell-incorrect-hook + #'(lambda (&rest _) + (and yas-active-field-overlay + (overlay-buffer yas-active-field-overlay)))) +#+END_SRC + +* How do I use alternative keys, i.e. not TAB? + +Edit the keymaps [[sym:yas-minor-mode-map][=yas-minor-mode-map=]] and [[sym:yas-keymap][=yas-keymap=]] as you would +any other keymap, but use [[sym:yas-filtered-definition][=yas-filtered-definition=]] on the definition +if you want to respect [[sym:yas-keymap-disable-hook][=yas-keymap-disable-hook=]]: + +#+begin_src emacs-lisp :exports code + (define-key yas-minor-mode-map (kbd "") nil) + (define-key yas-minor-mode-map (kbd "TAB") nil) + (define-key yas-minor-mode-map (kbd "") yas-maybe-expand) + + ;;keys for navigation + (define-key yas-keymap [(tab)] nil) + (define-key yas-keymap (kbd "TAB") nil) + (define-key yas-keymap [(shift tab)] nil) + (define-key yas-keymap [backtab] nil) + (define-key yas-keymap (kbd "") + (yas-filtered-definition 'yas-next-field-or-maybe-expand)) + (define-key yas-keymap (kbd "") + (yas-filtered-definition 'yas-prev-field)) +#+end_src + +* How do I define an abbrev key containing characters not supported by the filesystem? + +- *Note*: This question applies if you're still defining snippets + whose key /is/ the filename. This is behavior still provided by + version 0.6 for backward compatibilty, but is somewhat + deprecated... + +For example, you want to define a snippet by the key =<= which is not a +valid character for filename on Windows. This means you can't use the +filename as a trigger key in this case. + +You should rather use the =# key:= directive to specify the key of the +defined snippet explicitly and name your snippet with an arbitrary valid +filename, =lt.YASnippet= for example, using =<= for the =# key:= +directive: + +#+BEGIN_SRC snippet + # key: < + # name: <...> + # -- + <${1:div}>$0 +#+END_SRC diff --git a/lisp/yasnippet/doc/index.org b/lisp/yasnippet/doc/index.org new file mode 100644 index 00000000..c5e0be32 --- /dev/null +++ b/lisp/yasnippet/doc/index.org @@ -0,0 +1,47 @@ +#+SETUPFILE: org-setup.inc +#+TITLE: Yet another snippet extension + +The YASnippet documentation has been split into separate parts: + +0. [[https://github.com/joaotavora/yasnippet/blob/master/README.mdown][README]] + + Contains an introduction, installation instructions and other important + notes. + +1. [[file:snippet-organization.org][Organizing Snippets]] + + Describes ways to organize your snippets in the hard disk. + +2. [[file:snippet-expansion.org][Expanding Snippets]] + + Describes how YASnippet chooses snippets for expansion at point. + + Maybe, you'll want some snippets to be expanded in a particular mode, + or only under certain conditions, or be prompted using =ido=, etc... + +3. [[file:snippet-development.org][Writing Snippets]] + + Describes the YASnippet definition syntax, which is very close (but + not equivalent) to Textmate's. Includes a section about converting + TextMate snippets. + +4. [[file:snippet-menu.org][The YASnippet menu]] + + Explains how to use the YASnippet menu to explore, learn and modify + snippets. + +5. [[file:faq.org][Frequently asked questions]] + + Answers to frequently asked questions. + +6. [[file:snippet-reference.org][YASnippet Symbol Reference]] + + An automatically generated listing of all YASnippet commands, + (customization) variables, and functions. + + +# Local Variables: +# mode: org +# fill-column: 80 +# coding: utf-8 +# End: diff --git a/lisp/yasnippet/doc/snippet-development.org b/lisp/yasnippet/doc/snippet-development.org new file mode 100644 index 00000000..806f82e5 --- /dev/null +++ b/lisp/yasnippet/doc/snippet-development.org @@ -0,0 +1,474 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Writing snippets + +* Snippet development + +** Quickly finding snippets + +There are some ways you can quickly find a snippet file or create a new one: + +- =M-x yas-new-snippet=, key binding: =C-c & C-n= + + Creates a new buffer with a template for making a new snippet. The + buffer is in =snippet-mode= (see [[snippet-mode][below]]). When you are done editing + the new snippet, use [[yas-load-snippet-buffer-and-close][=C-c C-c=]] to save it. + +- =M-x yas-visit-snippet-file=, key binding: =C-c & C-v= + + Prompts you for possible snippet expansions like + [[sym:yas-insert-snippet][=yas-insert-snippet=]], but instead of expanding it, takes you directly + to the snippet definition's file, if it exists. + +Once you find this file it will be set to =snippet-mode= (see [[snippet-mode][ahead]]) +and you can start editing your snippet. + +** Using the =snippet-mode= major mode <> + +There is a major mode =snippet-mode= to edit snippets. You can set the +buffer to this mode with =M-x snippet-mode=. It provides reasonably +useful syntax highlighting. + +Three commands are defined in this mode: + +- =M-x yas-load-snippet-buffer=, key binding: =C-c C-l= + + Prompts for a snippet table (with a default based on snippet's + major mode) and loads the snippet currently being edited. + +- =M-x yas-load-snippet-buffer-and-close=, key binding: =C-c C-c= + <> + + Like =yas-load-snippet-buffer=, but also saves the snippet and + calls =quit-window=. The destination is decided based on the + chosen snippet table and snippet collection directly (defaulting to + the first directory in =yas-snippet-dirs= (see [[file:snippet-organization.org][Organizing Snippets]] + for more detail on how snippets are organized). + +- =M-x yas-tryout-snippet=, key binding: =C-c C-t= + + When editing a snippet, this opens a new empty buffer, sets it to + the appropriate major mode and inserts the snippet there, so you + can see what it looks like. + +There are also /snippets for writing snippets/: =vars=, =$f= and =$m= +:-). + +* File content + +A file defining a snippet generally contains the template to be +expanded. + +Optionally, if the file contains a line of =# --=, the lines above it +count as comments, some of which can be /directives/ (or meta data). +Snippet directives look like =# property: value= and tweak certain +snippet properties described below. If no =# --= is found, the whole +file is considered the snippet template. + +Here's a typical example: + +#+BEGIN_SRC snippet + # contributor: pluskid + # name: __...__ + # -- + __${init}__ +#+END_SRC + +Here's a list of currently supported directives: + +** =# key:= snippet abbrev + +This is the probably the most important directive, it's the +abbreviation you type to expand a snippet just before hitting the key +that runs [[sym:yas-expand][=yas-expand=]]. If you don't specify this, +the snippet will not be expandable through the trigger mechanism. + +** =# name:= snippet name + +This is a one-line description of the snippet. It will be displayed in +the menu. It's a good idea to select a descriptive name for a snippet -- +especially distinguishable among similar snippets. + +If you omit this name, it will default to the file name the snippet +was loaded from. + +** =# condition:= snippet condition + +This is a piece of Emacs Lisp code. If a snippet has a condition, then +it will only be expanded when the condition code evaluate to some +non-nil value. + +See also [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] in +[[./snippet-expansion.org][Expanding snippets]] + +** =# group:= snippet menu grouping + +When expanding/visiting snippets from the menu-bar menu, snippets for a +given mode can be grouped into sub-menus. This is useful if one has too +many snippets for a mode which will make the menu too long. + +The =# group:= property only affect menu construction (See +[[./snippet-menu.org][the YASnippet menu]]) and the same effect can be +achieved by grouping snippets into sub-directories and using the +=.yas-make-groups= special file (for this see +[[./snippet-organization.org][Organizing Snippets]] + +Refer to the bundled snippets for =ruby-mode= for examples of the +=# group:= directive. Group can also be nested, e.g. +=control structure.loops= indicates that the snippet is under the =loops= +group which is under the =control structure= group. + +** =# expand-env:= expand environment + +This is another piece of Emacs Lisp code in the form of a =let= /varlist +form/, i.e. a list of lists assigning values to variables. It can be +used to override variable values while the snippet is being expanded. + +Interesting variables to override are [[sym:yas-wrap-around-region][=yas-wrap-around-region=]] and +[[sym:yas-indent-line][=yas-indent-line=]] (see [[./snippet-expansion.org][Expanding Snippets]]). + +As an example, you might normally have [[sym:yas-indent-line][=yas-indent-line=]] set to '=auto= +and [[sym:yas-wrap-around-region][=yas-wrap-around-region=]] set to =t=, but for this particularly +brilliant piece of ASCII art these values would mess up your hard work. +You can then use: + +#+BEGIN_SRC snippet + # name: ASCII home + # expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil)) + # -- + welcome to my + X humble + / \ home, + / \ $0 + / \ + /-------\ + | | + | +-+ | + | | | | + +--+-+--+ +#+END_SRC + +** =# binding:= direct keybinding + +You can use this directive to expand a snippet directly from a normal +Emacs keybinding. The keybinding will be registered in the Emacs keymap +named after the major mode the snippet is active for. + +Additionally a variable [[sym:yas-prefix][=yas-prefix=]] is set to the prefix argument +you normally use for a command. This allows for small variations on the +same snippet, for example in this =html-mode= snippet. + +#+BEGIN_SRC snippet + # name:

    ...

    + # binding: C-c C-c C-m + # -- +

    `(when yas-prefix "\n")`$0`(when yas-prefix "\n")`

    +#+END_SRC + +This binding will be recorded in the keymap =html-mode-map=. To expand a +paragraph tag newlines, just press =C-u C-c C-c C-m=. Omitting the =C-u= +will expand the paragraph tag without newlines. + +** =# type:= =snippet= or =command= + +If the =type= directive is set to =command=, the body of the snippet +is interpreted as Lisp code to be evaluated when the snippet is +triggered. + +If it's =snippet= (the default when there is no =type= directive), the +snippet body will be parsed according to the [[Template Syntax]], +described below. + +** =# uuid:= unique identifier + +This provides to a way to identify a snippet, independent of its name. +Loading a second snippet file with the same uuid would replace the +previous snippet. + +** =# contributor:= snippet author + +This is optional and has no effect whatsoever on snippet functionality, +but it looks nice. + +* Template Syntax + +The syntax of the snippet template is simple but powerful, very similar +to TextMate's. + +** Plain Text + +Arbitrary text can be included as the content of a template. They are +usually interpreted as plain text, except =$= and =`=. You need to +use =\= to escape them: =\$= and =\`=. The =\= itself may also needed to be +escaped as =\\= sometimes. + +** Embedded Emacs Lisp code + +Emacs Lisp code can be embedded inside the template, written inside +back-quotes (=`=). The Lisp forms are evaluated when the snippet is +being expanded. The evaluation is done in the same buffer as the +snippet being expanded. + +Here's an example for =c-mode= to calculate the header file guard +dynamically: + +#+BEGIN_SRC snippet + #ifndef ${1:_`(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`_H_} + #define $1 + + $0 + + #endif /* $1 */ +#+END_SRC + +From version 0.6, snippet expansions are run with some special +Emacs Lisp variables bound. One of these is [[sym:yas-selected-text][=yas-selected-text=]]. You can +therefore define a snippet like: + +#+BEGIN_SRC snippet + for ($1;$2;$3) { + `yas-selected-text`$0 + } +#+END_SRC + +to "wrap" the selected region inside your recently inserted snippet. +Alternatively, you can also customize the variable +[[sym:yas-wrap-around-region][=yas-wrap-around-region=]] to =t= which will do this automatically. + +*** Note: backquote expressions should not modify the buffer + +Please note that the Lisp forms in backquotes should *not* modify the +buffer, doing so will trigger a warning. For example, instead of +doing + +#+BEGIN_SRC snippet + Timestamp: `(insert (current-time-string))` +#+END_SRC + +do this: +#+BEGIN_SRC snippet + Timestamp: `(current-time-string)` +#+END_SRC + +The warning may be suppressed with the following code in your init file: +#+BEGIN_SRC emacs-lisp + (add-to-list 'warning-suppress-types '(yasnippet backquote-change)) +#+END_SRC + + +** Tab stop fields + +Tab stops are fields that you can navigate back and forth by =TAB= and +=S-TAB=. They are written by =$= followed with a number. =$0= has the +special meaning of the /exit point/ of a snippet. That is the last place +to go when you've traveled all the fields. Here's a typical example: + +#+BEGIN_SRC snippet + + $0 + +#+END_SRC +** Placeholder fields + +Tab stops can have default values -- a.k.a placeholders. The syntax is +like this: + +#+BEGIN_SRC snippet + ${N:default value} +#+END_SRC + +They act as the default value for a tab stop. But when you first +type at a tab stop, the default value will be replaced by your typing. +The number can be omitted if you don't want to create [[mirrors-fields][mirrors]] or +[[mirror-transformations][transformations]] for this field. + +** Mirrors <> + +We refer to tab stops with placeholders as a /field/. A field can +have mirrors. *All* mirrors get updated whenever you update any field +text. Here's an example: + +#+BEGIN_SRC snippet + \begin{${1:enumerate}} + $0 + \end{$1} +#+END_SRC + +When you type "document" at =${1:enumerate}=, the word "document" will +also be inserted at =\end{$1}=. The best explanation is to see the +screencast([[http://www.youtube.com/watch?v=vOj7btx3ATg][YouTube]] or [[http://yasnippet.googlecode.com/files/yasnippet.avi][avi video]]). + +The tab stops with the same number to the field act as its mirrors. If +none of the tab stops have an initial value, the first one is selected as +the field and the others are its mirrors. + +** Mirrors with transformations <> + +If the value of an =${n:=-construct starts with and contains =$(=, +then it is interpreted as a mirror for field =n= with a +transformation. The mirror's text content is calculated according to +this transformation, which is Emacs Lisp code that gets evaluated in +an environment where the variable [[sym:yas-text][=yas-text=]] is bound to the text +content (string) contained in the field =n=. Here's an example for +Objective-C: + +#+BEGIN_SRC snippet + - (${1:id})${2:foo} + { + return $2; + } + + - (void)set${2:$(capitalize yas-text)}:($1)aValue + { + [$2 autorelease]; + $2 = [aValue retain]; + } + $0 +#+END_SRC + +Look at =${2:$(capitalize yas-text)}=, it is a mirror with +transformation instead of a field. The actual field is at the first +line: =${2:foo}=. When you type text in =${2:foo}=, the transformation +will be evaluated and the result will be placed there as the +transformed text. So in this example, if you type "baz" in the field, +the transformed text will be "Baz". This example is also available in +the screencast. + +Another example is for =rst-mode=. In reStructuredText, the document +title can be some text surrounded by "===" below and above. The "===" +should be at least as long as the text. So + +#+BEGIN_SRC rst + ===== + Title + ===== +#+END_SRC + +is a valid title but + +#+BEGIN_SRC rst + === + Title + === +#+END_SRC + +is not. Here's an snippet for rst title: + +#+BEGIN_SRC snippet + ${1:$(make-string (string-width yas-text) ?\=)} + ${1:Title} + ${1:$(make-string (string-width yas-text) ?\=)} + + $0 +#+END_SRC + +Note that a mirror with a transform is not restricted to the text of +the field it is mirroring. By making use of [[sym:yas-field-value][=yas-field-value=]], a +mirror can look at any of the snippet's field (as mentioned above, all +mirrors are updated when any field is updated). Here is an example +which shows a "live" result of calling format: + +#+BEGIN_SRC snippet +(format "${1:formatted %s}" "${2:value}") +=> "${1:$(ignore-errors (format (yas-field-value 1) (yas-field-value 2)))}" +#+END_SRC + +To keep the example simple, it uses =ignore-errors= to suppress errors +due to incomplete format codes. + +** Fields with transformations + +From version 0.6 on, you can also have Lisp transformation inside +fields. These work mostly like mirror transformations. However, they +are evaluated when you first enter the field, after each change you +make to the field and also just before you exit the field. + +The syntax is also a tiny bit different, so that the parser can +distinguish between fields and mirrors. In the following example + +: #define "${1:mydefine$(upcase yas-text)}" + +=mydefine= gets automatically upcased to =MYDEFINE= once you enter the +field. As you type text, it gets filtered through the transformation +every time. + +Note that to tell this kind of expression from a mirror with a +transformation, YASnippet needs extra text between the =:= and the +transformation's =$=. If you don't want this extra-text, you can use two +=$='s instead. + +: #define "${1:$$(upcase yas-text)}" + +Please note that as soon as a transformation takes place, it changes the +value of the field and sets it its internal modification state to +=true=. As a consequence, the auto-deletion behaviour of normal fields +does not take place. This is by design. + +** Choosing fields value from a list and other tricks + +As mentioned, the field transformation is invoked just after you enter +the field, and with some useful variables bound, notably +[[sym:yas-modified-p][=yas-modified-p=]] and [[sym:yas-moving-away-p][=yas-moving-away-p=]]. Because of this feature you +can place a transformation in the primary field that lets you select +default values for it. + +For example, the [[sym:yas-choose-value][=yas-completing-read=]] function is version of +=completing-read= which checks these variables. For example, asking +the user for the initial value of a field: + +#+BEGIN_SRC snippet +
    + $0 +
    +#+END_SRC + +See the definition of [[sym:yas-choose-value][=yas-completing-read=]] to see how it was written +using the two variables. If you're really lazy :) and can't spare a +tab keypress, you can automatically move to the next field (or exit) +after choosing the value with [[sym:yas-auto-next][=yas-auto-next=]]. The snippet above +becomes: + +#+BEGIN_SRC snippet +
    + $0 +
    +#+END_SRC + +Here's another use, for =LaTeX-mode=, which calls reftex-label just as you +enter snippet field 2. This one makes use of [[sym:yas-modified-p][=yas-modified-p=]] directly. + +#+BEGIN_SRC snippet + \section{${1:"Titel der Tour"}}% + \index{$1}% + \label{{2:"waiting for reftex-label call..."$(unless yas-modified-p (reftex-label nil 'dont-insert))}}% +#+END_SRC + +The function [[sym:yas-verify-value][=yas-verify-value=]] has another neat trick, and makes use +of [[sym:yas-moving-away-p][=yas-moving-away-p=]]. Try it and see! Also, check out this [[http://groups.google.com/group/smart-snippet/browse_thread/thread/282a90a118e1b662][thread]] + +** Nested placeholder fields + +From version 0.6 on, you can also have nested placeholders of the type: + +#+BEGIN_SRC snippet + $0 +#+END_SRC + +This allows you to choose if you want to give this =div= an =id= +attribute. If you tab forward after expanding, it will let you change +"some\_id" to whatever you like. Alternatively, you can just press =C-d= +(which executes [[sym:yas-skip-and-clear-or-delete-char][=yas-skip-and-clear-or-delete-char=]]) and go straight to +the exit marker. + +By the way, =C-d= will only clear the field if you cursor is at the +beginning of the field /and/ it hasn't been changed yet. Otherwise, it +performs the normal Emacs =delete-char= command. + +** Indentation markers + +If [[sym:yas-indent-line][=yas-indent-line=]] is *not* set to '=auto=, it's still possible to +indent specific lines by adding an indentation marker, =$>=, somewhere +on the line. diff --git a/lisp/yasnippet/doc/snippet-expansion.org b/lisp/yasnippet/doc/snippet-expansion.org new file mode 100644 index 00000000..2ff0e458 --- /dev/null +++ b/lisp/yasnippet/doc/snippet-expansion.org @@ -0,0 +1,284 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Expanding snippets + + This section describes how YASnippet chooses snippets for expansion at point. + + Maybe, you'll want some snippets to be expanded in a particular + mode, or only under certain conditions, or be prompted using + +* Triggering expansion + + You can use YASnippet to expand snippets in different ways: + + - When [[sym:yas-minor-mode][=yas-minor-mode=]] is active: + - Type the snippet's *trigger key* then calling [[sym:yas-expand][=yas-expand=]] + (bound to =TAB= by default). + + - Use the snippet's *keybinding*. + + - By expanding directly from the "YASnippet" menu in the menu-bar + + - Using hippie-expand + + - Call [[sym:yas-insert-snippet][=yas-insert-snippet=]] (use =M-x yas-insert-snippet= or its + keybinding =C-c & C-s=). + + - Use m2m's excellent auto-complete + TODO: example for this + + - Expanding from emacs-lisp code + +** Trigger key + +[[sym:yas-expand][=yas-expand=]] tries to expand a /snippet abbrev/ (also known as +/snippet key/) before point. YASnippet also provides a /conditional +binding/ for this command: the variable [[sym:yas-expand][=yas-maybe-expand=]] contains a +special value which, when bound in a keymap, tells Emacs to call +[[sym:yas-expand][=yas-expand=]] if and only if there is a snippet abbrev before point. +If there is no snippet to expand, Emacs will behave as if [[sym:yas-expand][=yas-expand=]] +is unbound and so will run whatever command is bound to that key +normally. + +When [[sym:yas-minor-mode][=yas-minor-mode=]] is enabled, it binds [[sym:yas-maybe-expand][=yas-maybe-expand=]] to =TAB= +and == by default, however, you can freely remove those bindings: + +#+begin_src emacs-lisp :exports code + (define-key yas-minor-mode-map (kbd "") nil) + (define-key yas-minor-mode-map (kbd "TAB") nil) +#+end_src + +And set your own: + +#+begin_src emacs-lisp :exports code + ;; Bind `SPC' to `yas-expand' when snippet expansion available (it + ;; will still call `self-insert-command' otherwise). + (define-key yas-minor-mode-map (kbd "SPC") yas-maybe-expand) + ;; Bind `C-c y' to `yas-expand' ONLY. + (define-key yas-minor-mode-map (kbd "C-c y") #'yas-expand) +#+end_src + + +To enable the YASnippet minor mode in all buffers globally use the +command [[sym:yas-global-mode][=yas-global-mode=]]. This will enable a modeline indicator, +=yas=: + +[[./images/minor-mode-indicator.png]] + +When you use [[sym:yas-global-mode][=yas-global-mode=]] you can also selectively disable +YASnippet in some buffers by calling [[sym:yas-minor-mode][=yas-minor-mode=]] with a negative +argument in the buffer's mode hook. + +*** Fallback behaviour + +YASnippet used to support a more complicated way of sharing +keybindings before [[sym:yas-expand][=yas-maybe-expand=]] was added. This is now +obsolete. + +** Insert at point + +The command [[sym:yas-insert-snippet][=yas-insert-snippet=]] lets you insert snippets at point +/for your current major mode/. It prompts you for the snippet key +first, and then for a snippet template if more than one template +exists for the same key. + +The list presented contains the snippets that can be inserted at point, +according to the condition system. If you want to see all applicable +snippets for the major mode, prefix this command with =C-u=. + +The prompting methods used are again controlled by +[[sym:yas-prompt-functions][=yas-prompt-functions=]]. + +*** Inserting region or register contents into snippet + +It's often useful to inject already written text in the middle of a +snippet. The variable [[sym:yas-wrap-around-region][=yas-wrap-around-region=]] when to t substitute +the region contents into the =$0= placeholder of a snippet expanded by +[[sym:yas-insert-snippet][=yas-insert-snippet=]]. Setting it to a character value (e.g. =?0=) +will insert the contents of corresponding register. + +Older (versions 0.9.1 and below) of Yasnippet, supported a setting of +=cua= that is equivalent to =?0= but only worked with =cua-mode= +turned on. This setting is still supported for backwards +compatibility, but is now entirely equivalent to =?0=. + +** Snippet keybinding + +See the section of the =# binding:= directive in +[[./snippet-development.org][Writing Snippets]]. + +** Expanding from the menu + +See [[./snippet-menu.org][the YASnippet Menu]]. + +** Expanding with =hippie-expand= + +To integrate with =hippie-expand=, just put +[[sym:yas-hippie-try-expand][=yas-hippie-try-expand=]] in +=hippie-expand-try-functions-list=. This probably makes more sense +when placed at the top of the list, but it can be put anywhere you +prefer. + +** Expanding from emacs-lisp code + +Sometimes you might want to expand a snippet directly from your own +elisp code. You should call [[sym:yas-expand-snippet][=yas-expand-snippet=]] instead of +[[sym:yas-expand][=yas-expand=]] in this case. [[sym:yas-expand-snippet][=yas-expand-snippet=]] takes a string in +snippet template syntax, if you want to expand an existing snippet you +can use [[sym:yas-lookup-snippet][=yas-lookup-snippet=]] to find its contents by name. + +As with expanding from the menubar, the condition system and multiple +candidates doesn't affect expansion (the condition system does affect +[[sym:yas-lookup-snippet][=yas-lookup-snippet=]] though). In fact, expanding from the YASnippet +menu has the same effect of evaluating the follow code: + +#+BEGIN_SRC emacs-lisp + (yas-expand-snippet template) +#+END_SRC + +See the internal documentation on [[sym:yas-expand-snippet][=yas-expand-snippet=]] and +[[sym:yas-lookup-snippet][=yas-lookup-snippet=]] for more information. + +* Controlling expansion + +** Eligible snippets<> + +YASnippet does quite a bit of filtering to find out which snippets are +eligible for expanding at the current cursor position. + +In particular, the following things matter: + +- Currently loaded snippets tables + + These are loaded from a directory hierarchy in your file system. See + [[./snippet-organization.org][Organizing Snippets]]. They are named + after major modes like =html-mode=, =ruby-mode=, etc... + +- Major mode of the current buffer + + If the currrent major mode matches one of the loaded snippet tables, + then all that table's snippets are considered for expansion. Use + =M-x describe-variable RET major-mode RET= to find out which major + mode you are in currently. + +- Parent tables + + Snippet tables defined as the parent of some other eligible table + are also considered. This works recursively, i.e., parents of + parents of eligible tables are also considered. As a special case, + if a mode doesn't have a parent, then =fundamental-mode= is + considered to be its parent. + +- Buffer-local list of extra modes + + Use [[sym:yas-activate-extra-mode][=yas-activate-extra-mode=]] to + consider snippet tables whose name does not correspond to a major + mode. Typically, you call this from a minor mode hook, for example: + +#+BEGIN_SRC emacs-lisp + ;; When entering rinari-minor-mode, consider also the snippets in the + ;; snippet table "rails-mode" + (add-hook 'rinari-minor-mode-hook + #'(lambda () + (yas-activate-extra-mode 'rails-mode))) +#+END_SRC + +- Buffer-local [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] variable + + This variable provides finer grained control over what snippets can + be expanded in the current buffer. For example, the constant + [[sym:yas-not-string-or-comment-condition][=yas-not-string-or-comment-condition=]] has a value that disables + snippet expansion inside comments or string literals. See [[condition-system][the + condition system]] for more info. + +** The condition system <> + +Consider this scenario: you are an old Emacs hacker. You like the +abbrev-way and bind [[sym:yas-expand][=yas-expand=]] to =SPC=. However, you don't want +=if= to be expanded as a snippet when you are typing in a comment +block or a string (e.g. in =python-mode=). + +If you use the =# condition := directive (see [[./snippet-development.org][Writing Snippets]]) you +could just specify the condition for =if= to be =(not +(python-syntax-comment-or-string-p))=. But how about =while=, =for=, +etc? Writing the same condition for all the snippets is just boring. +So you can instead set [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] to =(not +(python-syntax-comment-or-string-p))= in =python-mode-hook=. + +Then, what if you really want some particular snippet to expand even +inside a comment? Set [[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]] like this + +#+BEGIN_SRC emacs-lisp + (add-hook 'python-mode-hook + (lambda () + (setq yas-buffer-local-condition + (lambda () + (if (python-syntax-comment-or-string-p) + '(require-snippet-condition . force-in-comment) + t))))) +#+END_SRC + +... and for a snippet that you want to expand in comments, specify a +condition which evaluates to the symbol =force-in-comment=. Then it +can be expanded as you expected, while other snippets like =if= still +can't expanded in comments. + +For the full set of possible conditions, see the documentation for +[[sym:yas-buffer-local-condition][=yas-buffer-local-condition=]]. + +** Multiples snippet with the same key + +The rules outlined [[eligible-snippets][above]] can return more than +one snippet to be expanded at point. + +When there are multiple candidates, YASnippet will let you select one. +The UI for selecting multiple candidate can be customized through +[[sym:yas-prompt-functions][=yas-prompt-functions=]] , which defines your preferred methods of being +prompted for snippets. + +You can customize it with +=M-x customize-variable RET yas-prompt-functions RET=. Alternatively you +can put in your emacs-file: + +#+BEGIN_SRC emacs-lisp + (setq yas-prompt-functions '(yas-x-prompt yas-dropdown-prompt)) +#+END_SRC + +Currently there are some alternatives solution with YASnippet. + +*** Use the X window system + +[[./images/x-menu.png]] + +The function [[sym:yas-x-prompt][=yas-x-prompt=]] can be used to show a popup menu for you to +select. This menu will be part of you native window system widget, which +means: + +- It usually looks beautiful. E.g. when you compile Emacs with gtk + support, this menu will be rendered with your gtk theme. +- Your window system may or may not allow to you use =C-n=, =C-p= to + navigate this menu. +- This function can't be used when in a terminal. + +*** Minibuffer prompting + +[[./images/ido-menu.png]] + +You can use functions [[sym:yas-completing-prompt][=yas-completing-prompt=]] for the classic emacs +completion method or [[sym:yas-ido-prompt][=yas-ido-prompt=]] for a much nicer looking method. +The best way is to try it. This works in a terminal. + +*** Use =dropdown-menu.el= + +[[./images/dropdown-menu.png]] + +The function [[sym:yas-dropdown-prompt][=yas-dropdown-prompt=]] can also be placed in the +[[sym:yas-prompt-functions][=yas-prompt-functions=]] list. + +This works in both window system and terminal and is customizable, you +can use =C-n=, =C-p= to navigate, =q= to quit and even press =6= as a +shortcut to select the 6th candidate. + +*** Roll your own + +See the documentation on variable [[sym:yas-prompt-functions][=yas-prompt-functions=]] diff --git a/lisp/yasnippet/doc/snippet-menu.org b/lisp/yasnippet/doc/snippet-menu.org new file mode 100644 index 00000000..fee3a196 --- /dev/null +++ b/lisp/yasnippet/doc/snippet-menu.org @@ -0,0 +1,68 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: YASnippet menu + +When [[sym:yas-minor-mode][=yas-minor-mode=]] is active, YASnippet will setup a menu just after +the "Buffers" menu in the menubar. + +In this menu, you can find + +- The currently loaded snippet definitions, organized by major mode, + and optional grouping. + +- A rundown of the most common commands, (followed by their + keybindings) including commands to load directories and reload all + snippet definitions. + +- A series of submenus for customizing and exploring YASnippet + behavior. + +[[./images/menu-1.png]] + +* Loading snippets from menu + +Invoking "Load snippets..." from the menu invokes [[sym:yas-load-directory][=yas-load-directory=]] +and prompts you for a snippet directory hierarchy to load. + +Also useful is the "Reload everything" item to invoke [[sym:yas-reload-all][=yas-reload-all=]] +which uncondionally reloads all the snippets directories defined in +[[sym:yas-snippet-dirs][=yas-snippet-dirs=]] and rebuilds the menus. + +* Snippet menu behavior + +YASnippet will list in this section all the loaded snippet definitions +organized by snippet table name. + +You can use this section to explore currently loaded snippets. If you +click on one of them, the default behavior is to expand it, +unconditionally, inside the current buffer. + +You can however, customize variable [[sym:yas-visit-from-menu][=yas-visit-from-menu=]] to be =t= +which will take you to the snippet definition file when you select it +from the menu. + +If you want the menu show only snippet tables whose name corresponds to +a "real" major mode. You do this by setting [[sym:yas-use-menu][=yas-use-menu=]] to +'=real-modes=. + +Finally, to have the menu show only the tables for the currently active +mode, set [[sym:yas-use-menu][=yas-use-menu=]] to =abbreviate=. + +These customizations can also be found in the menu itself, under the +"Snippet menu behavior" submenu. + +* Controlling indenting + +The "Indenting" submenu contains options to control the values of +[[sym:yas-indent-line][=yas-indent-line=]] and [[sym:yas-also-auto-indent-first-line][=yas-also-auto-indent-first-line=]]. See +[[./snippet-development.org][Writing snippets]]. + +* Prompting method + +The "Prompting method" submenu contains options to control the value of +[[sym:yas-prompt-functions][=yas-prompt-functions=]]. See [[./snippet-expansion.org][Expanding snippets]]. + +* Misc + +The "Misc" submenu contains options to control the values of more +variables. diff --git a/lisp/yasnippet/doc/snippet-organization.org b/lisp/yasnippet/doc/snippet-organization.org new file mode 100644 index 00000000..6b8feef9 --- /dev/null +++ b/lisp/yasnippet/doc/snippet-organization.org @@ -0,0 +1,132 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Organizing snippets + +* Basic structure + + Snippet collections can be stored in plain text files. They are + arranged by sub-directories naming *snippet tables*. These mostly + name Emacs major mode names. + + #+begin_example + . + |-- c-mode + | `-- printf + |-- java-mode + | `-- println + `-- text-mode + |-- email + `-- time + #+end_example + + The collections are loaded into *snippet tables* which the + triggering mechanism (see [[file:snippet-expansion.org][Expanding Snippets]]) looks up and + (hopefully) causes the right snippet to be expanded for you. + +* Setting up =yas-snippet-dirs= + + The emacs variable [[sym:yas-snippet-dirs][=yas-snippet-dirs=]] tells YASnippet + which collections to consider. It's used when you activate + [[sym:yas-global-mode][=yas-global-mode=]] or call + [[sym:yas-reload-all][=yas-reload-all=]] interactively. + + The default considers: + + - a personal collection that lives in =~/.emacs.d/snippets= + - the bundled collection, taken as a relative path to =yasnippet.el= location + + When you come across other snippet collections, do the following to try them + out: + + #+begin_src emacs-lisp :exports code + ;; Develop in ~/emacs.d/mysnippets, but also + ;; try out snippets in ~/Downloads/interesting-snippets + (setq yas-snippet-dirs '("~/emacs.d/mysnippets" + "~/Downloads/interesting-snippets")) + + ;; OR, keeping YASnippet defaults try out ~/Downloads/interesting-snippets + (setq yas-snippet-dirs (append yas-snippet-dirs + '("~/Downloads/interesting-snippets"))) + #+end_src + + Collections appearing earlier in the list override snippets with same names + appearing in collections later in the list. [[sym:yas-new-snippet][=yas-new-snippet=]] always stores + snippets in the first collection. + +* The =.yas-parents= file + + It's very useful to have certain modes share snippets between + themselves. To do this, choose a mode subdirectory and place a + =.yas-parents= containing a whitespace-separated list of other mode + names. When you reload those modes become parents of the original + mode. + + #+begin_example + . + |-- c-mode + | |-- .yas-parents # contains "cc-mode text-mode" + | `-- printf + |-- cc-mode + | |-- for + | `-- while + |-- java-mode + | |-- .yas-parents # contains "cc-mode text-mode" + | `-- println + `-- text-mode + |-- email + `-- time + #+end_example + + +* TODO The =.yas-make-groups= file + + If you place an empty plain text file =.yas-make-groups= inside one + of the mode directories, the names of these sub-directories are + considered groups of snippets and [[file:snippet-menu.org][the menu]] is organized much more + cleanly: + + [[./images/menu-groups.png]] + + Another way to achieve this is to place a =# group:= directive + inside the snippet definition. See [[./snippet-development.org][Writing Snippets]]. + + #+begin_example + $ tree ruby-mode/ + ruby-mode/ + |-- .yas-make-groups + |-- collections + | |-- each + | `-- ... + |-- control structure + | |-- forin + | `-- ... + |-- definitions + | `-- ... + `-- general + `-- ... + #+end_example + + Yet another way to create a nice snippet menu is to write into + =.yas-make-groups= a menu definition. TODO + +* The =.yas-setup.el= file + + If there is file named =.yas-setup.el= in a mode's snippet + subdirectory, it is loaded along with the snippets. Utility + functions used by the snippets can be put here. + +* The =.yas-compiled-snippet.el= file + + You may compile a top-level snippet directory with the + =yas-compile-directory= function, which will create a + =.yas-compiled-snippets.el= file under each mode subdirectory, + which contains definitions for all snippets in the subdirectory. + Compilation helps improve loading time. + + Alternatively, you may compile all directories in the list + =yas-snippet-dirs= with the =yas-recompile-all= function. + +* The =.yas-skip= file + + A =.yas-skip= file in a mode's snippet subdirectory tells YASnippet + not to load snippets from there. diff --git a/lisp/yasnippet/doc/snippet-reference.org b/lisp/yasnippet/doc/snippet-reference.org new file mode 100644 index 00000000..a38fca5a --- /dev/null +++ b/lisp/yasnippet/doc/snippet-reference.org @@ -0,0 +1,12 @@ +#+SETUPFILE: org-setup.inc + +#+TITLE: Reference + +#+BEGIN_SRC emacs-lisp :exports results :results value raw +(yas--document-symbols 1 `("Interactive functions" . ,#'interactive-form) + `("Customization variables" . ,#'(lambda (sym) + (and (boundp sym) + (get sym 'standard-value)))) + `("Useful functions" . ,#'fboundp) + `("Useful variables" . ,#'boundp)) +#+END_SRC diff --git a/lisp/yasnippet/yasnippet-debug.el b/lisp/yasnippet/yasnippet-debug.el new file mode 100644 index 00000000..6fd3e3f6 --- /dev/null +++ b/lisp/yasnippet/yasnippet-debug.el @@ -0,0 +1,354 @@ +;;; yasnippet-debug.el --- debug functions for yasnippet -*- lexical-binding: t -*- + +;; Copyright (C) 2010-2025 Free Software Foundation, Inc. + +;; Author: João Távora +;; Keywords: emulations, convenience + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Some debug functions. When loaded from the command line, provides +;; quick way to test out snippets in a fresh Emacs instance. +;; +;; emacs -Q -l yasnippet-debug [-v[v]] +;; [-M:] [-M.] [-S:[]] +;; [-- ...] +;; +;; See the source in `yas-debug-process-command-line' for meaning of +;; args. +;; +;;; Code: + +(defconst yas--loaddir + (file-name-directory (or load-file-name buffer-file-name)) + "Directory that yasnippet was loaded from.") + +(require 'yasnippet (if (boundp 'yas--loaddir) + ;; Don't require '-L ' when debugging. + (expand-file-name "yasnippet" yas--loaddir))) +(require 'cl-lib) +(require 'color nil t) +(require 'edebug) +(eval-when-compile + (require 'subr-x nil t) + (cond ((fboundp 'when-let*) nil) ; Introduced in 26. + ((fboundp 'when-let) ; Introduced in 25.1, + (defalias 'when-let* 'when-let)) ; deprecated in 26. + (t (defmacro when-let* (key-vals &rest body) + (declare (indent 1) (debug ((symbolp form) body))) + (let ((key-val (pop key-vals))) + (if key-val + `(let ((,(car key-val) ,(cadr key-val))) + (if ,(car key-val) + (when-let* ,key-vals + ,@body))) + `(progn ,@body))))))) + +(defvar yas-debug-live-indicators + (make-hash-table :test #'eq)) + +(defun yas-debug-live-colors () + (let ((colors ())) + (maphash (lambda (_k v) (push (nth 1 (car v)) colors)) yas-debug-live-indicators) + colors)) + +(defvar yas-debug-recently-live-indicators) + +(defun yas-debug-get-live-indicator (location) + (require 'color) + (when (boundp 'yas-debug-recently-live-indicators) + (push location yas-debug-recently-live-indicators)) + (let (beg end) + (if (markerp location) + (setq beg (setq end (marker-position location))) + (setq beg (yas-debug-ov-fom-start location) + end (yas-debug-ov-fom-end location))) + (or (when-let* ((color-ov (gethash location yas-debug-live-indicators))) + (if (and beg end) (move-overlay (cdr color-ov) beg end) + (delete-overlay (cdr color-ov))) + color-ov) + (let* ((live-colors (yas-debug-live-colors)) + (color + (cl-loop with best-color = nil with max-dist = -1 + for color = (format "#%06X" (random #x1000000)) + for comp = (if (fboundp 'color-complement) + (apply #'color-rgb-to-hex (color-complement color)) + color) + if (< (color-distance color (face-foreground 'default)) + (color-distance comp (face-foreground 'default))) + do (setq color comp) + for dist = (cl-loop for c in live-colors + minimize (color-distance c color)) + if (or (not live-colors) (> dist max-dist)) + do (setq best-color color) (setq max-dist dist) + repeat (if live-colors 100 1) + finally return `(:background ,best-color))) + (ov (make-overlay beg end))) + (if (markerp location) + (overlay-put ov 'before-string (propertize "↓" 'face color)) + (overlay-put ov 'before-string (propertize "↘" 'face color)) + (overlay-put ov 'after-string (propertize "↙" 'face color))) + (puthash location (cons color ov) yas-debug-live-indicators))))) + +(defun yas-debug-live-marker (marker) + (let* ((color-ov (yas-debug-get-live-indicator marker)) + (color (car color-ov)) + (ov (cdr color-ov)) + (decorator (overlay-get ov 'before-string)) + (str (format "at %d" (+ marker)))) + (if (markerp marker) + (propertize str + 'cursor-sensor-functions + `(,(lambda (_window _oldpos dir) + (overlay-put + ov 'before-string + (propertize decorator + 'face (if (eq dir 'entered) + 'mode-line-highlight color))))) + 'face color) + str))) + +(defun yas-debug-ov-fom-start (ovfom) + (cond ((overlayp ovfom) (overlay-start ovfom)) + ((integerp ovfom) ovfom) + (t (yas--fom-start ovfom)))) +(defun yas-debug-ov-fom-end (ovfom) + (cond ((overlayp ovfom) (overlay-end ovfom)) + ((integerp ovfom) ovfom) + (t (yas--fom-end ovfom)))) + +(defun yas-debug-live-range (range) + (let* ((color-ov (yas-debug-get-live-indicator range)) + (color (car color-ov)) + (ov (cdr color-ov)) + (decorator-beg (overlay-get ov 'before-string)) + (decorator-end (overlay-get ov 'after-string)) + (beg (yas-debug-ov-fom-start range)) + (end (yas-debug-ov-fom-end range))) + (if (and beg end (or (overlayp range) + (and (not (integerp beg)) + (not (integerp end))))) + (propertize (format "from %d to %d" (+ beg) (+ end)) + 'cursor-sensor-functions + `(,(lambda (_window _oldpos dir) + (let ((face (if (eq dir 'entered) + 'mode-line-highlight color))) + (overlay-put ov 'before-string + (propertize decorator-beg 'face face)) + (overlay-put ov 'after-string + (propertize decorator-end 'face face))))) + 'face color) + ""))) + +(defmacro yas-debug-with-tracebuf (outbuf &rest body) + (declare (indent 1) (debug (sexp body))) + (let ((tracebuf-var (make-symbol "tracebuf"))) + `(let ((,tracebuf-var (or ,outbuf (get-buffer-create "*YASnippet trace*")))) + (unless (eq ,tracebuf-var (current-buffer)) + (cl-flet ((printf (fmt &rest args) + (with-current-buffer ,tracebuf-var + (insert (apply #'format fmt args))))) + (unless ,outbuf + (with-current-buffer ,tracebuf-var + (erase-buffer) + (when (fboundp 'cursor-sensor-mode) + (cursor-sensor-mode +1)) + (setq truncate-lines t))) + (setq ,outbuf ,tracebuf-var) + (save-restriction + (widen) + ,@body)))))) + + +(defun yas-debug-snippet (snippet &optional outbuf) + (yas-debug-with-tracebuf outbuf + (when-let* ((overlay (yas--snippet-control-overlay snippet))) + (printf "\tsid: %d control overlay %s\n" + (yas--snippet-id snippet) + (yas-debug-live-range overlay))) + (when-let* ((active-field (yas--snippet-active-field snippet))) + (unless (consp (yas--field-start active-field)) + (printf "\tactive field: #%d %s %s covering \"%s\"\n" + (or (yas--field-number active-field) -1) + (if (yas--field-modified-p active-field) "**" "--") + (yas-debug-live-range active-field) + (buffer-substring-no-properties (yas--field-start active-field) (yas--field-end active-field))))) + (when-let* ((exit (yas--snippet-exit snippet))) + (printf "\tsnippet-exit: %s next: %s\n" + (yas-debug-live-marker (yas--exit-marker exit)) + (yas--exit-next exit))) + (dolist (field (yas--snippet-fields snippet)) + (unless (consp (yas--field-start field)) + (printf "\tfield: %d %s %s covering \"%s\" next: %s%s\n" + (or (yas--field-number field) -1) + (if (yas--field-modified-p field) "**" "--") + (yas-debug-live-range field) + (buffer-substring-no-properties (yas--field-start field) (yas--field-end field)) + (yas--debug-format-fom-concise (yas--field-next field)) + (if (yas--field-parent-field field) + (format " parent: %s" + (yas--debug-format-fom-concise + (yas--field-parent-field field))) + ""))) + (dolist (mirror (yas--field-mirrors field)) + (unless (consp (yas--mirror-start mirror)) + (printf "\t\tmirror: %s covering \"%s\" next: %s\n" + (yas-debug-live-range mirror) + (buffer-substring-no-properties (yas--mirror-start mirror) (yas--mirror-end mirror)) + (yas--debug-format-fom-concise (yas--mirror-next mirror)))))))) + +(defvar yas-debug-target-buffer nil) +(defvar yas-debug-target-snippets nil nil) +(make-variable-buffer-local 'yas-debug-target-snippets) + +(defvar yas-debug-undo nil) + +(defun yas-toggle-debug-undo (value) + (interactive (list (not yas-debug-undo))) + (setq yas-debug-undo value) + (yas--message 3 "debug undo %sabled" (if yas-debug-undo "en" "dis"))) + +(defun yas-debug--target-snippet (snippet) + (add-to-list 'yas-debug-target-snippets snippet)) + +(defun yas-debug--untarget-snippet (snippet) + (setq yas-debug-target-snippets + (remq snippet yas-debug-target-snippets)) + (maphash (lambda (_k color-ov) + (delete-overlay (cdr color-ov))) + yas-debug-live-indicators) + (clrhash yas-debug-live-indicators)) + +(defun yas-debug-snippets (&optional outbuf hook) + "Print debug information on active snippets to buffer OUTBUF. +If OUTBUF is nil, use a buffer named \"*YASsnippet trace*\". +If HOOK is non-nil, install `yas-debug-snippets' in +`post-command-hook' to update the information on every command +after this one. If it is `snippet-navigation' then install hook +buffer-locally, otherwise install it globally. If HOOK is +`edebug-create', also instrument the function +`yas--snippet-parse-create' with `edebug' and show its source." + (interactive (list nil t)) + (condition-case err + (yas-debug-with-tracebuf outbuf + (unless (buffer-live-p yas-debug-target-buffer) + (setq yas-debug-target-buffer nil)) + (with-current-buffer (or yas-debug-target-buffer (current-buffer)) + (when yas-debug-target-snippets + (setq yas-debug-target-snippets + (cl-delete-if-not #'yas--snippet-p yas-debug-target-snippets))) + (let ((yas-debug-recently-live-indicators nil)) + (printf "(length yas--snippets-snippets) => %d\n" + (length yas--active-snippets)) + (dolist (snippet (or yas-debug-target-snippets + (yas-active-snippets))) + (printf "snippet %d\n" (yas--snippet-id snippet)) + (yas-debug-snippet snippet outbuf)) + (maphash (lambda (loc color-ov) + (unless (memq loc yas-debug-recently-live-indicators) + (delete-overlay (cdr color-ov)) + (remhash loc yas-debug-live-indicators))) + yas-debug-live-indicators)) + (when (and yas-debug-undo (listp buffer-undo-list)) + (printf "Undo list has %s elements:\n" (length buffer-undo-list)) + (cl-loop for undo-elem in buffer-undo-list + do (printf "%S\n" undo-elem)))) + (when hook + (setq yas-debug-target-buffer (current-buffer)) + (advice-add 'yas--snippet-parse-create :before #'yas-debug--target-snippet) + (advice-add 'yas--commit-snippet :after #'yas-debug--untarget-snippet) + (add-hook 'post-command-hook #'yas-debug-snippets + nil (eq hook 'snippet-navigation)) + ;; Window management is slapped together, it does what I + ;; want when the caller has a single window open. Good + ;; enough for now. + (when (eq hook 'edebug-create) + (edebug-instrument-function 'yas--snippet-parse-create) + (let ((buf-point (find-function-noselect 'yas--snippet-parse-create))) + (with-current-buffer (car buf-point) + (goto-char (cdr buf-point))))) + outbuf)) + ((debug error) (signal (car err) (cdr err))))) + +(defun yas-debug-snippet-create () + (yas-debug-snippets nil 'create)) + +(defun yas--debug-format-fom-concise (fom) + (when fom + (cond ((yas--field-p fom) + (format "field %s from %d to %d" + (yas--field-number fom) + (+ (yas--field-start fom)) + (+ (yas--field-end fom)))) + ((yas--mirror-p fom) + (format "mirror from %d to %d" + (+ (yas--mirror-start fom)) + (+ (yas--mirror-end fom)))) + (t + (format "snippet exit at %d" + (+ (yas--fom-start fom))))))) + +(defun yas-debug-process-command-line (&optional options) + "Implement command line processing." + (setq yas-verbosity 99) + (setq yas-triggers-in-field t) + (setq debug-on-error t) + (let* ((snippet-mode 'fundamental-mode) + (snippet-key nil)) + (unless options + (setq options (cl-loop for opt = (pop command-line-args-left) + while (and opt (not (equal opt "--")) + (string-prefix-p "-" opt)) + collect opt))) + (when-let* ((mode (cl-member "-M:" options :test #'string-prefix-p))) + (setq snippet-mode (intern (concat (substring (car mode) 3) "-mode")))) + (when-let* ((mode (cl-member "-M." options :test #'string-prefix-p))) + (setq snippet-mode + (cdr (cl-assoc (substring (car mode) 2) auto-mode-alist + :test (lambda (ext regexp) (string-match-p regexp ext)))))) + (switch-to-buffer (get-buffer-create "*yas test*")) + (funcall snippet-mode) + (when-let* ((snippet-file (cl-member "-S:" options :test #'string-prefix-p))) + (setq snippet-file (substring (car snippet-file) 3)) + (if (file-exists-p snippet-file) + (with-temp-buffer + (insert-file-contents snippet-file) + (let ((snippet-deflist (yas--parse-template snippet-file))) + (yas-define-snippets snippet-mode (list snippet-deflist)) + (setq snippet-key (car snippet-deflist)))) + (yas-reload-all) + (let ((template (yas--lookup-snippet-1 snippet-file snippet-mode))) + (if template + (setq snippet-key (yas--template-key template)) + (error "No such snippet `%s'" snippet-file))))) + (display-buffer (find-file-noselect + (expand-file-name "yasnippet.el" yas--loaddir))) + (when-let* ((verbosity (car (or (member "-v" options) (member "-vv" options))))) + (set-window-buffer + (split-window) (yas-debug-snippets + nil (if (equal verbosity "-vv") 'edebug-create t)))) + (yas-minor-mode +1) + (when snippet-key (insert snippet-key)))) + +(when command-line-args-left + (yas-debug-process-command-line)) + +(provide 'yasnippet-debug) +;; Local Variables: +;; indent-tabs-mode: nil +;; autoload-compute-prefixes: nil +;; End: +;;; yasnippet-debug.el ends here diff --git a/lisp/yasnippet/yasnippet-pkg.el b/lisp/yasnippet/yasnippet-pkg.el index 3cefaed7..621873b8 100644 --- a/lisp/yasnippet/yasnippet-pkg.el +++ b/lisp/yasnippet/yasnippet-pkg.el @@ -7,4 +7,7 @@ :commit "dd570a6b22364212fff9769cbf4376bdbd7a63c5" :revdesc "dd570a6b2236" :keywords '("convenience" "emulation") + :authors '(("pluskid" . "pluskid@gmail.com") + ("João Távora" . "joaotavora@gmail.com") + ("Noam Postavsky" . "npostavs@gmail.com")) :maintainers '(("Noam Postavsky" . "npostavs@gmail.com")))