From c289786886bade70f284035d85ae2c9b10df67c5 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Wed, 18 Jan 2023 15:32:12 -0800 Subject: [PATCH 01/23] ; Add commentary and dostring in c-ts-mode * lisp/progmodes/c-ts-mode.el: Add commentary. (c-ts-mode, c++-ts-mode): Add docstring. --- lisp/progmodes/c-ts-mode.el | 50 +++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index f9f75a0e452..0cf77c21d83 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -24,6 +24,34 @@ ;;; Commentary: ;; +;; This package provides major modes for C and C++, plus some handy +;; functions that are useful generally to major modes for C-like +;; languages. +;; +;; This package provides `c-ts-mode' for C, `c++-ts-mode' for C++, and +;; `c-or-c++-ts-mode' which automatically chooses the right mode for +;; C/C++ header files. +;; +;; To use these more by default, evaluate +;; +;; (add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode)) +;; (add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode)) +;; (add-to-list 'major-mode-remap-alist '(c-or-c++-mode . c-or-c++-ts-mode)) +;; +;; in your configuration. +;; +;; For C-like language major modes: +;; +;; - Use `c-ts-mode-comment-setup' to setup comment variables and +;; filling. +;; +;; - Use simple-indent matcher `c-ts-mode--looking-at-star' and anchor +;; `c-ts-mode--comment-start-after-first-star' for indenting block +;; comments. See `c-ts-mode--indent-styles' for example. +;; +;; - Use variable `c-ts-mode-indent-block-type-regexp' with indent +;; offset c-ts-mode--statement-offset for indenting statements. +;; Again, see `c-ts-mode--indent-styles' for example. ;;; Code: @@ -936,7 +964,16 @@ Set up: This mode is independent from the classic cc-mode.el based `c-mode', so configuration variables of that mode, like -`c-basic-offset', don't affect this mode." +`c-basic-offset', doesn't affect this mode. + +To use tree-sitter C/C++ modes by default, evaluate + + (add-to-list \\='major-mode-remap-alist \\='(c-mode . c-ts-mode)) + (add-to-list \\='major-mode-remap-alist \\='(c++-mode . c++-ts-mode)) + (add-to-list \\='major-mode-remap-alist + \\='(c-or-c++-mode . c-or-c++-ts-mode)) + +in your configuration." :group 'c (when (treesit-ready-p 'c) @@ -957,7 +994,16 @@ This mode is independent from the classic cc-mode.el based This mode is independent from the classic cc-mode.el based `c++-mode', so configuration variables of that mode, like -`c-basic-offset', don't affect this mode." +`c-basic-offset', don't affect this mode. + +To use tree-sitter C/C++ modes by default, evaluate + + (add-to-list \\='major-mode-remap-alist \\='(c-mode . c-ts-mode)) + (add-to-list \\='major-mode-remap-alist \\='(c++-mode . c++-ts-mode)) + (add-to-list \\='major-mode-remap-alist + \\='(c-or-c++-mode . c-or-c++-ts-mode)) + +in your configuration." :group 'c++ (when (treesit-ready-p 'cpp) From 0c6bfeddb21df16a6001328882fe2aaf6b063f68 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Wed, 18 Jan 2023 15:45:29 -0800 Subject: [PATCH 02/23] ; Update tree-sitter major mode manual * doc/lispref/parsing.texi (Tree-sitter Major Modes): Update. --- doc/lispref/parsing.texi | 51 +++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index e4a25249829..cebb59b6501 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi @@ -1692,26 +1692,48 @@ integration for a major mode. A major mode supporting tree-sitter features should roughly follow this pattern: -@c FIXME: Update this part once we settle on the exact format. @example @group (define-derived-mode woomy-mode prog-mode "Woomy" "A mode for Woomy programming language." - ;; Shared setup. - ... - (cond - ;; Tree-sitter setup. - ((treesit-ready-p 'woomy) + (when (treesit-ready-p 'woomy) (setq-local treesit-variables ...) - (treesit-major-mode-setup)) - ;; Non-tree-sitter setup. - (t - ...))) + ... + (treesit-major-mode-setup))) @end group @end example -First, the major mode should use @code{treesit-ready-p} to determine -whether tree-sitter can be activated in this mode. +@code{treesit-ready-p} automatically emits a warning if conditions for +enabling tree-sitter aren't met. + +If a tree-sitter major mode shares setup with their ``native'' +counterpart, they can create a ``base mode'' that contains the common +setup, like this: + +@example +@group +(define-derived-mode woomy--base-mode prog-mode "Woomy" + "An internal mode for Woomy programming language." + (common-setup) + ...) +@end group + +@group +(define-derived-mode woomy-mode woomy--base-mode "Woomy" + "A mode for Woomy programming language." + (native-setup) + ...) +@end group + +@group +(define-derived-mode woomy-ts-mode woomy--base-mode "Woomy" + "A mode for Woomy programming language." + (when (treesit-ready-p 'woomy) + (setq-local treesit-variables ...) + ... + (treesit-major-mode-setup))) +@end group +@end example @defun treesit-ready-p language &optional quiet This function checks for conditions for activating tree-sitter. It @@ -1722,15 +1744,12 @@ language grammar for @var{language} is available on the system This function emits a warning if tree-sitter cannot be activated. If @var{quiet} is @code{message}, the warning is turned into a message; -if @var{quiet} is @code{nil}, no warning or message is displayed. +if @var{quiet} is @code{t}, no warning or message is displayed. If all the necessary conditions are met, this function returns non-@code{nil}; otherwise it returns @code{nil}. @end defun -Next, the major mode should set up tree-sitter variables and call -@code{treesit-major-mode-setup}. - @defun treesit-major-mode-setup This function activates some tree-sitter features for a major mode. From b7d6bb47ee5f6a459a873c5053c2dde9df4f2e2f Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 19 Jan 2023 11:53:14 -0800 Subject: [PATCH 03/23] ; * lisp/treesit.el (treesit-font-lock-fontify-region): Minor fix. The check for treesit--font-lock-fast-mode is not really necessary, but anyway. --- lisp/treesit.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/treesit.el b/lisp/treesit.el index 7669ed6d18c..5a65d565236 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -987,7 +987,8 @@ If LOUDLY is non-nil, display some debugging information." (end-time (current-time))) ;; If for any query the query time is strangely long, ;; switch to fast mode (see comments above). - (when (and (> (time-to-seconds + (when (and (null treesit--font-lock-fast-mode) + (> (time-to-seconds (time-subtract end-time start-time)) 0.01)) (if (> treesit--font-lock-fast-mode-grace-count 0) From 7ca71d66dc7365a3dd8dd5f20638b4fa612fdc5e Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 19 Jan 2023 14:22:56 -0800 Subject: [PATCH 04/23] Fix various problems in treesit-explore-mode (bug#60800) * lisp/treesit.el: (treesit--explorer-kill-explorer-buffer): New function. (treesit-explore-mode): 1. Move prompt for language earlier, and terminate early if language not available. 2. Make sure desktop-save doesn't save the explorer buffer. 3. Kill the explorer buffer when the source buffer is killed. --- lisp/treesit.el | 59 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/lisp/treesit.el b/lisp/treesit.el index 5a65d565236..660039cc7cc 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -2621,6 +2621,11 @@ leaves point at the end of the last line of NODE." (when (not named) (overlay-put ov 'face 'treesit-explorer-anonymous-node))))) +(defun treesit--explorer-kill-explorer-buffer () + "Kill the explorer buffer of this buffer." + (when (buffer-live-p treesit--explorer-buffer) + (kill-buffer treesit--explorer-buffer))) + (define-derived-mode treesit--explorer-tree-mode special-mode "TS Explorer" "Mode for displaying syntax trees for `treesit-explore-mode'." @@ -2632,30 +2637,46 @@ Pops up a window showing the syntax tree of the source in the current buffer in real time. The corresponding node enclosing the text in the active region is highlighted in the explorer window." - :lighter " TSplay" + :lighter " TSexplore" (if treesit-explore-mode - (progn - (unless (buffer-live-p treesit--explorer-buffer) - (setq-local treesit--explorer-buffer - (get-buffer-create - (format "*tree-sitter explorer for %s*" - (buffer-name)))) - (setq-local treesit--explorer-language - (intern (completing-read + (let ((language (intern (completing-read "Language: " (mapcar #'treesit-parser-language - (treesit-parser-list))))) - (with-current-buffer treesit--explorer-buffer - (treesit--explorer-tree-mode))) - (display-buffer treesit--explorer-buffer - (cons nil '((inhibit-same-window . t)))) - (treesit--explorer-refresh) - (add-hook 'post-command-hook - #'treesit--explorer-post-command 0 t) - (setq-local treesit--explorer-last-node nil)) + (treesit-parser-list)))))) + (if (not (treesit-language-available-p language)) + (user-error "Cannot find tree-sitter grammar for %s: %s" + language (cdr (treesit-language-available-p + language t))) + ;; Create explorer buffer. + (unless (buffer-live-p treesit--explorer-buffer) + (setq-local treesit--explorer-buffer + (get-buffer-create + (format "*tree-sitter explorer for %s*" + (buffer-name)))) + (setq-local treesit--explorer-language language) + (with-current-buffer treesit--explorer-buffer + (treesit--explorer-tree-mode))) + (display-buffer treesit--explorer-buffer + (cons nil '((inhibit-same-window . t)))) + (treesit--explorer-refresh) + ;; Setup variables and hooks. + (add-hook 'post-command-hook + #'treesit--explorer-post-command 0 t) + (add-hook 'kill-buffer-hook + #'treesit--explorer-kill-explorer-buffer 0 t) + (setq-local treesit--explorer-last-node nil) + ;; Tell `desktop-save' to not save explorer buffers. + (when (boundp 'desktop-modes-not-to-save) + (unless (memq 'treesit--explorer-tree-mode + desktop-modes-not-to-save) + (push 'treesit--explorer-tree-mode + desktop-modes-not-to-save))))) + ;; Turn off explore mode. (remove-hook 'post-command-hook #'treesit--explorer-post-command t) - (kill-buffer treesit--explorer-buffer))) + (remove-hook 'post-command-hook + #'treesit--explorer-kill-explorer-buffer t) + (treesit--explorer-kill-explorer-buffer))) ;;; Install & build language grammar From 7b7b2b95138e691f1b155060b91a8998e3905651 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Thu, 19 Jan 2023 14:46:17 -0800 Subject: [PATCH 05/23] Fix c-ts-mode indent (bug#60873) * lisp/progmodes/c-ts-mode.el: (c-ts-mode--statement-offset): Handle the edge case. * test/lisp/progmodes/c-ts-mode-resources/indent.erts: Add a test. --- lisp/progmodes/c-ts-mode.el | 13 ++++++++++++- test/lisp/progmodes/c-ts-mode-resources/indent.erts | 13 +++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 0cf77c21d83..3d887971f64 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -285,7 +285,18 @@ PARENT is NODE's parent." (cl-incf level) (save-excursion (goto-char (treesit-node-start node)) - (cond ((bolp) nil) + ;; Add an extra level if the opening bracket is on its own + ;; line, except (1) it's at top-level, or (2) it's immedate + ;; parent is another block. + (cond ((bolp) nil) ; Case (1). + ((let ((parent-type (treesit-node-type + (treesit-node-parent node)))) + ;; Case (2). + (and parent-type + (string-match-p c-ts-mode-indent-block-type-regexp + parent-type))) + nil) + ;; Add a level. ((looking-back (rx bol (* whitespace)) (line-beginning-position)) (cl-incf level)))))) diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent.erts b/test/lisp/progmodes/c-ts-mode-resources/indent.erts index 70fce68b0ec..b8524432d02 100644 --- a/test/lisp/progmodes/c-ts-mode-resources/indent.erts +++ b/test/lisp/progmodes/c-ts-mode-resources/indent.erts @@ -92,6 +92,19 @@ int main() } =-=-= +Name: Concecutive blocks (GNU Style) (bug#60873) + +=-= +int +main (int argc, + char *argv[]) +{ + { + int i = 0; + } +} +=-=-= + Name: Multiline Parameter List (bug#60398) =-= From 370b1ac99ec4328981ce8502ecb03353dbea5041 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 03:54:36 +0200 Subject: [PATCH 06/23] ; ruby-ts-mode.el: Add customize-group mention to commentary --- lisp/progmodes/ruby-ts-mode.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 71562b46306..da2d00ce168 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -71,6 +71,8 @@ ;; ruby-ts-mode tries to adhere to the indentation related user ;; options from ruby-mode, such as ruby-indent-level, ;; ruby-indent-tabs-mode, and so on. +;; +;; Type 'M-x customize-group RET ruby RET' to see the options. ;; * IMenu ;; * Navigation From d66ac5285f72e0343d1cc6aae2db70a00b35feed Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 03:56:44 +0200 Subject: [PATCH 07/23] ruby-ts-mode: Highlight builtin methods * lisp/progmodes/ruby-mode.el (ruby-builtin-methods-with-reqs) (ruby-builtin-methods-no-reqs): New constants, extracted. (ruby-font-lock-keywords): Replace values with references. * lisp/progmodes/ruby-ts-mode.el (ruby-ts--builtin-methods): New variable. Construct regexp from aforementioned constants' values. * lisp/progmodes/ruby-ts-mode.el (ruby-ts--font-lock-settings): Use it. * lisp/progmodes/ruby-ts-mode.el (ruby-ts-mode): Add new font-lock feature: builtin-functions. * lisp/progmodes/ruby-ts-mode.el (ruby-ts--predefined-constants) (ruby-ts--predefined-variables): Unrelated to the rest of the patch, add string-start and string-end anchors. --- lisp/progmodes/ruby-mode.el | 150 +++++++++++++++++---------------- lisp/progmodes/ruby-ts-mode.el | 27 ++++-- 2 files changed, 98 insertions(+), 79 deletions(-) diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 2de7395f765..6e524693e37 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -141,6 +141,81 @@ This should only be called after matching against `ruby-here-doc-beg-re'." It should match the part after \"def\" and until \"=\".") +(defconst ruby-builtin-methods-with-reqs + '( ;; built-in methods on Kernel + "at_exit" + "autoload" + "autoload?" + "callcc" + "catch" + "eval" + "exec" + "format" + "lambda" + "load" + "loop" + "open" + "p" + "printf" + "proc" + "putc" + "require" + "require_relative" + "spawn" + "sprintf" + "syscall" + "system" + "throw" + "trace_var" + "trap" + "untrace_var" + "warn" + ;; keyword-like private methods on Module + "alias_method" + "attr" + "attr_accessor" + "attr_reader" + "attr_writer" + "define_method" + "extend" + "include" + "module_function" + "prepend" + "private_class_method" + "private_constant" + "public_class_method" + "public_constant" + "refine" + "using") + "List of built-in methods that require at least one argument.") + +(defconst ruby-builtin-methods-no-reqs + '("__callee__" + "__dir__" + "__method__" + "abort" + "binding" + "block_given?" + "caller" + "exit" + "exit!" + "fail" + "fork" + "global_variables" + "local_variables" + "print" + "private" + "protected" + "public" + "puts" + "raise" + "rand" + "readline" + "readlines" + "sleep" + "srand") + "List of built-in methods that only have optional arguments.") + (defvar ruby-use-smie t) (make-obsolete-variable 'ruby-use-smie nil "28.1") @@ -2292,84 +2367,13 @@ It will be properly highlighted even when the call omits parens.") ;; Core methods that have required arguments. (,(concat ruby-font-lock-keyword-beg-re - (regexp-opt - '( ;; built-in methods on Kernel - "at_exit" - "autoload" - "autoload?" - "callcc" - "catch" - "eval" - "exec" - "format" - "lambda" - "load" - "loop" - "open" - "p" - "printf" - "proc" - "putc" - "require" - "require_relative" - "spawn" - "sprintf" - "syscall" - "system" - "throw" - "trace_var" - "trap" - "untrace_var" - "warn" - ;; keyword-like private methods on Module - "alias_method" - "attr" - "attr_accessor" - "attr_reader" - "attr_writer" - "define_method" - "extend" - "include" - "module_function" - "prepend" - "private_class_method" - "private_constant" - "public_class_method" - "public_constant" - "refine" - "using") - 'symbols)) + (regexp-opt ruby-builtin-methods-with-reqs 'symbols)) (1 (unless (looking-at " *\\(?:[]|,.)}=]\\|$\\)") font-lock-builtin-face))) ;; Kernel methods that have no required arguments. (,(concat ruby-font-lock-keyword-beg-re - (regexp-opt - '("__callee__" - "__dir__" - "__method__" - "abort" - "binding" - "block_given?" - "caller" - "exit" - "exit!" - "fail" - "fork" - "global_variables" - "local_variables" - "print" - "private" - "protected" - "public" - "puts" - "raise" - "rand" - "readline" - "readlines" - "sleep" - "srand") - 'symbols)) + (regexp-opt ruby-builtin-methods-no-reqs 'symbols)) (1 font-lock-builtin-face)) ;; Here-doc beginnings. (,ruby-here-doc-beg-re diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index da2d00ce168..f365ca7f8c2 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -116,21 +116,30 @@ "Ruby's punctuation characters.") (defvar ruby-ts--predefined-constants - (rx (or "ARGF" "ARGV" "DATA" "ENV" "RUBY_COPYRIGHT" + (rx string-start + (or "ARGF" "ARGV" "DATA" "ENV" "RUBY_COPYRIGHT" "RUBY_DESCRIPTION" "RUBY_ENGINE" "RUBY_ENGINE_VERSION" "RUBY_PATCHLEVEL" "RUBY_PLATFORM" "RUBY_RELEASE_DATE" "RUBY_REVISION" "RUBY_VERSION" "STDERR" "STDIN" "STDOUT" - "TOPLEVEL_BINDING")) + "TOPLEVEL_BINDING") + string-end) "Ruby predefined global constants.") (defvar ruby-ts--predefined-variables - (rx (or "$!" "$@" "$~" "$&" "$‘" "$‘" "$+" "$=" "$/" "$\\" "$," "$;" + (rx string-start + (or "$!" "$@" "$~" "$&" "$‘" "$‘" "$+" "$=" "$/" "$\\" "$," "$;" "$." "$<" "$>" "$_" "$*" "$$" "$?" "$:" "$LOAD_PATH" "$LOADED_FEATURES" "$DEBUG" "$FILENAME" "$stderr" "$stdin" "$stdout" "$VERBOSE" "$-a" "$-i" "$-l" "$-p" - (seq "$" (+ digit)))) + (seq "$" (+ digit))) + string-end) "Ruby predefined global variables.") +(defvar ruby-ts--builtin-methods + (format "\\`%s\\'" (regexp-opt (append ruby-builtin-methods-no-reqs + ruby-builtin-methods-with-reqs))) + "Ruby built-in methods.") + (defconst ruby-ts--class-or-module-regex (rx string-start (or "class" "module" "singleton_class") @@ -324,6 +333,12 @@ values of OVERRIDE" (in_clause pattern: (identifier) @font-lock-variable-name-face)) + :language language + :feature 'builtin-functions + `((((identifier) @font-lock-builtin-face) + (:match ,ruby-ts--builtin-methods + @font-lock-builtin-face))) + ;; Yuan recommends also putting method definitions into the ;; 'function' category (thus keeping it in both). I've opted to ;; just use separate categories for them -- dgutov. @@ -1022,9 +1037,9 @@ leading double colon is not added." (setq-local treesit-font-lock-feature-list '(( comment method-definition parameter-definition) ( keyword regexp string type) - ( builtin-variable builtin-constant constant + ( builtin-variable builtin-constant builtin-functions delimiter escape-sequence - global instance + constant global instance interpolation literal symbol assignment) ( bracket error function operator punctuation))) From d0d34514097c03d787012478d5217449481cfc04 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 04:14:38 +0200 Subject: [PATCH 08/23] (ruby-ts-mode): Rename 'builtin-functions' to 'builtin-function' * lisp/progmodes/ruby-ts-mode.el (ruby-ts--font-lock-settings) (ruby-ts-mode): Rename 'builtin-functions' to 'builtin-function', for consistency with similar features. --- lisp/progmodes/ruby-ts-mode.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index f365ca7f8c2..58da5ef9c69 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -334,7 +334,7 @@ values of OVERRIDE" pattern: (identifier) @font-lock-variable-name-face)) :language language - :feature 'builtin-functions + :feature 'builtin-function `((((identifier) @font-lock-builtin-face) (:match ,ruby-ts--builtin-methods @font-lock-builtin-face))) @@ -1037,7 +1037,7 @@ leading double colon is not added." (setq-local treesit-font-lock-feature-list '(( comment method-definition parameter-definition) ( keyword regexp string type) - ( builtin-variable builtin-constant builtin-functions + ( builtin-variable builtin-constant builtin-function delimiter escape-sequence constant global instance interpolation literal symbol assignment) From d94dc606a0934e52f86bd939684867ada4b944fe Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 05:35:12 +0200 Subject: [PATCH 09/23] ruby-ts-mode: Claw back half of the performance drop from last change * lisp/progmodes/ruby-ts-mode.el (ruby-ts--builtin-method-p): New function. (ruby-ts--font-lock-settings): Use it instead of :match. --- lisp/progmodes/ruby-ts-mode.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 58da5ef9c69..2105aaaecab 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -208,6 +208,9 @@ values of OVERRIDE" (treesit-fontify-with-override (max plus-1 start) (min node-end end) font-lock-comment-face override))) +(defun ruby-ts--builtin-method-p (node) + (string-match-p ruby-ts--builtin-methods (treesit-node-text node t))) + (defun ruby-ts--font-lock-settings (language) "Tree-sitter font-lock settings for Ruby." (treesit-font-lock-rules @@ -336,8 +339,7 @@ values of OVERRIDE" :language language :feature 'builtin-function `((((identifier) @font-lock-builtin-face) - (:match ,ruby-ts--builtin-methods - @font-lock-builtin-face))) + (:pred ruby-ts--builtin-method-p @font-lock-builtin-face))) ;; Yuan recommends also putting method definitions into the ;; 'function' category (thus keeping it in both). I've opted to From b56cf28b325c927d5e51173b00f5e5354ba62def Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 05:41:39 +0200 Subject: [PATCH 10/23] ; (ruby-ts--predefined-variables): Make it a little shorter --- lisp/progmodes/ruby-ts-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 2105aaaecab..45174811605 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -131,7 +131,7 @@ "$." "$<" "$>" "$_" "$*" "$$" "$?" "$:" "$LOAD_PATH" "$LOADED_FEATURES" "$DEBUG" "$FILENAME" "$stderr" "$stdin" "$stdout" "$VERBOSE" "$-a" "$-i" "$-l" "$-p" - (seq "$" (+ digit))) + "$0" "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8" "$9") string-end) "Ruby predefined global variables.") From 6b2f85caa6cae1178b8abee531b0b9b0cf618a00 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 20 Jan 2023 10:28:26 +0200 Subject: [PATCH 11/23] Make tree-sitter based modes optional * lisp/progmodes/c-ts-mode.el: Update Commentary. Make 'auto-mode-alist' update conditional on the tree-sitter and grammar libraries being available. * lisp/progmodes/cmake-ts-mode.el: * lisp/progmodes/csharp-mode.el: * lisp/progmodes/dockerfile-ts-mode.el: * lisp/progmodes/go-ts-mode.el: * lisp/progmodes/java-ts-mode.el: * lisp/progmodes/js.el: * lisp/progmodes/json-ts-mode.el: * lisp/progmodes/python.el: * lisp/progmodes/ruby-ts-mode.el: * lisp/progmodes/typescript-ts-mode.el: * lisp/textmodes/css-mode.el: * lisp/textmodes/toml-ts-mode.el: * lisp/textmodes/yaml-ts-mode.el: Make 'auto-mode-alist' update for tree-sitter based modes be conditional on the tree-sitter and grammar libraries being available. (Bug#60559) --- etc/NEWS | 63 +++++++++++++--------------- lisp/progmodes/c-ts-mode.el | 50 +++++++++++++++++++--- lisp/progmodes/cmake-ts-mode.el | 8 ++-- lisp/progmodes/csharp-mode.el | 7 ++-- lisp/progmodes/dockerfile-ts-mode.el | 12 +++--- lisp/progmodes/go-ts-mode.el | 12 +++--- lisp/progmodes/java-ts-mode.el | 3 ++ lisp/progmodes/js.el | 5 ++- lisp/progmodes/json-ts-mode.el | 4 ++ lisp/progmodes/python.el | 5 ++- lisp/progmodes/ruby-ts-mode.el | 14 +++++++ lisp/progmodes/rust-ts-mode.el | 6 +-- lisp/progmodes/typescript-ts-mode.el | 12 +++--- lisp/textmodes/css-mode.el | 4 +- lisp/textmodes/toml-ts-mode.el | 5 ++- lisp/textmodes/yaml-ts-mode.el | 6 +-- 16 files changed, 140 insertions(+), 76 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 38f2db26a1a..fa0e7a1f661 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -34,13 +34,14 @@ This feature existed in Emacs 28.1, but was less easy to request. +++ ** Emacs can be built with the tree-sitter parsing library. -This library, together with grammar libraries, provides incremental -parsing capabilities for several popular programming languages and -other formatted files. Emacs built with this library offers major -modes, described elsewhere in this file, that are based on the -tree-sitter's parsers. If you have the tree-sitter library -installed, the configure script will automatically include it in the -build; use '--without-tree-sitter' at configure time to disable that. +This library, together with separate grammar libraries for each +language, provides incremental parsing capabilities for several +popular programming languages and other formatted files. Emacs built +with this library offers major modes, described elsewhere in this +file, that are based on the tree-sitter's parsers. If you have the +tree-sitter library installed, the configure script will automatically +include it in the build; use '--without-tree-sitter' at configure time +to disable that. Emacs modes based on the tree-sitter library require an additional grammar library for each mode. These grammar libraries provide the @@ -3183,19 +3184,19 @@ indentation, and navigation by defuns based on parsing the buffer text by a tree-sitter parser. Some major modes also offer support for Imenu and 'which-func'. -Where major modes already exist in Emacs for editing certain kinds of -files, the new modes based on tree-sitter are for now entirely -optional, and you must turn them on manually, or customize -'auto-mode-alist' to turn them on automatically. +The new modes based on tree-sitter are for now entirely optional, and +you must turn them on manually, or load them in your init file, or +customize 'auto-mode-alist' to turn them on automatically for certain +files. You can also customize 'major-mode-remap-alist' to +automatically turn on some tree-sitter based modes for the same files +for which a "built-in" mode would be turned on. For example: -Where no major modes previously existed in Emacs for editing the kinds -of files for which Emacs now provides a tree-sitter based mode, Emacs -will now try to enable these new modes automatically when you visit -such files, and will display a warning if the tree-sitter library or -the parser grammar library is not available. To prevent the warnings, -either build Emacs with tree-sitter and install the grammar libraries, -or customize 'auto-mode-alist' to specify some other major mode (or -even 'fundamental-mode') for those kinds of files. + (add-to-list 'major-mode-remap-alist '(ruby-mode . ruby-ts-mode)) + +If you try these modes and don't like them, you can go back to the +"built-in" modes by restarting Emacs. But please tell us why you +didn't like the tree-sitter based modes, so that we could try +improving them. Each major mode based on tree-sitter needs a language grammar library, usually named "libtree-sitter-LANG.so" ("libtree-sitter-LANG.dll" on @@ -3212,20 +3213,18 @@ We recommend to install these libraries in one of the standard system locations (the last place in the above list). If a language grammar library required by a mode is not found in any -of the above places, the mode will signal an error when you try to +of the above places, the mode will display a warning when you try to turn it on. +++ *** New major mode 'typescript-ts-mode'. A major mode based on the tree-sitter library for editing programs -in the TypeScript language. This mode is auto-enabled for files with -the ".ts" extension. +in the TypeScript language. +++ *** New major mode 'tsx-ts-mode'. A major mode based on the tree-sitter library for editing programs -in the TypeScript language, with support for TSX. This mode is -auto-enabled for files with the ".tsx" extension. +in the TypeScript language, with support for TSX. +++ *** New major mode 'c-ts-mode'. @@ -3275,15 +3274,12 @@ Bash shell scripts. +++ *** New major mode 'dockerfile-ts-mode'. A major mode based on the tree-sitter library for editing -Dockerfiles. This mode is auto-enabled for files which are named -"Dockerfile", have the "Dockerfile." prefix, or have the ".dockerfile" -extension. +Dockerfiles. +++ *** New major mode 'cmake-ts-mode'. A major mode based on the tree-sitter library for editing CMake files. -It is auto-enabled for files whose name is "CMakeLists.txt" or whose -extension is ".cmake". + +++ *** New major mode 'toml-ts-mode'. @@ -3293,23 +3289,22 @@ files written in TOML, a format for writing configuration files. +++ *** New major mode 'go-ts-mode'. A major mode based on the tree-sitter library for editing programs in -the Go language. It is auto-enabled for files with the ".go" extension. +the Go language. +++ *** New major mode 'go-mod-ts-mode'. A major mode based on the tree-sitter library for editing "go.mod" -files. It is auto-enabled for files which are named "go.mod". +files. +++ *** New major mode 'yaml-ts-mode'. A major mode based on the tree-sitter library for editing files -written in YAML. It is auto-enabled for files with the ".yaml" or -".yml" extensions. +written in YAML. +++ *** New major mode 'rust-ts-mode'. A major mode based on the tree-sitter library for editing programs in -the Rust language. It is auto-enabled for files with the ".rs" extension. +the Rust language. --- *** New major mode 'ruby-ts-mode'. diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 3d887971f64..5749e568185 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -32,13 +32,37 @@ ;; `c-or-c++-ts-mode' which automatically chooses the right mode for ;; C/C++ header files. ;; -;; To use these more by default, evaluate +;; To use these modes by default, assuming you have the respective +;; tree-sitter grammars available, do one of the following: ;; -;; (add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode)) -;; (add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode)) -;; (add-to-list 'major-mode-remap-alist '(c-or-c++-mode . c-or-c++-ts-mode)) +;; - If you have both C and C++ grammars installed, add ;; -;; in your configuration. +;; (require 'c-ts-mode) +;; +;; to your init file. +;; +;; - Add one or mode of the following to your init file: +;; +;; (add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode)) +;; (add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode)) +;; (add-to-list 'major-mode-remap-alist '(c-or-c++-mode . c-or-c++-ts-mode)) +;; +;; If you have only C grammar available, use only the first one; if +;; you have only the C++ grammar, use only the second one. +;; +;; - Customize 'auto-mode-alist' to turn one or more of the modes +;; automatically. For example: +;; +;; (add-to-list 'auto-mode-alist +;; '("\\(\\.ii\\|\\.\\(CC?\\|HH?\\)\\|\\.[ch]\\(pp\\|xx\\|\\+\\+\\)\\|\\.\\(cc\\|hh\\)\\)\\'" +;; . c++-ts-mode)) +;; +;; will turn on the c++-ts-mode for C++ source files. +;; +;; You can also turn on these modes manually in a buffer. Doing so +;; will set up Emacs to use the C/C++ modes defined here for other +;; files, provided that you have the corresponding parser grammar +;; libraries installed. ;; ;; For C-like language major modes: ;; @@ -1072,6 +1096,22 @@ the code is C or C++ and based on that chooses whether to enable (re-search-forward c-ts-mode--c-or-c++-regexp nil t)))) (c++-ts-mode) (c-ts-mode))) +;; The entries for C++ must come first to prevent *.c files be taken +;; as C++ on case-insensitive filesystems, since *.C files are C++, +;; not C. +(if (treesit-ready-p 'cpp) + (add-to-list 'auto-mode-alist + '("\\(\\.ii\\|\\.\\(CC?\\|HH?\\)\\|\\.[ch]\\(pp\\|xx\\|\\+\\+\\)\\|\\.\\(cc\\|hh\\)\\)\\'" + . c++-ts-mode))) + +(if (treesit-ready-p 'c) + (add-to-list 'auto-mode-alist + '("\\(\\.[chi]\\|\\.lex\\|\\.y\\(acc\\)?\\|\\.x[bp]m\\)\\'" + . c-ts-mode))) + +(if (and (treesit-ready-p 'cpp) + (treesit-ready-p 'c)) + (add-to-list 'auto-mode-alist '("\\.h\\'" . c-or-c++-ts-mode))) (provide 'c-ts-mode) diff --git a/lisp/progmodes/cmake-ts-mode.el b/lisp/progmodes/cmake-ts-mode.el index a31250f68be..c241a2868e5 100644 --- a/lisp/progmodes/cmake-ts-mode.el +++ b/lisp/progmodes/cmake-ts-mode.el @@ -194,10 +194,6 @@ the 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." @@ -229,6 +225,10 @@ the subtrees." (treesit-major-mode-setup))) +(if (treesit-ready-p 'cmake) + (add-to-list 'auto-mode-alist + '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)\\'" . cmake-ts-mode))) + (provide 'cmake-ts-mode) ;;; cmake-ts-mode.el ends here diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 81ce41618e7..04f7f222362 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -883,9 +883,6 @@ Return nil if there is no name or if NODE is not a defun node." node "name") t)))) -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode)) - ;;;###autoload (define-derived-mode csharp-mode prog-mode "C#" "Major mode for editing Csharp code. @@ -941,7 +938,9 @@ Key bindings: ("Struct" "\\`struct_declaration\\'" nil nil) ("Method" "\\`method_declaration\\'" nil nil))) - (treesit-major-mode-setup)) + (treesit-major-mode-setup) + + (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-ts-mode))) (provide 'csharp-mode) diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el index 3f8766e6713..2a295e885b0 100644 --- a/lisp/progmodes/dockerfile-ts-mode.el +++ b/lisp/progmodes/dockerfile-ts-mode.el @@ -132,12 +132,6 @@ the subtrees." (t `((,name . ,marker)))))) -;;;###autoload -(add-to-list 'auto-mode-alist - ;; NOTE: We can't use `rx' here, as it breaks bootstrap. - '("\\(?:Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" - . dockerfile-ts-mode)) - ;;;###autoload (define-derived-mode dockerfile-ts-mode prog-mode "Dockerfile" "Major mode for editing Dockerfiles, powered by tree-sitter." @@ -172,6 +166,12 @@ the subtrees." (treesit-major-mode-setup))) +(if (treesit-ready-p 'dockerfile) + (add-to-list 'auto-mode-alist + ;; NOTE: We can't use `rx' here, as it breaks bootstrap. + '("\\(?:Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" + . dockerfile-ts-mode))) + (provide 'dockerfile-ts-mode) ;;; dockerfile-ts-mode.el ends here diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el index 64e761d2f72..d552e1360e0 100644 --- a/lisp/progmodes/go-ts-mode.el +++ b/lisp/progmodes/go-ts-mode.el @@ -174,9 +174,6 @@ '((ERROR) @font-lock-warning-face)) "Tree-sitter font-lock settings for `go-ts-mode'.") -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode)) - ;;;###autoload (define-derived-mode go-ts-mode prog-mode "Go" "Major mode for editing Go, powered by tree-sitter." @@ -226,6 +223,9 @@ (treesit-major-mode-setup))) +(if (treesit-ready-p 'go) + (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))) + (defun go-ts-mode--defun-name (node) "Return the defun name of NODE. Return nil if there is no name or if NODE is not a defun node." @@ -345,9 +345,6 @@ what the parent of the node would be if it were a node." '((ERROR) @font-lock-warning-face)) "Tree-sitter font-lock settings for `go-mod-ts-mode'.") -;;;###autoload -(add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode)) - ;;;###autoload (define-derived-mode go-mod-ts-mode prog-mode "Go Mod" "Major mode for editing go.mod files, powered by tree-sitter." @@ -376,6 +373,9 @@ what the parent of the node would be if it were a node." (treesit-major-mode-setup))) +(if (treesit-ready-p 'gomod) + (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode))) + (provide 'go-ts-mode) ;;; go-ts-mode.el ends here diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index d29fcd80861..d909a366e5d 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -331,6 +331,9 @@ Return nil if there is no name or if NODE is not a defun node." ("Method" "\\`method_declaration\\'" nil nil))) (treesit-major-mode-setup)) +(if (treesit-ready-p 'java) + (add-to-list 'auto-mode-alist '("\\.java\\'" . java-ts-mode))) + (provide 'java-ts-mode) ;;; java-ts-mode.el ends here diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index cc556c4d0ec..176024863f1 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3843,7 +3843,10 @@ Currently there are `js-mode' and `js-ts-mode'." "method_definition") eos) nil nil))) - (treesit-major-mode-setup))) + (treesit-major-mode-setup) + + (add-to-list 'auto-mode-alist + '("\\(\\.js[mx]\\|\\.har\\)\\'" . js-ts-mode)))) ;;;###autoload (define-derived-mode js-json-mode js-mode "JSON" diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el index fbcda22acca..f54d0187f98 100644 --- a/lisp/progmodes/json-ts-mode.el +++ b/lisp/progmodes/json-ts-mode.el @@ -160,6 +160,10 @@ Return nil if there is no name or if NODE is not a defun node." (treesit-major-mode-setup)) +(if (treesit-ready-p 'json) + (add-to-list 'auto-mode-alist + '("\\.json\\'" . json-ts-mode))) + (provide 'json-ts-mode) ;;; json-ts-mode.el ends here diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 21d16db287c..a869cdc5fdb 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -6713,7 +6713,10 @@ implementations: `python-mode' and `python-ts-mode'." (treesit-major-mode-setup) (when python-indent-guess-indent-offset - (python-indent-guess-indent-offset)))) + (python-indent-guess-indent-offset)) + + (add-to-list 'auto-mode-alist + '("\\.py[iw]?\\'\\|python[0-9.]*" . python-ts-mode)))) ;;; Completion predicates for M-x ;; Commands that only make sense when editing Python code diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 45174811605..d143c06a8a4 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -1047,6 +1047,20 @@ leading double colon is not added." (treesit-major-mode-setup)) +(if (treesit-ready-p 'ruby) + ;; Copied from ruby-mode.el. + (add-to-list 'auto-mode-alist + (cons (concat "\\(?:\\.\\(?:" + "rbw?\\|ru\\|rake\\|thor" + "\\|jbuilder\\|rabl\\|gemspec\\|podspec" + "\\)" + "\\|/" + "\\(?:Gem\\|Rake\\|Cap\\|Thor" + "\\|Puppet\\|Berks\\|Brew" + "\\|Vagrant\\|Guard\\|Pod\\)file" + "\\)\\'") + 'ruby-ts-mode))) + (provide 'ruby-ts-mode) ;;; ruby-ts-mode.el ends here diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el index 7536726165e..08590ae6a86 100644 --- a/lisp/progmodes/rust-ts-mode.el +++ b/lisp/progmodes/rust-ts-mode.el @@ -275,9 +275,6 @@ Return nil if there is no name or if NODE is not a defun node." (treesit-node-text (treesit-node-child-by-field-name node "name") t)))) -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode)) - ;;;###autoload (define-derived-mode rust-ts-mode prog-mode "Rust" "Major mode for editing Rust, powered by tree-sitter." @@ -322,6 +319,9 @@ Return nil if there is no name or if NODE is not a defun node." (treesit-major-mode-setup))) +(if (treesit-ready-p 'rust) + (add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))) + (provide 'rust-ts-mode) ;;; rust-ts-mode.el ends here diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index ffd5b941daf..6aaa852895c 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -314,12 +314,6 @@ Argument LANGUAGE is either `typescript' or `tsx'." :override t '((escape_sequence) @font-lock-escape-face))) -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode)) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode)) - ;;;###autoload (define-derived-mode typescript-ts-base-mode prog-mode "TypeScript" "Major mode for editing TypeScript." @@ -375,6 +369,9 @@ Argument LANGUAGE is either `typescript' or `tsx'." (treesit-major-mode-setup))) +(if (treesit-ready-p 'typescript) + (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))) + ;;;###autoload (define-derived-mode tsx-ts-mode typescript-ts-base-mode "TypeScript[TSX]" "Major mode for editing TypeScript." @@ -410,6 +407,9 @@ Argument LANGUAGE is either `typescript' or `tsx'." (treesit-major-mode-setup))) +(if (treesit-ready-p 'tsx) + (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))) + (provide 'typescript-ts-mode) ;;; typescript-ts-mode.el ends here diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el index 8991610a50f..a1d7d4bbbec 100644 --- a/lisp/textmodes/css-mode.el +++ b/lisp/textmodes/css-mode.el @@ -1827,7 +1827,9 @@ can also be used to fill comments. (setq-local treesit-simple-imenu-settings `(( nil ,(rx bos (or "rule_set" "media_statement") eos) nil nil))) - (treesit-major-mode-setup))) + (treesit-major-mode-setup) + + (add-to-list 'auto-mode-alist '("\\.css\\'" . css-ts-mode)))) ;;;###autoload (define-derived-mode css-mode css-base-mode "CSS" diff --git a/lisp/textmodes/toml-ts-mode.el b/lisp/textmodes/toml-ts-mode.el index 2430c5f3e76..416542084f1 100644 --- a/lisp/textmodes/toml-ts-mode.el +++ b/lisp/textmodes/toml-ts-mode.el @@ -117,8 +117,6 @@ Return nil if there is no name or if NODE is not a defun node." (or (treesit-node-text (treesit-node-child node 1) t) "Root table")))) -(add-to-list 'auto-mode-alist '("\\.toml\\'" . toml-ts-mode)) - ;;;###autoload (define-derived-mode toml-ts-mode text-mode "TOML" "Major mode for editing TOML, powered by tree-sitter." @@ -155,6 +153,9 @@ Return nil if there is no name or if NODE is not a defun node." (treesit-major-mode-setup))) +(if (treesit-ready-p 'toml) + (add-to-list 'auto-mode-alist '("\\.toml\\'" . toml-ts-mode))) + (provide 'toml-ts-mode) ;;; toml-ts-mode.el ends here diff --git a/lisp/textmodes/yaml-ts-mode.el b/lisp/textmodes/yaml-ts-mode.el index 8c61ee062cf..a25230e6e61 100644 --- a/lisp/textmodes/yaml-ts-mode.el +++ b/lisp/textmodes/yaml-ts-mode.el @@ -117,9 +117,6 @@ '((ERROR) @font-lock-warning-face)) "Tree-sitter font-lock settings for `yaml-ts-mode'.") -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode)) - ;;;###autoload (define-derived-mode yaml-ts-mode text-mode "YAML" "Major mode for editing YAML, powered by tree-sitter." @@ -146,6 +143,9 @@ (treesit-major-mode-setup))) +(if (treesit-ready-p 'yaml) + (add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode))) + (provide 'yaml-ts-mode) ;;; yaml-ts-mode.el ends here From 40cf494b7ce8e2ad457d0e6841496239b756f313 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Fri, 20 Jan 2023 12:20:08 +0100 Subject: [PATCH 12/23] ; * etc/NEWS: Fix typos. --- etc/NEWS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index fa0e7a1f661..64c26f93c50 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3191,7 +3191,7 @@ files. You can also customize 'major-mode-remap-alist' to automatically turn on some tree-sitter based modes for the same files for which a "built-in" mode would be turned on. For example: - (add-to-list 'major-mode-remap-alist '(ruby-mode . ruby-ts-mode)) + (add-to-list 'major-mode-remap-alist '(ruby-mode . ruby-ts-mode)) If you try these modes and don't like them, you can go back to the "built-in" modes by restarting Emacs. But please tell us why you @@ -3280,7 +3280,6 @@ Dockerfiles. *** New major mode 'cmake-ts-mode'. A major mode based on the tree-sitter library for editing CMake files. - +++ *** New major mode 'toml-ts-mode'. An optional major mode based on the tree-sitter library for editing From 628b624176357117f6ff89980a81eff0920cde37 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Thu, 19 Jan 2023 20:19:40 -0800 Subject: [PATCH 13/23] Don't load erc-goodies atop erc.el * lisp/erc/erc.el: Commit c2d657e7c4fd9685591f2120007eabf78745919d "Move ERC's core dependencies to a separate file" ironed out ERC's interwoven dependencies for the better but didn't cleanly sidestep the goodies interdependency, specifically with regard to custom options. This reverts the tiny portion impacting this aspect by once again requiring `erc-goodies' at the very end of ERC's main library. Special thanks to Libera.Chat user jrm for reporting this bug. --- lisp/erc/erc.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 7f51b7bfb2e..ff1820cfaf2 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -61,7 +61,6 @@ (load "erc-loaddefs" 'noerror 'nomessage) (require 'erc-networks) -(require 'erc-goodies) (require 'erc-backend) (require 'cl-lib) (require 'format-spec) @@ -7386,4 +7385,6 @@ Customize `erc-url-connect-function' to override this." (provide 'erc) +;; FIXME this is a temporary stopgap for Emacs 29. +(require 'erc-goodies) ;;; erc.el ends here From c7e02eaa3d9af545f2acbb747da7a606fb0d1277 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Thu, 19 Jan 2023 14:34:10 +0100 Subject: [PATCH 14/23] Handle after arg correctly in `keymap-set-after' * lisp/keymap.el (keymap-set-after): AFTER: t means the same as nil, so just change it to nil. (Bug#60867) --- lisp/keymap.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lisp/keymap.el b/lisp/keymap.el index 315eaab7560..2caaafabb94 100644 --- a/lisp/keymap.el +++ b/lisp/keymap.el @@ -186,6 +186,7 @@ a menu, so this function is not useful for non-menu keymaps." (declare (indent defun) (compiler-macro (lambda (form) (keymap--compile-check key) form))) (keymap--check key) + (when (eq after t) (setq after nil)) ; nil and t are treated the same (when after (keymap--check after)) (define-key-after keymap (key-parse key) definition From 8904a26a9d227e464ac1c6b054a0e3cf1bfb3016 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Thu, 19 Jan 2023 15:56:21 +0100 Subject: [PATCH 15/23] Improve `keymap-set-after' documentation * doc/lispref/keymaps.texi (Changing Key Bindings): Mention `key-valid-p' (Modifying Menus): Correct description of KEY arg. --- doc/lispref/keymaps.texi | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi index 1c548af1990..7876780dcd4 100644 --- a/doc/lispref/keymaps.texi +++ b/doc/lispref/keymaps.texi @@ -1378,7 +1378,8 @@ Binding Conventions}). or if @var{key} is not a valid key. @var{key} is a string representing a single key or a series of key -strokes. Key strokes are separated by a single space character. +strokes, and must satisfy @code{key-valid-p}. Key strokes are +separated by a single space character. Each key stroke is either a single character, or the name of an event, surrounded by angle brackets. In addition, any key stroke @@ -1413,6 +1414,7 @@ The only keys that have a special shorthand syntax are @kbd{NUL}, The modifiers have to be specified in alphabetical order: @samp{A-C-H-M-S-s}, which is @samp{Alt-Control-Hyper-Meta-Shift-super}. +@findex keymap-set @defun keymap-set keymap key binding This function sets the binding for @var{key} in @var{keymap}. (If @var{key} is more than one event long, the change is actually made @@ -3079,13 +3081,13 @@ the menu. To put it elsewhere in the menu, use @code{keymap-set-after}: @defun keymap-set-after map key binding &optional after Define a binding in @var{map} for @var{key}, with value @var{binding}, just like @code{define-key}, but position the binding in @var{map} after -the binding for the event @var{after}. The argument @var{key} should be -of length one---a vector or string with just one element. But -@var{after} should be a single event type---a symbol or a character, not -a sequence. The new binding goes after the binding for @var{after}. If -@var{after} is @code{t} or is omitted, then the new binding goes last, at -the end of the keymap. However, new bindings are added before any -inherited keymap. +the binding for the event @var{after}. The argument @var{key} should +represent a single menu item or key, and @var{after} should be a +single event type---a symbol or a character, not a sequence. The new +binding goes after the binding for @var{after}. If @var{after} is +@code{t} or is omitted, then the new binding goes last, at the end of +the keymap. However, new bindings are added before any inherited +keymap. Here is an example: From dcd59457b48f1fb115e9847eef1c983406885717 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Thu, 19 Jan 2023 15:58:51 +0100 Subject: [PATCH 16/23] Use `key-parse' in `keymap-lookup' It's stricter than `kbd', and doesn't try to do anything with key sequences that look like macros. * lisp/keymap.el (keymap-lookup): Use `key-parse' instead of `kbd'. * test/src/keymap-tests.el (keymap-set-after-menus): Test the `keymap-set-after' API. --- lisp/keymap.el | 2 +- test/src/keymap-tests.el | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lisp/keymap.el b/lisp/keymap.el index 2caaafabb94..89461416a9c 100644 --- a/lisp/keymap.el +++ b/lisp/keymap.el @@ -405,7 +405,7 @@ specified buffer position instead of point are used." (symbolp value)) (or (command-remapping value) value) value)) - (key-binding (kbd key) accept-default no-remap position))) + (key-binding (key-parse key) accept-default no-remap position))) (defun keymap-local-lookup (keys &optional accept-default) "Return the binding for command KEYS in current local keymap only. diff --git a/test/src/keymap-tests.el b/test/src/keymap-tests.el index b7715a280a6..d7be2cac53a 100644 --- a/test/src/keymap-tests.el +++ b/test/src/keymap-tests.el @@ -430,6 +430,22 @@ g .. h foo (make-non-key-event 'keymap-tests-event) (should (equal (where-is-internal 'keymap-tests-command) '([3 103])))) +(ert-deftest keymap-set-consistency () + (let ((k (make-sparse-keymap))) + ;; `keymap-set' returns the binding, `keymap-set-after' doesn't, + ;; so we need to check for nil. + (should (keymap-set k "a" "a")) + (should (equal (keymap-lookup k "a") (key-parse "a"))) + (should-not (keymap-set-after k "b" "b")) + (should (equal (keymap-lookup k "b") (key-parse "b"))) + (should-not (keymap-set-after k "d" "d" t)) + (should (equal (keymap-lookup k "d") (key-parse "d"))) + (should-not (keymap-set-after k "e" "e" nil)) + (should (equal (keymap-lookup k "e") (key-parse "e"))) + ;; This doesn't fail, but it does not add the 'f' binding after 'a' + (should-not (keymap-set-after k "f" "f" "a")) + (should (equal (keymap-lookup k "f") (key-parse "f"))))) + (ert-deftest keymap-test-duplicate-definitions () "Check that defvar-keymap rejects duplicate key definitions." (should-error From 06953fc8e1d70397f421e1c6efc327af8e0bad6c Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Thu, 19 Jan 2023 16:05:45 +0100 Subject: [PATCH 17/23] Make `keymap-set-after' work for menus It still doesn't work for an AFTER that's a key, though, since `key-parse' produces vectors, and keymaps contain integers. * lisp/keymap.el (keymap-set-after): Only parse AFTER as a key if it's a string. For consistency, use `key-parse' on the definition if it's a string, just like `keymap-set'. * test/src/keymap-tests.el (keymap-tests--command-3): New dummy command. (keymap-set-after-menus): New test. Check that we can insert a menu item after a specific entry. --- lisp/keymap.el | 12 +++++++++--- test/src/keymap-tests.el | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lisp/keymap.el b/lisp/keymap.el index 89461416a9c..791221f2459 100644 --- a/lisp/keymap.el +++ b/lisp/keymap.el @@ -187,10 +187,16 @@ a menu, so this function is not useful for non-menu keymaps." (compiler-macro (lambda (form) (keymap--compile-check key) form))) (keymap--check key) (when (eq after t) (setq after nil)) ; nil and t are treated the same - (when after - (keymap--check after)) + (when (stringp after) + (keymap--check after) + (setq after (key-parse after))) + ;; If we're binding this key to another key, then parse that other + ;; key, too. + (when (stringp definition) + (keymap--check definition) + (setq definition (key-parse definition))) (define-key-after keymap (key-parse key) definition - (and after (key-parse after)))) + after)) (defun key-parse (keys) "Convert KEYS to the internal Emacs key representation. diff --git a/test/src/keymap-tests.el b/test/src/keymap-tests.el index d7be2cac53a..aa710519825 100644 --- a/test/src/keymap-tests.el +++ b/test/src/keymap-tests.el @@ -226,6 +226,7 @@ commit 86c19714b097aa477d339ed99ffb5136c755a046." (defun keymap-tests--command-1 () (interactive) nil) (defun keymap-tests--command-2 () (interactive) nil) +(defun keymap-tests--command-3 () (interactive) nil) (put 'keymap-tests--command-1 :advertised-binding [?y]) (ert-deftest keymap-where-is-internal () @@ -446,6 +447,22 @@ g .. h foo (should-not (keymap-set-after k "f" "f" "a")) (should (equal (keymap-lookup k "f") (key-parse "f"))))) +(ert-deftest keymap-set-after-menus () + (let ((map (make-sparse-keymap))) + (keymap-set map "" + '(menu-item "Run Command 1" keymap-tests--command-1 + :help "Command 1 Help")) + (keymap-set-after map "" + '(menu-item "Run Command 2" keymap-tests--command-2 + :help "Command 2 Help")) + (keymap-set-after map "" + '(menu-item "Run Command 3" keymap-tests--command-3 + :help "Command 3 Help") + 'cmd1) + (should (equal (caadr map) 'cmd1)) + (should (equal (caaddr map) 'cmd3)) + (should (equal (caar (last map)) 'cmd2)))) + (ert-deftest keymap-test-duplicate-definitions () "Check that defvar-keymap rejects duplicate key definitions." (should-error From 67ee627c38d5817a091a89e0a356fb8028c70e1a Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 18:43:56 +0200 Subject: [PATCH 18/23] (project-try-vc): Add string-start and string-end anchors to marker-re * lisp/progmodes/project.el (project-try-vc): Add string-start and string-end anchors to marker-re (bug#60956). --- lisp/progmodes/project.el | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index dc87cb8e15d..59270070484 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1,7 +1,7 @@ ;;; project.el --- Operations on the current project -*- lexical-binding: t; -*- ;; Copyright (C) 2015-2023 Free Software Foundation, Inc. -;; Version: 0.9.4 +;; Version: 0.9.5 ;; Package-Requires: ((emacs "26.1") (xref "1.4.0")) ;; This is a GNU ELPA :core package. Avoid using functionality that @@ -514,11 +514,14 @@ project backend implementation of `project-external-roots'.") (lambda (b) (assoc-default b backend-markers-alist)) vc-handled-backends))) (marker-re - (mapconcat - (lambda (m) (format "\\(%s\\)" (wildcard-to-regexp m))) - (append backend-markers - (project--value-in-dir 'project-vc-extra-root-markers dir)) - "\\|")) + (concat + "\\`" + (mapconcat + (lambda (m) (format "\\(%s\\)" (wildcard-to-regexp m))) + (append backend-markers + (project--value-in-dir 'project-vc-extra-root-markers dir)) + "\\|") + "\\'")) (locate-dominating-stop-dir-regexp (or vc-ignore-dir-regexp locate-dominating-stop-dir-regexp)) last-matches From 0cf053648a4f7d33f78700e40c0d5b790814135a Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 19:08:22 +0200 Subject: [PATCH 19/23] ; ruby-ts-mode: Update font-lock features list in Commentary --- lisp/progmodes/ruby-ts-mode.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index d143c06a8a4..4a00914371f 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -50,11 +50,11 @@ ;; Currently tree treesit-font-lock-feature-list is set with the ;; following levels: -;; 1: comment method-definition +;; 1: comment method-definition parameter-definition ;; 2: keyword regexp string type -;; 3: builtin-variable builtin-constant constant +;; 3: builtin-variable builtin-constant builtin-function ;; delimiter escape-sequence -;; global instance +;; constant global instance ;; interpolation literal symbol assignment ;; 4: bracket error function operator punctuation From 472f142598566fbaeedcacaf9a9c757a1281c0c5 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 19:25:12 +0200 Subject: [PATCH 20/23] ; ruby-ts-mode: Add a Version tag --- lisp/progmodes/ruby-ts-mode.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 4a00914371f..2a7d850c3e3 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -5,6 +5,7 @@ ;; Author: Perry Smith ;; Created: December 2022 ;; Keywords: ruby languages tree-sitter +;; Version: 0.2 ;; This file is part of GNU Emacs. From 34793337783489297313c67d4a56682514877597 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Fri, 20 Jan 2023 23:32:21 +0200 Subject: [PATCH 21/23] * lisp/org/ob-ruby.el: Fix outdated comments. --- lisp/org/ob-ruby.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lisp/org/ob-ruby.el b/lisp/org/ob-ruby.el index 03c94b1ba99..b94bc73dd79 100644 --- a/lisp/org/ob-ruby.el +++ b/lisp/org/ob-ruby.el @@ -29,11 +29,10 @@ ;; - ruby and irb executables :: https://www.ruby-lang.org/ ;; -;; - ruby-mode :: Can be installed through ELPA, or from -;; https://github.com/eschulte/rinari/raw/master/util/ruby-mode.el +;; - ruby-mode :: Comes with Emacs. ;; ;; - inf-ruby mode :: Can be installed through ELPA, or from -;; https://github.com/eschulte/rinari/raw/master/util/inf-ruby.el +;; https://raw.githubusercontent.com/nonsequitur/inf-ruby/master/inf-ruby.el ;;; Code: From d63e1a89518338bc3450b11d6c2d4644cb0440e1 Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Fri, 20 Jan 2023 22:37:47 +0100 Subject: [PATCH 22/23] Use point-min to anchor top-level constructs (bug#60602) * lisp/progmodes/c-ts-mode.el (c-ts-mode--indent-styles): New anchor. * lisp/progmodes/java-ts-mode.el (java-ts-mode--indent-rules): New anchor. * lisp/progmodes/ruby-ts-mode.el (ruby-ts--indent-rules): New anchor. * lisp/progmodes/typescript-ts-mode.el (typescript-ts-mode--indent-rules): New anchor. --- lisp/progmodes/c-ts-mode.el | 2 +- lisp/progmodes/java-ts-mode.el | 2 +- lisp/progmodes/ruby-ts-mode.el | 2 +- lisp/progmodes/typescript-ts-mode.el | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 5749e568185..b13e2036f4f 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -167,7 +167,7 @@ delimiters < and >'s." "Indent rules supported by `c-ts-mode'. MODE is either `c' or `cpp'." (let ((common - `(((parent-is "translation_unit") parent-bol 0) + `(((parent-is "translation_unit") point-min 0) ((node-is ")") parent 1) ((node-is "]") parent-bol 0) ((node-is "else") parent-bol 0) diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index d909a366e5d..8251d9603c3 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -69,7 +69,7 @@ (defvar java-ts-mode--indent-rules `((java - ((parent-is "program") parent-bol 0) + ((parent-is "program") point-min 0) ((node-is "}") (and parent parent-bol) 0) ((node-is ")") parent-bol 0) ((node-is "]") parent-bol 0) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 2a7d850c3e3..ac0b5d7c6d4 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -555,7 +555,7 @@ a statement container is a node that matches (let ((common `( ;; Slam all top level nodes to the left margin - ((parent-is "program") parent 0) + ((parent-is "program") point-min 0) ;; Do not indent here docs or the end. Not sure why it ;; takes the grand-parent but ok fine. diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index 6aaa852895c..903be93422d 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -69,7 +69,7 @@ "Rules used for indentation. Argument LANGUAGE is either `typescript' or `tsx'." `((,language - ((parent-is "program") parent-bol 0) + ((parent-is "program") point-min 0) ((node-is "}") parent-bol 0) ((node-is ")") parent-bol 0) ((node-is "]") parent-bol 0) From b3de81a6ee3b379fc1dfb9a071e469365081f438 Mon Sep 17 00:00:00 2001 From: Mike Kupfer Date: Mon, 16 Jan 2023 13:50:49 -0800 Subject: [PATCH 23/23] MH-E: handle removal of mhparam libdir from nmh 1.8 * lisp/mh-e/mh-e.el (mh-variant-nmh-info): If "libdir" doesn't work, try "libexecdir" (Bug#60952) (SF#491). --- lisp/mh-e/mh-e.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lisp/mh-e/mh-e.el b/lisp/mh-e/mh-e.el index 1640c23e002..34c809a5ecd 100644 --- a/lisp/mh-e/mh-e.el +++ b/lisp/mh-e/mh-e.el @@ -764,6 +764,8 @@ This assumes that a temporary buffer is set up." ;; Sample '-version' outputs: ;; mhparam -- nmh-1.1-RC1 [compiled on chaak at Fri Jun 20 11:03:28 PDT 2003] ;; install-mh -- nmh-1.7.1 built October 26, 2019 on build-server-000 + ;; "libdir" was deprecated in nmh-1.7 in favor of "libexecdir", and + ;; removed completely in nmh-1.8. (let ((install-mh (expand-file-name "install-mh" dir))) (when (mh-file-command-p install-mh) (erase-buffer) @@ -774,7 +776,8 @@ This assumes that a temporary buffer is set up." (mh-progs dir)) `(,version (variant nmh) - (mh-lib-progs ,(mh-profile-component "libdir")) + (mh-lib-progs ,(or (mh-profile-component "libdir") + (mh-profile-component "libexecdir"))) (mh-lib ,(mh-profile-component "etcdir")) (mh-progs ,dir) (flists ,(file-exists-p