commit 1568123196cd8b57ed64e284b7deb058026be713 (HEAD, refs/remotes/origin/master) Author: Stefan Kangas Date: Wed Dec 14 06:38:14 2022 +0100 ; * lisp/keymap.el (keymap-substitute): Doc fix. (Bug#60059) diff --git a/lisp/keymap.el b/lisp/keymap.el index eaeba966444..b355f68aa2f 100644 --- a/lisp/keymap.el +++ b/lisp/keymap.el @@ -147,7 +147,7 @@ keymap-substitute If you don't specify OLDMAP, you can usually get the same results in a cleaner way with command remapping, like this: (define-key KEYMAP [remap OLDDEF] NEWDEF) -\n(fn OLDDEF NEWDEF KEYMAP &optional OLDMAP)" +\n(fn KEYMAP OLDDEF NEWDEF &optional OLDMAP)" ;; Don't document PREFIX in the doc string because we don't want to ;; advertise it. It's meant for recursive calls only. Here's its ;; meaning commit f1840cf12fda7f3d4e9c31e5d92354bf3f94acf3 Author: Stefan Kangas Date: Wed Dec 14 00:15:03 2022 +0100 ; * etc/NEWS: Fix another merge error. diff --git a/etc/NEWS b/etc/NEWS index 8f6c67a3cb1..9c60e444c08 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -86,11 +86,6 @@ point is not in a comment or a string. It is by default bound to * New Modes and Packages in Emacs 30.1 -** New major mode 'dockerfile-ts-mode'. -A major mode based on the tree-sitter library for editing -Dockerfiles. It includes support for font-locking, indentation, Imenu, -and which-func. - * Incompatible Lisp Changes in Emacs 30.1 commit f8bc26a3db7b965ab5993c20489586c16dd7992c Author: Stefan Kangas Date: Wed Dec 14 00:12:52 2022 +0100 ; Fix merge error diff --git a/etc/NEWS b/etc/NEWS index 457a3516349..8f6c67a3cb1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -91,11 +91,6 @@ A major mode based on the tree-sitter library for editing Dockerfiles. It includes support for font-locking, indentation, Imenu, and which-func. -** New major mode 'cmake-ts-mode'. -A major mode based on the tree-sitter library for editing CMake files. -It includes support for font-locking, indentation, Imenu, and -which-func. - * Incompatible Lisp Changes in Emacs 30.1 diff --git a/etc/NEWS.29 b/etc/NEWS.29 index e4a19e29869..6cb531cd0f1 100644 --- a/etc/NEWS.29 +++ b/etc/NEWS.29 @@ -3068,6 +3068,16 @@ A major mode based on the tree-sitter library for editing Bash shell scripts. It includes support for font-locking, indentation, Imenu, which-func, and navigation. +** New major mode 'dockerfile-ts-mode'. +A major mode based on the tree-sitter library for editing +Dockerfiles. It includes support for font-locking, indentation, Imenu, +and which-func. + +** New major mode 'cmake-ts-mode'. +A major mode based on the tree-sitter library for editing CMake files. +It includes support for font-locking, indentation, Imenu, and +which-func. + * Incompatible Lisp Changes in Emacs 29.1 commit 8036739c1bb6fab46b7728b3acac7511e066d64b Merge: 19d608da2ab cd5856e4038 Author: Stefan Kangas Date: Wed Dec 14 00:06:29 2022 +0100 Merge from origin/emacs-29 cd5856e4038 Fix bug when calling `rgrep` non-interactively ba4bdd6a259 Adapt Tramp specific tests in eglot-tests.el 1d5c35c8e46 * lisp/textmodes/texinfo.el (texinfo-flymake): Improve do... a99d0e7e6c9 Support a function in the BUFFER-LIST arg of list-buffers... def51dd6458 ; Fix typos 4980ed7a6d9 Don't allow lazy highlight from recursive minibuffers 4ef8b9f5441 Improve resetting face attributes when looking for suitab... c4b8bc90a8e ; Fix typos in doc strings c45eb138451 ; * lisp/bs.el (bs-attributes-list): Doc fix d6adaf487d8 Add lexical-binding to example package header 03ad1a92a2d Add improved tree-sitter navigation a5272e2a7cc ; * test/src/treesit-tests.el: Add outline headers. 489bcacc7c3 Add cross-reference to flush-lines 0f9e6532b14 Use font-lock-number-face for numeric values in csharp-mode 4bccb7b211e Make treesit-query-validate create a read-only buffer c0fe6c72cec Improve dockerfile-ts-mode imenu generation (Bug#59979) 631908f7017 Add "->" to python--treesit-operators (bug#59968) 5d4274d9b65 ; * admin/notes/tree-sitter/build-module/build.sh: Add -f... d264b75669d Align C++ access specifiers to their enclosing class/stru... ca67d988d87 Add cmake-ts-mode 8ec923775de Tweak various ts-mode's indent and fontification (bug#59931) 647b6a8099f Add expression for generic_name in csharp-ts-mode (bug#59... 5b178efd85a ; Adjust eglot test to recent autopep8/pycodestyle 58b8ed8b55c ; Avoid compilation warning on MS-Windows 40c23c11e88 * lisp/outline.el: Fix the value 'insert' of outline-mino... 527eb11de20 * lisp/minibuffer.el (completions-group-separator): Rever... 42d740fb2cb ; Skip two eglot tests when typescript is missing 19ef86f775a ; Remove outdated text describing overlays 081bf583007 Skip Eglot rust-analyzer tests if 'cargo' isn't available # Conflicts: # lisp/progmodes/typescript-ts-mode.el # lisp/treesit.el commit cd5856e40386779675fc9ada36eefd8bd80a82b5 Author: Stefan Kangas Date: Tue Dec 13 22:01:45 2022 +0100 Fix bug when calling `rgrep` non-interactively * lisp/progmodes/grep.el (rgrep): Fix bug where running non-interactively requires running it interactively first. (Bug#60024) diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el index 2446e86abbe..35cac43bee1 100644 --- a/lisp/progmodes/grep.el +++ b/lisp/progmodes/grep.el @@ -1254,6 +1254,10 @@ rgrep nil default-directory t)) (confirm (equal current-prefix-arg '(4)))) (list regexp files dir confirm)))))) + ;; If called non-interactively, also compute the defaults if we + ;; haven't already. + (unless grep-find-template + (grep-compute-defaults)) (when (and (stringp regexp) (> (length regexp) 0)) (unless (and dir (file-accessible-directory-p dir)) (setq dir default-directory)) commit ba4bdd6a25928320ae3fe301ef16a011352bce75 Author: Michael Albinus Date: Tue Dec 13 20:06:48 2022 +0100 Adapt Tramp specific tests in eglot-tests.el * test/lisp/progmodes/eglot-tests.el (tramp): Move up. (tramp-sh): Don't require. (eglot--cleanup-after-test): Delete Tramp buffers if needed. (eglot-tests-lsp-abiding-column): Rename from `eglot-lsp-abiding-column'. (tramp-histfile-override): Declare. (eglot--call-with-tramp-test): Use `ert-remote-temporary-file-directory'. Skip if needed. (eglot--tramp-test, eglot--tramp-test-2): Don't skip. diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 47d2e2ee6a6..f6b53fd0c6f 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -31,18 +31,29 @@ ;; Some of these tests rely on the GNU ELPA package company.el and ;; yasnippet.el being available. +;; Some of the tests require access to a remote host files. Since +;; this could be problematic, a mock-up connection method "mock" is +;; used. Emulating a remote connection, it simply calls "sh -i". +;; Tramp's file name handlers still run, so this test is sufficient +;; except for connection establishing. + +;; If you want to test a real Tramp connection, set +;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to +;; overwrite the default value. If you want to skip tests accessing a +;; remote host, set this environment variable to "/dev/null" or +;; whatever is appropriate on your system. + ;;; Code: (require 'eglot) (require 'cl-lib) (require 'ert) +(require 'tramp) ; must be prior ert-x (require 'ert-x) ; ert-simulate-command (require 'edebug) (require 'python) ; some tests use pylsp (require 'cc-mode) ; c-mode-hook (require 'company nil t) (require 'yasnippet nil t) -(require 'tramp) -(require 'tramp-sh) (require 'subr-x) (require 'flymake) ; project-diagnostics @@ -159,7 +170,11 @@ eglot--cleanup-after-test do (set sym val)) (dolist (buf buffers-to-delete) ;; have to save otherwise will get prompted (with-current-buffer buf (save-buffer) (kill-buffer))) - (delete-directory fixture-directory 'recursive))) + (delete-directory fixture-directory 'recursive) + ;; Delete Tramp buffers if needed. + (when (file-remote-p temporary-file-directory) + (tramp-cleanup-connection + (tramp-dissect-file-name temporary-file-directory) nil 'keep-password)))) (cl-defmacro eglot--with-timeout (timeout &body body) (declare (indent 1) (debug t)) @@ -855,7 +870,7 @@ eglot-tests--lsp-abiding-column-1 (funcall eglot-move-to-column-function 71) (should (looking-at "p"))))))) -(ert-deftest eglot-lsp-abiding-column () +(ert-deftest eglot-tests-lsp-abiding-column () "Test basic `eglot-lsp-abiding-column' and `eglot-move-to-lsp-abiding-column'." (skip-unless (executable-find "clangd")) (eglot-tests--lsp-abiding-column-1)) @@ -1241,37 +1256,25 @@ eglot--glob-test ;; (should (eglot--glob-match "prefix/{**/*.d.ts,**/*.js,foo.[0-9]}" "prefix/foo.8")) ) +(defvar tramp-histfile-override) (defun eglot--call-with-tramp-test (fn) - ;; Set up a loopback TRAMP method that’s just a shell so the remote - ;; host is really just the local host. + ;; Set up a Tramp method that’s just a shell so the remote host is + ;; really just the local host. (let ((tramp-remote-path (cons 'tramp-own-remote-path tramp-remote-path)) (tramp-histfile-override t) - (tramp-methods '(("loopback" - (tramp-login-program "/bin/sh") - (tramp-remote-shell "/bin/sh") - (tramp-remote-shell-login ("-l")) - (tramp-remote-shell-args ("-c"))))) - (temporary-file-directory (concat "/loopback::" - temporary-file-directory))) - ;; With ‘temporary-file-directory’ bound to the ‘loopback’ TRAMP - ;; method, fixtures will be automatically made “remote". - (unwind-protect - (funcall fn) - ;; Tramp leave some buffers behind, and some time later, - ;; `project-buffers' will trip over them causing a hard to debug - ;; intermittent test failure somewhere else. - (dolist (buf (buffer-list)) - (when (string-match-p "^\\*tramp" (buffer-name buf)) - (kill-buffer buf)))))) + (temporary-file-directory ert-remote-temporary-file-directory)) + ;; We must check the remote LSP server. So far, just "clangd" is used. + (let ((default-directory temporary-file-directory)) + (unless (executable-find "clangd" 'remote) + (ert-skip "Remote clangd not found"))) + (funcall fn))) (ert-deftest eglot--tramp-test () "Ensure LSP servers can be used over TRAMP." - (skip-unless (executable-find "clangd")) (eglot--call-with-tramp-test #'eglot-tests--auto-detect-running-server-1)) (ert-deftest eglot--tramp-test-2 () "Ensure LSP servers can be used over TRAMP." - (skip-unless (executable-find "clangd")) (eglot--call-with-tramp-test #'eglot-tests--lsp-abiding-column-1)) (ert-deftest eglot--path-to-uri-windows () commit 1d5c35c8e46872a6c9f46bc78c9fb0dc400814de Author: Stefan Kangas Date: Tue Dec 13 18:57:25 2022 +0100 * lisp/textmodes/texinfo.el (texinfo-flymake): Improve docstring. diff --git a/lisp/textmodes/texinfo.el b/lisp/textmodes/texinfo.el index 98672f42b3f..24e93bf001d 100644 --- a/lisp/textmodes/texinfo.el +++ b/lisp/textmodes/texinfo.el @@ -347,6 +347,8 @@ texinfo--flymake-proc (defun texinfo-flymake (report-fn &rest _) "Texinfo checking for Flymake. +It uses either \"makeinfo\" or \"texi2any\", in that order. + REPORT-FN is the callback function." (let ((executable (or (executable-find "makeinfo") (executable-find "texi2any"))) commit a99d0e7e6c903e0e1cb32a458802c1d8d7443cd9 Author: Juri Linkov Date: Tue Dec 13 19:47:47 2022 +0200 Support a function in the BUFFER-LIST arg of list-buffers-noselect (bug#59935) * lisp/buff-menu.el (Buffer-menu-buffer-list): New buffer-local variable. (list-buffers-noselect): Set Buffer-menu-buffer-list to 'buffer-list' that now keeps the buffer-local value of the provided buffer list or a function that returns the buffer list. (list-buffers--refresh): Handle buffer-list and Buffer-menu-buffer-list as a function and as a list. * lisp/progmodes/project.el (project-list-buffers): Let-bind 'buffer-list-function' used by both legacy code and the new version of list-buffers-noselect that supports its arg BUFFER-LIST as a function. diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el index 588fe599a46..448a5395c04 100644 --- a/lisp/buff-menu.el +++ b/lisp/buff-menu.el @@ -107,6 +107,9 @@ Buffer-menu-filter-predicate called with the buffer. If this function returns non-nil, then the buffer will be displayed in the buffer list.") +(defvar-local Buffer-menu-buffer-list nil + "The current list of buffers or function to return buffers.") + (defvar-keymap Buffer-menu-mode-map :doc "Local keymap for `Buffer-menu-mode' buffers." :parent tabulated-list-mode-map @@ -628,8 +631,10 @@ list-buffers-noselect This is called by `buffer-menu' and others as a subroutine. If FILES-ONLY is non-nil, show only file-visiting buffers. -If BUFFER-LIST is non-nil, it should be a list of buffers; it -means list those buffers and no others. +If BUFFER-LIST is non-nil, it should be either a list of buffers +or a function that returns a list of buffers; it means +list those buffers and no others. +See more at `Buffer-menu-buffer-list'. If FILTER-PREDICATE is non-nil, it should be a function that filters out buffers from the list of buffers. See more at `Buffer-menu-filter-predicate'." @@ -639,6 +644,7 @@ list-buffers-noselect (Buffer-menu-mode) (setq Buffer-menu-files-only (and files-only (>= (prefix-numeric-value files-only) 0))) + (setq Buffer-menu-buffer-list buffer-list) (setq Buffer-menu-filter-predicate filter-predicate) (list-buffers--refresh buffer-list old-buffer) (tabulated-list-print)) @@ -665,9 +671,16 @@ list-buffers--refresh Buffer-menu-filter-predicate)) entries name-width) ;; Collect info for each buffer we're interested in. - (dolist (buffer (or buffer-list - (buffer-list (if Buffer-menu-use-frame-buffer-list - (selected-frame))))) + (dolist (buffer (cond + ((functionp buffer-list) + (funcall buffer-list)) + (buffer-list) + ((functionp Buffer-menu-buffer-list) + (funcall Buffer-menu-buffer-list)) + (Buffer-menu-buffer-list) + (t (buffer-list + (if Buffer-menu-use-frame-buffer-list + (selected-frame)))))) (with-current-buffer buffer (let* ((name (buffer-name)) (file buffer-file-name)) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 7cdaba9c07d..4fd855255b0 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1340,18 +1340,33 @@ project-list-buffers start with a space (which are for internal use). With prefix argument ARG, show only buffers that are visiting files." (interactive "P") - (let ((pr (project-current t))) + (let* ((pr (project-current t)) + (buffer-list-function + (lambda () + (seq-filter + (lambda (buffer) + (let ((name (buffer-name buffer)) + (file (buffer-file-name buffer))) + (and (or (not (string= (substring name 0 1) " ")) + file) + (not (eq buffer (current-buffer))) + (or file (not Buffer-menu-files-only))))) + (project-buffers pr))))) (display-buffer (if (version< emacs-version "29.0.50") - (let ((buf (list-buffers-noselect arg (project-buffers pr)))) + (let ((buf (list-buffers-noselect + arg (with-current-buffer + (get-buffer-create "*Buffer List*") + (let ((Buffer-menu-files-only arg)) + (funcall buffer-list-function)))))) (with-current-buffer buf (setq-local revert-buffer-function (lambda (&rest _ignored) - (list-buffers--refresh (project-buffers pr)) + (list-buffers--refresh + (funcall buffer-list-function)) (tabulated-list-print t)))) buf) - (list-buffers-noselect - arg nil (lambda (buf) (memq buf (project-buffers pr)))))))) + (list-buffers-noselect arg buffer-list-function))))) (defcustom project-kill-buffer-conditions '(buffer-file-name ; All file-visiting buffers are included. commit def51dd6458d2f276018aaac36283bbe44428de7 Author: Stefan Kangas Date: Mon Dec 12 08:50:21 2022 +0100 ; Fix typos diff --git a/ChangeLog.3 b/ChangeLog.3 index edb738b56c9..b162f96cb11 100644 --- a/ChangeLog.3 +++ b/ChangeLog.3 @@ -1654,7 +1654,7 @@ This reverts commit 78f76fe16e2737b40694f82af28d17a90a21ed7b. The commit made calls to cl-concatenate bug out, since - autoloading defalises doesn't work very well (bug#54901). + autoloading defaliases doesn't work very well (bug#54901). 2022-04-12 Eli Zaretskii diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15 index 8a21c291e72..ac1befac1e6 100644 --- a/lisp/ChangeLog.15 +++ b/lisp/ChangeLog.15 @@ -10320,7 +10320,7 @@ * bs.el, expand.el, ido.el, image-dired.el, lpr.el, pcomplete.el, * skeleton.el, term.el, time.el, wid-edit.el, woman.el, * calc/calc-graph.el, calc/calc-help.el, calc/calc-incom.el, - * calc/calc.el, emacs-lisp/cl-extra.el, emacs-lips/cl-loaddefs.el, + * calc/calc.el, emacs-lisp/cl-extra.el, emacs-lisp/cl-loaddefs.el, * emulation/cua-rect.el, emulation/viper-ex.el, eshell/esh-test.el, * eshell/eshell.el, gnus/gnus-uu.el, gnus/nndoc.el, gnus/nnrss.el, * gnus/rfc2047.el, gnus/utf7.el, international/utf-7.el, diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el index 66b214554ee..60b01051a33 100644 --- a/lisp/emacs-lisp/cl-extra.el +++ b/lisp/emacs-lisp/cl-extra.el @@ -552,7 +552,7 @@ cl-subseq ,new))))) (seq-subseq seq start end)) -;;; This isn't a defalias because autoloading defalises doesn't work +;;; This isn't a defalias because autoloading defaliases doesn't work ;;; very well. ;;;###autoload diff --git a/lisp/treesit.el b/lisp/treesit.el index d404bc96327..9b33bc8e765 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1685,7 +1685,7 @@ treesit--defuns-around Return a list of (PREV NEXT PARENT), where PREV and NEXT are previous and next sibling defuns around POS, and PARENT is the -parent defun surrouding POS. All of three could be nil if no +parent defun surrounding POS. All of three could be nil if no sound defun exists. REGEXP and PRED are the same as in `treesit-defun-type-regexp'." @@ -1767,7 +1767,7 @@ treesit--navigate-defun move backward. If SIDE is `beg', stop at the beginning of a defun, if SIDE is `end', stop at the end. -This function doesn't actaully move point, it just returns the +This function doesn't actually move point, it just returns the position it would move to. If there aren't enough defuns to move across, return nil. diff --git a/src/xfaces.c b/src/xfaces.c index 94247f55969..be4a7ca71cc 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -7443,7 +7443,7 @@ syms_of_xfaces (void) have exactly the same values of attributes as the face requests. This variable exists for debugging of the font-selection process, -and we advise not to change it othersie. */); +and we advise not to change it otherwise. */); Vface_font_lax_matched_attributes = Qt; #ifdef HAVE_WINDOW_SYSTEM diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 04ecd7891ab..47d2e2ee6a6 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -329,7 +329,7 @@ eglot-tests--auto-detect-running-server-1 (should-error (eglot--current-server-or-lose)))))) (ert-deftest auto-detect-running-server () - "Visit a file and \\[eglot], then visit a neighbour." + "Visit a file and \\[eglot], then visit a neighbor." (skip-unless (executable-find "clangd")) (eglot-tests--auto-detect-running-server-1)) @@ -704,7 +704,7 @@ python-yapf-formatting (string= (buffer-string) "def a():\n pass\n\n\ndef b():\n pass\n"))))) (ert-deftest rust-on-type-formatting () - "Test textDocument/onTypeFormatting agains rust-analyzer." + "Test textDocument/onTypeFormatting against rust-analyzer." (skip-unless (executable-find "rust-analyzer")) (skip-unless (executable-find "cargo")) (eglot--with-fixture diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index eaf2df62104..3617264502b 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el @@ -611,7 +611,7 @@ treesit-node-check ;; ;; I've setup a framework for easier testing of defun navigation. ;; -;; To use it for a particular langauge, first write a test program +;; To use it for a particular language, first write a test program ;; similar to `treesit--ert-defun-navigation-python-program', and ;; insert markers. Markers that marks BOLs are defined as follows: ;; @@ -661,7 +661,7 @@ treesit--ert-insert-and-parse-marker (nreverse result))) (defun treesit--ert-collect-positions (positions functions) - "Collect posifions after caling each function in FUNCTIONS. + "Collect positions after calling each function in FUNCTIONS. POSITIONS should be a list of buffer positions, FUNCTIONS should be a list of functions. This function collects the return value @@ -710,7 +710,7 @@ treesit--ert-test-defun-navigation ;; Collect positions each function returns. (positions (treesit--ert-collect-positions - ;; The first columnn of DECODED-MASTER. + ;; The first column of DECODED-MASTER. (mapcar #'car decoded-master) ;; Four functions: next-end, prev-beg, next-beg, prev-end. (mapcar (lambda (conf) commit 4980ed7a6d96d5a8803fae6135804cd87faa6d59 Author: Augusto Stoffel Date: Mon Dec 12 23:07:28 2022 +0100 Don't allow lazy highlight from recursive minibuffers See bug#59918. * lisp/isearch.el (minibuffer-lazy-highlight-setup): Don't activate when starting a recursive minibuffer. diff --git a/lisp/isearch.el b/lisp/isearch.el index bc3697deb0a..6a17d18c45e 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -4435,7 +4435,7 @@ minibuffer-lazy-highlight-setup highlighting. LAX-WHITESPACE: The value of `isearch-lax-whitespace' and `isearch-regexp-lax-whitespace' to use for lazy highlighting." - (if (not highlight) + (if (or (not highlight) (minibufferp)) #'ignore (let ((unwind (make-symbol "minibuffer-lazy-highlight--unwind")) (after-change (make-symbol "minibuffer-lazy-highlight--after-change")) commit 4ef8b9f5441cce75c53579317220b763ce83e3b9 Author: Eli Zaretskii Date: Tue Dec 13 17:35:17 2022 +0200 Improve resetting face attributes when looking for suitable fonts * src/xfaces.c (syms_of_xfaces): Change the default value to t. Update doc string. (realize_gui_face): When 'Vface_font_lax_matched_attributes' is t, reset the 3 default font attributes efficiently. Call 'font_maybe_unset_attribute' only if the value is neither nil nor t. (Bug#59347) diff --git a/src/xfaces.c b/src/xfaces.c index 7dbcacb35ac..94247f55969 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -6023,6 +6023,8 @@ font_maybe_unset_attribute (Lisp_Object font_object, { Lisp_Object tail = Vface_font_lax_matched_attributes; + eassert (CONSP (tail)); + FOR_EACH_TAIL_SAFE (tail) { if (EQ (XCAR (tail), symbol)) @@ -6100,20 +6102,31 @@ realize_gui_face (struct face_cache *cache, Lisp_Object attrs[LFACE_VECTOR_SIZE] fonts that are exact matches for these weight, slant, and width attributes, which could lead to suboptimal or wrong font selection. (bug#5934) */ - font_maybe_unset_attribute (spec, FONT_WEIGHT_INDEX, QCweight); - font_maybe_unset_attribute (spec, FONT_SLANT_INDEX, QCslant); - font_maybe_unset_attribute (spec, FONT_WIDTH_INDEX, QCwidth); - /* Also allow unsetting other attributes for debugging - purposes. But not FONT_EXTRA_INDEX; that is not safe to - touch, at least in the Haiku font backend. */ - font_maybe_unset_attribute (spec, FONT_FAMILY_INDEX, QCfamily); - font_maybe_unset_attribute (spec, FONT_FOUNDRY_INDEX, QCfoundry); - font_maybe_unset_attribute (spec, FONT_REGISTRY_INDEX, QCregistry); - font_maybe_unset_attribute (spec, FONT_ADSTYLE_INDEX, QCadstyle); - font_maybe_unset_attribute (spec, FONT_SIZE_INDEX, QCsize); - font_maybe_unset_attribute (spec, FONT_DPI_INDEX, QCdpi); - font_maybe_unset_attribute (spec, FONT_SPACING_INDEX, QCspacing); - font_maybe_unset_attribute (spec, FONT_AVGWIDTH_INDEX, QCavgwidth); + if (EQ (Vface_font_lax_matched_attributes, Qt)) + { + /* The default case: clear the font attributes that + affect its appearance the least, to try to find some + font that is close, if not exact, match. */ + ASET (spec, FONT_WEIGHT_INDEX, Qnil); + ASET (spec, FONT_SLANT_INDEX, Qnil); + ASET (spec, FONT_WIDTH_INDEX, Qnil); + } + else if (!NILP (Vface_font_lax_matched_attributes)) + { + /* Also allow unsetting specific attributes for + debugging purposes. */ + font_maybe_unset_attribute (spec, FONT_WEIGHT_INDEX, QCweight); + font_maybe_unset_attribute (spec, FONT_SLANT_INDEX, QCslant); + font_maybe_unset_attribute (spec, FONT_WIDTH_INDEX, QCwidth); + font_maybe_unset_attribute (spec, FONT_FAMILY_INDEX, QCfamily); + font_maybe_unset_attribute (spec, FONT_FOUNDRY_INDEX, QCfoundry); + font_maybe_unset_attribute (spec, FONT_REGISTRY_INDEX, QCregistry); + font_maybe_unset_attribute (spec, FONT_ADSTYLE_INDEX, QCadstyle); + font_maybe_unset_attribute (spec, FONT_SIZE_INDEX, QCsize); + font_maybe_unset_attribute (spec, FONT_DPI_INDEX, QCdpi); + font_maybe_unset_attribute (spec, FONT_SPACING_INDEX, QCspacing); + font_maybe_unset_attribute (spec, FONT_AVGWIDTH_INDEX, QCavgwidth); + } attrs[LFACE_FONT_INDEX] = font_load_for_lface (f, attrs, spec); } @@ -7406,22 +7419,32 @@ syms_of_xfaces (void) DEFVAR_LISP ("face-font-lax-matched-attributes", Vface_font_lax_matched_attributes, - doc: /* Font-related face attributes to match in lax manner when realizing faces. - -The value should be a list of font-related face attribute symbols; -see `set-face-attribute' for the full list of attributes. The + doc: /* Whether to match some face attributes in lax manner when realizing faces. + +If non-nil, some font-related face attributes will be matched in a lax +manner when looking for candidate fonts. +If the value is t, the default, the search for fonts will not insist +on exact match for 3 font attributes: weight, width, and slant. +Instead, it will examine the available fonts with various values of +these attributes, and select the font that is the closest possible +match. (If an exact match is available, it will still be selected, +as that is the closest match.) For example, looking for a semi-bold +font might select a bold or a medium-weight font if no semi-bold font +matching other attributes can be found. This is especially important +when the `default' face specifies unusual values for one or more of +these 3 attributes, which other installed fonts don't support. + +The value can also be a list of font-related face attribute symbols; +see `set-face-attribute' for the full list of attributes. Then the corresponding face attributes will be treated as "soft" constraints -when looking for suitable fonts: if an exact match is not possible, -a font can be selected that is a close, but not an exact, match. For -example, looking for a semi-bold font might select a bold or a medium -font if no semi-bold font matching other attributes is found. Emacs -still tries to find a font that is the closest possible match; in -particular, if a font is available that matches the face attributes -exactly, it will be selected. - -Note that if the `:extra' attribute is present in the value, it -will be ignored. */); - Vface_font_lax_matched_attributes = list3 (QCweight, QCslant, QCwidth); +in the manner described above, instead of the default 3 attributes. + +If the value is nil, candidate fonts might be rejected if the don't +have exactly the same values of attributes as the face requests. + +This variable exists for debugging of the font-selection process, +and we advise not to change it othersie. */); + Vface_font_lax_matched_attributes = Qt; #ifdef HAVE_WINDOW_SYSTEM defsubr (&Sbitmap_spec_p); commit 19d608da2abd1bb682f98207a655a2b484031a10 Author: Mattias Engdegård Date: Tue Dec 13 16:10:34 2022 +0100 ; * lisp/progmodes/gdb-mi.el (gdb-locals-table-row-config): fix type diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el index eb0e5b0481c..ff14546c63a 100644 --- a/lisp/progmodes/gdb-mi.el +++ b/lisp/progmodes/gdb-mi.el @@ -4368,7 +4368,7 @@ gdb-locals-table-row-config Additionally, the order the element in the alist determines the left-to-right display order of the properties." - :type '(alist :key-type 'symbol :value-type 'integer) + :type '(alist :key-type symbol :value-type integer) :group 'gud :version "30.1") commit e6bad7999a28a545bb833225b148b55c68eb42de Author: Eli Zaretskii Date: Tue Dec 13 16:24:09 2022 +0200 ; Improve recently installed documentation * lisp/progmodes/prog-mode.el (prog-fill-reindent-defun): Fix wording of the doc string. * doc/emacs/programs.texi (Multi-line Indent): Improve wording of the description of 'prog-fill-reindent-defun' and related variables. diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index 3b60732171e..a6688572134 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -437,16 +437,15 @@ Multi-line Indent @vindex beginning-of-defun-function @vindex end-of-defun-function @vindex fill-paragraph-function - Major modes that derive from @code{prog-mode} can either fill a -single paragraph in a defun, such as a doc-string, or a comment, or -(re)indent the surrounding defun if point is not in a comment or a -string by typing @kbd{M-q} or using the command @kbd{M-x -prog-fill-reindent-defun}. The bounds of a defun is decided by the -variable @code{beginning-of-defun-function} and -@code{end-of-defun-function}, and the filling mechanism is decided by -@code{fill-paragraph-function} (@ref{List Motion,,, elisp, The Emacs -Lisp Reference Manual}, or @ref{Filling,,, elisp, The Emacs Lisp -Reference Manual} for more information). + To reindent the entire defun around point, type @kbd{M-q} +(@code{prog-fill-reindent-defun}). If point is in a comment or a +string, this command fills and indents the comment or string instead. +What exactly constitutes a comment, a string, or a defun depends on +the major mode: the bounds of a defun are decided by the variables +@code{beginning-of-defun-function} and @code{end-of-defun-function} +(@pxref{List Motion,,, elisp, The Emacs Lisp Reference Manual}), +and the filling mechanism is decided by @code{fill-paragraph-function} +(@pxref{Filling,,, elisp, The Emacs Lisp Reference Manual}). @kindex C-u TAB If you like the relative indentation within a grouping but not the diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el index 1bd8234dc9c..b46a9affcd3 100644 --- a/lisp/progmodes/prog-mode.el +++ b/lisp/progmodes/prog-mode.el @@ -151,8 +151,8 @@ prog-fill-reindent-defun If the point is in a string or a comment, fill the paragraph that contains point or follows point. -Otherwise, reindent the definition that contains point or follows -point." +Otherwise, reindent the function definition that contains point +or follows point." (interactive "P") (save-excursion (let ((treesit-text-node commit 12e6ff037a10af2736c807bee41497af8da421b3 Author: Eli Zaretskii Date: Tue Dec 13 16:10:23 2022 +0200 ; Fix typos in xterm.c * src/xterm.c (syms_of_xterm) : Fix typos in the name and doc string of the variable. diff --git a/src/xterm.c b/src/xterm.c index cd7c8a35ec8..08dd41c64e1 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -31932,14 +31932,14 @@ syms_of_xterm (void) features that do not work for untrusted clients. */); Vx_detect_server_trust = Qnil; - DEFVAR_LISP ("x-lax-frame-geometry", Vx_lax_frame_positioning, - doc: /* If non-nil nil, Emacs won't compensate for WM geometry behavior. + DEFVAR_LISP ("x-lax-frame-positioning", Vx_lax_frame_positioning, + doc: /* If non-nil, Emacs won't compensate for WM geometry behavior. Setting this to non-nil is useful when the compensation proves to be too slow, which is usually true when the X server is located over a network connection with high latency. Doing so will make frame creation and placement faster at the cost of reducing the accuracy of -frame placement via frame properties, `set-frame-position', and +frame placement via frame parameters, `set-frame-position', and `set-frame-size'. */); Vx_lax_frame_positioning = Qnil; } commit c4b8bc90a8ed350000e2bc50f56513c7bdb539e1 Author: Eli Zaretskii Date: Tue Dec 13 16:01:13 2022 +0200 ; Fix typos in doc strings * lisp/treesit.el (treesit-defun-tactic) (treesit-default-defun-skipper): Fix typos in doc strings. diff --git a/lisp/treesit.el b/lisp/treesit.el index 3d7ae7031ef..d404bc96327 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1579,7 +1579,7 @@ treesit-defun-type-regexp (defvar-local treesit-defun-tactic 'nested "Determines how does Emacs treat nested defuns. -If the value is `top-level', Emacs only move across top-level +If the value is `top-level', Emacs only moves across top-level defuns, if the value is `nested', Emacs recognizes nested defuns.") (defvar-local treesit-defun-skipper #'treesit-default-defun-skipper @@ -1658,7 +1658,7 @@ treesit-end-of-defun (defun treesit-default-defun-skipper () "Skips spaces after navigating a defun. -This fucntion tries to move to the beginning of a line, either by +This function tries to move to the beginning of a line, either by moving to the empty newline after a defun, or to the beginning of the current line if the beginning of the defun is indented." (cond ((and (looking-at (rx (* (or " " "\\t")) "\n")) commit f5948449d6ef189237b1b7e607110be6fdabdd98 Author: Po Lu Date: Tue Dec 13 21:49:41 2022 +0800 ; * src/xterm.c: Improve commentary. Describe error handling. diff --git a/src/xterm.c b/src/xterm.c index e78ec3c32c0..cd7c8a35ec8 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -26,6 +26,22 @@ Copyright (C) 1989, 1993-2022 Free Software Foundation, Inc. contains subroutines comprising the redisplay interface, setting up scroll bars and widgets, and handling input. + X WINDOW SYSTEM + + The X Window System is a windowing system for bitmap graphics + displays which originated at MIT in 1984. Version 11, which is + currently supported by Emacs, first appeared in September 1987. + + X has a long history and has been developed by many different + organizations over the years; at present, it is being primarily + developed by the X.Org Foundation. It is the main window system + that Emacs is developed and tested against, and X version 10 was + the first window system that Emacs was ported to. As a consequence + of its age and wide availability, X contains many idiosyncrasies, + but that has not prevented it from becoming the dominant free + window system, and the platform of reference for all GUI code in + Emacs. + Some of what is explained below also applies to the other window systems that Emacs supports, to varying degrees. YMMV. @@ -555,7 +571,56 @@ Copyright (C) 1989, 1993-2022 Free Software Foundation, Inc. drop happening with the primary selection and synthetic button events (see `x_dnd_do_unsupported_drop'). That function implements the OffiX drag-and-drop protocol by default. See - `x-dnd-handle-unsupported-drop' in `x-dnd.el' for more details. */ + `x-dnd-handle-unsupported-drop' in `x-dnd.el' for more details. + + DISPLAY ERROR HANDLING + + While error handling under X was originally designed solely as a + mechanism for the X server to report fatal errors to clients, most + clients (including Emacs) have adopted a system of "error traps" to + handle or discard these errors as they arrive. Discarding errors is + usually necessary when Emacs performs an X request that might fail: + for example, sending a message to a window that may no longer exist, + or might not exist at all. Handling errors is then necessary when + the detailed error must be reported to another piece of code: for + example, as a Lisp error. + + It is not acceptable for Emacs to crash when it is sent invalid data + by another client, or by Lisp. As a result, errors must be caught + around Xlib functions generating requests containing resource + identifiers that could potentially be invalid, such as window or + atom identifiers provided in a client message from another program, + or a child window ID obtained through XTranslateCoordinates that may + refer to a window that has been deleted in the meantime. + + There are two sets of functions used to perform this "error + trapping". Which one should be used depends on what kind of + processing must be done on the error. The first consists of the + functions `x_ignore_errors_for_next_request' and + `x_stop_ignoring_errors', which ignore errors generated by requests + made in between a call to the first function and a corresponding + call to the second. They should be used for simple asynchronous + requests that do not require a reply from the X server: using them + instead of the second set improves performance, as they simply + record a range of request serials to ignore errors from, instead of + synchronizing with the X server to handle errors. + + The second set consists of the following functions: + + - x_catch_errors_with_handler + - x_catch_errors + - x_uncatch_errors_after_check + - x_uncatch_errors + - x_check_errors + - x_had_errors_p + - x_clear_errors + + Callers using this set should consult the comment(s) on top of the + aformentioned functions. They should not be used when the requests + being made do not require roundtrips to the X server, and obtaining + the details of any error generated is unecessary, as + `x_uncatch_errors' will always synchronize with the X server, which + is a potentially slow operation. */ #include #include commit c45eb138451c10fa8122820d9615c80a2caeede2 Author: Juanma Barranquero Date: Tue Dec 13 14:45:58 2022 +0100 ; * lisp/bs.el (bs-attributes-list): Doc fix diff --git a/lisp/bs.el b/lisp/bs.el index 1fd31fb3b85..6ddc7774aae 100644 --- a/lisp/bs.el +++ b/lisp/bs.el @@ -157,15 +157,15 @@ bs-attributes-list which calculates column title. MINIMUM-LENGTH : Minimum width of column (number or name of function). The function must return a positive integer. -MAXIMUM-LENGTH : Maximum width of column (number or name of function) - (currently ignored). +MAXIMUM-LENGTH : Ignored. ALIGNMENT : Alignment of column (`left', `right', `middle'). FUN-OR-STRING : Name of a function for calculating the value or a string for a constant value. -The function gets as parameter the buffer where we have started -buffer selection and the list of all buffers to show. The function must -return a string representing the column's value." +Functions for HEADER and MINIMUM-LENGTH are called with no arguments. +FUN-OR-STRING gets as argument the buffer where we have started +buffer selection and the list of all buffers to show. The function +must return a string representing the column's value." :group 'bs-appearance :type '(repeat sexp)) commit e9ec7e53495b94627174ee1b228a6fc6a6e4d859 Author: Po Lu Date: Tue Dec 13 20:02:46 2022 +0800 ; * src/xterm.c (x_send_scroll_bar_event): Minor clean up. diff --git a/src/xterm.c b/src/xterm.c index 3c8dbce30e7..e78ec3c32c0 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -15105,15 +15105,11 @@ x_send_scroll_bar_event (Lisp_Object window, enum scroll_bar_part part, ev->window = FRAME_X_WINDOW (f); ev->format = 32; - /* A 32-bit X client can pass a window pointer through the X server - as-is. - - A 64-bit client is in trouble because a pointer does not fit in - the 32 bits given for ClientMessage data and will be truncated by - Xlib. So use two slots and hope that X12 will resolve such - issues someday. */ - ev->data.l[0] = iw >> 31 >> 1; - ev->data.l[1] = sign_shift <= 0 ? iw : iw << sign_shift >> sign_shift; + /* These messages formerly contained a pointer to the window, but + now that information is kept internally. The following two + fields are thus zero. */ + ev->data.l[0] = 0; + ev->data.l[1] = 0; ev->data.l[2] = part; ev->data.l[3] = portion; ev->data.l[4] = whole; commit d6adaf487d89b0d47f4e7f361a5897081c54d300 Author: Stefan Kangas Date: Tue Dec 13 07:27:16 2022 +0100 Add lexical-binding to example package header * doc/lispref/package.texi (Simple Packages): Add lexical-binding cookie to example package header. diff --git a/doc/lispref/package.texi b/doc/lispref/package.texi index a503a7edde8..4cb0c3214a7 100644 --- a/doc/lispref/package.texi +++ b/doc/lispref/package.texi @@ -152,9 +152,9 @@ Simple Packages @example @group -;;; superfrobnicator.el --- Frobnicate and bifurcate flanges +;;; superfrobnicator.el --- Frobnicate and bifurcate flanges -*- lexical-binding:t -*- -;; Copyright (C) 2011 Free Software Foundation, Inc. +;; Copyright (C) 2022 Free Software Foundation, Inc. @end group ;; Author: J. R. Hacker commit 03ad1a92a2dae107277805f5b24ce1dab3479059 Author: Yuan Fu Date: Mon Dec 12 20:25:53 2022 -0800 Add improved tree-sitter navigation This new set of functions (and tests) should eliminate defun-navigation bugs and limitations we currently have. This commit doesn't change any existing bahavior: treesit-beginning/end-of-defun and friends are unchanged. The plan is to later switch gear and replace the current functions with the new ones introduced in this change. This is a relatively big change, but I've setup a comprehensive test, and it should fix current bugs, so I think it's ok to put it on the release branch. The gist of the new navigation is to use treesit--defuns-around to find the previous sibling defun, next sibling defun, and the parent defun, then use this information to move to previous/next beginning/end of defun in treesit--navigate-defun. I also added comprehensive testing that tests all four possible operations (prev-beg, next-beg, prev-end, next-end) starting at all possible positions (between two sibling defuns, inside a sibling defun, etc). * lisp/treesit.el (treesit-defun-type-regexp): Expand definition to allow (REGEXP . FILTER). Old functions don't support this, but it should be fine since we are soon replacing them. (treesit-defun-tactic) (treesit-defun-skipper): New variables. (treesit-default-defun-skipper) (treesit--defuns-around) (treesit--top-level-defun) (treesit--navigate-defun): New functions. * test/src/treesit-tests.el (treesit--ert-insert-and-parse-marker) (treesit--ert-collect-positions) (treesit--ert-test-defun-navigation): New helper functions. (treesit--ert-defun-navigation-python-program) (treesit--ert-defun-navigation-js-program) (treesit--ert-defun-navigation-bash-program) (treesit--ert-defun-navigation-nested-master): New variables. (treesit-defun-navigation-nested-1) (treesit-defun-navigation-nested-2) (treesit-defun-navigation-nested-3) (treesit-defun-navigation-top-level): New tests. diff --git a/lisp/treesit.el b/lisp/treesit.el index f176664bfde..3d7ae7031ef 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1569,8 +1569,25 @@ treesit-defun-type-regexp "A regexp that matches the node type of defun nodes. For example, \"(function|class)_definition\". +Sometimes not all nodes matched by the regexp are valid defuns. +In that case, set this variable to a cons cell of the +form (REGEXP . FILTER), where FILTER is a function that takes a +node (the matched node) and returns t if node is valid, or nil +for invalid node. + This is used by `treesit-beginning-of-defun' and friends.") +(defvar-local treesit-defun-tactic 'nested + "Determines how does Emacs treat nested defuns. +If the value is `top-level', Emacs only move across top-level +defuns, if the value is `nested', Emacs recognizes nested defuns.") + +(defvar-local treesit-defun-skipper #'treesit-default-defun-skipper + "A function called after tree-sitter navigation moved a step. +It is called with no arguments. By default, this function tries +to move to the beginning of a line, either by moving to the empty +newline after a defun, or the beginning of a defun.") + (defvar-local treesit-defun-prefer-top-level nil "When non-nil, Emacs prefers top-level defun. @@ -1639,6 +1656,196 @@ treesit-end-of-defun (when top (goto-char (treesit-node-end top))))) +(defun treesit-default-defun-skipper () + "Skips spaces after navigating a defun. +This fucntion tries to move to the beginning of a line, either by +moving to the empty newline after a defun, or to the beginning of +the current line if the beginning of the defun is indented." + (cond ((and (looking-at (rx (* (or " " "\\t")) "\n")) + (not (looking-at (rx bol)))) + (goto-char (match-end 0))) + ((save-excursion + (skip-chars-backward " \t") + (eq (point) (line-beginning-position))) + (goto-char (line-beginning-position))))) + +;; prev-sibling: +;; 1. end-of-node before pos +;; 2. highest such node +;; +;; next-sibling: +;; 1. beg-of-node after pos +;; 2. highest such node +;; +;; parent: +;; 1. node covers pos +;; 2. smallest such node +(defun treesit--defuns-around (pos regexp &optional pred) + "Return the previous, next, and parent defun around POS. + +Return a list of (PREV NEXT PARENT), where PREV and NEXT are +previous and next sibling defuns around POS, and PARENT is the +parent defun surrouding POS. All of three could be nil if no +sound defun exists. + +REGEXP and PRED are the same as in `treesit-defun-type-regexp'." + (let* ((node (treesit-node-at pos)) + ;; NODE-BEFORE/AFTER = NODE when POS is completely in NODE, + ;; but if not, that means point could be in between two + ;; defun, in that case we want to use a node that's actually + ;; before/after point. + (node-before (if (>= (treesit-node-start node) pos) + (treesit-search-forward-goto node "" t t t) + node)) + (node-after (if (<= (treesit-node-end node) pos) + (treesit-search-forward-goto node "" nil nil t) + node)) + (result (list nil nil nil)) + (pred (or pred (lambda (_) t)))) + ;; 1. Find previous and next sibling defuns. + (cl-loop + for idx from 0 to 1 + for node in (list node-before node-after) + for backward in '(t nil) + for pos-pred in (list (lambda (n) (<= (treesit-node-end n) pos)) + (lambda (n) (>= (treesit-node-start n) pos))) + ;; If point is inside a defun, our process below will never + ;; return a next/prev sibling outside of that defun, effectively + ;; any prev/next sibling is locked inside the smallest defun + ;; covering point, which is the correct behavior. That's because + ;; when there exists a defun that covers point, + ;; `treesit-search-forward' will first reach that defun, after + ;; that we only go upwards in the tree, so other defuns outside + ;; of the covering defun is never reached. (Don't use + ;; `treesit-search-forward-goto' as it breaks when NODE-AFTER is + ;; the last token of a parent defun: it will skip the parent + ;; defun because it wants to ensure progress.) + do (cl-loop for cursor = (when node + (save-excursion + (treesit-search-forward + node regexp backward backward))) + then (treesit-node-parent cursor) + while cursor + if (and (string-match-p + regexp (treesit-node-type cursor)) + (funcall pred cursor) + (funcall pos-pred cursor)) + do (setf (nth idx result) cursor))) + ;; 2. Find the parent defun. + (setf (nth 2 result) + (cl-loop for cursor = (or (nth 0 result) + (nth 1 result) + node) + then (treesit-node-parent cursor) + while cursor + if (and (string-match-p + regexp (treesit-node-type cursor)) + (funcall pred cursor) + (not (member cursor result))) + return cursor)) + result)) + +(defun treesit--top-level-defun (node regexp &optional pred) + "Return the top-level parent defun of NODE. +REGEXP and PRED are the same as in `treesit-defun-type-regexp'." + (let* ((pred (or pred (lambda (_) t)))) + ;; `treesit-search-forward-goto' will make sure the matched node + ;; is before POS. + (cl-loop for cursor = node + then (treesit-node-parent cursor) + while cursor + if (and (string-match-p + regexp (treesit-node-type cursor)) + (funcall pred cursor)) + do (setq node cursor)) + node)) + +(defun treesit--navigate-defun (pos arg side &optional recursing) + "Navigate defun ARG steps from POS. + +If ARG is positive, move forward that many steps, if negative, +move backward. If SIDE is `beg', stop at the beginning of a +defun, if SIDE is `end', stop at the end. + +This function doesn't actaully move point, it just returns the +position it would move to. If there aren't enough defuns to move +across, return nil. + +RECURSING is an internal parameter, if non-nil, it means this +function is called recursively." + (pcase-let* + ((counter (abs arg)) + (`(,regexp . ,pred) + (if (consp treesit-defun-type-regexp) + treesit-defun-type-regexp + (cons treesit-defun-type-regexp nil))) + ;; Move POS to the beg/end of NODE. If NODE is nil, terminate. + ;; Return the position we moved to. + (advance (lambda (node) + (let ((dest (pcase side + ('beg (treesit-node-start node)) + ('end (treesit-node-end node))))) + (if (null dest) + (throw 'term nil) + dest))))) + (catch 'term + (while (> counter 0) + (pcase-let + ((`(,prev ,next ,parent) + (treesit--defuns-around pos regexp pred))) + ;; When PARENT is nil, nested and top-level are the same, + ;; there there is a PARENT, make PARENT to be the top-level + ;; parent and pretend there is no nested PREV and NEXT. + (when (and (eq treesit-defun-tactic 'top-level) + parent) + (setq parent (treesit--top-level-defun + parent regexp pred) + prev nil + next nil)) + ;; Move... + (if (> arg 0) + ;; ...forward. + (if (and (eq side 'beg) + ;; Should we skip the defun (recurse)? + (cond (next (not recursing)) ; [1] (see below) + (parent t) ; [2] + (t nil))) + ;; Special case: go to next beg-of-defun. Set POS + ;; to the end of next/parent defun, and run one more + ;; step. If there is a next defun, step over it, so + ;; we only need to recurse once, so we don't need to + ;; recurse if we are already recursing [1]. If there + ;; is no next but a parent, keep stepping out + ;; (recursing) until we got out of the parents until + ;; (1) there is a next sibling defun, or (2) no more + ;; parents [2]. + (setq pos + (or (treesit--navigate-defun + (treesit-node-end (or next parent)) + 1 'beg t) + (throw 'term nil))) + ;; Normal case. + (setq pos (funcall advance (or next parent)))) + ;; ...backward. + (if (and (eq side 'end) + (cond (prev (not recursing)) + (parent t) + (t nil))) + ;; Special case: go to prev end-of-defun. + (setq pos + (or (treesit--navigate-defun + (treesit-node-start (or prev parent)) + -1 'end t) + (throw 'term nil))) + ;; Normal case. + (setq pos (funcall advance (or prev parent))))) + ;; A successful step! Decrement counter. + (cl-decf counter)))) + ;; Counter equal to 0 means we successfully stepped ARG steps. + (if (eq counter 0) + pos + nil))) + ;;; Activating tree-sitter (defun treesit-ready-p (language &optional quiet) diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index 188a9557928..eaf2df62104 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el @@ -607,6 +607,288 @@ treesit-node-check (insert "]") (should (treesit-node-check array-node 'outdated)))) +;;; Defun navigation +;; +;; I've setup a framework for easier testing of defun navigation. +;; +;; To use it for a particular langauge, first write a test program +;; similar to `treesit--ert-defun-navigation-python-program', and +;; insert markers. Markers that marks BOLs are defined as follows: +;; +;; 100 Before 1st parent +;; 101 Beg of 1st parent +;; 102 End of 1st parent +;; 103 Beg of 2nd parent +;; 104 Beg of 1st method +;; 105 End of 1st method +;; 106 Beg of 2nd method +;; 107 End of 2nd method +;; 108 End of 2nd parent +;; 109 Beg of 3rd parent +;; 110 End of 3rd parent +;; 999 Dummy markers +;; +;; Then add marker 0-9 following the definition given in +;; `treesit--ert-defun-navigation-nested-master'. Then you can use +;; `treesit--ert-test-defun-navigation', pass the test program you +;; just wrote, and the appropriate master: +;; +;; - `treesit--ert-defun-navigation-nested-master' for nested defun +;; - `treesit--ert-defun-navigation-top-level-master' for top-level + + +(defun treesit--ert-insert-and-parse-marker (opening closing text) + "Insert TEXT and parse the marker positions in it. + +TEXT should be a string in which contains position markings +like (1). OPENING and CLOSING are position marking's delimiters, +for (1), OPENING and CLOSING should be \"(\" and \")\", +respectively. + +This function inserts TEXT, parses and removes all the markings, +and returns an alist of (NUMBER . POS), where number is each +marking's number, and POS is each marking's position." + (declare (indent 2)) + (let (result) + (insert text) + (goto-char (point-min)) + (while (re-search-forward + (rx-to-string `(seq ,opening (group (+ digit)) ,closing)) + nil t) + (let ((idx (string-to-number (match-string 1)))) + (push (cons idx (match-beginning 0)) result) + (delete-region (match-beginning 0) (match-end 0)))) + (nreverse result))) + +(defun treesit--ert-collect-positions (positions functions) + "Collect posifions after caling each function in FUNCTIONS. + +POSITIONS should be a list of buffer positions, FUNCTIONS should +be a list of functions. This function collects the return value +of each function in FUNCTIONS starting at each position in +POSITIONS. + +Return a list of (POS...) where each POS corresponds to a +function in FUNCTIONS. For example, if buffer content is +\"123\", POSITIONS is (2 3), FUNCTIONS is (point-min point-max), +the return value is ((1 3) (1 3))." + (cl-loop for pos in positions + collect (cl-loop for fn in functions + collect (progn + (goto-char pos) + (funcall fn))))) + +(defun treesit--ert-test-defun-navigation + (init program master &optional opening closing) + "Run defun navigation tests on PROGRAM and MASTER. + +INIT is a setup function that runs right after this function +creates a temporary buffer. It should take no argument. + +PROGRAM is a program source in string, MASTER is a list of +\(START PREV-BEG NEXT-END PREV-END NEXT-BEG), where START is the +starting marker position, and the rest are marker positions the +corresponding navigation should stop at (after running +`treesit-defun-skipper'). + +OPENING and CLOSING are the same as in +`treesit--ert-insert-and-parse-marker', by default they are \"[\" +and \"]\"." + (with-temp-buffer + (funcall init) + (let* ((opening (or opening "[")) + (closing (or closing "]")) + ;; Insert program and parse marker positions. + (marker-alist (treesit--ert-insert-and-parse-marker + opening closing program)) + ;; Translate marker positions into buffer positions. + (decoded-master + (cl-loop for record in master + collect + (cl-loop for pos in record + collect (alist-get pos marker-alist)))) + ;; Collect positions each function returns. + (positions + (treesit--ert-collect-positions + ;; The first columnn of DECODED-MASTER. + (mapcar #'car decoded-master) + ;; Four functions: next-end, prev-beg, next-beg, prev-end. + (mapcar (lambda (conf) + (lambda () + (if-let ((pos (funcall + #'treesit--navigate-defun + (point) (car conf) (cdr conf)))) + (save-excursion + (goto-char pos) + (funcall treesit-defun-skipper) + (point))))) + '((-1 . beg) + (1 . end) + (-1 . end) + (1 . beg)))))) + ;; Verify each position. + (cl-loop for record in decoded-master + for orig-record in master + for poss in positions + for name = (format "marker %d" (car orig-record)) + do (should (equal (cons name (cdr record)) + (cons name poss))))))) + +(defvar treesit--ert-defun-navigation-python-program + "[100] +[101]class Class1(): +[999] prop = 0 +[102] +[103]class Class2():[0] +[104] [1]def method1(): +[999] [2]return 0[3] +[105] [4] +[106] [5]def method2(): +[999] [6]return 0[7] +[107] [8] +[999] prop = 0[9] +[108] +[109]class Class3(): +[999] prop = 0[10] +[110] +" + "Python source for navigation test.") + +(defvar treesit--ert-defun-navigation-js-program + "[100] +[101]class Class1 { +[999]} +[102] +[103]class Class2 {[0] +[104] [1]method1() { +[999] [2]return 0; +[999] }[3] +[105] [4] +[106] [5]method2() { +[999] [6]return 0; +[999] }[7] +[107][8] +[999]}[9] +[108] +[109]class class3 { +[999]}[10] +[110] +" + "Javascript source for navigation test.") + +(defvar treesit--ert-defun-navigation-bash-program + "[100] +[101]parent1 () { +[999]} +[102] +[103]parent2 () {[0] +[104] [1]sibling1 () { +[999] [2]echo hi +[999] }[3] +[105] [4] +[106] [5]sibling2 () { +[999] [6]echo hi +[999] }[7] +[107][8] +[999]}[9] +[108] +[109]parent3 () { +[999]} +[110] +" + "Javascript source for navigation test.") + +(defvar treesit--ert-defun-navigation-nested-master + ;; START PREV-BEG NEXT-END PREV-END NEXT-BEG + '((0 103 105 102 106) ; Between Beg of parent & 1st sibling. + (1 103 105 102 106) ; Beg of 1st sibling. + (2 104 105 102 106) ; Inside 1st sibling. + (3 104 107 102 109) ; End of 1st sibling. + (4 104 107 102 109) ; Between 1st sibling & 2nd sibling. + (5 104 107 102 109) ; Beg of 2nd sibling. + (6 106 107 105 109) ; Inside 2nd sibling. + (7 106 108 105 109) ; End of 2nd sibling. + (8 106 108 105 109) ; Between 2nd sibling & end of parent. + (9 103 110 102 nil) ; End of parent. + + (100 nil 102 nil 103) ; Before 1st parent. + (101 nil 102 nil 103) ; Beg of 1st parent. + (102 101 108 nil 109) ; Between 1st & 2nd parent. + (103 101 108 nil 109) ; Beg of 2nd parent. + (110 109 nil 108 nil) ; After 3rd parent. + ) + "Master of nested navigation test. + +This basically says, e.g., \"start with point on marker 0, go to +the prev-beg, now point should be at marker 103\", etc.") + +(defvar treesit--ert-defun-navigation-top-level-master + ;; START PREV-BEG NEXT-END NEXT-BEG PREV-END + '((0 103 108 102 109) ; Between Beg of parent & 1st sibling. + (1 103 108 102 109) ; Beg of 1st sibling. + (2 103 108 102 109) ; Inside 1st sibling. + (3 103 108 102 109) ; End of 1st sibling. + (4 103 108 102 109) ; Between 1st sibling & 2nd sibling. + (5 103 108 102 109) ; Beg of 2nd sibling. + (6 103 108 102 109) ; Inside 2nd sibling. + (7 103 108 102 109) ; End of 2nd sibling. + (8 103 108 102 109) ; Between 2nd sibling & end of parent. + (9 103 110 102 nil) ; End of parent. + + ;; Top-level defuns should be identical to the nested test. + (100 nil 102 nil 103) ; Before 1st parent. + (101 nil 102 nil 103) ; Beg of 1st parent. + (102 101 108 nil 109) ; Between 1st & 2nd parent. + (103 101 108 nil 109) ; Beg of 2nd parent. + (110 109 nil 108 nil) ; After 3rd parent. + ) + "Master of top-level navigation test.") + +(ert-deftest treesit-defun-navigation-nested-1 () + "Test defun navigation." + (skip-unless (treesit-language-available-p 'python)) + ;; Nested defun navigation + (let ((treesit-defun-tactic 'nested)) + (require 'python) + (treesit--ert-test-defun-navigation + 'python-ts-mode + treesit--ert-defun-navigation-python-program + treesit--ert-defun-navigation-nested-master))) + +(ert-deftest treesit-defun-navigation-nested-2 () + "Test defun navigation using `js-ts-mode'." + (skip-unless (treesit-language-available-p 'javascript)) + ;; Nested defun navigation + (let ((treesit-defun-tactic 'nested)) + (require 'js) + (treesit--ert-test-defun-navigation + 'js-ts-mode + treesit--ert-defun-navigation-js-program + treesit--ert-defun-navigation-nested-master))) + +(ert-deftest treesit-defun-navigation-nested-3 () + "Test defun navigation using `bash-ts-mode'." + (skip-unless (treesit-language-available-p 'bash)) + ;; Nested defun navigation + (let ((treesit-defun-tactic 'nested)) + (treesit--ert-test-defun-navigation + (lambda () + (treesit-parser-create 'bash) + (setq-local treesit-defun-type-regexp "function_definition")) + treesit--ert-defun-navigation-bash-program + treesit--ert-defun-navigation-nested-master))) + +(ert-deftest treesit-defun-navigation-top-level () + "Test top-level only defun navigation." + (skip-unless (treesit-language-available-p 'python)) + ;; Nested defun navigation + (let ((treesit-defun-tactic 'top-level)) + (require 'python) + (treesit--ert-test-defun-navigation + 'python-ts-mode + treesit--ert-defun-navigation-python-program + treesit--ert-defun-navigation-top-level-master))) + ;; TODO ;; - Functions in treesit.el ;; - treesit-load-name-override-list commit a5272e2a7cc77f17efa144c5482dcfcb62d563d3 Author: Yuan Fu Date: Mon Dec 12 18:14:30 2022 -0800 ; * test/src/treesit-tests.el: Add outline headers. diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index 1cc2217bd3b..188a9557928 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el @@ -54,6 +54,7 @@ (declare-function treesit-node-descendant-for-range "treesit.c") (declare-function treesit-node-eq "treesit.c") +;;; Basic API (ert-deftest treesit-basic-parsing () "Test basic parsing routines." @@ -161,6 +162,8 @@ treesit-node-api (should (treesit-node-eq root-node root-node)) (should (not (treesit-node-eq root-node doc-node)))))) +;;; Indirect buffer + (ert-deftest treesit-indirect-buffer () "Tests for indirect buffers." (skip-unless (treesit-language-available-p 'json)) @@ -195,6 +198,8 @@ treesit-indirect-buffer (kill-buffer base) (kill-buffer indirect)))) +;;; Query + (ert-deftest treesit-query-api () "Tests for query API." (skip-unless (treesit-language-available-p 'json)) @@ -249,6 +254,8 @@ treesit-query-api '((type field: (_) @capture :anchor) :? :* :+ "return"))))))) +;;; Narrow + (ert-deftest treesit-narrow () "Tests if narrowing works." (skip-unless (treesit-language-available-p 'json)) @@ -385,6 +392,8 @@ treesit-cross-boundary ;; that calls that. ))) +;;; Range + (ert-deftest treesit-range () "Tests if range works." (skip-unless (treesit-language-available-p 'json)) @@ -438,6 +447,8 @@ treesit-range ;; TODO: More tests. ))) +;;; Multiple language + (ert-deftest treesit-multi-lang () "Tests if parsing multiple language works." (skip-unless (and (treesit-language-available-p 'html) @@ -474,6 +485,8 @@ treesit-multi-lang ;; TODO: More tests. ))) +;;; Supplemental functions + (ert-deftest treesit-parser-supplemental () "Supplemental node functions." (skip-unless (treesit-language-available-p 'json)) commit 489bcacc7c30207b9dbaeb90b9002255af8bfe64 Author: Stefan Kangas Date: Tue Dec 13 01:43:48 2022 +0100 Add cross-reference to flush-lines * lisp/replace.el (flush-lines, kill-matching-lines): Add cross-references between commands, using the names '(kill|delete)-matching-lines' for symmetry. (Bug#59934) diff --git a/lisp/replace.el b/lisp/replace.el index 6f59166e352..302cb65543b 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -1039,7 +1039,10 @@ flush-lines starting on the same line at which another match ended is ignored. Return the number of deleted matching lines. When called interactively, -also print the number." +also print the number. + +If you want to not just delete the lines, but also add them to +the kill ring, use the \\[kill-matching-lines] command instead." (interactive (progn (barf-if-buffer-read-only) @@ -1104,7 +1107,7 @@ kill-matching-lines interactively, also print the number. If you merely want to delete the lines, without adding them to -the kill ring, the \\[flush-lines] command is faster." +the kill ring, the \\[delete-matching-lines] command is faster." (interactive (progn (barf-if-buffer-read-only) commit 0f9e6532b14c3a4117a4419acda9968d30570e04 Author: Jostein Kjønigsen Date: Mon Dec 12 12:50:22 2022 +0100 Use font-lock-number-face for numeric values in csharp-mode (bug#60004) * lisp/progmodes/csharp-mode.el: update fontification code. diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 17b8ae53f84..dda0b105b74 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -709,8 +709,8 @@ csharp-ts-mode--font-lock-settings :language 'c-sharp :override t :feature 'literal - `((integer_literal) @font-lock-constant-face - (real_literal) @font-lock-constant-face + `((integer_literal) @font-lock-number-face + (real_literal) @font-lock-number-face (null_literal) @font-lock-constant-face (boolean_literal) @font-lock-constant-face) :language 'c-sharp commit 4bccb7b211e465310a34493dea8a66ce395956fc Author: Jostein Kjønigsen Date: Mon Dec 12 08:42:33 2022 +0100 Make treesit-query-validate create a read-only buffer * lisp/treesit.el: use view-mode for generated validation buffer. diff --git a/lisp/treesit.el b/lisp/treesit.el index 2ca4f1c7ddc..f176664bfde 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1835,7 +1835,8 @@ treesit-query-validate (goto-char (point-min)) (insert (format "%s: %d\n" message start)) (forward-char start))) - (pop-to-buffer buf)))))) + (pop-to-buffer buf))))) + (view-mode)) ;;; Explorer commit c0fe6c72cec477fdd2494bf3d26ab75790d2f720 Author: Randy Taylor Date: Sun Dec 11 20:50:54 2022 -0500 Improve dockerfile-ts-mode imenu generation (Bug#59979) * lisp/progmodes/dockerfile-ts-mode.el (treesit-node-child-by-field-name): Declare. (dockerfile-ts-mode--imenu-1): Use stage name if available. diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el index 544e0f82d6d..40d90cc2dfc 100644 --- a/lisp/progmodes/dockerfile-ts-mode.el +++ b/lisp/progmodes/dockerfile-ts-mode.el @@ -33,6 +33,7 @@ (declare-function treesit-parser-create "treesit.c") (declare-function treesit-induce-sparse-tree "treesit.c") (declare-function treesit-node-child "treesit.c") +(declare-function treesit-node-child-by-field-name "treesit.c") (declare-function treesit-node-start "treesit.c") (declare-function treesit-node-type "treesit.c") @@ -117,8 +118,10 @@ dockerfile-ts-mode--imenu-1 children)) (name (when ts-node (pcase (treesit-node-type ts-node) - ("from_instruction" (treesit-node-text - (treesit-node-child ts-node 1) t))))) + ("from_instruction" + (treesit-node-text + (or (treesit-node-child-by-field-name ts-node "as") + (treesit-node-child ts-node 1)) t))))) (marker (when ts-node (set-marker (make-marker) (treesit-node-start ts-node))))) commit 631908f70172a117f83596936abe7f042005b57a Author: Daniel Martín Date: Sun Dec 11 17:33:13 2022 +0100 Add "->" to python--treesit-operators (bug#59968) * lisp/progmodes/python.el (python--treesit-operators): Add "->", used to specify the return types of functions in Python. diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index ebee703499a..0e0898ffe28 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -993,7 +993,8 @@ python--treesit-constants (defvar python--treesit-operators '("-" "-=" "!=" "*" "**" "**=" "*=" "/" "//" "//=" "/=" "&" "%" "%=" - "^" "+" "+=" "<" "<<" "<=" "<>" "=" "==" ">" ">=" ">>" "|" "~" "@" "@=")) + "^" "+" "->" "+=" "<" "<<" "<=" "<>" "=" ":=" "==" ">" ">=" ">>" "|" + "~" "@" "@=")) (defvar python--treesit-special-attributes '("__annotations__" "__closure__" "__code__" commit 5d4274d9b65d87fe7d672e7a930abd615f431f34 Author: Xi Lu Date: Sun Dec 11 23:33:33 2022 +0800 ; * admin/notes/tree-sitter/build-module/build.sh: Add -fPIC. (bug#59967) diff --git a/admin/notes/tree-sitter/build-module/build.sh b/admin/notes/tree-sitter/build-module/build.sh index 3467be272b8..d020ee92c39 100755 --- a/admin/notes/tree-sitter/build-module/build.sh +++ b/admin/notes/tree-sitter/build-module/build.sh @@ -46,7 +46,7 @@ grammardir= ### Build -cc -c -I. parser.c +cc -fPIC -c -I. parser.c # Compile scanner.c. if test -f scanner.c then commit d264b75669d7d9d7ffe536a79af0ea34d8fd4b6a Author: Daniel Martín Date: Sun Dec 11 15:57:43 2022 +0100 Align C++ access specifiers to their enclosing class/struct/union (bug#59966) The default style in c++-mode aligns access specifiers like "private", "public" or "protected" to their enclosing class, struct, or union. The "ellemtel" style indents access specifiers, but this C++ style is not supported by c++-ts-mode yet. * lisp/progmodes/c-ts-mode.el (c-ts-mode--indent-styles): Align access specifiers to their enclosing class/struct/union. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index deba83641b2..4cf06e10ebf 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -120,6 +120,8 @@ c-ts-mode--indent-styles ((query "(call_expression arguments: (_) @indent)") parent c-ts-mode-indent-offset) ((parent-is "call_expression") parent 0) ((parent-is "enumerator_list") parent-bol c-ts-mode-indent-offset) + ,@(when (eq mode 'cpp) + '(((node-is "access_specifier") parent-bol 0))) ((parent-is "field_declaration_list") parent-bol c-ts-mode-indent-offset) ((parent-is "initializer_list") parent-bol c-ts-mode-indent-offset) ((parent-is "if_statement") parent-bol c-ts-mode-indent-offset) commit ca67d988d8721e9ec24a040b977393136457873f Author: Randy Taylor Date: Sat Dec 10 21:40:25 2022 -0500 Add cmake-ts-mode * admin/notes/tree-sitter/build-module/batch.sh: * admin/notes/tree-sitter/build-module/build.sh: Add cmake support. * etc/NEWS: Mention it. * lisp/progmodes/cmake-ts-mode.el: New major mode with tree-sitter support. * lisp/progmodes/eglot.el (eglot-server-programs): Add it. diff --git a/admin/notes/tree-sitter/build-module/batch.sh b/admin/notes/tree-sitter/build-module/batch.sh index 6dce000caa6..c3954499774 100755 --- a/admin/notes/tree-sitter/build-module/batch.sh +++ b/admin/notes/tree-sitter/build-module/batch.sh @@ -2,6 +2,7 @@ languages=( 'c' + 'cmake' 'cpp' 'css' 'c-sharp' diff --git a/admin/notes/tree-sitter/build-module/build.sh b/admin/notes/tree-sitter/build-module/build.sh index cc31e3f6f02..3467be272b8 100755 --- a/admin/notes/tree-sitter/build-module/build.sh +++ b/admin/notes/tree-sitter/build-module/build.sh @@ -23,6 +23,9 @@ grammardir= "dockerfile") namespace="camdencheek" ;; + "cmake") + namespace="uyha" + ;; "typescript") sourcedir="tree-sitter-typescript/typescript/src" grammardir="tree-sitter-typescript/typescript" diff --git a/etc/NEWS b/etc/NEWS index 233ef3f5729..6cb531cd0f1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3073,6 +3073,11 @@ A major mode based on the tree-sitter library for editing Dockerfiles. It includes support for font-locking, indentation, Imenu, and which-func. +** New major mode 'cmake-ts-mode'. +A major mode based on the tree-sitter library for editing CMake files. +It includes support for font-locking, indentation, Imenu, and +which-func. + * Incompatible Lisp Changes in Emacs 29.1 diff --git a/lisp/progmodes/cmake-ts-mode.el b/lisp/progmodes/cmake-ts-mode.el new file mode 100644 index 00000000000..15934a290b7 --- /dev/null +++ b/lisp/progmodes/cmake-ts-mode.el @@ -0,0 +1,234 @@ +;;; cmake-ts-mode.el --- tree-sitter support for CMake -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Author : Randy Taylor +;; Maintainer : Randy Taylor +;; Created : December 2022 +;; Keywords : cmake languages tree-sitter + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: +;; + +;;; Code: + +(require 'treesit) +(eval-when-compile (require 'rx)) + +(declare-function treesit-parser-create "treesit.c") +(declare-function treesit-induce-sparse-tree "treesit.c") +(declare-function treesit-node-child "treesit.c") +(declare-function treesit-node-start "treesit.c") +(declare-function treesit-node-type "treesit.c") + +(defcustom cmake-ts-mode-indent-offset 2 + "Number of spaces for each indentation step in `cmake-ts-mode'." + :version "29.1" + :type 'integer + :safe 'integerp + :group 'cmake) + +(defvar cmake-ts-mode--syntax-table + (let ((table (make-syntax-table))) + (modify-syntax-entry ?# "<" table) + (modify-syntax-entry ?\n ">" table) + (modify-syntax-entry ?$ "'" table) + table) + "Syntax table for `cmake-ts-mode'.") + +(defvar cmake-ts-mode--indent-rules + `((cmake + ((node-is ")") parent-bol 0) + ((node-is "else_command") parent-bol 0) + ((node-is "elseif_command") parent-bol 0) + ((node-is "endforeach_command") parent-bol 0) + ((node-is "endfunction_command") parent-bol 0) + ((node-is "endif_command") parent-bol 0) + ((parent-is "foreach_loop") parent-bol cmake-ts-mode-indent-offset) + ((parent-is "function_def") parent-bol cmake-ts-mode-indent-offset) + ((parent-is "if_condition") parent-bol cmake-ts-mode-indent-offset) + ((parent-is "normal_command") parent-bol cmake-ts-mode-indent-offset))) + "Tree-sitter indent rules for `cmake-ts-mode'.") + +(defvar cmake-ts-mode--constants + '("1" "ON" "TRUE" "YES" "Y" "0" "OFF" "FALSE" "NO" "N" "IGNORE" + "NOTFOUND") + "CMake constants for tree-sitter font-locking.") + +(defvar cmake-ts-mode--keywords + '((else) (elseif) (endforeach) (endfunction) (endif) (endmacro) + (endwhile) (foreach) (function) (if) (macro) (while)) + "CMake keywords for tree-sitter font-locking.") + +(defvar cmake-ts-mode--foreach-options + '("IN" "ITEMS" "LISTS" "RANGE" "ZIP_LISTS") + "CMake foreach options for tree-sitter font-locking.") + +(defvar cmake-ts-mode--if-conditions + '("AND" "COMMAND" "DEFINED" "EQUAL" "EXISTS" "GREATER" + "GREATER_EQUAL" "LESS" "LESS_EQUAL" "MATCHES" "NOT" "OR" + "PATH_EQUAL" "STREQUAL" "STRGREATER" "STRGREATER_EQUAL" "STRLESS" + "STRLESS_EQUAL" "VERSION_EQUAL" "VERSION_GREATER" + "VERSION_GREATER_EQUAL" "VERSION_LESS" "VERSION_LESS_EQUAL") + "CMake if conditions for tree-sitter font-locking.") + +(defvar cmake-ts-mode--font-lock-settings + (treesit-font-lock-rules + :language 'cmake + :feature 'bracket + '((["(" ")"]) @font-lock-bracket-face) + + :language 'cmake + :feature 'builtin + `(((foreach_command + ((argument) @font-lock-constant-face + (:match ,(rx-to-string + `(seq bol + (or ,@cmake-ts-mode--foreach-options) + eol)) + @font-lock-constant-face)))) + ((if_command + ((argument) @font-lock-constant-face + (:match ,(rx-to-string + `(seq bol + (or ,@cmake-ts-mode--if-conditions) + eol)) + @font-lock-constant-face))))) + + :language 'cmake + :feature 'comment + '([(bracket_comment) (line_comment)] @font-lock-comment-face) + + :language 'cmake + :feature 'constant + `(((argument) @font-lock-constant-face + (:match ,(rx-to-string + `(seq bol + (or ,@cmake-ts-mode--constants) + eol)) + @font-lock-constant-face))) + + :language 'cmake + :feature 'function + '((normal_command (identifier) @font-lock-function-name-face)) + + :language 'cmake + :feature 'keyword + `([,@cmake-ts-mode--keywords] @font-lock-keyword-face) + + :language 'cmake + :feature 'number + '(((unquoted_argument) @font-lock-number-face + (:match "^[[:digit:]]*\\.?[[:digit:]]*\\.?[[:digit:]]+$" @font-lock-number-face))) + + :language 'cmake + :feature 'string + '([(bracket_argument) (quoted_argument)] @font-lock-string-face) + + :language 'cmake + :feature 'escape-sequence + :override t + '((escape_sequence) @font-lock-escape-face) + + :language 'cmake + :feature 'misc-punctuation + ;; Don't override strings. + :override 'nil + '((["$" "{" "}" "<" ">"]) @font-lock-misc-punctuation-face) + + :language 'cmake + :feature 'variable + :override t + '((variable) @font-lock-variable-name-face) + + :language 'cmake + :feature 'error + :override t + '((ERROR) @font-lock-warning-face)) + "Tree-sitter font-lock settings for `cmake-ts-mode'.") + +(defun cmake-ts-mode--imenu () + "Return Imenu alist for the current buffer." + (let* ((node (treesit-buffer-root-node)) + (func-tree (treesit-induce-sparse-tree + node "function_def" nil 1000)) + (func-index (cmake-ts-mode--imenu-1 func-tree))) + (append + (when func-index `(("Function" . ,func-index)))))) + +(defun cmake-ts-mode--imenu-1 (node) + "Helper for `cmake-ts-mode--imenu'. +Find string representation for NODE and set marker, then recurse +the subtrees." + (let* ((ts-node (car node)) + (children (cdr node)) + (subtrees (mapcan #'cmake-ts-mode--imenu-1 + children)) + (name (when ts-node + (pcase (treesit-node-type ts-node) + ("function_def" + (treesit-node-text + (treesit-node-child (treesit-node-child ts-node 0) 2) t))))) + (marker (when ts-node + (set-marker (make-marker) + (treesit-node-start ts-node))))) + (cond + ((or (null ts-node) (null name)) subtrees) + (subtrees + `((,name ,(cons name marker) ,@subtrees))) + (t + `((,name . ,marker)))))) + +;;;###autoload +(add-to-list 'auto-mode-alist + '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)$" . cmake-ts-mode)) + +;;;###autoload +(define-derived-mode cmake-ts-mode prog-mode "CMake" + "Major mode for editing CMake files, powered by tree-sitter." + :group 'cmake + :syntax-table cmake-ts-mode--syntax-table + + (when (treesit-ready-p 'cmake) + (treesit-parser-create 'cmake) + + ;; Comments. + (setq-local comment-start "# ") + (setq-local comment-end "") + (setq-local comment-start-skip (rx "#" (* (syntax whitespace)))) + + ;; Imenu. + (setq-local imenu-create-index-function #'cmake-ts-mode--imenu) + (setq-local which-func-functions nil) + + ;; Indent. + (setq-local treesit-simple-indent-rules cmake-ts-mode--indent-rules) + + ;; Font-lock. + (setq-local treesit-font-lock-settings cmake-ts-mode--font-lock-settings) + (setq-local treesit-font-lock-feature-list + '((comment) + (keyword string) + (builtin constant escape-sequence function number variable) + (bracket error misc-punctuation))) + + (treesit-major-mode-setup))) + +(provide 'cmake-ts-mode) + +;;; cmake-ts-mode.el ends here diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 2427e7b9d31..9c5a361df7b 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -183,7 +183,7 @@ eglot-alternatives finally (funcall err))))))) (defvar eglot-server-programs `((rust-mode . ,(eglot-alternatives '("rust-analyzer" "rls"))) - (cmake-mode . ("cmake-language-server")) + ((cmake-mode cmake-ts-mode) . ("cmake-language-server")) (vimrc-mode . ("vim-language-server" "--stdio")) ((python-mode python-ts-mode) . ,(eglot-alternatives commit 8ec923775de3482acf4b158a79838eae64fd4980 Author: Theodor Thornhill Date: Fri Dec 9 22:36:03 2022 +0100 Tweak various ts-mode's indent and fontification (bug#59931) * lisp/progmodes/c-ts-mode.el (c-ts-mode--font-lock-settings): Add raw string literal font-locking. * lisp/progmodes/java-ts-mode.el (java-ts-mode--indent-rules): Add text_block indent rule. (java-ts-mode--font-lock-settings): Add text_block font-locking. * lisp/progmodes/js.el (js-ts-mode): Prefer top-level navigation. * lisp/progmodes/json-ts-mode.el (json-ts-mode--font-lock-settings): Add comment feature. (json-ts-mode): Use comment feature. * lisp/progmodes/typescript-ts-mode.el (typescript-ts-base-mode): Prefer top-level navigation. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 824325d83e0..deba83641b2 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -251,7 +251,9 @@ c-ts-mode--font-lock-settings :language mode :feature 'string `((string_literal) @font-lock-string-face - (system_lib_string) @font-lock-string-face) + (system_lib_string) @font-lock-string-face + ,@(when (eq mode 'cpp) + '((raw_string_literal) @font-lock-string-face))) :language mode :feature 'literal diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index 9155a7fff25..ac591a08599 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -73,6 +73,7 @@ java-ts-mode--indent-rules ((node-is "]") parent-bol 0) ((and (parent-is "comment") comment-end) comment-start -1) ((parent-is "comment") comment-start-skip 0) + ((parent-is "text_block") no-indent) ((parent-is "class_body") parent-bol java-ts-mode-indent-offset) ((parent-is "interface_body") parent-bol java-ts-mode-indent-offset) ((parent-is "constructor_body") parent-bol java-ts-mode-indent-offset) @@ -162,7 +163,8 @@ java-ts-mode--font-lock-settings :language 'java :override t :feature 'string - `((string_literal) @font-lock-string-face) + `((string_literal) @font-lock-string-face + (text_block) @font-lock-string-face) :language 'java :override t :feature 'literal diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index f7318c481a2..1de0f3442f0 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3871,6 +3871,7 @@ js-ts-mode ;; Indent. (setq-local treesit-simple-indent-rules js--treesit-indent-rules) ;; Navigation. + (setq-local treesit-defun-prefer-top-level t) (setq-local treesit-defun-type-regexp (rx (or "class_declaration" "method_definition" diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el index a118908a00c..6fb982e37bd 100644 --- a/lisp/progmodes/json-ts-mode.el +++ b/lisp/progmodes/json-ts-mode.el @@ -74,6 +74,9 @@ json-ts--indent-rules (defvar json-ts-mode--font-lock-settings (treesit-font-lock-rules + :language 'json + :feature 'comment + '((comment) @font-lock-comment-face) :language 'json :feature 'bracket '((["[" "]" "{" "}"]) @font-lock-bracket-face) @@ -161,7 +164,7 @@ json-ts-mode ;; Font-lock. (setq-local treesit-font-lock-settings json-ts-mode--font-lock-settings) (setq-local treesit-font-lock-feature-list - '((constant number pair string) + '((comment constant number pair string) (escape-sequence) (bracket delimiter error))) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index 8c4364ecc5b..e4d188971a0 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -329,6 +329,8 @@ typescript-ts-base-mode (group (or (syntax comment-end) (seq (+ "*") "/"))))) + (setq-local treesit-defun-prefer-top-level t) + ;; Electric (setq-local electric-indent-chars (append "{}():;," electric-indent-chars)) commit 647b6a8099f414e5a7b162efd3658e174064dfe7 Author: Theodor Thornhill Date: Thu Dec 8 11:08:28 2022 +0100 Add expression for generic_name in csharp-ts-mode (bug#59897) Given the below example, we want 'services' to be font-locked in 'font-lock-variable-name-face' in all cases. Previously this only worked in the first case, and the other was font-locked as 'font-lock-function-name-face'. namespace Foo { void Foo() { services.Add(); services.Add(); } } * lisp/progmodes/csharp-mode.el (csharp-ts-mode--font-lock-settings): Add new query that makes the mentioned example work. diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 8a7313b1ce8..17b8ae53f84 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -784,6 +784,10 @@ csharp-ts-mode--font-lock-settings (invocation_expression (member_access_expression (generic_name (identifier) @font-lock-function-name-face))) + (invocation_expression + (member_access_expression + expression: (identifier) @font-lock-variable-name-face + name: (generic_name (type_argument_list (identifier))))) (invocation_expression (member_access_expression ((identifier) @font-lock-variable-name-face commit 5b178efd85a65dd44c33058948ad5fe18b580067 Author: Stefan Kangas Date: Mon Dec 12 22:44:55 2022 +0100 ; Adjust eglot test to recent autopep8/pycodestyle * test/lisp/progmodes/eglot-tests.el (python-autopep-formatting): Support more recent autopep8/pycodestyle. diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 32eefd778fe..04ecd7891ab 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -673,7 +673,9 @@ python-autopep-formatting (eglot-format (line-beginning-position) (line-end-position)) (should (looking-at "ss")) (should - (string= (buffer-string) "def a():pass\n\n\ndef b(): pass\n")) + (or (string= (buffer-string) "def a():pass\n\n\ndef b(): pass\n") + ;; autopep8 2.0.0 (pycodestyle: 2.9.1) + (string= (buffer-string) "def a():pass\n\ndef b(): pass"))) ;; now format the whole buffer (eglot-format-buffer) (should commit 58b8ed8b55cd3c864823c6d2fbfdd12bcfe43247 Author: Eli Zaretskii Date: Mon Dec 12 20:01:23 2022 +0200 ; Avoid compilation warning on MS-Windows * src/timefns.c (sys_clock): Declare, to avoid compilation warning. diff --git a/src/timefns.c b/src/timefns.c index 9beec1ce384..dcc6403fd9d 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -40,6 +40,10 @@ Copyright (C) 1985-1987, 1989, 1993-2022 Free Software Foundation, Inc. #include #include +#ifdef WINDOWSNT +extern clock_t sys_clock (void); +#endif + #ifdef HAVE_TIMEZONE_T # include # if defined __NetBSD_Version__ && __NetBSD_Version__ < 700000000 commit 40c23c11e884bd14c9340a4853ab5db02901fe65 Author: Juri Linkov Date: Mon Dec 12 19:37:02 2022 +0200 * lisp/outline.el: Fix the value 'insert' of outline-minor-mode-use-buttons. (outline--insert-button): Keep text properties around point on the inserted whitespace placeholder. * lisp/apropos.el (apropos-mode): * lisp/progmodes/xref.el (xref--xref-buffer-mode): Change outline-minor-mode-use-buttons from t to 'insert'. diff --git a/lisp/apropos.el b/lisp/apropos.el index a731926f458..89f1f150a12 100644 --- a/lisp/apropos.el +++ b/lisp/apropos.el @@ -496,7 +496,7 @@ apropos-mode outline-level (lambda () 1) outline-minor-mode-cycle t outline-minor-mode-highlight t - outline-minor-mode-use-buttons t)) + outline-minor-mode-use-buttons 'insert)) (defvar apropos-multi-type t "If non-nil, this apropos query concerns multiple types. diff --git a/lisp/outline.el b/lisp/outline.el index 2c3f9798ec4..53bfc4d556f 100644 --- a/lisp/outline.el +++ b/lisp/outline.el @@ -1817,7 +1817,7 @@ outline--insert-button (unless o (when (eq outline-minor-mode-use-buttons 'insert) (let ((inhibit-read-only t)) - (insert " ") + (insert (apply #'propertize " " (text-properties-at (point)))) (beginning-of-line))) (setq o (make-overlay (point) (1+ (point)))) (overlay-put o 'outline-button t) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 1e4aa4eba52..0790d3a5650 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -994,7 +994,7 @@ xref--xref-buffer-mode (setq-local add-log-current-defun-function #'xref--add-log-current-defun) (setq-local outline-minor-mode-cycle t - outline-minor-mode-use-buttons t + outline-minor-mode-use-buttons 'insert outline-search-function (lambda (&optional bound move backward looking-at) (outline-search-text-property commit 527eb11de20ac77877e5eaf67fa145515bd9a6d4 Author: Juri Linkov Date: Mon Dec 12 19:30:55 2022 +0200 * lisp/minibuffer.el (completions-group-separator): Revert f7816c94b6. Change face attribute from :underline back to :strike-through. https://lists.gnu.org/archive/html/emacs-devel/2022-10/msg01792.html diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 5faa3c8d4e8..6e42296e7ba 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1326,9 +1326,9 @@ completions-group-title :version "28.1") (defface completions-group-separator - '((t :inherit shadow :underline t)) + '((t :inherit shadow :strike-through t)) "Face used for the separator lines between the candidate groups." - :version "29.1") + :version "28.1") (defun completion--cycle-threshold (metadata) (let* ((cat (completion-metadata-get metadata 'category)) commit 42d740fb2cb25eef26a5d2e3cab86fa38f909652 Author: Stefan Kangas Date: Mon Dec 12 11:50:38 2022 +0100 ; Skip two eglot tests when typescript is missing * test/lisp/progmodes/eglot-tests.el (javascript-basic, project-wide-diagnostics-typescript): Skip tests when the npm package "typescript" is not installed. diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index fd0a68973ea..32eefd778fe 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -723,7 +723,8 @@ rust-on-type-formatting (ert-deftest javascript-basic () "Test basic autocompletion in a JavaScript LSP." - (skip-unless (executable-find "typescript-language-server")) + (skip-unless (and (executable-find "typescript-language-server") + (executable-find "tsserver"))) (eglot--with-fixture '(("project" . (("hello.js" . "console.log('Hello world!');")))) (with-current-buffer @@ -752,7 +753,8 @@ javascript-basic (ert-deftest project-wide-diagnostics-typescript () "Test diagnostics through multiple files in a TypeScript LSP." - (skip-unless (executable-find "typescript-language-server")) + (skip-unless (and (executable-find "typescript-language-server") + (executable-find "tsserver"))) (eglot--with-fixture '(("project" . (("hello.ts" . "const thing = 5;\nexport { thin }") ("hello2.ts" . "import { thing } from './hello'")))) commit 19ef86f775a39eab46231c3eb6fc702e06857a5a Author: Eli Zaretskii Date: Mon Dec 12 15:37:49 2022 +0200 ; Remove outdated text describing overlays * doc/lispref/display.texi (Overlays): Remove outdated text. (Bug#59996) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 340aa400cfa..13dc228261a 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -1462,17 +1462,7 @@ Overlays beginning and end. It also has properties that you can examine and set; these affect the display of the text within the overlay. -@cindex scalability of overlays -@cindex overlays, scalability -The visual effect of an overlay is the same as of the corresponding -text property (@pxref{Text Properties}). However, due to a different -implementation, overlays generally don't scale well (many operations -take a time that is proportional to the number of overlays in the -buffer). If you need to affect the visual appearance of many portions -in the buffer, we recommend using text properties. - -An overlay uses markers to record its beginning and end; thus, -editing the text of the buffer adjusts the beginning and end of each +Editing the text of the buffer adjusts the beginning and end of each overlay so that it stays with the text. When you create the overlay, you can specify whether text inserted at the beginning should be inside the overlay or outside, and likewise for the end of the overlay. commit 081bf5830070b8e9d1fcf695e8da6cd0cb89b553 Author: João Távora Date: Mon Dec 12 09:39:32 2022 +0000 Skip Eglot rust-analyzer tests if 'cargo' isn't available * test/lisp/progmodes/eglot-tests.el (project-wide-diagnostics-rust-analyzer) (diagnostic-tags-unnecessary-code): Skip unless "cargo" is found. diff --git a/test/lisp/progmodes/eglot-tests.el b/test/lisp/progmodes/eglot-tests.el index 2b4de8c27d0..fd0a68973ea 100644 --- a/test/lisp/progmodes/eglot-tests.el +++ b/test/lisp/progmodes/eglot-tests.el @@ -431,6 +431,7 @@ basic-diagnostics (ert-deftest diagnostic-tags-unnecessary-code () "Test rendering of diagnostics tagged \"unnecessary\"." (skip-unless (executable-find "rust-analyzer")) + (skip-unless (executable-find "cargo")) (eglot--with-fixture '(("diagnostic-tag-project" . (("main.rs" . @@ -780,6 +781,7 @@ project-wide-diagnostics-typescript (ert-deftest project-wide-diagnostics-rust-analyzer () "Test diagnostics through multiple files in a TypeScript LSP." (skip-unless (executable-find "rust-analyzer")) + (skip-unless (executable-find "cargo")) (eglot--with-fixture '(("project" . (("main.rs" .