From b5bea14ca178b9f0c3944a9a14363c9923853814 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Sun, 19 Feb 2023 14:27:27 +0200 Subject: [PATCH 01/16] * lisp/progmodes/xref.el: Bump the version. --- lisp/progmodes/xref.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 581eda0513e..38c424402a0 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -1,7 +1,7 @@ ;;; xref.el --- Cross-referencing commands -*-lexical-binding:t-*- ;; Copyright (C) 2014-2023 Free Software Foundation, Inc. -;; Version: 1.6.1 +;; Version: 1.6.2 ;; Package-Requires: ((emacs "26.1")) ;; This is a GNU ELPA :core package. Avoid functionality that is not From 1ac05eac74bfd927de2d399cacc68a77ee72847d Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Sun, 19 Feb 2023 19:18:19 +0200 Subject: [PATCH 02/16] rust-ts-mode--font-lock-settings: Avoid the explicit 'default' face * lisp/progmodes/rust-ts-mode.el (rust-ts-mode--fontify-scope) (rust-ts-mode--fontify-tail): New functions. (rust-ts-mode--font-lock-settings): Use them instead of a lot of more complex queries (bug#61302). Thus avoid having to create block fontification by other features using the 'default' face. Replace the catch-all query for 'variable' with an enumeration of possible parent nodes. --- lisp/progmodes/rust-ts-mode.el | 109 ++++++++++++++------------------- 1 file changed, 47 insertions(+), 62 deletions(-) diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el index e53cdb5ce7d..2ecff631f54 100644 --- a/lisp/progmodes/rust-ts-mode.el +++ b/lisp/progmodes/rust-ts-mode.el @@ -209,50 +209,15 @@ `((scoped_use_list path: (identifier) @font-lock-constant-face) (scoped_use_list path: (scoped_identifier name: (identifier) @font-lock-constant-face)) - ((use_as_clause alias: (identifier) @font-lock-type-face) (:match "^[A-Z]" @font-lock-type-face)) ((use_as_clause path: (identifier) @font-lock-type-face) (:match "^[A-Z]" @font-lock-type-face)) - ((use_as_clause path: - (scoped_identifier path: (_) - name: (identifier) @font-lock-type-face)) - (:match "^[A-Z]" @font-lock-type-face)) - (use_as_clause path: (scoped_identifier name: (identifier) @default)) - - ((use_declaration - argument: (scoped_identifier - path: (_) @font-lock-constant-face - name: (identifier) @font-lock-type-face)) - (:match "^[A-Z]" @font-lock-type-face)) - (use_declaration - argument: (scoped_identifier - name: (identifier) @default)) - - (use_declaration - argument: (scoped_identifier - path: (scoped_identifier - path: (_) @font-lock-constant-face - name: (identifier) @font-lock-constant-face) - name: (identifier) @default)) - - (use_declaration - argument: (scoped_use_list - path: (scoped_identifier - path: (_) @font-lock-constant-face - name: (identifier) @font-lock-constant-face))) - ((use_list (identifier) @font-lock-type-face) (:match "^[A-Z]" @font-lock-type-face)) - (use_list (identifier) @default) - ((use_list (scoped_identifier path: (_) - name: (identifier) @font-lock-type-face)) - (:match "^[A-Z]" @font-lock-type-face)) - (use_list (scoped_identifier path: (_) - name: (identifier) @default)) - (use_wildcard (scoped_identifier - name: (identifier) @font-lock-constant-face)) - + (use_wildcard [(identifier) @rust-ts-mode--fontify-scope + (scoped_identifier + name: (identifier) @rust-ts-mode--fontify-scope)]) (enum_variant name: (identifier) @font-lock-type-face) (match_arm pattern: (match_pattern (_ type: (identifier) @font-lock-type-face))) @@ -263,31 +228,13 @@ (mod_item name: (identifier) @font-lock-constant-face) (primitive_type) @font-lock-type-face (type_identifier) @font-lock-type-face - ((scoped_identifier name: (identifier) @font-lock-type-face) - (:match "^[A-Z]" @font-lock-type-face)) - ((scoped_identifier path: (identifier) @font-lock-type-face) - (:match "^[A-Z]" @font-lock-type-face)) - ((scoped_identifier - path: [(identifier) @font-lock-type-face - (scoped_identifier - name: (identifier) @font-lock-type-face)]) - (:match "^[A-Z]" @font-lock-type-face)) + ((scoped_identifier name: (identifier) @rust-ts-mode--fontify-tail)) ((scoped_identifier path: (identifier) @font-lock-type-face) (:match "^\\(u8\\|u16\\|u32\\|u64\\|u128\\|usize\\|i8\\|i16\\|i32\\|i64\\|i128\\|isize\\|char\\|str\\)$" @font-lock-type-face)) - (scoped_identifier path: (_) @font-lock-constant-face - name: (identifier) @font-lock-type-face) - (scoped_identifier path: (scoped_identifier - name: (identifier) @font-lock-constant-face)) - (scoped_type_identifier path: (_) @font-lock-constant-face) - (scoped_type_identifier - path: (scoped_identifier - path: (_) @font-lock-constant-face - name: (identifier) @font-lock-constant-face)) - (type_identifier) @font-lock-type-face - ;; Ensure function calls aren't highlighted as types. - (call_expression function: (scoped_identifier name: (identifier) @default))) + ((scoped_identifier path: (identifier) @rust-ts-mode--fontify-scope)) + (type_identifier) @font-lock-type-face) :language 'rust :feature 'property @@ -303,9 +250,25 @@ :language 'rust :feature 'variable - '((identifier) @font-lock-variable-name-face - ;; Everything in a token_tree is an identifier. - (token_tree (identifier) @default)) + '((arguments (identifier) @font-lock-variable-name-face) + (array_expression (identifier) @font-lock-variable-name-face) + (assignment_expression right: (identifier) @font-lock-variable-name-face) + (binary_expression left: (identifier) @font-lock-variable-name-face) + (binary_expression right: (identifier) @font-lock-variable-name-face) + (block (identifier) @font-lock-variable-name-face) + (compound_assignment_expr right: (identifier) @font-lock-variable-name-face) + (field_expression value: (identifier) @font-lock-variable-name-face) + (field_initializer value: (identifier) @font-lock-variable-name-face) + (if_expression condition: (identifier) @font-lock-variable-name-face) + (let_condition value: (identifier) @font-lock-variable-name-face) + (let_declaration value: (identifier) @font-lock-variable-name-face) + (match_arm value: (identifier) @font-lock-variable-name-face) + (match_expression value: (identifier) @font-lock-variable-name-face) + (reference_expression value: (identifier) @font-lock-variable-name-face) + (return_expression (identifier) @font-lock-variable-name-face) + (tuple_expression (identifier) @font-lock-variable-name-face) + (unary_expression (identifier) @font-lock-variable-name-face) + (while_expression condition: (identifier) @font-lock-variable-name-face)) :language 'rust :feature 'escape-sequence @@ -318,6 +281,28 @@ '((ERROR) @font-lock-warning-face)) "Tree-sitter font-lock settings for `rust-ts-mode'.") +(defun rust-ts-mode--fontify-scope (node override start end &optional tail-p) + (let* ((case-fold-search nil) + (face + (cond + ((string-match-p "^[A-Z]" (treesit-node-text node)) + 'font-lock-type-face) + ((and + tail-p + (string-match-p + "\\`\\(?:use_list\\|call_expression\\|use_as_clause\\|use_declaration\\)\\'" + (treesit-node-type (treesit-node-parent (treesit-node-parent node))))) + nil) + (t 'font-lock-constant-face)))) + (when face + (treesit-fontify-with-override + (treesit-node-start node) (treesit-node-end node) + face + override start end)))) + +(defun rust-ts-mode--fontify-tail (node override start end) + (rust-ts-mode--fontify-scope node override start end t)) + (defalias 'rust-ts-mode--fontify-pattern (and (treesit-available-p) From 683961cd733d4a660ffdb9c1a6e6ad565ffae2cb Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Sun, 19 Feb 2023 21:30:02 +0200 Subject: [PATCH 03/16] * lisp/simple.el (choose-completion): Check for completion-in-region-mode. Don't use base-affixes when completion-use-base-affixes is non-nil in completion-in-region-mode (bug#61535). --- lisp/simple.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lisp/simple.el b/lisp/simple.el index 27aeb2fa8cc..5f23910efc6 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -9884,7 +9884,12 @@ minibuffer, but don't quit the completions window." (with-current-buffer buffer (choose-completion-string choice buffer - (or (and completion-use-base-affixes base-affixes) + ;; Don't allow affixes to replace the whole buffer when not + ;; in the minibuffer. Thus check for `completion-in-region-mode' + ;; to ignore non-nil value of `completion-use-base-affixes' set by + ;; `minibuffer-choose-completion'. + (or (and (not completion-in-region-mode) + completion-use-base-affixes base-affixes) base-position ;; If all else fails, just guess. (list (choose-completion-guess-base-position choice))) From d397f3d508464b1eab2d2b3b7e4c3b80aff9d536 Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Fri, 17 Feb 2023 23:46:24 +0100 Subject: [PATCH 04/16] Add comment style toggle for c-ts-mode (bug#61550) * lisp/progmodes/c-ts-mode.el (c-ts-mode-toggle-comment-style): New command. (c-ts-base-mode-map): Add binding. (c-ts-mode-set-modeline): New function. (c-ts-mode): Set modeline. (c++-ts-mode): Set modeline. --- lisp/progmodes/c-ts-mode.el | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index a60c464093e..05875e9267a 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -87,6 +87,25 @@ :safe 'integerp :group 'c) +(defun c-ts-mode-toggle-comment-style () + "Toggle the comment style between block and line comments. +Optional numeric ARG, if supplied, switches to block comment +style when positive, to line comment style when negative, and +just toggles it when zero or left out." + (interactive) + (pcase-let ((`(,starter . ,ender) + (if (string= comment-start "// ") + (cons "/* " " */") + (cons "// " "")))) + (setq-local comment-start starter + comment-end ender)) + (c-ts-mode-set-modeline)) + +(defun c-ts-mode-set-modeline () + (setq mode-name + (concat (if (eq major-mode 'c-ts-mode) "C" "C++") comment-start)) + (force-mode-line-update)) + (defun c-ts-mode--indent-style-setter (sym val) "Custom setter for `c-ts-mode-set-style'. @@ -740,7 +759,8 @@ the semicolon. This function skips the semicolon." :parent prog-mode-map "C-c C-q" #'c-ts-mode-indent-defun "C-c ." #'c-ts-mode-set-style - "C-c C-c" #'comment-region) + "C-c C-c" #'comment-region + "C-c C-k" #'c-ts-mode-toggle-comment-style) ;;;###autoload (define-derived-mode c-ts-base-mode prog-mode "C" @@ -824,6 +844,7 @@ To use tree-sitter C/C++ modes by default, evaluate in your configuration." :group 'c + :after-hook (c-ts-mode-set-modeline) (when (treesit-ready-p 'c) (treesit-parser-create 'c) @@ -856,6 +877,7 @@ To use tree-sitter C/C++ modes by default, evaluate in your configuration." :group 'c++ + :after-hook (c-ts-mode-set-modeline) (when (treesit-ready-p 'cpp) (treesit-parser-create 'cpp) From c544df4fa3f217d0039bb9cb38c809c830558c25 Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Fri, 17 Feb 2023 20:46:19 +0100 Subject: [PATCH 05/16] Cleanup preproc indent for c-ts-mode (bug#61558) * lisp/progmodes/c-ts-mode.el (c-ts-mode--indent-styles): Make sure we indent to great-grand-parent if inside an #ifdef...#endif block. If grand-parent is root node, then don't indent one step. (c-ts-mode--preproc-offset): New helper anchor function to calculate indent offset. --- lisp/progmodes/c-ts-mode.el | 30 ++++++++++--- .../c-ts-mode-resources/indent-preproc.erts | 45 +++++++++++++++++++ test/lisp/progmodes/c-ts-mode-tests.el | 4 ++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 test/lisp/progmodes/c-ts-mode-resources/indent-preproc.erts diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 05875e9267a..76c80d9a068 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -233,6 +233,25 @@ delimiters < and >'s." ;;; Indent +(defun c-ts-mode--preproc-offset (_n _p &rest _) + "This anchor is used for preprocessor directives. + +Because node is nil at the moment of indentation, we use +`treesit-node-on' to capture the anonymous node covering the +newline. If the grand-parent of that node is the +translation_unit itself, we don't indent. Otherwise, just indent +one step according to the great-grand-parent indent level. The +reason there is a difference between grand-parent and +great-grand-parent here is that the node containing the newline +is actually the parent of point at the moment of indentation." + (when-let ((node (treesit-node-on (point) (point)))) + (if (string-equal "translation_unit" + (treesit-node-type + (treesit-node-parent + (treesit-node-parent node)))) + 0 + c-ts-mode-indent-offset))) + (defun c-ts-mode--indent-styles (mode) "Indent rules supported by `c-ts-mode'. MODE is either `c' or `cpp'." @@ -265,13 +284,14 @@ MODE is either `c' or `cpp'." ((match nil "do_statement" "body") parent-bol c-ts-mode-indent-offset) ((match nil "for_statement" "body") parent-bol c-ts-mode-indent-offset) - ((match "preproc_ifdef" "compound_statement") point-min 0) - ((match "#endif" "preproc_ifdef") point-min 0) - ((match "preproc_if" "compound_statement") point-min 0) - ((match "#endif" "preproc_if") point-min 0) - ((match "preproc_function_def" "compound_statement") point-min 0) + ((node-is "preproc") point-min 0) + ((node-is "#endif") point-min 0) ((match "preproc_call" "compound_statement") point-min 0) + ((n-p-gp nil "preproc" "translation_unit") point-min 0) + ((n-p-gp nil "\n" "preproc") great-grand-parent c-ts-mode--preproc-offset) + ((parent-is "preproc") grand-parent c-ts-mode-indent-offset) + ((parent-is "function_definition") parent-bol 0) ((parent-is "conditional_expression") first-sibling 0) ((parent-is "assignment_expression") parent-bol c-ts-mode-indent-offset) diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent-preproc.erts b/test/lisp/progmodes/c-ts-mode-resources/indent-preproc.erts new file mode 100644 index 00000000000..5a4996f642e --- /dev/null +++ b/test/lisp/progmodes/c-ts-mode-resources/indent-preproc.erts @@ -0,0 +1,45 @@ +Code: + (lambda () + (c-ts-mode) + (newline) + (indent-for-tab-command)) + +Point-Char: | + +Name: Indents inside #if preproc + +=-= +static void +free_glyph_pool (struct glyph_pool *pool) +{ + if (pool) + { +#if defined GLYPH_DEBUG| +#endif + } +} +=-= +static void +free_glyph_pool (struct glyph_pool *pool) +{ + if (pool) + { +#if defined GLYPH_DEBUG + | +#endif + } +} +=-=-= + +Name: Indents to 0 if #if preproc at root + +=-= +#if 0| +/* */ +static void +=-= +#if 0 +| +/* */ +static void +=-=-= diff --git a/test/lisp/progmodes/c-ts-mode-tests.el b/test/lisp/progmodes/c-ts-mode-tests.el index ddf64b40736..ea5fab4cbef 100644 --- a/test/lisp/progmodes/c-ts-mode-tests.el +++ b/test/lisp/progmodes/c-ts-mode-tests.el @@ -27,6 +27,10 @@ (skip-unless (treesit-ready-p 'c)) (ert-test-erts-file (ert-resource-file "indent.erts"))) +(ert-deftest c-ts-mode-test-indentation-preproc () + (skip-unless (treesit-ready-p 'c)) + (ert-test-erts-file (ert-resource-file "indent-preproc.erts"))) + (ert-deftest c-ts-mode-test-indentation-bsd () (skip-unless (treesit-ready-p 'c)) (ert-test-erts-file (ert-resource-file "indent-bsd.erts"))) From 2e6093b425e21551806abed746765631a00942d6 Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Sat, 18 Feb 2023 23:10:13 +0100 Subject: [PATCH 06/16] Adjust jsx indentation We can use the fact that 'treesit-indent-1' uses 'treesit-node-on' when on a whitespace to set the actual current node as parent. Now we can correctly indent the 'jsx_text' nodes. We also add some more electric-indent-chars so that auto-indenting of jsx behaves a little more fluently. * lisp/progmodes/js.el (js--treesit-indent-rules): Add new rules. (js-ts-mode): Add more indent-chars. * lisp/progmodes/typescript-ts-mode.el (typescript-ts-mode--indent-rules): Add new rules. * lisp/progmodes/typescript-ts-mode.el (typescript-ts-base-mode): Add more indent-chars and layout rules. --- lisp/progmodes/js.el | 6 ++++-- lisp/progmodes/typescript-ts-mode.el | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 1f08f09935b..027d6053f6e 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3458,12 +3458,14 @@ This function is intended for use in `after-change-functions'." ((match "<" "jsx_fragment") parent 0) ((parent-is "jsx_fragment") parent js-indent-level) ((node-is "jsx_closing_element") parent 0) - ((node-is "jsx_element") parent js-indent-level) + ((match "jsx_element" "statement") parent js-indent-level) ((parent-is "jsx_element") parent js-indent-level) + ((parent-is "jsx_text") parent-bol js-indent-level) ((parent-is "jsx_opening_element") parent js-indent-level) ((parent-is "jsx_expression") parent-bol js-indent-level) ((match "/" "jsx_self_closing_element") parent 0) ((parent-is "jsx_self_closing_element") parent js-indent-level) + ;; FIXME(Theo): This no-node catch-all should be removed. When is it needed? (no-node parent-bol 0))))) (defvar js--treesit-keywords @@ -3820,7 +3822,7 @@ Currently there are `js-mode' and `js-ts-mode'." (setq-local comment-multi-line t) ;; Electric-indent. (setq-local electric-indent-chars - (append "{}():;," electric-indent-chars)) ;FIXME: js2-mode adds "[]*". + (append "{}():;,<>/" electric-indent-chars)) ;FIXME: js2-mode adds "[]*". (setq-local electric-layout-rules '((?\; . after) (?\{ . after) (?\} . before))) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index 88a1ff3e202..de9b2f6845c 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -101,12 +101,14 @@ Argument LANGUAGE is either `typescript' or `tsx'." `(((match "<" "jsx_fragment") parent 0) ((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset) ((node-is "jsx_closing_element") parent 0) - ((node-is "jsx_element") parent typescript-ts-mode-indent-offset) + ((match "jsx_element" "statement") parent typescript-ts-mode-indent-offset) ((parent-is "jsx_element") parent typescript-ts-mode-indent-offset) + ((parent-is "jsx_text") parent-bol typescript-ts-mode-indent-offset) ((parent-is "jsx_opening_element") parent typescript-ts-mode-indent-offset) ((parent-is "jsx_expression") parent-bol typescript-ts-mode-indent-offset) ((match "/" "jsx_self_closing_element") parent 0) ((parent-is "jsx_self_closing_element") parent typescript-ts-mode-indent-offset))) + ;; FIXME(Theo): This no-node catch-all should be removed. When is it needed? (no-node parent-bol 0)))) (defvar typescript-ts-mode--keywords @@ -326,8 +328,9 @@ Argument LANGUAGE is either `typescript' or `tsx'." ;; Electric (setq-local electric-indent-chars - (append "{}():;," electric-indent-chars)) - + (append "{}():;,<>/" electric-indent-chars)) + (setq-local electric-layout-rules + '((?\; . after) (?\{ . after) (?\} . before))) ;; Navigation. (setq-local treesit-defun-type-regexp (regexp-opt '("class_declaration" From afbce8bb46798518998f517cbacdbd65d4531a3f Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Sun, 19 Feb 2023 12:22:27 -0800 Subject: [PATCH 07/16] Improve tree-sitter indent anchor prev-adaptive-prefix (bug#61314) Now prev-adaptive-prefix looks at the current line and checks if it begins with a prefix itself. If it does, prev-adaptive-prefix tries to place the anchor before the prefix on the previous line, rather than after it. - prev line - this line -> This line starts with a "-", i.e., begins with a prefix, so we place the anchor at the beginning of the "-" of the previous line, rather than after it - prev line this line -> This line doesn't start with a prefix, so the anchor is placed after the previous line's "-". * doc/lispref/modes.texi (Parser-based Indentation): Update manual. * lisp/treesit.el: (treesit-simple-indent-presets): Add local variable this-line-has-prefix, base what anchor to return on the value of this-line-has-prefix and whether the prev line has a prefix. --- doc/lispref/modes.texi | 7 ++-- lisp/treesit.el | 32 ++++++++++++++----- .../progmodes/c-ts-mode-resources/indent.erts | 15 +++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 750c4b47894..052a10e3797 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -5111,8 +5111,11 @@ This anchor is a function that is called with 3 arguments: @var{node}, @var{parent}, and @var{bol}. It tries to go to the beginning of the previous non-empty line, and matches @code{adaptive-fill-regexp}. If there is a match, this function returns the end of the match, -otherwise it returns nil. This anchor is useful for a -@code{indent-relative}-like indent behavior for block comments. +otherwise it returns nil. However, if the current line begins with a +prefix (e.g., ``-''), return the beginning of the prefix of the +previous line instead, so that the two prefixes aligns. This anchor +is useful for a @code{indent-relative}-like indent behavior for block +comments. @end ftable @end defvar diff --git a/lisp/treesit.el b/lisp/treesit.el index 297812f23f7..045fdf21cba 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -1186,12 +1186,18 @@ See `treesit-simple-indent-presets'.") (skip-syntax-backward "-") (point)))) (cons 'prev-adaptive-prefix - (lambda (_n parent &rest _) - (let ((comment-start-bol - (save-excursion - (goto-char (treesit-node-start parent)) - (line-beginning-position)))) + (lambda (_n parent bol &rest _) + (let (comment-start-bol + this-line-has-prefix) (save-excursion + (goto-char (treesit-node-start parent)) + (setq comment-start-bol (line-beginning-position)) + + (goto-char bol) + (setq this-line-has-prefix + (and (looking-at adaptive-fill-regexp) + (match-string 1))) + (forward-line -1) (and (>= (point) comment-start-bol) adaptive-fill-regexp @@ -1199,7 +1205,14 @@ See `treesit-simple-indent-presets'.") ;; If previous line is an empty line, don't ;; indent. (not (looking-at (rx (* whitespace) eol))) - (match-end 0)))))) + ;; Return the anchor. If the indenting line + ;; has a prefix and the previous line also + ;; has a prefix, indent to the beginning of + ;; prev line's prefix rather than the end of + ;; prev line's prefix. (Bug#61314). + (or (and this-line-has-prefix + (match-beginning 1)) + (match-end 0))))))) ;; TODO: Document. (cons 'grand-parent (lambda (_n parent &rest _) @@ -1336,8 +1349,11 @@ prev-adaptive-prefix Goes to the beginning of previous non-empty line, and tries to match `adaptive-fill-regexp'. If it matches, return the - end of the match, otherwise return nil. This is useful for a - `indent-relative'-like indent behavior for block comments.") + end of the match, otherwise return nil. However, if the + current line begins with a prefix, return the beginning of + the prefix of the previous line instead, so that the two + prefixes aligns. This is useful for a `indent-relative'-like + indent behavior for block comments.") (defun treesit--simple-indent-eval (exp) "Evaluate EXP. diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent.erts b/test/lisp/progmodes/c-ts-mode-resources/indent.erts index 05d59c10a42..09c8ebcec44 100644 --- a/test/lisp/progmodes/c-ts-mode-resources/indent.erts +++ b/test/lisp/progmodes/c-ts-mode-resources/indent.erts @@ -242,6 +242,21 @@ line 2 */ =-=-= +Name: Block Comment prefixes (Bug#61314) + +=-=-= +/* +- item1 +- item2 +- item3 +*/ +=-=-= +/* + - item1 + - item2 + - item3 + */ +=-=-= Code: (lambda () From fc4bfa76db958f48daed7c5812921b1653c31175 Mon Sep 17 00:00:00 2001 From: Kyle Meyer Date: Mon, 20 Feb 2023 00:41:31 -0500 Subject: [PATCH 08/16] Update to Org 9.6.1-34-geea8da --- lisp/org/ob-octave.el | 4 ++-- lisp/org/org-agenda.el | 2 +- lisp/org/org-cycle.el | 3 +++ lisp/org/org-element.el | 6 +++--- lisp/org/org-version.el | 2 +- lisp/org/ox-texinfo.el | 16 ++++++++++------ 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lisp/org/ob-octave.el b/lisp/org/ob-octave.el index 9bf16b9849c..1de263a52d0 100644 --- a/lisp/org/ob-octave.el +++ b/lisp/org/ob-octave.el @@ -243,8 +243,8 @@ value of the last statement in BODY, as elisp." (`output (setq results (if matlabp - (cdr (reverse (delq "" (mapcar #'org-strip-quotes - (mapcar #'org-trim raw))))) + (cdr (reverse (delete "" (mapcar #'org-strip-quotes + (mapcar #'org-trim raw))))) (cdr (member org-babel-octave-eoe-output (reverse (mapcar #'org-strip-quotes (mapcar #'org-trim raw))))))) diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el index 3db33c4d63e..a33d84f1cd4 100644 --- a/lisp/org/org-agenda.el +++ b/lisp/org/org-agenda.el @@ -7330,7 +7330,7 @@ Any match of REMOVE-RE will be removed from TXT." (let ((s (org-format-outline-path (org-get-outline-path) (1- (frame-width)) nil org-agenda-breadcrumbs-separator))) - (if (eq "" s) "" (concat s org-agenda-breadcrumbs-separator)))))) + (if (equal "" s) "" (concat s org-agenda-breadcrumbs-separator)))))) (setq time (cond (s2 (concat (org-agenda-time-of-day-to-ampm-maybe s1) "-" (org-agenda-time-of-day-to-ampm-maybe s2) diff --git a/lisp/org/org-cycle.el b/lisp/org/org-cycle.el index 828c84cd0ac..90fc95f41f7 100644 --- a/lisp/org/org-cycle.el +++ b/lisp/org/org-cycle.el @@ -648,6 +648,9 @@ With a numeric prefix, show all headlines up to that level." (org-fold-show-hidden-entry) (org-fold-show-children)) ("content" + ;; Newline before heading will be outside the + ;; narrowing. Make sure that it is revealed. + (org-fold-heading nil) (save-excursion (save-restriction (org-narrow-to-subtree) diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el index 4f4eebfcc2d..8524dd29d83 100644 --- a/lisp/org/org-element.el +++ b/lisp/org/org-element.el @@ -7562,15 +7562,15 @@ the cache." ;; beginning. (next-element-re (pcase granularity ((or `headline - (guard (eq '(headline) - restrict-elements))) + (guard (equal '(headline) + restrict-elements))) (cons (org-with-limited-levels org-element-headline-re) 'match-beg)) (`headline+inlinetask (cons - (if (eq '(inlinetask) restrict-elements) + (if (equal '(inlinetask) restrict-elements) (org-inlinetask-outline-regexp) org-element-headline-re) 'match-beg)) diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el index 5f587fb3fd8..5f93356709e 100644 --- a/lisp/org/org-version.el +++ b/lisp/org/org-version.el @@ -11,7 +11,7 @@ Inserted by installing Org mode or when a release is made." (defun org-git-version () "The Git version of Org mode. Inserted by installing Org or when a release is made." - (let ((org-git-version "release_9.6.1-31-gaf1bb1")) + (let ((org-git-version "release_9.6.1-34-geea8da")) org-git-version)) (provide 'org-version) diff --git a/lisp/org/ox-texinfo.el b/lisp/org/ox-texinfo.el index 8e3a0456299..4ff482cc3f5 100644 --- a/lisp/org/ox-texinfo.el +++ b/lisp/org/ox-texinfo.el @@ -32,6 +32,8 @@ (require 'cl-lib) (require 'ox) +(eval-when-compile (require 'subr-x)) + (defvar orgtbl-exp-regexp) (defvar org-texinfo-supports-math--cache) @@ -2025,12 +2027,14 @@ Once computed, the results remain cached." (unless (boundp 'org-texinfo-supports-math--cache) (setq org-texinfo-supports-math--cache (let ((math-example "1 + 1 = 2")) - (let* ((input-file - (make-temp-file "test" nil ".info")) - (input-content - (concat (format "@setfilename %s" input-file) "\n" - "@node Top" "\n" - (format "@displaymath{%s}" math-example) "\n"))) + (let* ((input-file (make-temp-file "test" nil ".info")) + (input-content (string-join + (list (format "@setfilename %s" input-file) + "@node Top" + "@displaymath" + math-example + "@end displaymath") + "\n"))) (with-temp-file input-file (insert input-content)) (let* ((output-file (org-texinfo-compile input-file)) From dfc850ca0223ffd1d2048657ea6d20c971865d51 Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Sun, 19 Feb 2023 20:48:36 +0100 Subject: [PATCH 09/16] Fix object initializer for csharp-mode (bug#61541) * lisp/progmodes/csharp-mode.el (csharp-guess-basic-syntax): Make sure we check the openers as well as closers. --- lisp/progmodes/csharp-mode.el | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index b6b842d7fd4..293a910081c 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -474,28 +474,37 @@ compilation and evaluation time conflicts." (and (eq (char-before) ?\]) (not (eq (char-after) ?\;)))))) `((annotation-top-cont ,(c-point 'iopl)))) - ((and ;; Heuristics to find object initializers (save-excursion ;; Next non-whitespace character should be '{' (goto-char (c-point 'boi)) - (eq (char-after) ?{)) - (save-excursion - ;; 'new' should be part of the line - (goto-char (c-point 'iopl)) - (looking-at ".*new.*")) + (unless (eq (char-after) ?{) + (backward-up-list 1 t t)) + (save-excursion + ;; 'new' should be part of the line + (goto-char (c-point 'iopl)) + (looking-at ".*new.*"))) ;; Line should not already be terminated (save-excursion (goto-char (c-point 'eopl)) (or (not (eq (char-before) ?\;)) (not (eq (char-before) ?\{))))) - (if (save-excursion - ;; if we have a hanging brace on line before - (goto-char (c-point 'eopl)) - (eq (char-before) ?\{)) - `((brace-list-intro ,(c-point 'iopl))) - `((block-open) (statement ,(c-point 'iopl))))) + (cond + ((save-excursion + ;; if we have a hanging brace on line before + (goto-char (c-point 'eopl)) + (eq (char-before) ?\{)) + `((brace-list-intro ,(c-point 'iopl)))) + ((save-excursion + ;; if we have a hanging brace on line before + (goto-char (c-point 'boi)) + (and (eq (char-after) ?\}) + `((brace-list-close ,(save-excursion + (backward-up-list 1 t t) + (point))))))) + (t + `((block-open) (statement ,(c-point 'iopl)))))) (t (apply orig-fun args)))) From 19480aa30e37c760b608c39aff67f28792a1ce94 Mon Sep 17 00:00:00 2001 From: Theodor Thornhill Date: Mon, 20 Feb 2023 13:38:55 +0100 Subject: [PATCH 10/16] Typescript-ts-mode: Add switch case handling * lisp/progmodes/typescript-ts-mode.el (typescript-ts-mode--indent-rules): New rules. --- lisp/progmodes/typescript-ts-mode.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index de9b2f6845c..41cf4ff08f6 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -81,6 +81,8 @@ Argument LANGUAGE is either `typescript' or `tsx'." ((parent-is "member_expression") parent-bol typescript-ts-mode-indent-offset) ((parent-is "named_imports") parent-bol typescript-ts-mode-indent-offset) ((parent-is "statement_block") parent-bol typescript-ts-mode-indent-offset) + ((parent-is "switch_case") parent-bol typescript-ts-mode-indent-offset) + ((parent-is "switch_default") parent-bol typescript-ts-mode-indent-offset) ((parent-is "type_arguments") parent-bol typescript-ts-mode-indent-offset) ((parent-is "variable_declarator") parent-bol typescript-ts-mode-indent-offset) ((parent-is "arguments") parent-bol typescript-ts-mode-indent-offset) From d7010d64b216b3adf7dd5adcc9bb62c7208b8d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= Date: Sun, 19 Feb 2023 22:57:54 +0100 Subject: [PATCH 11/16] Add declaration_list to c-ts-common-indent-type-regexp-alist * lisp/progmodes/c-ts-mode.el (c-ts-base-mode): Consider a "declaration_list" a block. (Bug#61635) * test/lisp/progmodes/c-ts-mode-resources/indent.erts (Code): Add a test case. --- lisp/progmodes/c-ts-mode.el | 3 ++- .../progmodes/c-ts-mode-resources/indent.erts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 76c80d9a068..6d70dc3198e 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -814,7 +814,8 @@ the semicolon. This function skips the semicolon." `((block . ,(rx (or "compound_statement" "field_declaration_list" "enumerator_list" - "initializer_list"))) + "initializer_list" + "declaration_list"))) (if . "if_statement") (else . ("if_statement" . "alternative")) (do . "do_statement") diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent.erts b/test/lisp/progmodes/c-ts-mode-resources/indent.erts index 09c8ebcec44..36d7af4faf1 100644 --- a/test/lisp/progmodes/c-ts-mode-resources/indent.erts +++ b/test/lisp/progmodes/c-ts-mode-resources/indent.erts @@ -369,3 +369,19 @@ int main() }; } =-=-= + +Code: + (lambda () + (c++-ts-mode) + (setq-local indent-tabs-mode nil) + (setq-local c-ts-mode-indent-offset 2) + (indent-region (point-min) (point-max))) + +Name: Declaration List (Namespace) (Bug#61635) + +=-= +namespace test { + class Name { + }; +} +=-=-= From 02aba20d528f1ec45ad36424e8d65ace985dd04a Mon Sep 17 00:00:00 2001 From: Jonas Bernoulli Date: Mon, 20 Feb 2023 16:36:16 +0100 Subject: [PATCH 12/16] Update to Transient v0.3.7-216-gfe40342 --- doc/misc/transient.texi | 5 ++--- lisp/transient.el | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi index 7be8b630412..850930a290f 100644 --- a/doc/misc/transient.texi +++ b/doc/misc/transient.texi @@ -1214,7 +1214,7 @@ Suffix specifications have this form: @lisp ([@var{LEVEL}] - [@var{KEY}] [@var{DESCRIPTION}] + [@var{KEY} [@var{DESCRIPTION}]] @var{COMMAND}|@var{ARGUMENT} [@var{KEYWORD} @var{VALUE}]...) @end lisp @@ -1253,8 +1253,7 @@ Any command will do; it does not need to have an object associated with it (as would be the case if @code{transient-define-suffix} or @code{transient-define-infix} were used to define it). -Anonymous, dynamically defined suffix commands are also supported. -See information about the @code{:setup-children} function in @ref{Group Specifications}. +COMMAND can also be a @code{lambda} expression. As mentioned above, the object that is associated with a command can be used to set the default for certain values that otherwise have to diff --git a/lisp/transient.el b/lisp/transient.el index c0ecd2950d7..96e711e950c 100644 --- a/lisp/transient.el +++ b/lisp/transient.el @@ -1048,26 +1048,30 @@ example, sets a variable, use `transient-define-infix' instead. (setq args (plist-put args :key pop))) (cond ((or (stringp car) - (eq (car-safe car) 'lambda)) + (and (eq (car-safe car) 'lambda) + (not (commandp car)))) (setq args (plist-put args :description pop))) ((and (symbolp car) + (not (keywordp car)) (not (commandp car)) (commandp (cadr spec))) (setq args (plist-put args :description (macroexp-quote pop))))) (cond ((keywordp car) - (error "Need command, got %S" car)) + (error "Need command, got `%s'" car)) ((symbolp car) (setq args (plist-put args :command (macroexp-quote pop)))) ((and (commandp car) (not (stringp car))) (let ((cmd pop) - (sym (intern (format "transient:%s:%s" - prefix - (or (plist-get args :description) - (plist-get args :key)))))) - (defalias sym cmd) - (setq args (plist-put args :command (macroexp-quote sym))))) + (sym (intern + (format "transient:%s:%s" + prefix + (let ((desc (plist-get args :description))) + (if (and desc (or (stringp desc) (symbolp desc))) + desc + (plist-get args :key))))))) + (setq args (plist-put args :command `(defalias ',sym ,cmd))))) ((or (stringp car) (and car (listp car))) (let ((arg pop)) From 88ee92e61d786586064f3792b9d0be2dee6eb30c Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 20 Feb 2023 19:30:02 +0200 Subject: [PATCH 13/16] ; * lisp/progmodes/rust-ts-mode.el (treesit-node-end): Declare. --- lisp/progmodes/rust-ts-mode.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el index 2ecff631f54..ee73ebf7a9f 100644 --- a/lisp/progmodes/rust-ts-mode.el +++ b/lisp/progmodes/rust-ts-mode.el @@ -36,6 +36,7 @@ (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-end "treesit.c") (declare-function treesit-node-type "treesit.c") (declare-function treesit-node-parent "treesit.c") (declare-function treesit-query-compile "treesit.c") From 5d0b45cd67b383bc777dcf7589517e51e58f5a21 Mon Sep 17 00:00:00 2001 From: Andrea Corallo Date: Mon, 20 Feb 2023 21:03:58 +0100 Subject: [PATCH 14/16] Make the native compiler always use `make-temp-file' for temporary files * src/comp.c (CALL4I): Define macro. (Fcomp__compile_ctxt_to_file): Use `make-temp-file' instead of `make-temp-file-internal'. * lisp/emacs-lisp/comp.el (comp--trampoline-abs-filename): Likewise. --- lisp/emacs-lisp/comp.el | 4 ++-- src/comp.c | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el index a2bad6ce6a8..a0b18ea864f 100644 --- a/lisp/emacs-lisp/comp.el +++ b/lisp/emacs-lisp/comp.el @@ -3813,8 +3813,8 @@ Return the trampoline if found or nil otherwise." ;; found. finally (cl-return (expand-file-name - (make-temp-file-internal (file-name-sans-extension rel-filename) - 0 ".eln" nil) + (make-temp-file (file-name-sans-extension rel-filename) 0 ".eln" + nil) temporary-file-directory)))) (defun comp-trampoline-compile (subr-name) diff --git a/src/comp.c b/src/comp.c index beaf443a0f7..1fce108fea4 100644 --- a/src/comp.c +++ b/src/comp.c @@ -514,6 +514,10 @@ load_gccjit_if_necessary (bool mandatory) #define CALL2I(fun, arg1, arg2) \ CALLN (Ffuncall, intern_c_string (STR (fun)), arg1, arg2) +/* Like call4 but stringify and intern. */ +#define CALL4I(fun, arg1, arg2, arg3, arg4) \ + CALLN (Ffuncall, intern_c_string (STR (fun)), arg1, arg2, arg3, arg4) + #define DECL_BLOCK(name, func) \ gcc_jit_block *(name) = \ gcc_jit_function_new_block ((func), STR (name)) @@ -4991,7 +4995,8 @@ DEFUN ("comp--compile-ctxt-to-file", Fcomp__compile_ctxt_to_file, format_string ("%s_libgccjit_repro.c", SSDATA (ebase_name))); Lisp_Object tmp_file = - Fmake_temp_file_internal (base_name, Qnil, build_string (".eln.tmp"), Qnil); + CALL4I (make-temp-file, base_name, Qnil, build_string (".eln.tmp"), Qnil); + Lisp_Object encoded_tmp_file = ENCODE_FILE (tmp_file); #ifdef WINDOWSNT encoded_tmp_file = ansi_encode_filename (encoded_tmp_file); From e83c78b8c7784254c2c6f043530ab325c2fa7f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Mon, 20 Feb 2023 22:43:50 +0000 Subject: [PATCH 15/16] Eglot: respect user's Eldoc configuration by default This change addresses the problems reported in many Elglot reports dating back to early 2021 at least: https://github.com/joaotavora/eglot/issues/648 https://github.com/joaotavora/eglot/issues/894 https://github.com/joaotavora/eglot/issues/920 https://github.com/joaotavora/eglot/issues/1031 https://github.com/joaotavora/eglot/issues/1171 In one form or another, the reports point out that the multiple pieces of information about the "thing at point" made available by the LSP server are not all being considered by the ElDoc system. The reason for this is Eglot setting/trampling the variables 'eldoc-documentation-strategy' and 'eldoc-documentation-functions' in its minor more entry function. The reason it did that is historical and is partially described in the issues above. But, evidently, it never made much sense, because so many people want to override it, which requires setting 'eldoc-documentation-strategy' to the non-default value 'eldoc-documentation-compose'. The problem was made worse by the fact that setting it as usual in either the Customize menu or their init file didn't work, requiring a fairly complex Elisp snippet. That is now solved as of this commit. If the user does not do any setting, then Eglot works basically the same as before (i.e. shows only one piece of information). It is arguable that the default value for 'eldoc-documentation-strategy' should change globally to 'eldoc-documentation-compose', but that has other subtle implications and is not part of this commit. * lisp/progmodes/eglot.el (eglot--managed-mode): Don't set Eldoc variables greedily. --- lisp/progmodes/eglot.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index d941aec4688..abc090aea51 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -1760,11 +1760,6 @@ Use `eglot-managed-p' to determine if current buffer is managed.") (add-hook 'change-major-mode-hook #'eglot--managed-mode-off nil t) (add-hook 'post-self-insert-hook 'eglot--post-self-insert-hook nil t) (add-hook 'pre-command-hook 'eglot--pre-command-hook nil t) - (eglot--setq-saving eldoc-documentation-functions - '(eglot-signature-eldoc-function - eglot-hover-eldoc-function)) - (eglot--setq-saving eldoc-documentation-strategy - #'eldoc-documentation-enthusiast) (eglot--setq-saving xref-prompt-for-identifier nil) (eglot--setq-saving flymake-diagnostic-functions '(eglot-flymake-backend)) (eglot--setq-saving company-backends '(company-capf)) @@ -1773,7 +1768,12 @@ Use `eglot-managed-p' to determine if current buffer is managed.") (add-function :before-until (local 'imenu-create-index-function) #'eglot-imenu)) (unless (eglot--stay-out-of-p 'flymake) (flymake-mode 1)) - (unless (eglot--stay-out-of-p 'eldoc) (eldoc-mode 1)) + (unless (eglot--stay-out-of-p 'eldoc) + (add-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function + nil t) + (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function + nil t) + (eldoc-mode 1)) (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) (t (remove-hook 'after-change-functions 'eglot--after-change t) From 5a864f23eb8a36ef435136c5b41cb01b875df399 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 20 Feb 2023 21:22:41 -0500 Subject: [PATCH 16/16] regex-emacs.c: Reduce the use of backtracking a bit further bug#61514 exhibited some undesirable backtracking in a case where it's easy to avoid it by making `mutually_exclusive_p` just a bit more careful. * src/regex-emacs.c (mutually_exclusive_p): Handle `on_failure_jump`s. * test/src/regex-emacs-tests.el (regexp-tests-backtrack-optimization): Add a few tests. --- src/regex-emacs.c | 18 ++++++++++++++++++ test/src/regex-emacs-tests.el | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/regex-emacs.c b/src/regex-emacs.c index 2dca0d16ad9..2571812cb39 100644 --- a/src/regex-emacs.c +++ b/src/regex-emacs.c @@ -3653,6 +3653,7 @@ mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1, re_opcode_t op2; bool multibyte = RE_MULTIBYTE_P (bufp); unsigned char *pend = bufp->buffer + bufp->used; + re_char *p2_orig = p2; eassert (p1 >= bufp->buffer && p1 < pend && p2 >= bufp->buffer && p2 <= pend); @@ -3822,6 +3823,23 @@ mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1, case notcategoryspec: return ((re_opcode_t) *p1 == categoryspec && p1[1] == p2[1]); + case on_failure_jump_nastyloop: + case on_failure_jump_smart: + case on_failure_jump_loop: + case on_failure_keep_string_jump: + case on_failure_jump: + { + int mcnt; + p2++; + EXTRACT_NUMBER_AND_INCR (mcnt, p2); + /* Don't just test `mcnt > 0` because non-greedy loops have + their test at the end with an unconditional jump at the start. */ + if (p2 + mcnt > p2_orig) /* Ensure forward progress. */ + return (mutually_exclusive_p (bufp, p1, p2) + && mutually_exclusive_p (bufp, p1, p2 + mcnt)); + break; + } + default: ; } diff --git a/test/src/regex-emacs-tests.el b/test/src/regex-emacs-tests.el index cd4924f9785..c8f161c9b24 100644 --- a/test/src/regex-emacs-tests.el +++ b/test/src/regex-emacs-tests.el @@ -872,4 +872,15 @@ This evaluates the TESTS test cases from glibc." (should (equal (string-match "\\`\\(?:ab\\)*\\'" "a") nil)) (should (equal (string-match "\\`a\\{2\\}*\\'" "a") nil))) +(ert-deftest regexp-tests-backtrack-optimization () ;bug#61514 + ;; Make sure we don't use up the regexp stack needlessly. + (with-current-buffer (get-buffer-create "*bug*") + (erase-buffer) + (insert (make-string 1000000 ?x) "=") + (goto-char (point-min)) + (should (looking-at "x*=*")) + (should (looking-at "x*\\(=\\|:\\)")) + (should (looking-at "x*\\(=\\|:\\)*")) + (should (looking-at "x*=*?")))) + ;;; regex-emacs-tests.el ends here