commit 6cb8a67e4bcfea913fd0ee461e3a4c064250c39a (HEAD, refs/remotes/origin/master) Author: LdBeth Date: Sun Oct 19 00:11:52 2025 +0800 Fix a recent regression in Newsticker * lisp/net/newst-backend.el (newsticker--parse-text-container): New function. (newsticker--parse-atom-1.0): Use it. (Bug#79617) diff --git a/lisp/net/newst-backend.el b/lisp/net/newst-backend.el index 45883fe306f..345d68c8e21 100644 --- a/lisp/net/newst-backend.el +++ b/lisp/net/newst-backend.el @@ -1107,6 +1107,15 @@ same as in `newsticker--parse-atom-1.0'." (xml-node-children node)))) (or new-item new-feed))) +(defun newsticker--parse-text-container (node) + "Handle content according to ``type'' attirbute." + (let ((content (car (xml-node-children node)))) + (if (string= "html" (xml-get-attribute node 'type)) + ;; element contains entity escaped html + content + ;; plain text or xhtml + (newsticker--unxml content)))) + (defun newsticker--unxml (node) "Reverse parsing of an xml string. Restore an xml-string from a an xml NODE that was returned by xml-parse..." @@ -1159,23 +1168,14 @@ URL `http://www.atompub.org/2005/08/17/draft-ietf-atompub-format-11.html'" name time (xml-get-children topnode 'entry) ;; title-fn (lambda (node) - (car (xml-node-children - (car (xml-get-children node 'title))))) + (newsticker--parse-text-container + (car (xml-get-children node 'title)))) ;; desc-fn (lambda (node) - ;; unxml the content or the summary node. Atom - ;; allows for integrating (x)html into the atom - ;; structure but we need the raw html string. - ;; e.g. https://www.heise.de/open/news/news-atom.xml - ;; https://feeds.feedburner.com/ru_nix_blogs - (or (newsticker--unxml - (car (xml-node-children - (car (xml-get-children node 'content))))) - (newsticker--unxml - (car (xml-node-children - (car (xml-get-children node 'summary))))) - (car (xml-node-children - (car (xml-get-children node 'summary)))))) + (or (newsticker--parse-text-container + (car (xml-get-children node 'content))) + (newsticker--parse-text-container + (car (xml-get-children node 'summary))))) ;; link-fn (lambda (node) (xml-get-attribute commit 4ec24ce2a13661e3cd721b795f36bf76e0428abe Author: Stefan Monnier Date: Sat Oct 18 17:45:07 2025 -0400 (eval-and-compile): Preserve the surrounding lexical context Implement a better fix for bug#79634. * lisp/emacs-lisp/byte-run.el (eval-and-compile): * lisp/emacs-lisp/bytecomp.el (byte-compile-initial-macro-environment) : Preserve the surrounding lexical context (the part available during macroexpansion, i.e. which vars are dynbound). * lisp/emacs-lisp/rx.el ( rx): Remove workaround. * test/lisp/emacs-lisp/macroexp-tests.el (macroexp--dynbound-eval-and-compile): New test. diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el index 6cce65e2c9b..05ee0615fec 100644 --- a/lisp/emacs-lisp/byte-run.el +++ b/lisp/emacs-lisp/byte-run.el @@ -683,7 +683,8 @@ enabled." ;; When the byte-compiler expands code, this macro is not used, so we're ;; either about to run `body' (plain interpretation) or we're doing eager ;; macroexpansion. - (list 'quote (eval (cons 'progn body) lexical-binding))) + (list 'quote (eval (cons 'progn body) + (when lexical-binding (or macroexp--dynvars t))))) (defun with-no-warnings (&rest body) "Like `progn', but prevents compiler warnings in the body." diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 4fc56ae4b5d..12079e0f53c 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -589,7 +589,11 @@ Only conses are traversed and duplicated, not arrays or any other structure." macroexpand-all-environment))) (eval (byte-run-strip-symbol-positions (bytecomp--copy-tree expanded)) - lexical-binding) + (when lexical-binding + (or (append + macroexp--dynvars + byte-compile-bound-variables) + t))) expanded))))) (with-suppressed-warnings . ,(lambda (warnings &rest body) diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el index 617a0fb1d40..229f1dbb57b 100644 --- a/lisp/emacs-lisp/rx.el +++ b/lisp/emacs-lisp/rx.el @@ -1685,10 +1685,6 @@ following constructs: REF can be a number, as usual, or a name introduced by a previous (let REF ...) construct." - ;; FIXME: We can't rely on the surrounding lexical context because - ;; `pcase-defmacro' wraps this function inside an `eval-and-compile', - ;; so we have to repeat the (defvar rx--pcase-vars). - (defvar rx--pcase-vars) (let* ((rx--pcase-vars nil) (regexp (rx--to-expr (rx--pcase-transform (cons 'seq regexps))))) `(and (pred stringp) diff --git a/test/lisp/emacs-lisp/macroexp-tests.el b/test/lisp/emacs-lisp/macroexp-tests.el index 817c5d7be49..91d66986bc6 100644 --- a/test/lisp/emacs-lisp/macroexp-tests.el +++ b/test/lisp/emacs-lisp/macroexp-tests.el @@ -124,6 +124,48 @@ (dyn dyn dyn dyn) (dyn dyn dyn lex)))))) +(ert-deftest macroexp--dynbound-eval-and-compile () + (let ((code1 '(progn + (eval-and-compile + (defun my-foo () (bound-and-true-p my-foo)) + (defun my-identity (x) + (defvar my-foo) + (let ((my-foo x)) + (my-foo)))) + (defmacro my-toto (y) + `(list ',y ',(my-identity y))) + (eval-when-compile (my-toto 7)))) + (code2 '(progn + (defvar my-foo) + (eval-and-compile + (defun my-foo () (bound-and-true-p my-foo)) + (defun my-identity (x) + (let ((my-foo x)) + (my-foo)))) + (defmacro my-toto (y) + `(list ',y ',(my-identity y))) + (eval-when-compile (my-toto 7)))) + (code3 '(progn + (eval-and-compile + (defvar my-foo) + (defun my-foo () (bound-and-true-p my-foo)) + (defun my-identity (x) + (let ((my-foo x)) + (my-foo)))) + (defmacro my-toto (y) + `(list ',y ',(my-identity y))) + (eval-when-compile (my-toto 7))))) + (should (equal (eval code1 t) '(7 7))) + (should (equal (eval code2 t) '(7 7))) + (should (equal (eval code3 t) '(7 7))) + (should (equal (eval (let ((lexical-binding t)) (byte-compile code1)) t) + '(7 7))) + (should (equal (eval (let ((lexical-binding t)) (byte-compile code2)) t) + '(7 7))) + (should (equal (eval (let ((lexical-binding t)) (byte-compile code3)) t) + '(7 7))) + )) + (defmacro macroexp--test-macro1 () (declare (obsolete "new-replacement" nil)) 1) commit 48357dc612ffe2d6b286e3f99a0e7e9c80cfcbb8 Author: Helmut Eller Date: Sat Oct 18 20:22:17 2025 +0200 Add a test for puthash It's actually for a bug on the feature/igc branch. * test/src/fns-tests.el (ft-puthash-weak): New test (ft--puthash-weak): New helper. diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index e0467c0b871..7e9286a4251 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el @@ -1330,6 +1330,22 @@ (ert-deftest ft-weak-and-removal () (ft--test-weak-removal 'key-and-value)) (ert-deftest ft-weak-or-removal () (ft--test-weak-removal 'key-or-value)) +(defun ft--test-puthash (weakness) + (let ((h (make-hash-table :weakness weakness)) + (a (string ?a)) + (b (string ?b)) + (c (string ?c))) + (puthash a a h) + (should (eq (gethash a h) a)) + (puthash a b h) + (should (eq (gethash a h) b)) + (puthash a c h) + (should (eq (gethash a h) c)))) + +(ert-deftest ft-puthash-weak () + (dolist (w '(nil key value key-and-value key-or-value)) + (ft--test-puthash w))) + (ert-deftest test-hash-function-that-mutates-hash-table () commit 08dc7d0c9306a0f9b7d330216098dc762f584624 Author: Juri Linkov Date: Sat Oct 18 20:53:44 2025 +0300 * lisp/minibuffer.el (completion--insert-strings): Use the correct frame. Provide the 'frame-height' call with the frame of the window where the *Completions* buffer is displayed for the case when the minibuffer window uses a dedicated frame (bug#79635). diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 4e4622fa7f9..29628fcb831 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2316,7 +2316,8 @@ Runs of equal candidate strings are eliminated. GROUP-FUN is a ;; Windows can't show less than 3 lines anyway. (max 1 (/ (length strings) 2)))) (colwidth (/ wwidth columns)) - (lines (or completions-max-height (frame-height)))) + (lines (or completions-max-height + (frame-height (window-frame window))))) (unless (or tab-stop-list (null completion-tab-width) (zerop (mod colwidth completion-tab-width))) ;; Align to tab positions for the case commit 97b960ec6d9874837f94c84ae91e6f2f49670c2c Author: Alan Mackenzie Date: Sat Oct 18 17:05:11 2025 +0000 Fix coding error in byte-run--strip-vector/record * lisp/emacs-lisp/byte-run.el (byte-run--strip-vector/record): Replace a symbol with position by its bare symbol, rather than returning it unchanged. diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el index 49fd6084693..6cce65e2c9b 100644 --- a/lisp/emacs-lisp/byte-run.el +++ b/lisp/emacs-lisp/byte-run.el @@ -75,7 +75,7 @@ This is done by destructively modifying ARG. Return ARG." (setq elt (aref arg i)) (cond ((symbol-with-pos-p elt) - (aset arg i elt)) + (aset arg i (bare-symbol elt))) ((consp elt) (byte-run--strip-list elt)) ((or (vectorp elt) (recordp elt)) commit 0b4079ddb6027b577b2085017ef7b7f462b437cd Author: Sean Whitton Date: Sat Oct 18 17:30:21 2025 +0100 vc-do-command: Don't print a spurious newline * lisp/vc/vc-dispatcher.el (vc-do-command): Don't print a spurious newline. Don't omit the format string with 'message'. (Bug#79650) diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index 78173786705..54154190dad 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -469,9 +469,11 @@ case, and the process object in the asynchronous case." (pop-to-buffer (current-buffer)) (goto-char (point-min)) (shrink-window-if-larger-than-buffer)) - (when noninteractive + (when-let* (noninteractive + (out (string-trim (buffer-string))) + (_ (not (string-empty-p out)))) (with-current-buffer buffer - (message (string-trim (buffer-string))))) + (message "%s" out))) (error "Failed (%s): %s" (if (integerp status) (format "status %d" status) commit c66680d0018f07a57e908d3b2971ceefc1b8fbd8 Author: Alcor Date: Fri Oct 3 10:09:00 2025 +0200 Rcirc: Fix invalid face error message when applying IRC color * lisp/net/rcirc.el (rcirc-color-attributes): Fix invalid face. (Bug#79561) Copyright-paperwork-exempt: yes diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el index a7f1d8bb575..93e9255cce8 100644 --- a/lisp/net/rcirc.el +++ b/lisp/net/rcirc.el @@ -3144,8 +3144,8 @@ indicated by RESPONSE)." ((<= 0 bg (1- (length rcirc-color-codes))))) (setq background (aref rcirc-color-codes bg))) (rcirc-add-face (match-beginning 0) (match-end 0) - `(face (,@(and foreground (list :foreground foreground)) - ,@(and background (list :background background)))))))) + (append (and foreground (list :foreground foreground)) + (and background (list :background background))))))) (defun rcirc-remove-markup-codes (_sender _response) "Remove ASCII control codes used to designate markup." commit 1f3d417736cbf363f64cf8b99752408bbfd266a5 Author: Lockywolf Date: Mon Sep 22 12:46:03 2025 +0800 Fix detecting a working hunspell on systems without a dictionary Fixes bug#79477 and bug#79514 * test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el (ispell-tests-hunspell--hunspell-working): Implement checker. (ispell/hunspell/ispell-word/russian/check-only): Add skip-check. (ispell/hunspell/ispell-word/english/check-only): Add skip-check. (ispell/hunspell/ispell-word/language-switch/check-only): Add skip-check. (ispell/hunspell/ispell-word/russian/check-only/wrong-language): Add skip-check. (ispell/hunspell/ispell-word/multilang): Add skip-check. * test/lisp/textmodes/ispell-tests/ispell-tests-common.el (fake-aspell-path): Improve checking that an engine works. (ispell-tests--letopt): Make restoring variables more robust, rename to a local name. Add debug spec. (ispell-tests--with-ispell-global-dictionary): Rename to a local name Add debug spec. (ispell-tests--some-valid-dictionary): Add a selector for a safe dictionary. (ispell/ispell-accept-buffer-local-defs/received-file): Add. (ispell-tests--constants/english/correct-list): Add. (ispell-tests--constants/english/correct-one): Add. (ispell-tests--constants/english/wrong): Add. (ispell-tests--constants/russian/correct): Add. (ispell-tests--constants/russian/wrong): Add. (ispell-tests--constants/completion): Add. * test/lisp/textmodes/ispell-tests/ispell-tests.el (ispell/ispell-buffer-local-words/ispell-buffer-session-localwords): reverse letopt and with-ispell-dictionary. (ispell/ispell-buffer-local-words/ispell-words-keyword): reverse letopt and with-ispell-dictionary. (ispell/ispell-complete-word-interior-frag/simple): reverse letopt and with-ispell-dictionary. (ispell/ispell-complete-word/ispell-completion-at-point): reverse letopt and with-ispell-dictionary. (ispell/ispell-init-process/works-with-home): reverse letopt and with-ispell-dictionary. (ispell/ispell-kill-ispell): reverse letopt and with-ispell-dictionary. * test/lisp/textmodes/ispell-resources/fake-aspell-new.bash: Add comments on unused function. Replace echo with printf. diff --git a/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash b/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash index dec4f2d1667..0cc7ede8e38 100755 --- a/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash +++ b/test/lisp/textmodes/ispell-resources/fake-aspell-new.bash @@ -1,5 +1,9 @@ #!/bin/bash +# Presumably it is okay to use GNU Bash, because it is one of the most +# popular shells, and even if it is not available, the calling side +# inside Emacs knows how to handle errors gracefully. + #exec aspell "$@" #rm -rf ~/lwf_mock-aspell.log @@ -8,15 +12,11 @@ #printf 'args="%s"\n' "$*" >> /tmp/lwf_mock-aspell.log || { printf "lwf:ERROR\n" ; exit 3 ; } -# coproc aspell { aspell "$@" ; } - if [[ "$HOME" == '' ]] ; then - echo "HOME is unset. Aspell usually fails in such a case\n" 1>2 + printf "HOME is unset. Aspell usually fails in such a case\n" 1>&2 exit 3 fi -vv= - show_vv() { printf '%s\n' "@(#) International Ispell Version 3.1.20 (but really Aspell 0.60.0)" @@ -69,6 +69,9 @@ imitate_pipe() imitate_interactive() { + : "This function is not used at the moment, but it might become + useful eventually, if Emacs starts supporting calling the backend + using the human interface, not just the pipe interface." exit 6 while true ; do read a diff --git a/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el index f3c9fc7a8a0..d843dd245ba 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-aspell-tests.el @@ -37,45 +37,37 @@ (ert-deftest ispell/aspell/ispell-check-version/works () "Test that aspell is correctly detected." (skip-unless (and (executable-find "aspell") - (with-temp-buffer - (call-process "aspell" nil t nil "-vv") - (search-backward "but really Aspell")))) - (should (stringp - (let ((test-saved-ispell-program-name ispell-program-name)) - (unwind-protect - (let () - (setq ispell-last-program-name (time-to-seconds)) - (setf ispell-program-name "aspell") - ispell-really-aspell) - (setf ispell-program-name test-saved-ispell-program-name)))))) + (with-temp-buffer + (call-process "aspell" nil t nil "-vv") + (search-backward "but really Aspell")))) + (ispell-tests--letopt ((ispell-program-name "aspell")) + (setq ispell-last-program-name (time-to-seconds)) + (setf ispell-program-name "aspell") + (should (stringp ispell-really-aspell))) + 'passed) (ert-deftest ispell/aspell/ispell-check-version/version-lowlow () - "Test that aspell is correctly detected." + "Test that low-version aspell is correctly detected and rejected." (skip-unless (progn (let ((fake-aspell-path (expand-file-name "./fake-aspell.bash" - tests-ispell-data-directory))) - (chmod fake-aspell-path 504) + ispell-tests--data-directory))) + (chmod fake-aspell-path #o770) (call-process fake-aspell-path nil nil nil)))) (let ((fake-aspell-path (expand-file-name "./fake-aspell.bash" - tests-ispell-data-directory))) - (let ((test-saved-ispell-program-name ispell-program-name) - (test-saved-ispell-last-program-name ispell-last-program-name)) - (unwind-protect - (progn - (setq ispell-last-program-name (time-to-seconds)) - (should-error - (progn - (setopt ispell-program-name fake-aspell-path) - (ispell-check-version t))) - ispell-really-aspell) - (set-variable 'ispell-program-name test-saved-ispell-program-name) - (set-variable 'ispell-last-program-name - test-saved-ispell-last-program-name))))) + ispell-tests--data-directory))) + (ispell-tests--letopt ((ispell-program-name "aspell")) + (setq ispell-last-program-name (time-to-seconds)) + (should-error + (progn + (setopt ispell-program-name fake-aspell-path) + (ispell-check-version t)))) + 'passed) + ) (ert-deftest ispell/aspell/ispell-word/english/correct () -"This test checks that Russian spellchecking works for Aspell." + "This test checks that Russian spellchecking works for Aspell." (skip-unless (executable-find "aspell")) (skip-unless (equal 0 @@ -90,28 +82,24 @@ "aspell" nil t nil "-a" "-denglish")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "aspell") - (ispell-dictionary "english")) + (ispell-tests--letopt ((ispell-program-name "aspell") + (ispell-dictionary "english")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "hello\n") - (goto-char 0) + ispell-tests--constants/english/correct-one "\n") + (goto-char 1) (ispell-change-dictionary "english") - (let ((debugmessage "")) - (ert-with-message-capture lres - (let ((ispell-check-only t)) - (ispell-word) - (setf debugmessage lres) - ;;(should (string-match "is correct" lres)) - )) - (message "lwf:lres=%s" debugmessage))) - 'passed - ))) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word) + (should (string-match "is correct" lres)) + ))))) + 'passed) ) (ert-deftest ispell/aspell/ispell-word/english/incorrect () -"This test checks that Russian spellchecking works for Aspell." + "This test checks that English spellchecking works for Aspell." (skip-unless (executable-find "aspell")) (skip-unless (equal 0 @@ -119,22 +107,22 @@ (skip-unless (equal 0 (with-temp-buffer - (insert "test") + (insert ispell-tests--constants/english/correct-one) (call-process-region nil nil "aspell" nil t nil "-a" "-denglish")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "aspell") - (ispell-dictionary "english")) + (ispell-tests--letopt ((ispell-program-name "aspell") + (ispell-dictionary "english")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert ;; there is no such a word in English, I swear. - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + ispell-tests--constants/english/wrong"\n" ) - (goto-char 0) + (goto-char 1) (ispell-change-dictionary "english") (ert-with-message-capture lres (let ((ispell-check-only t)) @@ -145,7 +133,7 @@ ) (ert-deftest ispell/aspell/ispell-word/english/wrong-language () - "This test checks that Russian spellchecking works for Aspell." + "This test checks that English spellchecking works for Aspell." :expected-result :failed (skip-unless (executable-find "aspell")) (skip-unless (equal @@ -154,26 +142,27 @@ (skip-unless (equal 0 (with-temp-buffer - (insert "test") + (insert ispell-tests--constants/english/correct-one) (call-process-region nil nil "aspell" nil '("*scratch*" t) nil "-a" "-denglish")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "aspell")) + (ispell-tests--letopt + ((ispell-program-name "aspell")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert ;; giving Aspell a wrong language should not fail - "привет\n" + ispell-tests--constants/russian/correct "\n" ) - (goto-char 0) + (goto-char 1) (ispell-change-dictionary "english") (ert-with-message-capture lres (let ((ispell-check-only t)) (ispell-word)) (should (not (string-match "Error" lres))))) - 'passed - ))) + 'passed))) ) -(provide 'tests-ispell-aspell) -;;; tests-ispell-aspell.el ends here +(provide 'ispell-aspell-tests) + +;;; ispell-aspell-tests.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el index 18206b08608..0ba9140137c 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-hunspell-tests.el @@ -34,12 +34,23 @@ load-path))) (require 'ispell-tests-common))) +(defun ispell-tests-hunspell--hunspell-working () + "Check that Hunspell can be called from Emacs." + (if (not (equal + 0 + (call-process "hunspell" nil nil nil "-a"))) + (progn + (message "Hunspell installation is broken and does not support a default dictionary!") + nil) + t)) + (ert-deftest ispell/hunspell/ispell-word/english/check-only () -"This test checks that Russian spellchecking works for Hunspell." + "This test checks that English spellchecking works for Hunspell." (skip-unless (executable-find "hunspell")) (skip-unless (equal 0 (call-process "hunspell" nil nil nil "-vv"))) + (skip-unless (ispell-tests-hunspell--hunspell-working)) (skip-unless (equal 0 (with-temp-buffer @@ -47,57 +58,37 @@ (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d en_US")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "hunspell")) + (ispell-tests--letopt ((ispell-program-name "hunspell")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "hello\n") - (goto-char 0) + ispell-tests--constants/english/correct-one "\n") + (goto-char 1) (ispell-change-dictionary "en_US") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres)))) (with-temp-buffer (insert - ;; there is no such a word in English, I swear. - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + ispell-tests--constants/english/wrong "\n" ) - (goto-char 0) + (goto-char 1) (ispell-change-dictionary "en_US") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres)))) (with-temp-buffer (insert - ;; giving Hunspell a wrong language should not fail - "привет\n" + ispell-tests--constants/russian/correct "\n" ) - (goto-char 0) + (goto-char 1) (ispell-change-dictionary "en_US") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres)))) ))) ) @@ -108,155 +99,121 @@ With UTF-8." (skip-unless (equal 0 (call-process "hunspell" nil nil nil "-vv"))) + (skip-unless (ispell-tests-hunspell--hunspell-working)) (skip-unless (equal 0 - (let ((retval (with-temp-buffer - (insert "привет") - (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d" "ru_RU")))) - (message "lwf:hunspell-test-call=%s" retval) - retval ))) + (let ((retval + (with-temp-buffer + (insert ispell-tests--constants/russian/correct) + (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d" "ru_RU")))) + retval))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "hunspell")) + (ispell-tests--letopt ((ispell-program-name "hunspell")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "привет\n") - (goto-char 0) + ispell-tests--constants/russian/correct "\n") + (goto-char 1) (ispell-change-dictionary "ru_RU") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres)))) (with-temp-buffer (insert - ;; there is no such a word in Russian, I swear. - "ыфаывфафыввпфывафывафывафывавы\n" + ispell-tests--constants/russian/wrong "\n" ) - (goto-char 0) + (goto-char 1) (ispell-change-dictionary "ru_RU") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres)))) ))) ) (ert-deftest ispell/hunspell/ispell-word/language-switch/check-only () - "This test checks that Russian spellchecking works Hunspell." + "This test checks that language switching works for Hunspell." (skip-unless (executable-find "hunspell")) (skip-unless (equal 0 (call-process "hunspell" nil nil nil "-vv"))) + (skip-unless (ispell-tests-hunspell--hunspell-working)) (skip-unless (equal 0 (with-temp-buffer - (insert "привет") + (insert ispell-tests--constants/russian/correct "\n") (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d" "ru_RU")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "hunspell")) + (ispell-tests--letopt + ((ispell-program-name "hunspell")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "привет\n") - (goto-char 0) + ispell-tests--constants/russian/correct "\n") + (goto-char 1) (ispell-change-dictionary "ru_RU") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))) (goto-char (buffer-end 1)) (insert - ;; there is no such a word in Russian, I swear. - "\nыфаывфафыввпфывафывафывафывавы\n" + "\n" ispell-tests--constants/russian/wrong "\n" ) (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - ) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres))) (goto-char (buffer-end 1)) (ispell-change-dictionary "en_US") (insert - "\nhello\n" + "\n" ispell-tests--constants/english/correct-one "\n" ) (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))) )))) -) + ) (ert-deftest ispell/hunspell/ispell-word/russian/check-only/wrong-language () - "If we give Russian-checking Hunspell an english word, it should. -Still process it gracefully. It will not say correct/incorrect, but. -It should at least not crash or something." + "If we give Russian-checking Hunspell an english word, it should still process it gracefully. +It will not say correct/incorrect, but it should at least not crash or something." (skip-unless (executable-find "hunspell")) (skip-unless (equal 0 (call-process "hunspell" nil nil nil "-vv"))) + (skip-unless (ispell-tests-hunspell--hunspell-working)) (skip-unless (equal 0 (with-temp-buffer - (insert "привет") + (insert ispell-tests--constants/russian/correct) (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d" "ru_RU")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "hunspell")) + (ispell-tests--letopt + ((ispell-program-name "hunspell")) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "hello\n") - (goto-char 0) + ispell-tests--constants/english/correct-one "\n") + (goto-char 1) (ispell-change-dictionary "ru_RU") (let ((ispell-check-only t)) ;; should not fail (ispell-word)) - (insert "\nпривет\n") + (insert "\n" ispell-tests--constants/russian/correct "\n") (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ))))) -) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))))))) + ) (ert-deftest ispell/hunspell/ispell-word/multilang () "Hunspell is able to check two languages at once." @@ -264,79 +221,54 @@ It should at least not crash or something." (skip-unless (equal 0 (call-process "hunspell" nil nil nil "-vv"))) + (skip-unless (ispell-tests-hunspell--hunspell-working)) (skip-unless (equal 0 (with-temp-buffer - (insert "привет") + (insert ispell-tests--constants/russian/correct) (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d" "ru_RU")))) (skip-unless (equal 0 (with-temp-buffer - (insert "привет") + (insert ispell-tests--constants/russian/correct) (call-process-region nil nil "hunspell" nil '("*scratch*" t) nil "-d" "en_US")))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory) (multidict "en_US,ru_RU")) - (letopt ((ispell-program-name "hunspell")) + (ispell-tests--letopt + ((ispell-program-name "hunspell")) (ignore-errors (ispell-kill-ispell t t)) (ispell-hunspell-add-multi-dic multidict) (with-temp-buffer (insert - "hello\n") - (goto-char 0) + ispell-tests--constants/english/correct-one "\n") + (goto-char 1) (ispell-change-dictionary multidict) - (insert "\nпривет\n") + (insert "\n" ispell-tests--constants/russian/correct "\n") (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ) - (insert "\nhello\n") + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))) + (insert "\n" ispell-tests--constants/english/correct-one "\n") (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ) - (insert "\nывафываываыфвавыафывавыфафывафываыва\n") + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))) + (insert "\n" ispell-tests--constants/russian/wrong "\n") (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - ) - (insert "\nhelooooooooo\n") + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres))) + (insert "\n" ispell-tests--constants/english/wrong "\n") (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - ) - )))) -) - - + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres))))))) + ) (provide 'ispell-hunspell-tests) ;;; ispell-hunspell-tests.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el index b0e887ae1a9..4993c64e49b 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-international-ispell-tests.el @@ -35,8 +35,7 @@ (require 'ispell-tests-common))) (ert-deftest ispell/international-ispell/ispell-word/russian/check-only () -"This test checks that Russian spellchecking works for. -International Ispell with UTF-8." + "This test checks that Russian spellchecking works for International Ispell with UTF-8." (skip-unless (executable-find "ispell")) (skip-unless (equal 0 @@ -48,80 +47,61 @@ International Ispell with UTF-8." привет " (with-temp-buffer - (insert "привет\n") + (insert ispell-tests--constants/russian/correct "\n") (forward-line -1) (call-process-region nil nil "ispell" nil t nil "-a" "-d" "russian") - (goto-char 0) + (goto-char 1) (kill-line) (buffer-string)))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "ispell") - (ispell-local-dictionary-alist - '(( - "russian" - "[A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" - "[^A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" - "" - nil - nil - nil - nil - )))) + (ispell-tests--letopt + ((ispell-program-name "ispell") + (ispell-local-dictionary-alist + '(( + "russian" + "[A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" + "[^A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" + "" + nil + nil + nil + nil + )))) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "привет\n") - (goto-char 0) + ispell-tests--constants/russian/correct "\n") + (goto-char 1) (ispell-change-dictionary "russian") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres)))) (with-temp-buffer (insert - "ёлка\n") - (goto-char 0) + ispell-tests--constants/russian/correct "\n") + (goto-char 1) (ispell-change-dictionary "russian") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres)))) (with-temp-buffer (insert - ;; there is no such a word in Russian, I swear. - "ыфаывфафыввпфывафывафывафывавы\n" + ispell-tests--constants/russian/wrong "\n" ) - (goto-char 0) + (goto-char 1) (ispell-change-dictionary "russian") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - )) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres)))) ))) ) (ert-deftest ispell/international-ispell/ispell-word/language-switch/check-only () - "This test checks that Russian spellchecking works for - International Ispell with UTF-8." + "This test checks that Russian spellchecking works for International Ispell with UTF-8 when switching the language." (skip-unless (executable-find "ispell")) (skip-unless (equal 0 @@ -133,133 +113,110 @@ International Ispell with UTF-8." привет " (with-temp-buffer - (insert "привет\n") + (insert ispell-tests--constants/russian/correct "\n") (forward-line -1) (call-process-region nil nil "ispell" nil t nil "-a" "-d" "russian") - (goto-char 0) + (goto-char 1) (kill-line) (buffer-string)))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "ispell") - (ispell-local-dictionary-alist - '(( - "russian" - "[A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" - "[^A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" - "" - nil - nil - nil - nil - )))) + (ispell-tests--letopt + ((ispell-program-name "ispell") + (ispell-local-dictionary-alist + '(( + "russian" + "[A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" + "[^A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" + "" + nil + nil + nil + nil + )))) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "привет\n") - (goto-char 0) + ispell-tests--constants/russian/correct "\n") + (goto-char 1) (ispell-change-dictionary "russian") - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))) (goto-char (buffer-end 1)) (insert - ;; there is no such a word in Russian, I swear. - "\nыфаывфафыввпфывафывафывафывавы\n" + "\n" ispell-tests--constants/russian/wrong "\n" ) (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is incorrect" nil t) - current-point))) - ) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is incorrect" lres))) (goto-char (buffer-end 1)) (ispell-change-dictionary "english") (insert - "\nhello\n" + "\n" ispell-tests--constants/english/correct-one "\n" ) (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))) )))) -) + ) (ert-deftest ispell/international-ispell/ispell-word/russian/check-only/wrong-language () -"If we give Russian-checking Ispell an english word, it should. -Still process it gracefully. It will not say correct/incorrect, but -it should at least not crash or something." + "If we give Russian-checking Ispell an english word, it should still process it gracefully. +It will not say correct/incorrect, but it should at least not crash or something." (skip-unless (executable-find "ispell")) (skip-unless (equal 0 (call-process "ispell" nil nil nil "-vv"))) (skip-unless (string-equal - " + (seq-concatenate 'string +" * -привет -" +" ispell-tests--constants/russian/correct " +") (with-temp-buffer - (insert "привет\n") + (insert ispell-tests--constants/russian/correct "\n") (forward-line -1) (call-process-region nil nil "ispell" nil t nil "-a" "-d" "russian") - (goto-char 0) + (goto-char 1) (kill-line) (buffer-string)))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name "ispell") - (ispell-local-dictionary-alist - '(( - "russian" - "[A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" - "[^A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" - "" - nil - nil - nil - nil - )))) + (ispell-tests--letopt + ((ispell-program-name "ispell") + (ispell-local-dictionary-alist + '(( + "russian" + "[A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" + "[^A-Za-zабвгдеёжзиклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ]" + "" + nil + nil + nil + nil + )))) (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "hello\n") - (goto-char 0) + ispell-tests--constants/english/correct-one "\n") + (goto-char 1) (ispell-change-dictionary "russian") (let ((ispell-check-only t)) (ispell-word)) - (insert "\nпривет\n") + (insert "\n" ispell-tests--constants/russian/correct "\n") (forward-line -1) - (let ((ispell-check-only t) - (current-point - (with-current-buffer "*Messages*" - (point)))) - (ispell-word) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (search-backward "is correct" nil t) - current-point))) - ))))) -) + (ert-with-message-capture lres + (let ((ispell-check-only t)) + (ispell-word)) + (should (string-match "is correct" lres))))))) + ) (provide 'ispell-international-ispell-tests) ;;; ispell-international-ispell-tests.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell-tests-common.el b/test/lisp/textmodes/ispell-tests/ispell-tests-common.el index 0692e11e567..30a4336f094 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-tests-common.el +++ b/test/lisp/textmodes/ispell-tests/ispell-tests-common.el @@ -1,72 +1,146 @@ -;;; common.el --- -*- lexical-binding: t; -*- +;;; ispell-tests-common.el --- Shared procedures for ispell tests. -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Lockywolf + +;; Author: Lockywolf +;; Keywords: languages, text + +;; 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: + +;; Shared procedures used by most ispell test files. + +;;; Code: -;; (defvar tests-ispell-data-directory -;; (expand-file-name "test/lisp/textmodes/ispell-resources/" source-directory)) (require 'ert) (require 'ert-x) -(defvar tests-ispell-data-directory +(defvar ispell-tests--data-directory (let ((ert-resource-directory-trim-right-regexp "-tests/.*-tests-common\\.el")) - (ert-resource-directory))) - -;;(message "lwf:tests-ispell-data-directory=%s" tests-ispell-data-directory) + (ert-resource-directory)) + "Resource directory for ispell tests.") (defvar fake-aspell-path - (expand-file-name "fake-aspell-new.bash" tests-ispell-data-directory)) + (expand-file-name "fake-aspell-new.bash" ispell-tests--data-directory) + "Path to the mock backend.") (let* ((backend-binaries (list "ispell" "aspell" "hunspell" "enchant-2" fake-aspell-path) ) (filter-binaries (seq-filter - (lambda (b) - (and - (executable-find b) - ;; (equal 0 - ;; (with-temp-buffer - ;; (call-process b nil t "-v"))) - )) - backend-binaries))) + (lambda (b) + (and + (executable-find b) + (equal 0 + (with-temp-buffer + (call-process b nil t "-a"))))) + backend-binaries))) (defun ispell-tests--some-backend-available-p () + "Return t if some spellchecking backend is available. " (not (null filter-binaries))) (defun ispell-tests--some-backend () + "Return the string of some available backend." (let ((retval (car filter-binaries))) (message "available backend is:%s" retval) retval))) +(defun ispell-tests--some-valid-dictionary (backend) + "Return some dictionary name working for BACKEND." + (cond ((string-equal backend "ispell") + (with-temp-buffer + (call-process backend nil t nil "-vv") + (let* ((s "LIBDIR = ") + (slen (length s)) + (_ (search-backward s)) + (b (+ (point) slen 1)) + (e (- (line-end-position) 1)) + (ldir (buffer-substring b e)) + (d (file-name-sans-extension + (file-name-nondirectory + (car (directory-files ldir t ".+\\.aff$")))))) + d))) + ((string-equal backend "aspell") + (with-temp-buffer + (call-process backend nil t nil "dump" "dicts") + (goto-char 1) + (buffer-substring 1 (line-end-position)))) + ((string-equal backend "hunspell") + (with-temp-buffer + (call-process backend nil t nil "-D") + (search-backward "AVAILABLE DICTIONARIES" nil t) + (forward-line 1) + (let* ((s (buffer-substring (point) (line-end-position)))) + (file-name-sans-extension + (file-name-nondirectory s))))))) + (eval-when-compile (require 'cl-macs)) -(cl-defmacro letopt (bindings &body body) - (declare (indent 1)) +(cl-defmacro ispell-tests--letopt (bindings &body body) + "Bind BINDINGS with `setopt', then eval BODY. +The value of the last form in BODY is returned. +Each element of VARLIST is a list (SYMBOL VALUEFORM) +\(which binds SYMBOL to the value of VALUEFORM with `setopt'). +This macro is not expected to be used outside of +ispell-tests. As `setopt' is naturally mutative, +the environment after the end of the form is not +guaranteed to be identical to the one before. But the form +tries its best." + (declare (indent 1) (debug cl-letf)) (let* ((binding-var (lambda (binding) (car binding))) (binding-val (lambda (binding) (cadr binding))) - (make-setopt (lambda (a b) - (list 'setopt a b))) + (make-setopt + (lambda (a b) + (list 'setopt a b))) + (add-ignore-errors + (lambda (a) + (list 'ignore-errors a))) (vars (seq-map binding-var bindings)) (values (seq-map binding-val bindings)) (temp-vars (seq-map #'gensym vars)) (savebindings (seq-mapn #'list temp-vars vars)) (tempbindings (seq-mapn make-setopt vars values)) - (restorebindings (seq-mapn make-setopt vars temp-vars))) + (restorebindings (seq-mapn add-ignore-errors (seq-mapn make-setopt vars temp-vars)))) `(let ,savebindings (unwind-protect (progn ,@tempbindings ,@body) ,@(reverse restorebindings))))) -(cl-defmacro with-ispell-global-dictionary (bindings &body body) - "This macro should not really be needed, but `ispell.el'. -Sets up dictionaries in a stupid way." - (declare (indent 1)) - (let* ((dictionary-val (car bindings)) +(cl-defmacro ispell-tests--with-ispell-global-dictionary (ldict &body body) + "Temporarily bind `ispell-global-dictionary' to value of LDICT, then eval BODY. +Then attempt to restore the original value of +`ispell-global-dictionary', which may fail, but this form tries +its best." + (declare (indent 1) (debug t)) + (let* ((dictionary-val ldict) (temp-var (gensym 'old-dictionary))) `(let ((,temp-var (symbol-value 'ispell-dictionary))) (unwind-protect (progn (ispell-change-dictionary ,dictionary-val t) ,@body) - (progn - (unwind-protect - (ispell-change-dictionary ,temp-var t) - (message "restoring original dictionary failed"))))))) + (ignore-errors (ispell-change-dictionary ,temp-var)))))) + +(defconst ispell-tests--constants/english/correct-list + '("hello" "test" "test" "more" "obvious" "word")) +(defconst ispell-tests--constants/english/correct-one "hello") +(defconst ispell-tests--constants/english/wrong "hellooooooo") +(defconst ispell-tests--constants/russian/correct "привет") +(defconst ispell-tests--constants/russian/wrong "ыфаывфафыввпфыв") +(defconst ispell-tests--constants/completion "waveguides") (provide 'ispell-tests-common) + +;;; ispell-tests-common.el ends here diff --git a/test/lisp/textmodes/ispell-tests/ispell-tests.el b/test/lisp/textmodes/ispell-tests/ispell-tests.el index d4f3c281e95..605b634e9a8 100644 --- a/test/lisp/textmodes/ispell-tests/ispell-tests.el +++ b/test/lisp/textmodes/ispell-tests/ispell-tests.el @@ -45,56 +45,54 @@ (ert-deftest ispell/ispell-program-name/nil () "Sanity check. Setting a non-string should produce a warning. -Give ispell-program-name a wrong type." - (should (unwind-protect - (progn - (setq ispell-program-name "ispell") - (when (warnings-buffer-exists-p) - (kill-buffer "*Warnings*")) - (setopt ispell-program-name nil) - (if (warnings-buffer-exists-p) - t - nil)) - (when (warnings-buffer-exists-p) - (kill-buffer "*Warnings*"))))) +Give `ispell-program-name' a wrong type." + (should + (unwind-protect + (progn + (setq ispell-program-name "ispell") + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*")) + (setopt ispell-program-name nil) + (if (warnings-buffer-exists-p) + t + nil)) + (when (warnings-buffer-exists-p) + (kill-buffer "*Warnings*"))))) (ert-deftest ispell/ispell-program-name/noncommand () - "Sanity check. Should error or at least warn. -Give ispell-program-name a meaningless string." + "Should error or at least warn when `ispell-program-name' is a meaningless string." :expected-result :failed (should-error (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random ispellnonexistent name )) (ert-deftest ispell/ispell-program-name/noncommand/interactive () - "Sanity check. Should error or at least warn. -Give ispell-program-name a meaningless string." + "Should error or at least warn when `ispell-program-name' is a meaningless string." (should-error (progn - (setopt ispell-program-name "6c628ac4-63a0-11f0-b37c-e38fc166e3fc") ;; random ispellnonexistent name + (setopt ispell-program-name "baf8cc1a-a811-11f0-abc7-ef02b7a6dda1") ;; random ispellnonexistent name (ispell-check-version) ))) (ert-deftest ispell/ispell-program-name/non-executable () "Sanity check. Should error or at least warn. -Give ispell-program-name a path to a non-executable. +Give `ispell-program-name' a path to a non-executable. I personally think that this should always fail, but -at the moment only an interactive call fails." +at the moment only the interactive call fails." :expected-result :failed (should-error (progn (setopt ispell-program-name null-device)))) (ert-deftest ispell/ispell-program-name/non-executable/interactive () - "Sanity check. Should error or at least warn. -Give ispell-program-name a path to a non-executable." + "Should error or at least warn if `ispell-program-name' is a path to a non-executable." (should-error (progn (setopt ispell-program-name null-device) (ispell-check-version t)))) (ert-deftest ispell/ispell-program-name/non-spellchecker () - "Sanity check. Give ispell-program-name a path to a non-spellchecker. + "Give `ispell-program-name' a path to a non-spellchecker. Fails because for non-interactive runs, `ispell-check-version' does not actually err." :expected-result :failed @@ -102,7 +100,7 @@ not actually err." (should-error (string-equal "etags" (setopt ispell-ispell-program "etags")))) (ert-deftest ispell/ispell-program-name/non-spellchecker/interactive () - "Sanity check. Give ispell-program-name a path to a non-spellchecker." + "Give `ispell-program-name' a path to a non-spellchecker." (skip-unless (executable-find "etags")) (should-error (progn (setopt ispell-ispell-program "etags") @@ -111,7 +109,7 @@ not actually err." (ert-deftest ispell/ispell-program-name/ispell () "Sanity check. If at least some ispell is available, should pass. -Give ispell-program-name a real spellchecker" +Give `ispell-program-name' a real spellchecker." (skip-unless (and (executable-find "ispell") (with-temp-buffer (call-process "ispell" nil t nil "-vv") @@ -134,9 +132,9 @@ Give ispell-program-name a real spellchecker" (equal default-directory temporary-file-directory)))))) (ert-deftest ispell/ispell-call-process/simple () - "Check that ispell-call-process works. + "Check that `ispell-call-process' works. This test fails, because HOME is not defined. -This should not be the case, because ispell-call-process +This should not be the case, because `ispell-call-process' whould be making sure that the directory for running the backend's process exists." :expected-result :failed @@ -147,7 +145,7 @@ the backend's process exists." (search-backward (expand-file-name "~")))))) (ert-deftest ispell/ispell-call-process/simple-writable () - "Check that ispell-call-process works." + "Check that `ispell-call-process' works." (should (with-temp-buffer (let ((default-directory temporary-file-directory)) @@ -157,9 +155,9 @@ the backend's process exists." (search-backward (directory-file-name temporary-file-directory)))))) (ert-deftest ispell/ispell-call-process-region/cat-empty () - "Check ispell-call-process-region works with unrelated process. + "Check `ispell-call-process-region' works with unrelated process. This test is expected to fail, because at the moment, there is -a construction (let ((default-directory default-directory))...) in +a construction (let ((default-directory `default-directory'))...) in the `ispell-with-safe-default-directory' function, which effectively makes it useless." :expected-result :failed @@ -177,9 +175,9 @@ makes it useless." (equal (buffer-string) string-to-send)))))) (ert-deftest ispell/ispell-call-process-region/cat-random () - "Check ispell-call-process-region works with unrelad process. + "Check `ispell-call-process-region' works with unrelad process. This test is expected to fail, because at the moment, there is -a construction (let ((default-directory default-directory))...) in +a construction (let ((default-directory `default-directory'))...) in the `ispell-with-safe-default-directory' function, which effectively makes it useless." :expected-result :failed @@ -217,24 +215,19 @@ I think this is an issue in itself, but this test is added to verify that changes to third-party code do not break existing behaviour." (skip-unless (executable-find "hunspell")) (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) - (let ((old-ispell ispell-program-name) - (old-library-directory ispell-library-directory)) - (unwind-protect - (progn - (setopt ispell-program-name "hunspell") - (setopt ispell-library-directory nil) - (ispell-check-version t) - (should - (equal - (sort (ispell-valid-dictionary-list) 'string<) - (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) - (setopt ispell-library-directory old-library-directory) - (setopt ispell-program-name old-ispell))) + (ispell-tests--letopt + ((ispell-program-name "hunspell") + (ispell-library-directory nil)) + (ispell-check-version t) + (should + (equal + (sort (ispell-valid-dictionary-list) 'string<) + (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) ) -;; FIXME: this test should probably go into a separate file, dedicated -;; to the hunspell backend, but so far there is not partition between -;; backends, so let us add it here. It is easy to move it. +;; FIXME: +;; `ispell-valid-dictionary-list' should be calling backend-specific code, +;; and not check all possible backends by itself. (ert-deftest ispell/ispell-valid-dictionary-list/hunspell/library-directory () "If hunspell, `ispell-valid-dictionary-list' returns default. This function only works for aspell and ispell, for hunspell and @@ -243,24 +236,19 @@ I think this is an issue in itself, but this test is added to verify that changes to third-party code do not break existing behaviour." (skip-unless (executable-find "hunspell")) (skip-unless (equal 0 (call-process "hunspell" nil nil nil))) - (let ((old-ispell ispell-program-name) - (old-library-directory ispell-library-directory)) - (unwind-protect - (progn - (setopt ispell-program-name "hunspell") - (ispell-check-version t) - (setopt ispell-library-directory temporary-file-directory) - (should - (equal - (ispell-valid-dictionary-list) - '("default")))) - (setopt ispell-library-directory old-library-directory) - (setopt ispell-program-name old-ispell))) + (ispell-tests--letopt + ((ispell-program-name "hunspell") + (ispell-library-directory temporary-file-directory)) + (ispell-check-version t) + (should + (equal + (ispell-valid-dictionary-list) + '("default")))) ) -;; FIXME: this test should probably go into a separate file, dedicated -;; to the enchant-2 backend, but so far there is not partition between -;; backends, so let us add it here. It is easy to move it. +;; FIXME: +;; `ispell-valid-dictionary-list' should be calling backend-specific code, +;; and not check all possible backends by itself. (ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/no-library-directory () "If enchant-2, `ispell-valid-dictionary-list' returns default. This function only works for aspell and ispell, for hunspell and @@ -268,24 +256,19 @@ enchant-2 it always returns either default or everything. I think this is an issue in itself, but this test is added to verify that changes to third-party code do not break existing behaviour." (skip-unless (executable-find "enchant-2")) - (let ((old-ispell ispell-program-name) - (old-library-directory ispell-library-directory)) - (unwind-protect - (progn - (setopt ispell-program-name "enchant-2") - (setopt ispell-library-directory nil) + (ispell-tests--letopt + ((ispell-program-name "enchant-2") + (ispell-library-directory nil)) (ispell-check-version t) (should (equal (sort (ispell-valid-dictionary-list) 'string<) (sort (cl-substitute "default" nil (mapcar #'car ispell-dictionary-alist)) 'string<)))) - (setopt ispell-library-directory old-library-directory) - (setopt ispell-program-name old-ispell))) ) -;; FIXME: this test should probably go into a separate file, dedicated -;; to the enchant-2 backend, but so far there is not partition between -;; backends, so let us add it here. It is easy to move it. +;; FIXME: +;; `ispell-valid-dictionary-list' should be calling backend-specific code, +;; and not check all possible backends by itself. (ert-deftest ispell/ispell-valid-dictionary-list/enchant-2/library-directory () "If enchant-2, `ispell-valid-dictionary-list' returns default. This function only works for aspell and ispell, for hunspell and @@ -293,23 +276,18 @@ enchant-2 it always returns either default or everything. I think this is an issue in itself, but this test is added to verify that changes to third-party code do not break existing behaviour." (skip-unless (executable-find "enchant-2")) - (let ((old-ispell ispell-program-name) - (old-library-directory ispell-library-directory)) - (unwind-protect - (progn - (setopt ispell-program-name "enchant-2") - (setopt ispell-library-directory temporary-file-directory) + (ispell-tests--letopt + ((ispell-program-name "enchant-2") + (ispell-library-directory temporary-file-directory)) (ispell-check-version t) (should (equal (ispell-valid-dictionary-list) '("default")))) - (setopt ispell-library-directory old-library-directory) - (setopt ispell-program-name old-ispell))) ) (ert-deftest ispell/ispell-valid-dictionary-list/international-ispell () - "Check that ispell-valid-dictionary-list does something useful for ispell. + "Check that `ispell-valid-dictionary-list' does something useful for ispell. For ispell, `ispell-valid-dictionary-list' checks that a corresponding file is present in `ispell-library-directory'." (skip-unless (executable-find "ispell")) @@ -320,33 +298,47 @@ file is present in `ispell-library-directory'." "LIBDIR *= *\"\\([^\"]+\\)\"" nil t) (match-string 1))))) (file-readable-p (expand-file-name "english.hash" libdir)))) - (let ((old-ispell ispell-program-name) - (old-library-directory ispell-library-directory) - (old-ispell-local-dictionary-alist ispell-local-dictionary-alist)) - (unwind-protect - (progn + (ispell-tests--letopt + ((ispell-library-directory ispell-library-directory) + (ispell-local-dictionary-alist ispell-local-dictionary-alist) + (ispell-program-name "ispell")) (setopt ispell-program-name "ispell") ;; this should set ispell-library-directory (ispell-check-version t) ;; sets ispell-library-directory (should (not (null ispell-library-directory))) - ;; english is always shipped with international ispell, - ;; other languages not necessarily (setopt ispell-local-dictionary-alist '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) (should (member "english" (ispell-valid-dictionary-list))) - (should (member "default" (ispell-valid-dictionary-list))) - ) - (setopt ispell-library-directory old-library-directory) - (setopt ispell-program-name old-ispell) - (setopt ispell-local-dictionary-alist old-ispell-local-dictionary-alist)))) + (should (member "default" (ispell-valid-dictionary-list)))) + ) (ert-deftest ispell/ispell-valid-dictionary-list/aspell () - "Check that ispell-valid-dictionary-list does something useful for aspell. + "Check that `ispell-valid-dictionary-list' does something useful for aspell. For aspell, `ispell-valid-dictionary-list' computes an intersection of `ispell-dictionary-alist' and `ispell--aspell-found-dictionaries'." (skip-unless (executable-find "aspell")) (skip-unless (with-temp-buffer (call-process "aspell" nil t nil "dicts") (> (length (buffer-string)) 2))) + (skip-unless + (with-temp-buffer + (call-process "aspell" nil t nil "dicts") + (goto-char 1) + (let ((ht (make-hash-table :test 'equal)) + (has-duplicates-p nil)) + (while + (progn + (let* ((l (buffer-substring-no-properties + (line-beginning-position) + (line-end-position))) + (_ (setf has-duplicates-p (hash-table-contains-p l ht))) + (_ (puthash l t ht)) + (endfile (forward-line))) + (and (not has-duplicates-p) (= 0 endfile)))) + t) + (when has-duplicates-p + (message "Your aspell setup is unconventional: %s Cannot run tests." + "it has duplicate dictionaries. ")) + (not has-duplicates-p)))) (let ((old-ispell ispell-program-name) (old-library-directory ispell-library-directory) (old-ispell-local-dictionary-alist ispell-local-dictionary-alist) @@ -355,8 +347,6 @@ For aspell, `ispell-valid-dictionary-list' computes an intersection of (progn (setopt ispell-program-name "aspell") ;; this should set ispell-library-directory (ispell-check-version t) ;; sets ispell-library-directory - ;; english is always shipped with international ispell, - ;; other languages not necessarily (setopt ispell-local-dictionary-alist '(("english" "[A-Za-z]" "[^A-Za-z]" "[']" nil ("-B") nil utf-8))) (should @@ -371,8 +361,8 @@ For aspell, `ispell-valid-dictionary-list' computes an intersection of ;; backend in this function.) (ert-deftest ispell/ispell-add-per-file-word-list/simple () - "Adding a per-file word to an empty buffer. No comment -syntax expected." + "Adding a per-file word to an empty buffer. +No comment syntax expected." (with-temp-buffer (let ((testword (format "%s" (random)))) (ispell-add-per-file-word-list testword) @@ -382,8 +372,8 @@ syntax expected." ")))))) (ert-deftest ispell/ispell-add-per-file-word-list/comments () - "Adding a per-file word to an empty buffer. Uses default -emacs-lisp comment syntax." + "Adding a per-file word to an empty buffer. +Uses default emacs-lisp comment syntax." (with-temp-buffer (let ((testword (format "%s" (random)))) (emacs-lisp-mode) @@ -394,9 +384,9 @@ emacs-lisp comment syntax." ")))))) (ert-deftest ispell/ispell-add-per-file-word-list/nxml () - "Adding a per-file word to an empty buffer. Uses default -xml comment syntax, which has an opening and a closing -marker." + "Adding a per-file word to an empty buffer. +Uses default +xml comment syntax, which has an opening and a closing marker." (with-temp-buffer (let ((testword (format "%s" (random)))) (nxml-mode) @@ -408,9 +398,9 @@ marker." ")))))) (ert-deftest ispell/ispell-add-per-file-word-list/keyword-there-space () - "Adding a per-file word to buffer with keyword. Uses default -xml comment syntax, which has an opening and a closing -marker. " + "Adding a per-file word to buffer with keyword. +Uses default +xml comment syntax, which has an opening and a closing marker." (with-temp-buffer (let ((testword (format "%s" (random)))) (nxml-mode) @@ -426,24 +416,24 @@ marker. " ")))))) (ert-deftest ispell/ispell-add-per-file-word-list/longline () - "Adding a per-file word to buffer with keyword. Uses default -xml comment syntax, which has an opening and a closing -marker. + "Adding a per-file word to buffer with keyword. +Uses default +xml comment syntax, which has an opening and a closing marker. This test fails, because ispell.el does not work well with nXML comments." :expected-result :failed - (letopt ((ispell-program-name "ispell")) - (with-temp-buffer - (let* ((testword (format "%s" (random))) - (fill-column 50)) - (nxml-mode) - (insert " + (ispell-tests--letopt ((ispell-program-name "ispell")) + (with-temp-buffer + (let* ((testword (format "%s" (random))) + (fill-column 50)) + (nxml-mode) + (insert " ") - (ispell-add-per-file-word-list testword) - (should (equal (buffer-string) - (concat " + (ispell-add-per-file-word-list testword) + (should (equal (buffer-string) + (concat " " @@ -457,70 +447,75 @@ nXML comments." ;; this function.) (ert-deftest ispell/ispell-buffer-local-words/ispell-words-keyword () - "Send some words prefixed by @ from the file's cellar to backend. + "Send some words prefixed by @ from the file's footer to backend. Should pass regardless of the backend and the dictionary, because -presumably nobody will have `hellooooooo' in their dictionary." +presumably nobody will have (`hellooooooo') +`ispell-tests--constants/english/wrong' in their dictionary." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--with-ispell-global-dictionary + nil (with-temp-buffer (nxml-mode) (ignore-errors (ispell-kill-ispell)) (ispell-init-process) - (let ((test-output (ispell--run-on-word "hellooooooo"))) + (let ((test-output (ispell--run-on-word ispell-tests--constants/english/wrong))) (should (listp test-output)) (should-not (equal t test-output))) - (ispell-add-per-file-word-list "hellooooooo") + (ispell-add-per-file-word-list ispell-tests--constants/english/wrong) (ispell-buffer-local-words) - (should (equal t (ispell--run-on-word "hellooooooo"))))))) + (should (equal t (ispell--run-on-word ispell-tests--constants/english/wrong))))))) ) (ert-deftest ispell/ispell-buffer-local-words/ispell-buffer-session-localwords () - "Send some words prefixed by @ from the file's cellar to backend. + "Send some words prefixed by @ from the file's footer to backend. Should pass regardless of the backend and the dictionary, because -presumably nobody will have `hellooooooo' in their dictionary." +presumably nobody will have `ispell-tests--constants/english/wrong' +\(`hellooooooo') in their dictionary." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil)) + (ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) + (ispell-tests--with-ispell-global-dictionary + nil (cd temporary-file-directory) (with-temp-buffer (nxml-mode) (ignore-errors (ispell-kill-ispell)) (ispell-init-process) - (let ((test-output (ispell--run-on-word "hellooooooo"))) + (let ((test-output (ispell--run-on-word ispell-tests--constants/english/wrong))) (should (listp test-output)) (should-not (equal t test-output))) - (let ((ispell-buffer-session-localwords (list "hellooooooo"))) + (let ((ispell-buffer-session-localwords (list ispell-tests--constants/english/wrong))) (ispell-buffer-local-words) - (should (equal t (ispell--run-on-word "hellooooooo")))))))) + (should (equal t (ispell--run-on-word ispell-tests--constants/english/wrong)))))))) ) (ert-deftest ispell/ispell-init-process/works-no-home () - "Simple test to check that ispell-init-process works." + "Simple test to check that `ispell-init-process' works." :expected-result :failed (skip-unless (ispell-tests--some-backend-available-p)) - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend))) - (with-temp-buffer - (with-environment-variables - (("HOME" (make-temp-name temporary-file-directory))) - (ispell-init-process)))) - 'passed) -) + (ispell-tests--with-ispell-global-dictionary nil + (ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend))) + (with-temp-buffer + (with-environment-variables + (("HOME" (make-temp-name temporary-file-directory))) + (ispell-init-process)))) + 'passed) + ) (ert-deftest ispell/ispell-init-process/works-with-home () - "Simple test to check that ispell-init-process works." + "Simple test to check that `ispell-init-process' works." (skip-unless (ispell-tests--some-backend-available-p)) - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend))) - (with-temp-buffer - (with-environment-variables (("HOME" temporary-file-directory)) - (ispell-init-process)))))) + (ispell-tests--letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--with-ispell-global-dictionary + nil + (with-temp-buffer + (with-environment-variables (("HOME" temporary-file-directory)) + (ispell-init-process)))))) ;; Some more tests for buffer-local stuff. ;; `ispell-buffer-local-dict' @@ -540,7 +535,7 @@ personal-dictionary." (let ((test-dict "ispellnonexistent")) (seq-map (lambda (test-pdict) (insert - "hello\n\n\n" + (car ispell-tests--constants/english/correct-list) "\n\n\n" "" "") (ispell-buffer-local-dict t) @@ -557,10 +552,10 @@ personal-dictionary." (let ((test-dict "ispellnonexistent")) (seq-map (lambda (test-pdict) (insert - "hello\n\n\n" + (car ispell-tests--constants/english/correct-list) "\n\n\n" "" "") - (letopt ((ispell-current-dictionary "ispellnonexistent2")) + (ispell-tests--letopt ((ispell-current-dictionary "ispellnonexistent2")) (ispell-buffer-local-dict) (should (equal ispell-current-dictionary test-dict)) (should (equal ispell-current-personal-dictionary test-pdict)))) @@ -571,13 +566,13 @@ personal-dictionary." personal-dictionary. With no-reload it needs no backend at all." (with-temp-buffer (nxml-mode) - (let ((test-dict "ispellnonexistent")) + (let ((test-dict "ispellnonexistent3")) (seq-map (lambda (test-pdict) (insert - "hello\n\n\n" + (car ispell-tests--constants/english/correct-list) "\n\n\n" "" "") - (letopt ((ispell-current-dictionary "ispellnonexistent2")) + (ispell-tests--letopt ((ispell-current-dictionary "ispellnonexistent4")) (let ((ispell-local-dictionary-overridden t)) (ispell-buffer-local-dict t)) (should-not (equal ispell-local-dictionary test-dict)) @@ -590,62 +585,64 @@ personal-dictionary. With no-reload it needs no backend at all." :expected-result :failed (with-temp-buffer (nxml-mode) - (let ((test-dict "ispellnonexistent")) - (seq-map (lambda (test-pdict) - (insert - "hello\n\n\n" - "" - "") - (letopt ((ispell-current-dictionary "ispellnonexistent2")) - (let ((ispell-local-dictionary-overridden t)) - (ispell-buffer-local-dict t)) - (should-not (equal ispell-current-dictionary test-dict)) - (should (equal ispell-current-personal-dictionary - test-pdict)))) + (let ((test-dict "ispellnonexistent5")) + (seq-map + (lambda (test-pdict) + (insert + (car ispell-tests--constants/english/correct-list) "\n\n\n" + "" + "") + (ispell-tests--letopt ((ispell-current-dictionary "ispellnonexistent4")) + (let ((ispell-local-dictionary-overridden t)) + (ispell-buffer-local-dict t)) + (should-not (equal ispell-current-dictionary test-dict)) + (should (equal ispell-current-personal-dictionary + test-pdict)))) possible-pdict-paths))))) ;; parsing (ert-deftest ispell/ispell-buffer-local-parsing/local-keyword () - "Check that ispell.el can suscessfully pick up a tex parser -from a buffer-local keyword." + "Check that ispell.el can suscessfully pick up a tex parser from a buffer-local keyword." ;; FIXME: what if default dictionary sets ;; (ispell-get-extended-character-mode) ? (with-temp-buffer (let ((test-parser "~tex") (test-dictname "testdict") (test-extcharmode "~latin3")) - (letopt ((ispell-parser 'ispellnonexistent) - (ispell-local-dictionary-alist - `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" - nil ("-B") ,test-extcharmode utf-8))) - (ispell-current-dictionary test-dictname)) - - (insert - "hello\n\n\n" ispell-parsing-keyword test-parser) - (let* ((counter 0)) - (cl-labels ((checker (s) - (setq counter (+ 1 counter)) - (when (equal counter 1) - (should (string-equal s "!\n"))) - (when (equal counter 2) - (should (string-equal s "-\n"))) - (when (equal counter 3) - (should (string-equal s (concat test-extcharmode "\n")))) - (when (equal counter 4) - (should (string-equal s (concat test-parser "\n")))) - t)) - (unwind-protect (progn - (advice-add 'ispell-send-string :override - #'checker) - (let ((ispell-really-hunspell nil)) - (ispell-buffer-local-parsing))) - (advice-remove 'ispell-send-string #'checker))))))) + (ispell-tests--letopt + ((ispell-parser 'ispellnonexistent) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + + (insert + (car ispell-tests--constants/english/correct-list)"\n\n\n" + ispell-parsing-keyword test-parser) + (let* ((counter 0)) + (cl-labels ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "-\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (should (string-equal s (concat test-parser "\n")))) + t)) + (unwind-protect + (progn + (advice-add 'ispell-send-string :override + #'checker) + (let ((ispell-really-hunspell nil)) + (ispell-buffer-local-parsing))) + (advice-remove 'ispell-send-string #'checker))))))) ) (ert-deftest ispell/ispell-buffer-local-parsing/local-keyword/hunspell-bug () - "Check that ispell.el can suscessfully pick up a tex parser -from a buffer-local keyword." + "Check that ispell.el can suscessfully pick up a tex parser from a buffer-local keyword." ;; FIXME: what if default dictionary sets ;; (ispell-get-extended-character-mode) ? :expected-result :failed @@ -653,101 +650,109 @@ from a buffer-local keyword." (let ((test-parser "~tex") (test-dictname "testdict") (test-extcharmode "~latin3")) - (letopt ((ispell-parser 'ispellnonexistent) - (ispell-local-dictionary-alist - `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" - nil ("-B") ,test-extcharmode utf-8))) - (ispell-current-dictionary test-dictname)) - - (insert - "hello\n\n\n" ispell-parsing-keyword test-parser) - (let* ((counter 0)) - (cl-labels ((checker (s) - (setq counter (+ 1 counter)) - (when (equal counter 1) - (should (string-equal s "!\n"))) - (when (equal counter 2) - (should (string-equal s "-\n"))) - (when (equal counter 3) - (should (string-equal s (concat test-extcharmode "\n")))) - (when (equal counter 4) - (should (string-equal s (concat test-parser "\n")))) - t)) - (unwind-protect (progn - (advice-add 'ispell-send-string :override - #'checker) - (let ((ispell-really-hunspell t)) - (ispell-buffer-local-parsing))) - (advice-remove 'ispell-send-string #'checker))))))) + (ispell-tests--letopt ((ispell-parser 'ispellnonexistent) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + + (insert + ispell-tests--constants/english/correct-one "\n\n\n" ispell-parsing-keyword test-parser) + (let* ((counter 0)) + (cl-labels + ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "-\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (should (string-equal s (concat test-parser "\n")))) + t)) + (unwind-protect + (progn + (advice-add 'ispell-send-string :override + #'checker) + (let ((ispell-really-hunspell t)) + (ispell-buffer-local-parsing))) + (advice-remove 'ispell-send-string #'checker))))))) ) (ert-deftest ispell/ispell-buffer-local-parsing/mode-tex () - "Check that ispell.el can suscessfully pick up a tex parser -from tex-based mode-name. + "Check that ispell.el can suscessfully pick up a tex parser from tex-based mode-name. There is another implicit check here: explicit-character-mode -(argument 7 from the ispell.el dictionary structure) is nil." +\(argument 7 from the ispell.el dictionary structure) is nil." (with-temp-buffer (let ((test-dictname "testdict") (test-extcharmode nil)) - (letopt ((ispell-check-comments t) - (ispell-parser 'use-mode-name) - (ispell-local-dictionary-alist - `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" - nil ("-B") ,test-extcharmode utf-8))) - (ispell-current-dictionary test-dictname)) - (insert - "hello\n\n\n") - (tex-mode) - (let* ((counter 0)) - (cl-labels ((checker (s) - (setq counter (+ 1 counter)) - (when (equal counter 1) - (should (string-equal s "!\n"))) - (when (equal counter 2) - (should (string-equal s "+\n"))) - (when (equal counter 3) - (error "Should not have a third call to `ispell-send-string'")) - t)) - (unwind-protect (progn - (advice-add 'ispell-send-string :override - #'checker) - (ispell-buffer-local-parsing)) - (advice-remove 'ispell-send-string #'checker))))))) + (ispell-tests--letopt + ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8))) + (ispell-current-dictionary test-dictname)) + + (insert + (car ispell-tests--constants/english/correct-list) "\n\n\n") + (tex-mode) + (let* ((counter 0)) + (cl-labels + ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (error "Should not have a third call to `ispell-send-string'")) + t)) + (unwind-protect + (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) ) (ert-deftest ispell/ispell-buffer-local-parsing/extended-character-mode () - "Check that ispell.el can suscessfully pick up an extended character -mode from the dictionary." + "Check that ispell.el can suscessfully pick up an extended character mode from the dictionary." (with-temp-buffer (insert - "hello\n\n\n") + ispell-tests--constants/english/correct-one "\n\n\n") (let ((test-extcharmode "~latin3")) - (letopt ((ispell-check-comments t) - (ispell-parser 'use-mode-name) - ;; FIXME: what if default dictionary sets - ;; (ispell-get-extended-character-mode)? - (ispell-local-dictionary-alist - `(("english" "[A-Za-z]" "[^A-Za-z]" "[']" - nil ("-B") ,test-extcharmode utf-8))) - ) - (tex-mode) - (let* ((counter 0)) - (cl-labels ((checker (s) - (setq counter (+ 1 counter)) - (when (equal counter 1) - (should (string-equal s "!\n"))) - (when (equal counter 2) - (should (string-equal s "+\n"))) - (when (equal counter 3) - (should (string-equal s (concat test-extcharmode "\n")))) - (when (equal counter 4) - (error "Should not have a third call to `ispell-send-string'")) - t)) - (unwind-protect (progn - (advice-add 'ispell-send-string :override - #'checker) - (ispell-buffer-local-parsing)) - (advice-remove 'ispell-send-string #'checker))))))) + (ispell-tests--letopt + ((ispell-check-comments t) + (ispell-parser 'use-mode-name) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `(("english" "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8)))) + + (tex-mode) + (let* ((counter 0)) + (cl-labels + ((checker (s) + (setq counter (+ 1 counter)) + (when (equal counter 1) + (should (string-equal s "!\n"))) + (when (equal counter 2) + (should (string-equal s "+\n"))) + (when (equal counter 3) + (should (string-equal s (concat test-extcharmode "\n")))) + (when (equal counter 4) + (error "Should not have a third call to `ispell-send-string'")) + t)) + + (unwind-protect + (progn + (advice-add 'ispell-send-string :override + #'checker) + (ispell-buffer-local-parsing)) + (advice-remove 'ispell-send-string #'checker))))))) ) ;; Let us now test the most important state-related function: @@ -760,8 +765,7 @@ mode from the dictionary." (ert-deftest ispell/ispell-accept-buffer-local-defs/simple () - "Check that `ispell-accept-buffer-local-defs' works for a -batch mode. + "Check that `ispell-accept-buffer-local-defs' works for a batch mode. 1. local words 2. dictionary and pdict 3. parser and extcharmode. @@ -774,61 +778,93 @@ hunspell. Hence skipping." "enchant-2")))) (with-environment-variables (("HOME" temporary-file-directory)) (with-temp-buffer - (letopt ((ispell-program-name (ispell-tests--some-backend))) - (let ((test-dictname "english") + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) + + (let ((test-dictname (ispell-tests--some-valid-dictionary ispell-program-name)) (test-extcharmode "~latin3") (test-parser "~testparser") (test-localword1 "aaaaaaaaaaaaa") (test-localword2 "bbbbbbbbbbb") (test-pdict "test-pdict.pdict")) (insert - "hello\n\n\n" + (car ispell-tests--constants/english/correct-list) "\n\n\n" ispell-dictionary-keyword test-dictname "\n" ispell-pdict-keyword (expand-file-name test-pdict temporary-file-directory) "\n" ispell-parsing-keyword test-parser "\n" ispell-words-keyword " " test-localword1 " " test-localword2 "\n") - (letopt ((ispell-check-comments t) - (ispell-parser 'tex) - ;; FIXME: what if default dictionary sets - ;; (ispell-get-extended-character-mode)? - (ispell-local-dictionary-alist - `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" - nil ("-B") ,test-extcharmode utf-8)))) + (ispell-tests--letopt + ((ispell-check-comments t) + (ispell-parser 'tex) + ;; FIXME: what if default dictionary sets + ;; (ispell-get-extended-character-mode)? + (ispell-local-dictionary-alist + `((,test-dictname "[A-Za-z]" "[^A-Za-z]" "[']" + nil ("-B") ,test-extcharmode utf-8)))) + (tex-mode) (let* ((counter 0)) - (cl-labels ((checker-ispell-send-string (s) - (let ((references - (list nil - (concat test-extcharmode "\n") - (concat "@" test-localword1 "\n") - (concat "@" test-localword2 "\n") - "!\n" - "+\n" - (concat test-extcharmode "\n") - (concat test-parser "\n")))) - (setq counter (+ 1 counter)) - (should (<= counter (length references))) - (should (string-equal - (concat s) - (concat (nth counter references)))) - t))) - (unwind-protect (progn - (advice-add 'ispell-send-string :before - #'checker-ispell-send-string) - (ignore-errors (ispell-kill-ispell)) - (ispell-accept-buffer-local-defs) - (should (equal ispell-local-dictionary test-dictname)) - (should (equal ispell-local-pdict (expand-file-name test-pdict temporary-file-directory))) - ) + (cl-labels + ((checker-ispell-send-string (s) + (let ((references + (list nil + (concat test-extcharmode "\n") + (concat "@" test-localword1 "\n") + (concat "@" test-localword2 "\n") + "!\n" + "+\n" + (concat test-extcharmode "\n") + (concat test-parser "\n")))) + (setq counter (+ 1 counter)) + (should (<= counter (length references))) + (should (string-equal + (concat s) + (concat (nth counter references)))) + t))) + + (unwind-protect + (progn + (advice-add 'ispell-send-string :before + #'checker-ispell-send-string) + (ignore-errors (ispell-kill-ispell)) + (ispell-accept-buffer-local-defs) + (should (equal ispell-local-dictionary test-dictname)) + (should (equal ispell-local-pdict (expand-file-name test-pdict temporary-file-directory))) + ) (advice-remove 'ispell-send-string #'checker-ispell-send-string))))))))) ) +(ert-deftest ispell/ispell-accept-buffer-local-defs/received-file () + "Check that `ispell-accept-buffer-local-defs' is broken when a file has a nonexistent file-local dictionary. +We do not control this data, but this should make ispell.el failx." + :expected-result :failed + (with-environment-variables (("HOME" temporary-file-directory)) + (with-temp-buffer + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) + + (let ((test-dictname (format "%s" (random)))) + (insert + (car ispell-tests--constants/english/correct-list) "\n\n\n" + ispell-dictionary-keyword test-dictname "\n") + (ispell-tests--letopt + ((ispell-check-comments t) + (ispell-parser 'tex)) + + (tex-mode) + (progn + (ignore-errors (ispell-kill-ispell)) + ;; should not error + (ispell-accept-buffer-local-defs) + ))))))) + (ert-deftest ispell/ispell--run-on-word/default () - "`ispell--run-on-word' should be the simplest interface -for checking a word." + "`ispell--run-on-word' should be the simplest interface for checking a word." (skip-unless (ispell-tests--some-backend-available-p)) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary "default")) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary "default")) + (let ((default-directory temporary-file-directory)) (with-temp-buffer (with-environment-variables (("HOME" temporary-file-directory)) @@ -837,47 +873,36 @@ for checking a word." (ignore-errors (ispell-kill-ispell t t)) (ispell-init-process) - (let ((test-output (ispell--run-on-word "hellooooooo"))) + (let ((test-output (ispell--run-on-word ispell-tests--constants/english/wrong))) (should (listp test-output)) (should-not (equal t test-output)) (setq ispell-filter nil) (setq ispell-filter-continue nil)) - - (let ((test-output (ispell--run-on-word "hello"))) - (should-not (listp test-output)) - (should (equal t test-output)) - (setq ispell-filter nil) - (setq ispell-filter-continue nil)) - - (let ((test-output (ispell--run-on-word "fail"))) - (should-not (listp test-output)) - (should (equal t test-output)) - (setq ispell-filter nil) - (setq ispell-filter-continue nil)) - - (let ((test-output (ispell--run-on-word "tail"))) - (should-not (listp test-output)) - (should (equal t test-output)) - (setq ispell-filter nil) - (setq ispell-filter-continue nil)) + (mapc (lambda (word) + (let ((test-output (ispell--run-on-word word))) + (should-not (listp test-output)) + (should (equal t test-output)) + (setq ispell-filter nil) + (setq ispell-filter-continue nil))) + ispell-tests--constants/english/correct-list) )))) ) (ert-deftest ispell/ispell--run-on-word/default/fails () - "`ispell--run-on-word' should be the simplest interface -for checking a word. This test fails due to what I consider + "Check `ispell--run-on-word', the simplest interface for checking a word. +This test fails due to what I consider to be a bug. I am quite convinced that `ispell--run-on-word' should work twice in a row, without having to call -(`ispell-init-process') or (setq ispell-filter nil) -before each call. -" +\(`ispell-init-process') or (setq ispell-filter nil) +before each call." :expected-result :failed (skip-unless (ispell-tests--some-backend-available-p)) (skip-unless (equal 0 (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary "default")) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary "default")) (let ((default-directory temporary-file-directory)) (with-temp-buffer (with-environment-variables (("HOME" temporary-file-directory)) @@ -886,11 +911,11 @@ before each call. (ignore-errors (ispell-kill-ispell t t)) (ispell-init-process) - (let ((test-output (ispell--run-on-word "hellooooooo"))) + (let ((test-output (ispell--run-on-word ispell-tests--constants/english/wrong))) (should (listp test-output)) (should-not (equal t test-output))) - (let ((test-output (ispell--run-on-word "hello"))) + (let ((test-output (ispell--run-on-word (car ispell-tests--constants/english/correct-list)))) (should-not (listp test-output)) (should (equal t test-output))) @@ -898,23 +923,23 @@ before each call. ) (ert-deftest ispell/ispell-word/default/check-only/correct () - "Check that `ispell-word' works with a default -dictionary, which we expect to be english, as -Ispell ships it. This is probably wrong and should -be rewritten with a mock." + "Check that `ispell-word' works with a default dictionary. +We expect it to be english, as Ispell ships it." (skip-unless (ispell-tests--some-backend-available-p)) (skip-unless (equal 0 (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "hello\n") - (goto-char 0) + (car ispell-tests--constants/english/correct-list) "\n") + (goto-char 1) (ert-with-message-capture lres (ispell-word) (should (string-match "is correct" lres)))) @@ -922,28 +947,28 @@ be rewritten with a mock." ) (ert-deftest ispell/ispell-word/default/check-only/correct/add-init () - "Check that `ispell-word' works with a default -dictionary, which we expect to be english, as -Ispell ships it. This is probably wrong and should -be rewritten with a mock. -This test is different from the previous one in that an explicit init -call to (ispell-init-process) is added. I had issues with it, so I would -like to test it explicitly." + "Check that `ispell-word' works with a default dictionary. +We expect it to be english, as Ispell ships it. This test is different +from the previous one in that an explicit init call +to (`ispell-init-process') is added. I had issues with it, so I would like +to test it explicitly." (skip-unless (ispell-tests--some-backend-available-p)) (skip-unless (equal 0 (call-process (ispell-tests--some-backend) nil nil nil "-vv"))) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil) - (ispell-check-only t)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil) + (ispell-check-only t)) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (ispell-init-process) ;; this is added (insert - "hello\n") - (goto-char 0) + (car ispell-tests--constants/english/correct-list) "\n") + (goto-char 1) (ert-with-message-capture lres (ispell-word) (should (string-match "is correct" lres))) @@ -952,22 +977,22 @@ like to test it explicitly." ) (ert-deftest ispell/ispell-word/default/check-only/incorrect () - "Check that `ispell-word' works with a default -dictionary, which we expect to be english, as -Ispell ships it. This is probably wrong and should -be rewritten with a mock. -This test gives it a word which does not exist." + "Check that `ispell-word' works with a default dictionary. +We expect it to be english, as Ispell ships it. This test gives it a +word which does not exist." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil) - (ispell-check-only t)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil) + (ispell-check-only t)) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert - "hellooooooo\n") - (goto-char 0) + ispell-tests--constants/english/wrong "\n") + (goto-char 1) (ert-with-message-capture lres (ispell-word) (should (string-match "is incorrect" lres)) @@ -979,10 +1004,13 @@ This test gives it a word which does not exist." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (words '("hello" "test" "test" "more" "obvious" "word")) + (words ispell-tests--constants/english/correct-list) (text (string-join words " "))) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil)) + (ispell-tests--letopt + ((ispell-program-name + (ispell-tests--some-backend)) + (ispell-dictionary nil)) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert text) @@ -1000,17 +1028,22 @@ This test gives it a word which does not exist." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (words '("hello" "tarampampamtararam" "world")) + (words (list + (nth 0 ispell-tests--constants/english/correct-list) + ispell-tests--constants/english/wrong + (nth 1 ispell-tests--constants/english/correct-list))) (text (string-join words " "))) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil) - (ispell-check-only t)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil) + (ispell-check-only t)) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert text) (goto-char (length (nth 0 words))) (cl-labels ((checker () - (user-error "expected error"))) + (user-error "Expected error"))) (unwind-protect (progn (advice-add 'ispell-show-choices :override #'checker) @@ -1026,23 +1059,19 @@ so this test virtually mirrors the previous one." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (words '("hello" "test" "test" "more" "obvious" "word")) + (words ispell-tests--constants/english/correct-list) (text (string-join words " "))) - (letopt ((ispell-dictionary nil) - (ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-dictionary nil) + (ispell-program-name (ispell-tests--some-backend))) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert text) - (goto-char (length (nth 0 words))) - (let ((current-point - (with-current-buffer "*Messages*" - (point)))) + (ert-with-message-capture lres (ispell-buffer) - (with-current-buffer "*Messages*" - (goto-char (point-max)) - (should (> (re-search-backward "^Spell-checking .* using .* with .* dictionary...done" nil t) current-point)) - 'passed) - ))))) + (should (string-match "^Spell-checking .* using .* with .* dictionary...done" lres)) + ))))) ) (ert-deftest ispell/ispell-buffer/incorrect () @@ -1052,10 +1081,13 @@ so this test virtually mirrors the previous one." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (words '("tarampampamtararam" "test" "test" "more" "obvious" "word" "badworddd")) + (words (append ispell-tests--constants/english/correct-list + (list ispell-tests--constants/english/wrong))) (text (string-join words " "))) - (letopt ((ispell-dictionary nil) - (ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-dictionary nil) + (ispell-program-name (ispell-tests--some-backend))) + (ignore-errors (ispell-kill-ispell t t)) (with-temp-buffer (insert text) @@ -1063,8 +1095,9 @@ so this test virtually mirrors the previous one." ;; but `ispell-buffer' should move the point to the beginning ;; of the buffer. (goto-char (length (nth 0 words))) - (cl-labels ((checker () - (user-error "expected error"))) + (cl-labels + ((checker () + (user-error "Expected error"))) (unwind-protect (progn (advice-add 'ispell-show-choices :override @@ -1081,11 +1114,14 @@ so this test virtually mirrors the previous one." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (words '("tarampampamtararam" "test" "test" "more" "obvious" "word")) + (words (append ispell-tests--constants/english/correct-list + (list ispell-tests--constants/english/wrong))) (text (string-join words " "))) (with-temp-buffer - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--with-ispell-global-dictionary + nil (insert text) (ispell-init-process) (should ispell-async-processp) @@ -1093,12 +1129,11 @@ so this test virtually mirrors the previous one." (ispell-kill-ispell nil t))) 'passed ))) - (message "lwf:debug2:ispell-program-name=%s:ispell-dictionary=%s" - ispell-program-name ispell-dictionary) ) (ert-deftest ispell/ispell/buffer () - "`ispell' is just a wrapper around `ispell-region' + "Test `ispell-buffer'. +`ispell' is just a wrapper around `ispell-region' and `ispell-buffer', which is also a wrapper around `ispell-buffer'. This test might seem confusing, as it does not check @@ -1107,7 +1142,7 @@ not matter `ispell' function does not use the backend." (let ((transient-mark-mode t)) (with-temp-buffer - (insert "hello world test test") + (insert (string-join ispell-tests--constants/english/correct-list " ")) (goto-char 2) (set-mark (point)) (goto-char (point-max)) @@ -1115,7 +1150,7 @@ backend." (cl-labels ((checker-buffer () t) (checker-region (_a _b) - (user-error "test failed"))) + (user-error "Test failed"))) (unwind-protect (progn (advice-add 'ispell-buffer :override #'checker-buffer) @@ -1129,18 +1164,19 @@ backend." ) (ert-deftest ispell/ispell/region () - "`ispell' is just a wrapper around `ispell-region' + "Test `ispell-region'. +`ispell' is just a wrapper around `ispell-region' and `ispell-buffer', which is also a wrapper around `ispell-buffer'." (let ((transient-mark-mode t)) (with-temp-buffer - (insert "hello world test test") + (insert (string-join ispell-tests--constants/english/correct-list " ")) (goto-char 2) (set-mark (point)) (goto-char (point-max)) (activate-mark) (cl-labels ((checker-buffer () - (user-error "test failed")) + (user-error "Test failed")) (checker-region (_a _b) t)) (unwind-protect @@ -1156,11 +1192,12 @@ and `ispell-buffer', which is also a wrapper around ) (ert-deftest ispell/ispell-change-dictionary () - "Simple test for changing a dictionary" + "Simple test for changing a dictionary." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer (ispell-change-dictionary "english") (should (equal ispell-local-dictionary "english")) @@ -1173,37 +1210,42 @@ and `ispell-buffer', which is also a wrapper around ) (ert-deftest ispell/ispell-comments-and-strings/correct () - "Test that `ispell-comments-and-strings' does not err -on a correct buffer." + "Test that `ispell-comments-and-strings' does not err on a correct buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-dictionary nil) - (ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-dictionary nil) + (ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer (ispell-kill-ispell t t) (insert "#!/bin/bash\n" - "echo \"string to check\"\n" + "echo \"" + (string-join + ispell-tests--constants/english/correct-list " ") + "\"\n" "# commented line\n") (sh-mode) (ert-with-message-capture lres (ispell-comments-and-strings) (should (string-match "Spell-checking .* using .* with .* dictionary...done" lres)))) - 'passed - ))) + 'passed + ))) ) (ert-deftest ispell/ispell-comments-and-strings/incorrect () - "Test that `ispell-comments-and-strings' errs -on a correct buffer." + "Test that `ispell-comments-and-strings' errs on a correct buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer (insert "#!/bin/bash\n" - "echo \"string to check\"\n" - "# tarampampamtararam\n") + "echo \"" (string-join + ispell-tests--constants/english/correct-list " ") + "\"\n" + "# " ispell-tests--constants/english/wrong "\n") (sh-mode) (ert-with-message-capture lres (should-error (ispell-comments-and-strings))) @@ -1217,19 +1259,31 @@ One correct an one incorrect in the same buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-dictionary nil) - (ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-dictionary nil) + (ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer - (insert "#!/bin/bash\n" - "echo \"string to check\"\n" - "# tarampampamtararam\n") - (sh-mode) - (goto-char 25) - (ert-with-message-capture lres - (ispell-comment-or-string-at-point) - (should (string-match "Spell-checking .* using .* with .* dictionary...done" lres))) - (goto-char 47) - (should-error (ispell-comment-or-string-at-point)) + (let ((string1 "#!/bin/bash\necho\"") + (string2 (string-join + ispell-tests--constants/english/correct-list " ")) + (string3 "\"\n# ") + (string4 ispell-tests--constants/english/wrong) + (string5 "\n")) + (insert string1 + string2 + string3 + string4 + string5) + (sh-mode) + (goto-char (+ (length string1) (length (nth 0 ispell-tests--constants/english/correct-list)))) + (ert-with-message-capture lres + (ispell-comment-or-string-at-point) + (should (string-match "Spell-checking .* using .* with .* dictionary...done" lres))) + (goto-char (+ (length string1) + (length string2) + (length string3) + 1)) + (should-error (ispell-comment-or-string-at-point))) 'passed)))) ) @@ -1238,9 +1292,10 @@ One correct an one incorrect in the same buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer - (insert "test") + (insert (format "%s" (random))) (ispell-kill-ispell t t) (ispell-pdict-save t t) 'passed)))) @@ -1251,9 +1306,10 @@ One correct an one incorrect in the same buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer - (insert "testttttt") + (insert (format "%s" (random))) (goto-char 1) (ispell-kill-ispell t t) (ispell-pdict-save t t) @@ -1265,18 +1321,21 @@ One correct an one incorrect in the same buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend-available-p))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend-available-p))) (with-temp-buffer - (insert "testttttt") + (insert (format "%s" (random))) (goto-char 1) (ispell-kill-ispell t t) - (cl-labels ((checker (s) - (should (equal s "#\n")))) - (unwind-protect (progn - (advice-add 'ispell-send-string :override - #'checker) - (let ((ispell-pdict-modified-p t)) - (ispell-pdict-save t nil))) + (cl-labels + ((checker (s) + (should (equal s "#\n")))) + (unwind-protect + (progn + (advice-add 'ispell-send-string :override + #'checker) + (let ((ispell-pdict-modified-p t)) + (ispell-pdict-save t nil))) (advice-remove 'ispell-send-string #'checker)))) 'passed))) ) @@ -1286,18 +1345,20 @@ One correct an one incorrect in the same buffer." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer - (insert "testttttt") + (insert (format "%s" (random))) (goto-char 1) (ispell-kill-ispell t t) (cl-labels ((checker (_s) - (user-error "test failed"))) - (unwind-protect (progn - (advice-add 'ispell-send-string :override - #'checker) - (let ((ispell-pdict-modified-p nil)) - (ispell-pdict-save t nil))) + (user-error "Test failed"))) + (unwind-protect + (progn + (advice-add 'ispell-send-string :override + #'checker) + (let ((ispell-pdict-modified-p nil)) + (ispell-pdict-save t nil))) (advice-remove 'ispell-send-string #'checker)))) 'passed))) ) @@ -1306,34 +1367,36 @@ One correct an one incorrect in the same buffer." "Test if `ispell-lookup-words' is runnable." (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (tempfile (make-temp-file "emacs-ispell.el-test" nil nil "waveguides"))) - (letopt ((ispell-complete-word-dict tempfile)) + (tempfile (make-temp-file "emacs-ispell.el-test" nil nil ispell-tests--constants/completion))) + (ispell-tests--letopt + ((ispell-complete-word-dict tempfile)) (with-temp-buffer - (insert "waveguid") + (insert (substring ispell-tests--constants/completion 0 -2)) (unwind-protect - (progn - (should (equal - (ispell-lookup-words "waveguid") - '("waveguides"))) - (should (equal - (ispell-lookup-words "sdfsdfasdfsadfasdfasdf") - nil))) - (delete-file tempfile))) + (progn + (should (equal + (ispell-lookup-words (substring ispell-tests--constants/completion 0 -2)) + (list ispell-tests--constants/completion))) + (should (equal + (ispell-lookup-words ispell-tests--constants/english/wrong) + nil))) + (delete-file tempfile))) 'passed))) ) (ert-deftest ispell/ispell-complete-word/ispell-completion-at-point () - "Test if `ispell-complete-word' and `ispell-completion-at-point' -are runnable." + "Test if `ispell-complete-word' and `ispell-completion-at-point' are runnable." (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) - (tempfile (make-temp-file "emacs-ispell.el-test" nil nil "waveguides"))) + (tempfile (make-temp-file "emacs-ispell.el-test" nil nil ispell-tests--constants/completion))) (ignore-errors (ispell-kill-ispell t t)) - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-complete-word-dict tempfile)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-complete-word-dict tempfile)) + (ispell-tests--with-ispell-global-dictionary + nil (with-temp-buffer - (insert "waveguid") + (insert (substring ispell-tests--constants/completion 0 -2)) (cl-labels ((my-ispell-command-loop (_p _n _w _s _e) (car (nth 2 (ispell-completion-at-point))))) (unwind-protect @@ -1341,9 +1404,9 @@ are runnable." (advice-add 'ispell-command-loop :override #'my-ispell-command-loop) (should (equal (car (nth 2 (ispell-completion-at-point))) - "waveguides")) + ispell-tests--constants/completion)) (ispell-complete-word) - (should (equal "waveguides" (buffer-string)))) + (should (equal ispell-tests--constants/completion (buffer-string)))) (progn (delete-file tempfile) (advice-remove @@ -1357,20 +1420,24 @@ are runnable." (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory) (tempfile (make-temp-file "emacs-ispell.el-test" nil nil "waveguides"))) - (with-ispell-global-dictionary nil - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-complete-word-dict tempfile)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-complete-word-dict tempfile)) + (ispell-tests--with-ispell-global-dictionary + nil (with-temp-buffer - (insert "waveguid") - (cl-labels ((my-ispell-command-loop (_p _n _w _s _e) - (car (nth 2 (ispell-completion-at-point))))) + (insert (substring ispell-tests--constants/completion 0 -2)) + (cl-labels + ((my-ispell-command-loop (_p _n _w _s _e) + (car (nth 2 (ispell-completion-at-point))))) (unwind-protect (progn (advice-add 'ispell-command-loop :override #'my-ispell-command-loop) (goto-char 4) (ispell-complete-word-interior-frag) - (should (equal "waveguides" (buffer-string)))) + (should (equal ispell-tests--constants/completion + (buffer-string)))) (progn (delete-file tempfile) (advice-remove @@ -1380,21 +1447,26 @@ are runnable." ) (ert-deftest ispell/ispell-minor-mode/simple () - "Try enabling `ispell-minor-mode' and test -one test file." + "Try enabling `ispell-minor-mode' and test one test file." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) (with-temp-buffer (text-mode) (ispell-minor-mode) - (insert "tarampampamtararam") + (insert ispell-tests--constants/english/wrong) (set--this-command-keys " ") (ert-with-message-capture lres (ispell-minor-check) - (should (string-match "TARAMPAMPAMTARARAM is incorrect" lres))) + (should (string-match + (seq-concatenate + 'string + (upcase ispell-tests--constants/english/wrong) + " is incorrect") + lres))) ) 'passed))) ) @@ -1406,11 +1478,14 @@ a message is sent in `message-mode' or `mml-mode'." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend)) - (ispell-dictionary nil)) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend)) + (ispell-dictionary nil)) (with-temp-buffer (insert - "To: + (seq-concatenate + 'string + "To: Subject: From: Anon Fcc: /tmp/234234.cb022f1a625b65b2.mainframe:2,S @@ -1418,10 +1493,12 @@ User-Agent: mu4e 1.12.9; emacs 31.0.50 Date: Tue, 09 Sep 2025 07:43:58 +0800 Message-ID: <878qiov7b5.fsf@mainframe> --text follows this line-- -Hello World +" (string-join + ispell-tests--constants/english/correct-list " ") + " -- signature -") +")) (message-mode) (ert-with-message-capture lres (ispell-message) @@ -1438,10 +1515,13 @@ a message is sent in `message-mode' or `mml-mode'." (skip-unless (ispell-tests--some-backend-available-p)) (with-environment-variables (("HOME" temporary-file-directory)) (let* ((default-directory temporary-file-directory)) - (letopt ((ispell-program-name (ispell-tests--some-backend))) + (ispell-tests--letopt + ((ispell-program-name (ispell-tests--some-backend))) (with-temp-buffer (insert - "To: + (seq-concatenate + 'string + "To: Subject: From: Anon Fcc: /tmp/234234.cb022f1a625b65b2.mainframe:2,S @@ -1450,32 +1530,24 @@ Date: Tue, 09 Sep 2025 07:43:58 +0800 Message-ID: <878qiov7b5.fsf@mainframe> --text follows this line-- <#part sign=pgpmime> -tarampampamtararam +" ispell-tests--constants/english/wrong " -- signature -") +")) (message-mode) (cl-labels ((checker () - (user-error "expected error"))) + (user-error "Expected error"))) (unwind-protect (progn (advice-add 'ispell-show-choices :override #'checker) (should-error (ispell-message))) (progn (advice-remove 'ispell-show-choices #'checker) - (set-buffer-modified-p nil)))) - - ) + (set-buffer-modified-p nil))))) 'passed))) ) - - -;;====================================================================== -;; On this "emacs page" I want to test that customise variables change -;; function behavior in the way are intended to do. - +(provide 'ispell-tests) -(provide 'tests-ispell) -;;; tests-ispell.el ends here +;;; ispell-tests.el ends here commit 1b1ab741b8e3f0238b6367627fed328b57b6d808 Author: Eli Zaretskii Date: Sat Oct 18 06:18:26 2025 -0400 : Update ldefs-boot.el. diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el index 7d2c66b1e9f..7b0f356f476 100644 --- a/lisp/ldefs-boot.el +++ b/lisp/ldefs-boot.el @@ -144,6 +144,9 @@ non-nil, otherwise in local time. Find change log file in other window and add entry and item. This is just like `add-change-log-entry' except that it displays the change log file in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn &optional WHOAMI FILE-NAME)" t) (autoload 'change-log-mode "add-log" "\ @@ -2949,7 +2952,7 @@ To use tree-sitter C/C++ modes by default, evaluate (add-to-list \\='major-mode-remap-alist \\='(c-or-c++-mode . c-or-c++-ts-mode)) -in your init files. +in your init files, or customize `treesit-enabled-modes'. (fn)" t) (autoload 'c++-ts-mode "c-ts-mode" "\ @@ -2966,7 +2969,7 @@ To use tree-sitter C/C++ modes by default, evaluate (add-to-list \\='major-mode-remap-alist \\='(c-or-c++-mode . c-or-c++-ts-mode)) -in your init files. +in your init files, or customize `treesit-enabled-modes'. Since this mode uses a parser, unbalanced brackets might cause some breakage in indentation/fontification. Therefore, it's @@ -2985,7 +2988,7 @@ This function attempts to use file contents to determine whether the code is C or C++, and based on that chooses whether to enable `c-ts-mode' or `c++-ts-mode'." t) (make-obsolete 'c-or-c++-ts-mode 'c-or-c++-mode "30.1") -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(c-mode . c-ts-mode)) (add-to-list 'treesit-major-mode-remap-alist '(c++-mode . c++-ts-mode)) (add-to-list 'treesit-major-mode-remap-alist '(c-or-c++-mode . c-or-c++-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(c-mode . c-ts-mode)) (add-to-list 'treesit-major-mode-remap-alist '(c++-mode . c++-ts-mode)) (add-to-list 'treesit-major-mode-remap-alist '(c-or-c++-mode . c-or-c++-ts-mode))) (register-definition-prefixes "c-ts-mode" '("c-ts-")) @@ -4707,7 +4710,7 @@ Major mode for editing CMake files, powered by tree-sitter. Enable `cmake-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)\\'" . cmake-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(cmake-mode . cmake-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)\\'" . cmake-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(cmake-mode . cmake-ts-mode))) (register-definition-prefixes "cmake-ts-mode" '("cmake-ts-mode-")) @@ -5932,7 +5935,7 @@ Key bindings: Major mode for editing C# code. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(csharp-mode . csharp-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(csharp-mode . csharp-ts-mode))) (register-definition-prefixes "csharp-mode" '("codedoc-font-lock-" "csharp-")) @@ -5962,7 +5965,7 @@ can also be used to fill comments. \\{css-mode-map} (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(css-mode . css-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(css-mode . css-ts-mode))) (autoload 'css-mode "css-mode" "\ Major mode to edit Cascading Style Sheets (CSS). \\ @@ -7780,6 +7783,9 @@ If DIRNAME is already in a Dired buffer, that buffer is used without refresh. (keymap-set ctl-x-4-map "d" #'dired-other-window) (autoload 'dired-other-window "dired" "\ \"Edit\" directory DIRNAME. Like `dired' but select in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn DIRNAME &optional SWITCHES)" t) (keymap-set ctl-x-5-map "d" #'dired-other-frame) @@ -7867,6 +7873,9 @@ Interactively with prefix argument, read FILE-NAME. (fn &optional OTHER-WINDOW FILE-NAME)" t) (autoload 'dired-jump-other-window "dired" "\ Like \\[dired-jump] (`dired-jump') but in other window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn &optional FILE-NAME)" t) (register-definition-prefixes "dired" '("dired-")) @@ -8400,7 +8409,7 @@ Major mode for editing Dockerfiles, powered by tree-sitter. Enable `dockerfile-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\(?:Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . dockerfile-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(dockerfile-mode . dockerfile-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\(?:\\(?:\\(?:Contain\\|Dock\\)erfile\\)\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" . dockerfile-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(dockerfile-mode . dockerfile-ts-mode))) (register-definition-prefixes "dockerfile-ts-mode" '("dockerfile-ts-mode--")) @@ -9976,7 +9985,7 @@ Major mode for editing Elixir, powered by tree-sitter. Enable `elixir-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.elixir\\'" . elixir-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("\\.ex\\'" . elixir-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("\\.exs\\'" . elixir-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("mix\\.lock" . elixir-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(elixir-mode . elixir-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.elixir\\'" . elixir-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("\\.ex\\'" . elixir-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("\\.exs\\'" . elixir-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("mix\\.lock" . elixir-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(elixir-mode . elixir-ts-mode))) (register-definition-prefixes "elixir-ts-mode" '("elixir-ts-")) @@ -15140,7 +15149,7 @@ Major mode for editing Go, powered by tree-sitter. Enable `go-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(go-mode . go-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(go-mode . go-ts-mode))) (autoload 'go-mod-ts-mode "go-ts-mode" "\ Major mode for editing go.mod files, powered by tree-sitter. @@ -15149,7 +15158,7 @@ Major mode for editing go.mod files, powered by tree-sitter. Enable `go-mod-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(go-mod-mode . go-mod-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(go-mod-mode . go-mod-ts-mode))) (autoload 'go-work-ts-mode "go-ts-mode" "\ Major mode for editing go.work files, powered by tree-sitter. @@ -15158,7 +15167,7 @@ Major mode for editing go.work files, powered by tree-sitter. Enable `go-work-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("/go\\.work\\'" . go-work-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(go-work-mode . go-work-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("/go\\.work\\'" . go-work-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(go-work-mode . go-work-ts-mode))) (register-definition-prefixes "go-ts-mode" '("go-")) @@ -15835,7 +15844,7 @@ Major mode for editing HEEx, powered by tree-sitter. Enable `heex-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.[hl]?eex\\'" . heex-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(heex-mode . heex-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.[hl]?eex\\'" . heex-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(heex-mode . heex-ts-mode))) (register-definition-prefixes "heex-ts-mode" '("heex-ts-")) @@ -16913,6 +16922,13 @@ If `global-hl-line-sticky-flag' is non-nil, Global Hl-Line mode highlights the line about the current buffer's point in all live windows. +If `global-hl-line-sticky-flag' is customized to `window', +then instead of highlighting the line with buffer's point, +this mode highlights the line with window's point that might differ from +the buffer's point when the buffer is displayed in multiple windows. +In this case this mode uses the function +`global-hl-line-window-redisplay' on `pre-redisplay-functions'. + Global-Hl-Line mode uses the function `global-hl-line-highlight' on `post-command-hook'. @@ -19339,7 +19355,7 @@ Return the string read from the minibuffer. Major mode for editing Java, powered by tree-sitter. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(java-mode . java-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(java-mode . java-ts-mode))) (register-definition-prefixes "java-ts-mode" '("java-ts-mode-")) @@ -19386,7 +19402,7 @@ Major mode for editing JavaScript. \\ (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(javascript-mode . js-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(javascript-mode . js-ts-mode))) (autoload 'js-json-mode "js" "\ @@ -19422,7 +19438,7 @@ one of the aforementioned options instead of using this mode. Major mode for editing JSON, powered by tree-sitter. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(js-json-mode . json-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(js-json-mode . json-ts-mode))) (register-definition-prefixes "json-ts-mode" '("json-ts-")) @@ -20175,7 +20191,7 @@ Major mode for editing Lua files, powered by tree-sitter. \\{lua-ts-mode-map} (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(lua-mode . lua-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(lua-mode . lua-ts-mode))) (register-definition-prefixes "lua-ts-mode" '("lua-ts-")) @@ -20419,7 +20435,7 @@ If third arg ALL is non-nil, concatenate all such fields with commas between. If 4th arg LIST is non-nil, return a list of all such fields. If 5th arg DELETE is non-nil, delete all header lines that are included in the result. -The buffer should be narrowed to just the header, else false +The buffer should be narrowed to just the headers, else false matches may be returned from the message body. (fn FIELD-NAME &optional LAST ALL LIST DELETE)") @@ -20768,7 +20784,7 @@ Major mode for editing Markdown using tree-sitter grammar. Enable `markdown-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(markdown-mode . markdown-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(markdown-mode . markdown-ts-mode))) (register-definition-prefixes "markdown-ts-mode" '("markdown-ts-")) @@ -20929,6 +20945,9 @@ contains some mail you have written which has been bounced back to you." t) (autoload 'message-mail-other-window "message" "\ Like `message-mail' command, but display mail buffer in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn &optional TO SUBJECT)" t) (autoload 'message-mail-other-frame "message" "\ @@ -20937,6 +20956,9 @@ Like `message-mail' command, but display mail buffer in another frame. (fn &optional TO SUBJECT)" t) (autoload 'message-news-other-window "message" "\ Start editing a news article to be sent. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn &optional NEWSGROUPS SUBJECT)" t) (autoload 'message-news-other-frame "message" "\ @@ -21274,7 +21296,7 @@ Major mode for editing HTML with embedded JavaScript and CSS. Powered by tree-sitter. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(mhtml-mode . mhtml-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(mhtml-mode . mhtml-ts-mode))) (register-definition-prefixes "mhtml-ts-mode" '("mhtml-ts-mode-")) @@ -25224,7 +25246,7 @@ Optional CONFIG, if supplied, is the php.ini file to use. Enable `php-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.\\(?:php[s345]?\\|phtml\\)\\'" . php-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("\\.\\(?:php\\|inc\\|stub\\)\\'" . php-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("/\\.php_cs\\(?:\\.dist\\)?\\'" . php-ts-mode-maybe)) (add-to-list 'interpreter-mode-alist (cons "php\\(?:-?[34578]\\(?:\\.[0-9]+\\)*\\)?" 'php-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(php-mode . php-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.\\(?:php[s345]?\\|phtml\\)\\'" . php-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("\\.\\(?:php\\|inc\\|stub\\)\\'" . php-ts-mode-maybe)) (add-to-list 'auto-mode-alist '("/\\.php_cs\\(?:\\.dist\\)?\\'" . php-ts-mode-maybe)) (add-to-list 'interpreter-mode-alist (cons "php\\(?:-?[34578]\\(?:\\.[0-9]+\\)*\\)?" 'php-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(php-mode . php-ts-mode))) (register-definition-prefixes "php-ts-mode" '("inferior-php-ts-mode" "php-ts-")) @@ -26751,7 +26773,7 @@ Major mode for editing Python files, using tree-sitter library. \\{python-ts-mode-map} (fn)" t) -(when (and (fboundp 'treesit-available-p) (treesit-available-p) (boundp 'treesit-major-mode-remap-alist)) (add-to-list 'treesit-major-mode-remap-alist '(python-mode . python-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(python-mode . python-ts-mode))) (add-to-list 'auto-mode-alist '("/\\(?:Pipfile\\|\\.?flake8\\)\\'" . conf-mode)) (register-definition-prefixes "python" '("inferior-python-mode" "python-" "run-python-internal" "subword-mode")) @@ -28454,7 +28476,7 @@ Major mode for editing Ruby code. Major mode for editing Ruby, powered by tree-sitter. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(ruby-mode . ruby-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(ruby-mode . ruby-ts-mode))) (register-definition-prefixes "ruby-ts-mode" '("ruby-ts-")) @@ -28494,7 +28516,7 @@ Major mode for editing Rust, powered by tree-sitter. Enable `rust-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(rust-mode . rust-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(rust-mode . rust-ts-mode))) (register-definition-prefixes "rust-ts-mode" '("rust-ts-")) @@ -28701,10 +28723,6 @@ following constructs: (fn &rest REGEXPS)") (define-symbol-prop 'rx--pcase-macroexpander 'edebug-form-spec 'nil) (define-symbol-prop 'rx 'pcase-macroexpander #'rx--pcase-macroexpander) -(autoload 'rx--pcase-expand "rx" "\ - - -(fn REGEXPS)") (register-definition-prefixes "rx" '("rx-")) @@ -29322,6 +29340,9 @@ The seventh argument ACTIONS is a list of actions to take (fn &optional NOERASE TO SUBJECT IN-REPLY-TO CC REPLYBUFFER ACTIONS RETURN-ACTION)" t) (autoload 'mail-other-window "sendmail" "\ Like `mail' command, but display mail buffer in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn &optional NOERASE TO SUBJECT IN-REPLY-TO CC REPLYBUFFER SENDACTIONS)" t) (autoload 'mail-other-frame "sendmail" "\ @@ -29595,7 +29616,7 @@ This mode automatically falls back to `sh-mode' if the buffer is not written in Bash or sh. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(sh-mode . bash-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(sh-mode . bash-ts-mode))) (register-definition-prefixes "sh-script" '("sh-")) @@ -31265,7 +31286,7 @@ Also see `read-string-from-buffer'. (fn PROMPT STRING SUCCESS-CALLBACK &key ABORT-CALLBACK MAJOR-MODE-SYM READ)") (autoload 'read-string-from-buffer "string-edit" "\ Switch to a new buffer to edit STRING in a recursive edit. -The user finishes editing with \\\\[string-edit-done], or aborts with \\\\[string-edit-abort]). +The user finishes editing with \\\\[string-edit-done], or aborts with \\\\[string-edit-abort]. Insert PROMPT at the start of the buffer. If nil, no prompt is inserted. @@ -31514,6 +31535,9 @@ This takes into account combining characters and grapheme clusters: if compositions are enabled, each sequence of characters composed on display into a single grapheme cluster is treated as a single indivisible unit. +Caveat: for this function to recognize characters compositions, the +automatic compositions should be enabled (see `auto-composition-mode') +and the current buffer must be displayed in some window. (fn STRING)") (function-put 'string-glyph-split 'side-effect-free 't) @@ -33005,7 +33029,7 @@ Available values of TYPE and corresponding OBJECTs are: `name' -- the \"display name\" part of an email address; OBJECT should be a string. -`email-address' -- a full email address; OBJECT should be a string. + `email-address' -- a full email address; OBJECT should be a string. `email-address-header' -- a raw email address header in RFC 2822 format; OBJECT should be a string. @@ -33735,7 +33759,7 @@ Mode for displaying and reprioritizing top priority Todo. Major mode for editing TOML, powered by tree-sitter. (fn)" t) -(when (treesit-available-p) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(conf-toml-mode . toml-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(conf-toml-mode . toml-ts-mode))) (register-definition-prefixes "toml-ts-mode" '("toml-ts-mode-")) @@ -34154,7 +34178,7 @@ Interactively, with a prefix argument, prompt for a different method." t) ;;; Generated autoloads from transient.el -(push '(transient 0 10 0) package--builtin-versions) +(push '(transient 0 10 1) package--builtin-versions) (autoload 'transient-insert-suffix "transient" "\ Insert a SUFFIX into PREFIX before LOC. PREFIX is a prefix command, a symbol. @@ -34570,7 +34594,7 @@ Major mode for editing TypeScript. Enable `typescript-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(typescript-mode . typescript-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(typescript-mode . typescript-ts-mode))) (autoload 'tsx-ts-mode "typescript-ts-mode" "\ Major mode for editing TSX and JSX documents. @@ -34587,7 +34611,7 @@ at least 3 (which is the default value). Enable `tsx-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(tsx-mode . tsx-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(tsx-mode . tsx-ts-mode))) (register-definition-prefixes "typescript-ts-mode" '("tsx-ts-" "typescript-ts-")) @@ -35707,6 +35731,48 @@ prefix argument is given, in which case prompt for a file FILE to remove from the list of ignored files. (fn FILE &optional DIRECTORY REMOVE)" t) +(autoload 'vc-revision-cherry-pick "vc" "\ +Copy the changes from a single revision REV to the current branch. +When called interactively, prompts for REV. +Typically REV is a revision from another branch, where that branch is +one that will not be merged into the branch checked out in this working +tree. + +Normally a log message for the new commit is generated by the backend +and includes a reference to REV so that the copy can be traced. +When called interactively with a prefix argument, use REV's log message +unmodified, and also skip editing it. + +When called from Lisp, there are three calling conventions for the +COMMENT and INITIAL-CONTENTS optional arguments: +- COMMENT a string, INITIAL-CONTENTS nil means use that comment string + without prompting the user to edit it. +- COMMENT a string, INITIAL-CONTENTS non-nil means use that comment + string as the initial contents of the log entry buffer but stop for + editing. +- COMMENT t means use BACKEND's default cherry-pick comment for REV + without prompting for editing, and ignore INITIAL-CONTENTS. + +Optional argument BACKEND is the VC backend to use. + +(fn REV &optional COMMENT INITIAL-CONTENTS BACKEND)" t) +(autoload 'vc-revision-revert "vc" "\ +Undo the effects of revision REV. +When called interactively, prompts for REV. + +When called from Lisp, there are three calling conventions for the +COMMENT and INITIAL-CONTENTS optional arguments: +- COMMENT a string, INITIAL-CONTENTS nil means use that comment string + without prompting the user to edit it. +- COMMENT a string, INITIAL-CONTENTS non-nil means use that comment + string as the initial contents of the log entry buffer but stop for + editing. +- COMMENT t means use BACKEND's default revert comment for REV without + prompting for editing, and ignore INITIAL-CONTENTS. + +Optional argument BACKEND is the VC backend to use. + +(fn REV &optional COMMENT INITIAL-CONTENTS BACKEND)" t) (autoload 'vc-version-diff "vc" "\ Report diffs between revisions REV1 and REV2 in the repository history. This compares two revisions of the current fileset. @@ -35867,6 +35933,10 @@ Visit revision REV of the current file in another window. If the current file is named `F', the revision is named `F.~REV~'. If `F.~REV~' already exists, use it instead of checking it out again. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. + (fn REV)" t) (autoload 'vc-insert-headers "vc" "\ Insert headers into a file for use with a version control system. @@ -36217,6 +36287,14 @@ If any changes to be moved can't be reverse-applied to this working tree, it is an error, and no changes are moved. (fn DIRECTORY &optional MOVE PREVIEW)" t) +(autoload 'vc-kill-other-working-tree-buffers "vc" "\ +Kill buffers visiting versions of this file in other working trees. +BACKEND is the VC backend. + +This command kills the buffers that \\[vc-switch-working-tree] switches to, +except that this command works only in file-visiting buffers. + +(fn BACKEND)" t) (register-definition-prefixes "vc" '("log-view-vc-prev-" "vc-" "with-vc-properties")) @@ -37282,6 +37360,10 @@ For a list of all View commands, type H or h while viewing. This command runs the normal hook `view-mode-hook'. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. + (fn FILE)" t) (autoload 'view-file-other-frame "view" "\ View FILE in View mode in another frame. @@ -37342,6 +37424,10 @@ This function does not enable View mode if the buffer's major mode has a `special' mode-class, because such modes usually have their own View-like bindings. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. + (fn BUFFER &optional NOT-RETURN EXIT-ACTION)" t) (autoload 'view-buffer-other-frame "view" "\ View BUFFER in View mode in another frame. @@ -38551,6 +38637,8 @@ evaluate `(default-value \\='windmove-mode)'. The mode's hook is called both when the mode is enabled and when it is disabled. +\\{windmove-mode-map} + (fn &optional ARG)" t) (autoload 'windmove-default-keybindings "windmove" "\ Set up keybindings for `windmove'. @@ -39069,6 +39157,9 @@ Use \\[xref-go-back] to return back to where you invoked this command. (fn IDENTIFIER)" t) (autoload 'xref-find-definitions-other-window "xref" "\ Like `xref-find-definitions' but switch to the other window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically. (fn IDENTIFIER)" t) (autoload 'xref-find-definitions-other-frame "xref" "\ @@ -39217,7 +39308,7 @@ Major mode for editing YAML, powered by tree-sitter. Enable `yaml-ts-mode' when its grammar is available. Also propose to install the grammar when `treesit-enabled-modes' is t or contains the mode name.") -(when (treesit-available-p) (add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode-maybe)) (defvar treesit-major-mode-remap-alist) (add-to-list 'treesit-major-mode-remap-alist '(yaml-mode . yaml-ts-mode))) +(when (boundp 'treesit-major-mode-remap-alist) (add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode-maybe)) (add-to-list 'treesit-major-mode-remap-alist '(yaml-mode . yaml-ts-mode))) (register-definition-prefixes "yaml-ts-mode" '("yaml-ts-mode--")) @@ -39276,6 +39367,74 @@ run a specific program. The program must be a member of (fn &optional PGM)" t) (register-definition-prefixes "zone" '("zone-")) + + +;;; Generated autoloads from emacs-lisp/elisp-scope.el + +(autoload 'elisp-scope-get-symbol-role-property "elisp-scope" "\ +Return value of property PROP for symbol role ROLE. + +(fn ROLE PROP)") +(autoload 'elisp-scope-set-symbol-role-property "elisp-scope" "\ +Set value of property PROP for symbol role ROLE to VALUE. + +(fn ROLE PROP VALUE)") +(autoload 'elisp-scope-symbol-role-p "elisp-scope" "\ +Check whether a symbol SYM is the name of a \"symbol role\". + +(fn SYM)") +(autoload 'elisp-scope-add-symbol-roles-to-describe-symbol "elisp-scope") +(autoload 'elisp-scope-describe-symbol-role "elisp-scope" "\ +Describe ROLE of a symbol. +Interactively, prompt for ROLE. + +(fn ROLE &rest _)" t) +(autoload 'elisp-scope-analyze-form "elisp-scope" "\ +Read and analyze code from STREAM, reporting findings via CALLBACK. + +Call CALLBACK for each analyzed symbol SYM with arguments ROLE, POS, +LEN, ID and DEF, where ROLE is a symbol that specifies the semantics of +SYM; POS is the position of SYM in STREAM; LEN is SYM's length; ID is an +object that uniquely identifies (co-)occurrences of SYM in the current +defun; and DEF is the position in which SYM is locally defined, or nil. +If SYM is itself a binding occurrence, then POS and BINDER are equal. +If SYM is not lexically bound, then BINDER is nil. This function +ignores `read-symbol-shorthands', so SYM and LEN always correspond to +the symbol as it appears in STREAM. + +If STREAM is nil, it defaults to the current buffer. + +This function recursively analyzes Lisp forms (HEAD . TAIL), usually +starting with a top-level form, by inspecting HEAD at each level: + +- If HEAD is a symbol with a non-nil `elisp-scope-analyzer' symbol + property, then the value of that property specifies a bespoke analzyer + function, AF, that is called as (AF HEAD . TAIL) to analyze the form. + See more details about writing analyzer functions below. + +- If HEAD satisfies `functionp', which means it is a function in the + running Emacs session, analzye the form as a function call. + +- If HEAD is a safe macro (see `elisp-scope-safe-macro-p'), expand it + and analyze the resulting form. + +- If HEAD is unknown, then the arguments in TAIL are ignored, unless + `elisp-scope-assume-func' is non-nil, in which case they are analyzed + as evaluated forms (i.e. HEAD is assumed to be a function). + +An analyzer (function specified via the `elisp-scope-analyzer' property) +can use the functions `elisp-scope-report-s', `elisp-scope-1' and +`elisp-scope-n' to analyze its arguments, and it can consult the +variable `elisp-scope-output-spec' to obtain the expected output spec of +the analyzed form. For example, the following is a suitable analyzer +for the `identity' function: + + (lambda (fsym arg) + (elisp-scope-report-s fsym \\='function) + (elisp-scope-1 arg elisp-scope-output-spec)) + +(fn CALLBACK &optional STREAM)") +(register-definition-prefixes "elisp-scope" '("elisp-scope-")) ;;; End of scraped data commit 521e06d3ef32ed78a4c01a8af1131e7dcfc4180d Merge: bea16dfe69f 53aac6aecd2 Author: Eli Zaretskii Date: Sat Oct 18 06:15:46 2025 -0400 Merge from origin/emacs-30 53aac6aecd2 ; Improve documentation of commands that split current wi... commit bea16dfe69fba7d3bbb7a67bcf60b3c4739aad7c Author: Spencer Baugh Date: Fri Sep 26 18:13:30 2025 -0400 Respect keymaps in buffer text for clicks on displayed strings When clicking on a string displayed by a display property, also look at the text properties of the underlying buffer text for keymaps, not just the displayed string. The displayed string takes precedence over the buffer text, but it doesn't replace it. Also, we should use the buffer's local map even for clicks on the mode line. (Otherwise, what's the point of the event?) * src/keymap.c (Fcurrent_active_maps): Consider displayed string, then buffer text, then fall back to local map. (Bug#79505) * test/src/keymap-tests.el (keymap-test-keymaps-for-non-buffer-positions): Add more tests. diff --git a/src/keymap.c b/src/keymap.c index 295b209f06b..a01dee1476b 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -1721,33 +1721,12 @@ means to return the active maps for that window's buffer. */) /* For a mouse click, get the local text-property keymap of the place clicked on, rather than point. */ - if (POSN_INBUFFER_P (position)) - { - Lisp_Object pos = POSN_BUFFER_POSN (position); - if (FIXNUMP (pos) - && XFIXNUM (pos) >= BEG && XFIXNUM (pos) <= Z) - { - local_map = get_local_map (XFIXNUM (pos), - current_buffer, Qlocal_map); - - keymap = get_local_map (XFIXNUM (pos), - current_buffer, Qkeymap); - } - } - - Lisp_Object pos_area = POSN_POSN (position); - if (EQ (pos_area, Qmode_line) || EQ (pos_area, Qheader_line)) - { - /* For clicks on mode line or header line, ignore the maps - we found at POSITION, because properties at point are - not relevant in that case. */ - local_map = Qnil; - keymap = Qnil; - } + local_map = Qnil; + keymap = Qnil; /* If on a mode line string with a local keymap, or for a click on a string, i.e. overlay string or a string - displayed via the `display' property, consider only the + displayed via the `display' property, first consider the `local-map' and `keymap' properties of that string. */ if (CONSP (string) && STRINGP (XCAR (string))) @@ -1763,6 +1742,26 @@ means to return the active maps for that window's buffer. */) } } + Lisp_Object buffer_posn = POSN_BUFFER_POSN (position); + /* Then, if the click was in the buffer, get the local + text-property keymap of the place clicked on. */ + + if (FIXNUMP (buffer_posn) + && XFIXNUM (buffer_posn) >= BEG && XFIXNUM (buffer_posn) <= Z) + { + /* The properties in POSN_STRING take precedence, if set. */ + if (NILP (local_map)) + local_map = get_local_map (XFIXNUM (buffer_posn), + current_buffer, Qlocal_map); + + if (NILP (keymap)) + keymap = get_local_map (XFIXNUM (buffer_posn), + current_buffer, Qkeymap); + } + + /* Finally, fall back on the buffer's local map. */ + if (NILP (local_map)) + local_map = BVAR (current_buffer, keymap); } if (!NILP (local_map)) diff --git a/test/src/keymap-tests.el b/test/src/keymap-tests.el index 950c741a6dd..0d203f6fcb0 100644 --- a/test/src/keymap-tests.el +++ b/test/src/keymap-tests.el @@ -509,25 +509,36 @@ g .. h foo ;; From the parent this time/ (should (equal (keymap-lookup map "u") #'undo)))) +(defun keymap-test--get-maps (posn) + (let ((maps (current-active-maps nil posn))) + ;; The local map should always be present. + (should (memq (current-local-map) maps)) + maps)) + (defun keymap-test--maps-for-posn (area string) - (current-active-maps - nil + (keymap-test--get-maps ;; FIXME: This test would be better if this was a real position ;; created by a real click. - `(,(selected-window) ,area (1 . 1) 0 (,string . 0) nil (1 . 1) nil (1 . 1) (1 . 1)))) + (let (;; If AREA is passed, the click isn't in the buffer; otherwise + ;; it's at position 1. + (pos (if area nil 1))) + `(,(selected-window) ,area (1 . 1) 0 (,string . 0) + ,pos (1 . 1) nil (1 . 1) (1 . 1))))) (ert-deftest keymap-test-keymaps-for-non-buffer-positions () "`current-active-maps' with non-buffer positions. (bug#76620)" (with-temp-buffer (pop-to-buffer (current-buffer)) (let ((keymap (make-sparse-keymap "keymap-at-point"))) + (use-local-map (make-sparse-keymap "buffer-local-map")) (insert (propertize "string" 'keymap keymap)) (goto-char (point-min)) - (should (memq keymap (current-active-maps))) + (should (memq keymap (keymap-test--get-maps nil))) (should-not (memq keymap (keymap-test--maps-for-posn 'mode-line nil))) (should-not (memq keymap (keymap-test--maps-for-posn 'mode-line "s"))) - (should-not (memq keymap (keymap-test--maps-for-posn nil "s"))) + (should (memq keymap (keymap-test--maps-for-posn nil "s"))) (should (memq keymap (keymap-test--maps-for-posn nil nil))) + (should (memq keymap (keymap-test--get-maps "a"))) (let* ((mode-line-keymap (make-sparse-keymap "keymap-in-mode-line")) (s (propertize "string" 'keymap mode-line-keymap))) ;; Respect `keymap' in the string clicked on. commit 59da5020da703aae784ec8afeaa32348c6b6bfd5 Author: Augusto Stoffel Date: Sun Oct 12 16:13:11 2025 +0200 Fix an error when bookmark handler is a lambda * lisp/bookmark.el (bookmark-type-from-full-record): Ensure handler is a symbol before calling 'symbol-function'. (Bug#79618) diff --git a/lisp/bookmark.el b/lisp/bookmark.el index 344ad4ec7d0..75071ddb9ce 100644 --- a/lisp/bookmark.el +++ b/lisp/bookmark.el @@ -376,7 +376,7 @@ BOOKMARK-RECORD is, e.g., one element from `bookmark-alist'. Its type is read from the symbol property named `bookmark-handler-type' read on the record handler function." (let ((handler (bookmark-get-handler bookmark-record))) - (when (autoloadp (symbol-function handler)) + (when (and (symbolp handler) (autoloadp (symbol-function handler))) (autoload-do-load (symbol-function handler))) (if (symbolp handler) (get handler 'bookmark-handler-type) commit 0d8a31423af692d608e9d59df489cf5a9a7e3753 Author: Eli Zaretskii Date: Sat Oct 18 12:26:28 2025 +0300 ; Fix documentation of a recent commit * src/window.c (Fcombine_windows, Funcombine_window): Doc fixes. * doc/lispref/windows.texi (Recombining Windows): Fix markup and wording. diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 5393db9b562..1611ecb7389 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -2115,33 +2115,33 @@ windows starting with @var{first} and ending with @var{last} and returns the new parent window. This function is useful to operate on several siblings of the same -parent window as a whole. Consider three sibling windows @var{W1}, -@var{W2} and @var{W3} aligned side by side. To make a window below -@var{W1} and @var{W2} but not below @var{W3}, first combine @var{W1} and -@var{W2} using this function and then split the window it returned. It -can be also used to obtain the combined state of @var{W1} and @var{W2} -by running @code{window-state-get} (@pxref{Window Configurations}) on -the return value. +parent window as a whole. Consider three sibling windows @var{w1}, +@var{w2} and @var{w3} aligned side by side. To create a window below +@var{w1} and @var{w2} but not below @var{w3}, first combine @var{w1} and +@var{w2} using this function, and then split the window it returned. You +can also call @code{window-state-get} (@pxref{Window Configurations}) on +the return value of @code{combine-windows} to obtain the combined state +of @var{w1} and @var{w2}. @end defun @cindex uncombining windows @defun uncombine-window window This function uncombines the specified @var{window}. @var{window} should be an internal window whose parent window is an internal window -of the same type. This means, that @var{window} and its parent should +of the same type. This means that @var{window} and its parent should be either both horizontal or both vertical window combinations. If this -is the case, it makes the child windows of @var{window} child windows of -@var{window}'s parent and returns @code{t}. Otherwise, it leaves the -current configuration of @var{window}'s frame unchanged and returns -@code{nil}. +is the case, the function makes the child windows of @var{window} become +child windows of @var{window}'s parent, and returns @code{t}. +Otherwise, it leaves the current configuration of @var{window}'s frame +unchanged and returns @code{nil}. This function is useful to undo the effect of @code{combine-windows}. -For example, to calculate the state of two adjacent windows @var{W1} and -@var{W2}, proceed as follows: Run @code{combine-windows} with @var{W1} -and @var{W2} as arguments and store the result in @var{W}. Then run -@code{window-state-get} with the argument @var{W}. Finally, run this -function with @var{W} as its argument to obtain the window configuration -that existed before running @code{combine-windows}. +For example, to calculate the state of two adjacent windows @var{w1} and +@var{w2}, proceed as follows: first, call @code{combine-windows} with @var{w1} +and @var{w2} as arguments, and store the result in @var{w}. Then call +@code{window-state-get} with the argument @var{w}. Finally, call +@code{uncombine-window} with @var{w} as its argument to obtain the +window configuration that existed before the call to @code{combine-windows}. @end defun diff --git a/src/window.c b/src/window.c index f19f2041c77..d7348fb265b 100644 --- a/src/window.c +++ b/src/window.c @@ -5155,7 +5155,7 @@ windows with the same parent window. If neither FIRST has a previous nor LAST has a next sibling, return that parent window. Otherwise, make a new parent window whose first child window becomes FIRST and whose last child window becomes LAST, insert that parent window in the window -tree in lieu of the windows starting with FIRST and ending with LAST and +tree in lieu of the windows starting with FIRST and ending with LAST, and return the new parent window. */) (Lisp_Object first, Lisp_Object last) { @@ -5280,9 +5280,9 @@ DEFUN ("uncombine-window", Funcombine_window, Suncombine_window, 1, 1, 0, WINDOW should be an internal window whose parent window is an internal window of the same type. This means, that WINDOW and its parent should be either both horizontal or both vertical window combinations. If this -is the case, make the child windows of WINDOW child windows of WINDOW's -parent and return t. Otherwise, leave the current configuration of -WINDOW's frame unchanged and return nil. */) +is the case, make the child windows of WINDOW become child windows of +WINDOW's parent and return t. Otherwise, leave the current configuration +of WINDOW's frame unchanged and return nil. */) (Lisp_Object window) { struct window *w = decode_valid_window (window); commit a779f7e01bdd2afef52e7d4968a9a709019c8003 Merge: 30bb6770f47 ef2b0325677 Author: Eli Zaretskii Date: Sat Oct 18 12:14:23 2025 +0300 Merge branch 'master' of git.savannah.gnu.org:/srv/git/emacs commit 30bb6770f47b1475150e8bc8513432c8da83a171 Author: Eli Zaretskii Date: Sat Oct 18 12:13:49 2025 +0300 ; * etc/NEWS: Document last change (bug#79597). diff --git a/etc/NEWS b/etc/NEWS index 0b8d624d923..b1304bebfea 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2502,6 +2502,9 @@ version-list of a given package symbol. These functions provide public interfaces for external tools to query information about built-in packages. +--- +*** Uninstalling a package now removes its directory from 'load-path'. + ** Rcirc +++ commit 1eeadd1f14a2b695b8bcb0b2de108ebff35c1a70 Author: Lin Sun Date: Tue Oct 7 06:30:20 2025 +0000 Remove directory of deleted package from `load-path' * lisp/emacs-lisp/package.el (package--delete-directory): Remove the directory of the to-be-deleted package `load-path'. Doc fix. (Bug#79597) diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index ba9999c20e6..c39c8bf24cc 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -2509,7 +2509,9 @@ installed), maybe you need to \\[package-refresh-contents]") (defun package--delete-directory (dir) "Delete PKG-DESC directory DIR recursively. Clean-up the corresponding .eln files if Emacs is native -compiled." +compiled, and remove the DIR from `load-path'." + (setq load-path (cl-remove-if (lambda (s) (file-in-directory-p s dir)) + load-path)) (when (featurep 'native-compile) (cl-loop for file in (directory-files-recursively dir commit ef2b0325677286373980f7d641576253b4f1cf41 Author: Martin Rudalics Date: Sat Oct 18 10:33:10 2025 +0200 Add functions 'combine-windows' and 'uncombine-window' * src/window.c (make_parent_window, Fcombine_windows) (Funcombine_window): New functions. (Fsplit_window_internal): Use make_parent_window. * doc/lispref/windows.texi (Recombining Windows): Describe new functions 'combine-windows' and 'uncombine-window'. (Window Configurations): Explain "window clones". * etc/NEWS: Mention new functions 'combine-windows' and 'uncombine-window'. diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index e042418634d..5393db9b562 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -2099,6 +2099,51 @@ Deleting any of the live windows @var{W2}, @var{W3} or @var{W4} will distribute its space proportionally among the two remaining live windows. +The following two functions allow to expressly make a new parent window +for several adjacent windows and to undo that operation. + +@cindex combining windows +@defun combine-windows first last +This function combines the windows from @var{first} to @var{last} +inclusive. @var{first} and @var{last} must be valid windows in the same +combination, that is, windows with the same parent window. If neither +@var{first} has a previous nor @var{last} has a next sibling, it returns +that parent window. Otherwise, it makes a new parent window whose first +child window becomes @var{first} and whose last child window becomes +@var{last}, inserts that parent window in the window tree in lieu of the +windows starting with @var{first} and ending with @var{last} and returns +the new parent window. + +This function is useful to operate on several siblings of the same +parent window as a whole. Consider three sibling windows @var{W1}, +@var{W2} and @var{W3} aligned side by side. To make a window below +@var{W1} and @var{W2} but not below @var{W3}, first combine @var{W1} and +@var{W2} using this function and then split the window it returned. It +can be also used to obtain the combined state of @var{W1} and @var{W2} +by running @code{window-state-get} (@pxref{Window Configurations}) on +the return value. +@end defun + +@cindex uncombining windows +@defun uncombine-window window +This function uncombines the specified @var{window}. @var{window} +should be an internal window whose parent window is an internal window +of the same type. This means, that @var{window} and its parent should +be either both horizontal or both vertical window combinations. If this +is the case, it makes the child windows of @var{window} child windows of +@var{window}'s parent and returns @code{t}. Otherwise, it leaves the +current configuration of @var{window}'s frame unchanged and returns +@code{nil}. + +This function is useful to undo the effect of @code{combine-windows}. +For example, to calculate the state of two adjacent windows @var{W1} and +@var{W2}, proceed as follows: Run @code{combine-windows} with @var{W1} +and @var{W2} as arguments and store the result in @var{W}. Then run +@code{window-state-get} with the argument @var{W}. Finally, run this +function with @var{W} as its argument to obtain the window configuration +that existed before running @code{combine-windows}. +@end defun + @node Resurrecting Windows @section Resurrecting Windows @@ -7035,9 +7080,9 @@ sessions. @xref{Uniquify,,, emacs, The GNU Emacs Manual}. @end defun The value returned by @code{window-state-get} can be used in the same -session to make a clone of a window in another window. It can be also -written to disk and read back in another session. In either case, use -the following function to restore the state of the window. +session to clone a window (see below). It can be also written to disk +and read back in another session. In either case, use the following +function to restore the state of the window. @defun window-state-put state &optional window ignore This function puts the window state @var{state} into @var{window}. @@ -7059,11 +7104,22 @@ is @code{safe}, this means windows can get as small as one line and/or two columns. @end defun +@cindex window clone +@cindex clone of window +In the context of window states, the @dfn{clone of a window} is a window +that has the same decorations and contents as the window whose state was +used to produce it, but is actually represented by another window +object. Operating on the original or the clone of a window does not +affect the other in any way. Note that while @code{window-state-get} +clones existing windows, these clones are not yet valid windows. They +become valid only after @code{window-state-put} has put them into a live +frame. + By default, @code{set-window-configuration} and @code{window-state-put} -may delete a window from the restored configuration when they find out -that its buffer was killed since the corresponding configuration or -state has been recorded. The variable described next can be used to -fine-tune that behavior. +may delete a window from the restored configuration or state when they +find out that its buffer was killed since the corresponding +configuration or state has been recorded. The variable described next +can be used to fine-tune that behavior. @cindex restoring windows whose buffers have been killed @defvar window-restore-killed-buffer-windows diff --git a/etc/NEWS b/etc/NEWS index 0b8d624d923..9ade5212f6f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -413,6 +413,13 @@ during the execution of particular commands. This moves in the opposite direction of 'other-window' and is for its default keybinding consistent with 'repeat-mode'. ++++ +*** New functions 'combine-windows' and 'uncombine-window'. +'combine-windows' is useful to make a new parent window for several +adjacent windows and subsequently operate on that parent. +'uncombine-window' can then be used to restore the window configuration +to the state it had before running 'combine-windows'. + ** Frames +++ diff --git a/src/window.c b/src/window.c index dee17ae445a..f19f2041c77 100644 --- a/src/window.c +++ b/src/window.c @@ -5133,6 +5133,200 @@ resize_frame_windows (struct frame *f, int size, bool horflag) } +/** Make parent window on FRAME and return its object. */ +static Lisp_Object +make_parent_window (Lisp_Object frame) +{ + Lisp_Object parent; + struct window *p = allocate_window (); + + p->sequence_number = ++sequence_number; + wset_frame (p, frame); + XSETWINDOW (parent, p); + + return parent; +} + + +DEFUN ("combine-windows", Fcombine_windows, Scombine_windows, 2, 2, 0, + doc: /* Combine windows from FIRST to LAST. +FIRST and LAST must be valid windows in the same combination, that is, +windows with the same parent window. If neither FIRST has a previous +nor LAST has a next sibling, return that parent window. Otherwise, make +a new parent window whose first child window becomes FIRST and whose +last child window becomes LAST, insert that parent window in the window +tree in lieu of the windows starting with FIRST and ending with LAST and +return the new parent window. */) + (Lisp_Object first, Lisp_Object last) +{ + struct window *f = decode_valid_window (first); + struct window *l = decode_valid_window (last); + struct window *w = f; + + if (f == l) + /* Don't make a matryoshka window. */ + error ("Invalid window to parentify"); + + while (w != l && !NILP (w->next)) + w = XWINDOW (w->next); + + if (w != l) + { + w = l; + + while (w != f && !NILP (w->next)) + w = XWINDOW (w->next); + + if (w == f) + /* Invert FIRST and LAST. */ + { + f = l; + l = w; + XSETWINDOW (first, f); + XSETWINDOW (last, l); + } + else + error ("Invalid window to parentify"); + } + + if (NILP (f->prev) && NILP (l->next)) + return f->parent; + + /* Make new parent window PARENT. */ + Lisp_Object parent = make_parent_window (f->frame); + struct window *p = XWINDOW (parent); + double normal_size = 0.0; + + /* Splice in PARENT into the window tree. */ + p->parent = f->parent; + p->horizontal = XWINDOW (p->parent)->horizontal; + p->contents = first; + + if (NILP (f->prev)) + /* FIRST has no previous sibling. */ + XWINDOW (p->parent)->contents = parent; + else + /* FIRST has a previous sibling. */ + { + XWINDOW (f->prev)->next = parent; + p->prev = f->prev; + f->prev = Qnil; + } + + if (!NILP (l->next)) + /* LAST has a next sibling. */ + { + XWINDOW (l->next)->prev = parent; + p->next = l->next; + l->next = Qnil; + } + + /* Fix parent slots for PARENT's new children. */ + w = f; + + while (w) + { + w->parent = parent; + if (w == l) + break; + else + { + if (p->horizontal) + normal_size = normal_size + XFLOAT_DATA (f->normal_cols); + else + normal_size = normal_size + XFLOAT_DATA (f->normal_lines); + + w = XWINDOW (w->next); + } + } + + /* Set up PARENT's positions and sizes. */ + p->pixel_left = f->pixel_left; + p->left_col = f->left_col; + p->pixel_top = f->pixel_top; + p->top_line = f->top_line; + + if (p->horizontal) + { + p->pixel_width = l->pixel_left + l->pixel_width - f->pixel_left; + p->total_cols = l->left_col + l->total_cols - f->left_col; + p->pixel_height = f->pixel_height; + p->total_lines = f->total_lines; + } + else + { + p->pixel_height = l->pixel_top + l->pixel_height - f->pixel_top; + p->total_lines = l->top_line + l->total_lines - f->top_line; + p->pixel_width = f->pixel_width; + p->total_cols = f->total_cols; + } + + if (p->horizontal) + { + p->normal_cols = make_float (normal_size); + p->normal_lines = f->normal_lines; + } + else + { + p->normal_cols = f->normal_cols; + p->normal_lines = make_float (normal_size); + } + + return parent; +} + +DEFUN ("uncombine-window", Funcombine_window, Suncombine_window, 1, 1, 0, + doc: /* Uncombine specified WINDOW. +WINDOW should be an internal window whose parent window is an internal +window of the same type. This means, that WINDOW and its parent should +be either both horizontal or both vertical window combinations. If this +is the case, make the child windows of WINDOW child windows of WINDOW's +parent and return t. Otherwise, leave the current configuration of +WINDOW's frame unchanged and return nil. */) + (Lisp_Object window) +{ + struct window *w = decode_valid_window (window); + Lisp_Object parent = w->parent; + + if (MINI_WINDOW_P (w)) + error ("Cannot uncombine a mini window"); + + if (WINDOW_INTERNAL_P (w) && !NILP (parent) + && w->horizontal == XWINDOW (parent)->horizontal) + { + struct window *p = XWINDOW (w->parent); + /* WINDOW's first child. */ + Lisp_Object first = w->contents; + struct window *f = XWINDOW (first); + /* WINDOW's last child. */ + Lisp_Object last = Qnil; + struct window *l = f; + + /* Make WINDOW's parent new parent of WINDOW's children. */ + while (!NILP (l->next)) + { + wset_parent (l, parent); + l = XWINDOW (l->next); + } + wset_parent (l, parent); + XSETWINDOW (last, l); + + wset_prev (f, w->prev); + if (NILP (f->prev)) + wset_combination (p, p->horizontal, first); + else + wset_next (XWINDOW (f->prev), first); + + wset_next (l, w->next); + if (!NILP (l->next)) + wset_prev (XWINDOW (w->next), last); + + return Qt; + } + else + return Qnil; +} + DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 5, 0, doc: /* Split window OLD. Second argument PIXEL-SIZE specifies the number of pixels of the @@ -5302,12 +5496,9 @@ set correctly. See the code of `split-window' for how this is done. */) = horflag ? o->normal_cols : o->normal_lines; if (NILP (parent)) - /* This is the crux of the old make_parent_window. */ { - p = allocate_window (); - XSETWINDOW (parent, p); - p->sequence_number = ++sequence_number; - wset_frame (p, frame); + parent = make_parent_window (frame); + p = XWINDOW (parent); } else /* Pacify GCC. */ @@ -9345,6 +9536,8 @@ name to `'ignore'. */); defsubr (&Sselect_window); defsubr (&Sforce_window_update); defsubr (&Ssplit_window_internal); + defsubr (&Scombine_windows); + defsubr (&Suncombine_window); defsubr (&Sscroll_up); defsubr (&Sscroll_down); defsubr (&Sscroll_left); commit 81bc83c1a57999a1ff0584c605e7e13b4a9f7cbb Author: john muhl Date: Tue Oct 7 14:18:03 2025 -0500 New 'lua-mode' inferior process command & options * lisp/progmodes/lua-mode.el (lua-process-buffer-name) (lua-process-history-file, lua-process-startfile): New options. (lua-start-process): Use new options. (lua-send-file): New command. (lua--repl-buffer-p): Remove unused internal variable. (Bug#79573) diff --git a/lisp/progmodes/lua-mode.el b/lisp/progmodes/lua-mode.el index d65154a38cd..01d91637cf0 100644 --- a/lisp/progmodes/lua-mode.el +++ b/lisp/progmodes/lua-mode.el @@ -12,7 +12,7 @@ ;; with tons of assistance from ;; Paul Du Bois and ;; Aaron Smith . -;; +;; Maintainer: emacs-devel@gnu.org ;; Keywords: languages, processes, tools ;; This file is part of GNU Emacs. @@ -274,9 +274,6 @@ If the latter is nil, the keymap translates into `lua-mode-map' verbatim." :type 'regexp :version "31.1") -(defvar-local lua--repl-buffer-p nil - "Buffer-local flag saying if this is a Lua REPL buffer.") - (defcustom lua-indent-string-contents nil "If non-nil, contents of multiline string will be indented. Otherwise leading amount of whitespace on each line is preserved." @@ -1824,6 +1821,25 @@ This function just searches for a `end' at the beginning of a line." str t t) "'")) +(defcustom lua-process-buffer-name nil + "Name of the inferior Lua buffer. +The value nil means use the same name as `lua-default-application'." + :type '(choice (const :tag "Default" nil) string) + :safe 'string-or-null-p + :version "31.1") + +(defcustom lua-process-history-file nil + "File used to save command history of the inferior Lua process. +The value nil means that no command history is saved." + :type '(choice (const :tag "None" nil) file) + :safe 'string-or-null-p + :version "31.1") + +(defcustom lua-process-startfile nil + "Start file to load into the inferior Lua process at startup." + :type '(choice (const :tag "None" nil) (file :must-match t)) + :version "31.1") + ;;;###autoload (defalias 'run-lua #'lua-start-process) @@ -1838,31 +1854,36 @@ as its initial input. SWITCHES is a list of strings passed as arguments to PROGRAM." (interactive) - (setq name (or name (if (consp lua-default-application) - (car lua-default-application) - lua-default-application))) - (setq program (or program lua-default-application)) - ;; Don't re-initialize if there already is a lua process - (unless (comint-check-proc (format "*%s*" name)) - (setq lua-process-buffer (apply #'make-comint name program startfile - (or switches lua-default-command-switches))) - (setq lua-process (get-buffer-process lua-process-buffer)) - (set-process-query-on-exit-flag lua-process nil) - (with-current-buffer lua-process-buffer - (setq lua--repl-buffer-p t) - (compilation-shell-minor-mode 1) - (setq-local comint-prompt-regexp lua-prompt-regexp) - - ;; Don't send initialization code until seeing the prompt to - ;; ensure that the interpreter is ready. - (while (not (lua-prompt-line)) - (accept-process-output (get-buffer-process (current-buffer))) - (goto-char (point-max))) - (lua-send-string lua-process-init-code))) - - ;; When called interactively, switch to process buffer - (when (called-interactively-p 'any) - (switch-to-buffer lua-process-buffer))) + (if (not lua-default-application) + (user-error "You must set `lua-default-application' to use this command") + (let* ((name (or name lua-process-buffer-name + (if (consp lua-default-application) + (car lua-default-application) + lua-default-application))) + (program (or program lua-default-application))) + ;; Don't re-initialize if there already is a Lua process. + (unless (comint-check-proc (format "*%s*" name)) + (setq lua-process-buffer + (apply #'make-comint name program + (or startfile lua-process-startfile) + (or switches lua-default-command-switches))) + (setq lua-process (get-buffer-process lua-process-buffer)) + (set-process-query-on-exit-flag lua-process nil) + (with-current-buffer lua-process-buffer + (setq-local comint-prompt-regexp lua-prompt-regexp) + (setq-local comint-input-ring-file-name lua-process-history-file) + (comint-read-input-ring t) + (compilation-shell-minor-mode) + (add-hook 'kill-buffer-hook #'comint-write-input-ring nil t) + ;; Don't send initialization code until seeing the prompt to + ;; ensure that the interpreter is ready. + (while (not (lua-prompt-line)) + (accept-process-output (get-buffer-process (current-buffer))) + (goto-char (point-max))) + (lua-send-string lua-process-init-code))) + ;; When called interactively, switch to process buffer + (when (called-interactively-p 'any) + (switch-to-buffer lua-process-buffer))))) (defun lua-get-create-process () "Return active Lua process creating one if necessary." @@ -1984,6 +2005,13 @@ return START." (lua-kill-process) (lua-send-buffer)) +(defun lua-send-file (file) + "Send contents of FILE to Lua process." + (interactive "f") + (with-temp-buffer + (insert-file-contents-literally file) + (lua-send-buffer))) + (defun lua-show-process-buffer () "Make sure `lua-process-buffer' is being displayed. Create a Lua process if one doesn't already exist." commit 53aac6aecd24ad95a8224f05784687dd1de13ccb Author: Eli Zaretskii Date: Sat Oct 18 10:47:29 2025 +0300 ; Improve documentation of commands that split current window * doc/emacs/sending.texi (Sending Mail): * doc/emacs/dired.texi (Dired Enter, Dired Visiting): * doc/emacs/maintaining.texi (Old Revisions) (Change Log Commands, Looking Up Identifiers): * doc/emacs/windows.texi (Displaying Buffers): * doc/emacs/files.texi (Visiting): Add cross-references to where user options are described which control how windows are split. * lisp/mail/sendmail.el (mail-other-window): * lisp/gnus/message.el (message-mail-other-window) (message-news-other-window): * lisp/replace.el (occur-mode-goto-occurrence-other-window): * lisp/vc/vc.el (vc-revision-other-window): * lisp/vc/vc-dir.el (vc-dir-find-file-other-window): * lisp/progmodes/xref.el (xref-find-definitions-other-window): * lisp/simple.el (compose-mail-other-window) (clone-indirect-buffer-other-window): * lisp/vc/add-log.el (add-change-log-entry-other-window): * lisp/view.el (view-file-other-window) (view-buffer-other-window): * lisp/window.el (switch-to-buffer-other-window): * lisp/files.el (find-file-other-window) (find-file-read-only-other-window) (find-alternate-file-other-window): * lisp/dired.el (dired-other-window) (dired-mouse-find-file-other-window) (dired-find-file-other-window, dired-jump-other-window): Mention in the doc strings how to control the way the current window is split. diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index 53cbcd65c10..24a9b5775ea 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -124,7 +124,7 @@ buffer, Emacs displays the directory listing of the parent directory and places point on the line that corresponds to the directory where you invoked @code{dired-jump}. Typing @kbd{C-x 4 C-j} (@code{dired-jump-other-window}) has the same effect, but displays the -Dired buffer in a new window. +Dired buffer in a new window (@pxref{Window Choice}). The variable @code{dired-listing-switches} specifies the options to give to @command{ls} for listing the directory; this string @@ -182,7 +182,7 @@ peculiarities of this emulation. To display the Dired buffer in another window, use @kbd{C-x 4 d} (@code{dired-other-window}). @kbd{C-x 5 d} (@code{dired-other-frame}) displays the Dired buffer in a separate -frame. +frame. @xref{Window Choice}. @kindex q @r{(Dired)} @findex quit-window@r{, in Dired buffers} @@ -435,7 +435,8 @@ that of an alternate file or directory (@code{dired-find-alternate-file}). @item o @kindex o @r{(Dired)} @findex dired-find-file-other-window -Like @kbd{f}, but uses another window to display the file's buffer +Like @kbd{f}, but uses another window (@pxref{Window Choice}) to display +the file's buffer (@code{dired-find-file-other-window}). The Dired buffer remains visible in the first window. This is like using @kbd{C-x 4 C-f} to visit the file. @xref{Windows}. diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 7c798331711..cb2903166a2 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi @@ -287,9 +287,10 @@ yourself from entering changes accidentally, visit it with the command except that the buffer containing the specified file is selected in another window. The window that was selected before @kbd{C-x 4 f} continues to show the same buffer it was already showing. If this command is used when -only one window is being displayed, that window is split in two, with one -window showing the same buffer as before, and the other one showing the -newly requested file. @xref{Windows}. +only one window is being displayed, that window is split in two +(@pxref{Window Choice}), with one window showing the same buffer as +before, and the other one showing the newly requested file. +@xref{Windows}. @kindex C-x 5 f @findex find-file-other-frame diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index c6eeab770fe..e5e1ba9976d 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -944,7 +944,7 @@ and type @kbd{C-x v ~ @var{revision} @key{RET}} (@code{vc-revision-other-window}). This retrieves the file version corresponding to @var{revision}, saves it to @file{@var{filename}.~@var{revision}~}, and visits it in a separate -window. +window (@pxref{Window Choice}). @findex vc-annotate @vindex vc-annotate-background-mode @@ -2073,7 +2073,8 @@ a backup file, it makes an entry appropriate for the file's parent---that is useful for making log entries for functions that have been deleted in the current version. - @kbd{C-x 4 a} visits the change log file and creates a new entry + @kbd{C-x 4 a} visits the change log file in a window other than the +selected one (@pxref{Window Choice}) and creates a new entry unless the most recent entry is for today's date and your name. It also creates a new item for the current file. For many languages, it can even guess the name of the function or other object that was @@ -2341,8 +2342,8 @@ known identifier names being the completion candidates. @findex xref-find-definitions-other-frame Like most commands that can switch buffers, @code{xref-find-definitions} has a variant that displays the new -buffer in another window, and one that makes a new frame for it. The -former is @w{@kbd{C-x 4 .}} +buffer in another window (@pxref{Window Choice}), and one that makes a +new frame for it. The former is @w{@kbd{C-x 4 .}} (@code{xref-find-definitions-other-window}), and the latter is @w{@kbd{C-x 5 .}} (@code{xref-find-definitions-other-frame}). diff --git a/doc/emacs/sending.texi b/doc/emacs/sending.texi index 5d610148a4f..0c45330eb24 100644 --- a/doc/emacs/sending.texi +++ b/doc/emacs/sending.texi @@ -48,7 +48,8 @@ you pick up editing the message where you left off. The command @kbd{C-x 4 m} (@code{compose-mail-other-window}) does the same as @kbd{C-x m}, except it displays the mail buffer in a different window. The command @kbd{C-x 5 m} -(@code{compose-mail-other-frame}) does it in a new frame. +(@code{compose-mail-other-frame}) does it in a new frame. @xref{Window +Choice}. When you type @kbd{C-c C-c} or @kbd{C-c C-s} to send the mail, Emacs may ask you how it should deliver the mail---either directly via SMTP, diff --git a/doc/emacs/windows.texi b/doc/emacs/windows.texi index d0136db7b8a..6753bfb8d76 100644 --- a/doc/emacs/windows.texi +++ b/doc/emacs/windows.texi @@ -410,8 +410,9 @@ Reference Manual}. Commands with names ending in @code{-other-window} behave like @code{display-buffer}, except that they never display in the selected -window. Several of these commands are bound in the @kbd{C-x 4} prefix -key (@pxref{Pop Up Window}). +window, perhaps splitting the selected window to create a new one +(@pxref{Window Choice}). Several of these commands are bound in the +@kbd{C-x 4} prefix key (@pxref{Pop Up Window}). Commands with names ending in @code{-other-frame} behave like @code{display-buffer}, except that they (i) never display in the diff --git a/lisp/dired.el b/lisp/dired.el index cca6fb2e6ea..369c70b3d15 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -1203,7 +1203,10 @@ If DIRNAME is already in a Dired buffer, that buffer is used without refresh." ;;;###autoload (define-key ctl-x-4-map "d" 'dired-other-window) ;;;###autoload (defun dired-other-window (dirname &optional switches) - "\"Edit\" directory DIRNAME. Like `dired' but select in another window." + "\"Edit\" directory DIRNAME. Like `dired' but select in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (dired-read-dir-and-switches "in other window ")) (switch-to-buffer-other-window (dired-noselect dirname switches))) @@ -3077,7 +3080,10 @@ so that the original Dired buffer is not kept." (dired--find-file find-file-func (file-name-sans-versions file t))))) (defun dired-mouse-find-file-other-window (event) - "In Dired, visit the file or directory name you click on in another window." + "In Dired, visit the file or directory name you click on in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive "e" dired-mode) (dired-mouse-find-file event 'find-file-other-window 'dired-other-window)) @@ -3099,7 +3105,10 @@ Otherwise, display it in another buffer." (view-file file)))) (defun dired-find-file-other-window () - "In Dired, visit this file or directory in another window." + "In Dired, visit this file or directory in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive nil dired-mode) (dired--find-file #'find-file-other-window (dired-get-file-for-visit))) @@ -5215,7 +5224,10 @@ Interactively with prefix argument, read FILE-NAME." ;;;###autoload (defun dired-jump-other-window (&optional file-name) - "Like \\[dired-jump] (`dired-jump') but in other window." + "Like \\[dired-jump] (`dired-jump') but in other window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (list (and current-prefix-arg (read-file-name "Jump to Dired file: ")))) diff --git a/lisp/files.el b/lisp/files.el index 6544ee54ee4..c6211d84700 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1988,7 +1988,11 @@ current directory to be available on first \\[next-history-element] request. Interactively, or if WILDCARDS is non-nil in a call from Lisp, -expand wildcards (if any) and visit multiple files." +expand wildcards (if any) and visit multiple files. + +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (find-file-read-args "Find file in other window: " (confirm-nonexistent-file-or-buffer))) @@ -2068,7 +2072,10 @@ Use \\[read-only-mode] to permit editing." (defun find-file-read-only-other-window (filename &optional wildcards) "Edit file FILENAME in another window but don't allow changes. Like \\[find-file-other-window], but marks buffer as read-only. -Use \\[read-only-mode] to permit editing." +Use \\[read-only-mode] to permit editing. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (find-file-read-args "Find file read-only other window: " (confirm-nonexistent-file-or-buffer))) @@ -2090,7 +2097,11 @@ This command does not select that window. See \\[find-file] for the possible forms of the FILENAME argument. Interactively, or if WILDCARDS is non-nil in a call from Lisp, -expand wildcards (if any) and replace the file with multiple files." +expand wildcards (if any) and replace the file with multiple files. + +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (save-selected-window (other-window 1) diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el index d97d2bbc21c..099a538daa0 100644 --- a/lisp/gnus/message.el +++ b/lisp/gnus/message.el @@ -8116,7 +8116,10 @@ you." ;;;###autoload (defun message-mail-other-window (&optional to subject) - "Like `message-mail' command, but display mail buffer in another window." + "Like `message-mail' command, but display mail buffer in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive) (unless (message-mail-user-agent) (message-pop-to-buffer (message-buffer-name "mail" to) @@ -8138,7 +8141,10 @@ you." ;;;###autoload (defun message-news-other-window (&optional newsgroups subject) - "Start editing a news article to be sent." + "Start editing a news article to be sent. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive) (message-pop-to-buffer (message-buffer-name "posting" nil newsgroups) 'switch-to-buffer-other-window) diff --git a/lisp/mail/sendmail.el b/lisp/mail/sendmail.el index 44e960969d8..3ef22d2fd65 100644 --- a/lisp/mail/sendmail.el +++ b/lisp/mail/sendmail.el @@ -2047,7 +2047,10 @@ you can move to one of them and type C-c C-c to recover that one." ;;;###autoload (defun mail-other-window (&optional noerase to subject in-reply-to cc replybuffer sendactions) - "Like `mail' command, but display mail buffer in another window." + "Like `mail' command, but display mail buffer in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive "P") (switch-to-buffer-other-window "*mail*") (mail noerase to subject in-reply-to cc replybuffer sendactions)) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 4c27aff06a4..fa1ba8846fa 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -1645,7 +1645,10 @@ Use \\[xref-go-back] to return back to where you invoked this command." ;;;###autoload (defun xref-find-definitions-other-window (identifier) - "Like `xref-find-definitions' but switch to the other window." + "Like `xref-find-definitions' but switch to the other window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (list (xref--read-identifier "Find definitions of: "))) (xref--find-definitions identifier 'window)) diff --git a/lisp/replace.el b/lisp/replace.el index ab3db69bd20..ff22641eb48 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1522,7 +1522,10 @@ If not invoked by a mouse click, go to occurrence on the current line." (run-hooks 'occur-mode-find-occurrence-hook))) (defun occur-mode-goto-occurrence-other-window () - "Go to the occurrence the current line describes, in another window." + "Go to the occurrence the current line describes, in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive) (let ((buffer (current-buffer)) (pos (occur--targets-start (occur-mode--find-occurrences)))) diff --git a/lisp/simple.el b/lisp/simple.el index e1f6ae8b683..ae510722096 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -9768,7 +9768,10 @@ To disable this warning, set `compose-mail-user-agent-warnings' to nil." (defun compose-mail-other-window (&optional to subject other-headers continue yank-action send-actions return-action) - "Like \\[compose-mail], but edit the outgoing message in another window." + "Like \\[compose-mail], but edit the outgoing message in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (list nil nil nil current-prefix-arg)) (compose-mail to subject other-headers continue 'switch-to-buffer-other-window yank-action send-actions @@ -10707,7 +10710,10 @@ Returns the newly created indirect buffer." (defun clone-indirect-buffer-other-window (newname display-flag &optional norecord) - "Like `clone-indirect-buffer' but display in another window." + "Like `clone-indirect-buffer' but display in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (progn (if (get major-mode 'no-clone-indirect) diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el index 2208f50812e..408d6ba1c98 100644 --- a/lisp/vc/add-log.el +++ b/lisp/vc/add-log.el @@ -1020,7 +1020,10 @@ non-nil, otherwise in local time." (defun add-change-log-entry-other-window (&optional whoami file-name) "Find change log file in other window and add entry and item. This is just like `add-change-log-entry' except that it displays -the change log file in another window." +the change log file in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (if current-prefix-arg (list current-prefix-arg (prompt-for-change-log-name)))) diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index 54914bfd663..fde11926312 100644 --- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el @@ -912,7 +912,10 @@ system." (find-file (vc-dir-current-file))) (defun vc-dir-find-file-other-window (&optional event) - "Find the file on the current line, in another window." + "Find the file on the current line, in another window. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (list last-nonmenu-event)) (if event (posn-set-point (event-end event))) (find-file-other-window (vc-dir-current-file))) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 1ca895cbecc..dba415c03af 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2298,7 +2298,11 @@ Return nil if the root directory cannot be identified." (defun vc-revision-other-window (rev) "Visit revision REV of the current file in another window. If the current file is named `F', the revision is named `F.~REV~'. -If `F.~REV~' already exists, use it instead of checking it out again." +If `F.~REV~' already exists, use it instead of checking it out again. + +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (with-current-buffer (or (buffer-base-buffer) (current-buffer)) (vc-ensure-vc-buffer) diff --git a/lisp/view.el b/lisp/view.el index dc157d8996a..8a86fcc0070 100644 --- a/lisp/view.el +++ b/lisp/view.el @@ -226,7 +226,11 @@ are defined for moving around in the buffer. Space scrolls forward, Delete scrolls backward. For a list of all View commands, type H or h while viewing. -This command runs the normal hook `view-mode-hook'." +This command runs the normal hook `view-mode-hook'. + +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive "fIn other window view file: ") (unless (file-exists-p file) (error "%s does not exist" file)) (let ((had-a-buf (get-file-buffer file)) @@ -306,7 +310,11 @@ this argument instead of explicitly setting `view-exit-action'. This function does not enable View mode if the buffer's major mode has a `special' mode-class, because such modes usually have their -own View-like bindings." +own View-like bindings. + +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive "bIn other window view buffer:\nP") (let ((pop-up-windows t)) (pop-to-buffer buffer t)) diff --git a/lisp/window.el b/lisp/window.el index 5debf6ef119..0145cf98eca 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -9221,7 +9221,10 @@ Optional second argument NORECORD non-nil means do not put this buffer at the front of the list of recently selected ones. This uses the function `display-buffer' as a subroutine; see its -documentation for additional customization information." +documentation for additional customization information. +If this command needs to split the current window, it by default obeys +the user options `split-height-threshold' and `split-width-threshold', +when it decides whether to split the window horizontally or vertically." (interactive (list (read-buffer-to-switch "Switch to buffer in other window: "))) (let ((pop-up-windows t))