[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 63d69bd1549 07/11: Use new baseline indent rule in c-ts-mode
From: |
Yuan Fu |
Subject: |
master 63d69bd1549 07/11: Use new baseline indent rule in c-ts-mode |
Date: |
Sun, 1 Dec 2024 21:10:23 -0500 (EST) |
branch: master
commit 63d69bd154987bcc0434e0f85e09bf5dfa07b827
Author: Yuan Fu <casouri@gmail.com>
Commit: Yuan Fu <casouri@gmail.com>
Use new baseline indent rule in c-ts-mode
Rework the indent rules in c-ts-mode using
c-ts-common-baseline-indent-rule. Also reworked internal
functions for setting indent styles to make it simpler and (more
importantly) more readable. Now there's a single function,
c-ts-mode--simple-indent-rules, that returns a value that can go
straight to treesit-simple-indent-rules.
* lisp/progmodes/c-ts-mode.el:
(c-ts-mode--indent-style-setter): Simplify.
(c-ts-mode--get-indent-style): Remove function.
(c-ts-mode--prompt-for-style): Use a hard-coded list of styles.
This is a tiny downgrade from the computed lists but is
acceptable.
(c-ts-mode-set-style): Use c-ts-mode--simple-indent-rules.
(c-ts-mode--standalone-grandparent): Docstring change.
(c-ts-mode--for-loop-indent-rule): New function.
(c-ts-mode--preproc-indent-rules): New variable. Copied from
old indent rules and added some new rule.
(c-ts-mode--macro-heuristic-rules):
(c-ts-mode--simple-indent-rules):
(c-ts-mode--parenthesized-expression-indent-rule):
(c-ts-mode--label-indent-rules): New functions.
(c-ts-mode):
(c++-ts-mode): Use c-ts-mode--simple-indent-rules.
---
lisp/progmodes/c-ts-mode.el | 362 ++++++++++++++++++++++++++++++++++----------
1 file changed, 279 insertions(+), 83 deletions(-)
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 59b34ef6b8b..3059898f6d5 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -130,18 +130,11 @@ the value of SYM in `c-ts-mode' and `c++-ts-mode' buffers
to VAL.
SYM should be `c-ts-mode-indent-style', and VAL should be a style
symbol."
(set-default sym val)
- (named-let loop ((res nil)
- (buffers (buffer-list)))
- (if (null buffers)
- (mapc (lambda (b)
- (with-current-buffer b
- (c-ts-mode-set-style val)))
- res)
- (let ((buffer (car buffers)))
- (with-current-buffer buffer
- (if (derived-mode-p '(c-ts-mode c++-ts-mode))
- (loop (append res (list buffer)) (cdr buffers))
- (loop res (cdr buffers))))))))
+ (dolist (buffer (buffer-list))
+ (with-current-buffer buffer
+ (when (derived-mode-p '(c-ts-mode c++-ts-mode))
+ (setq-local c-ts-mode-indent-style val)
+ (c-ts-mode-set-style val)))))
(defun c-ts-indent-style-safep (style)
"Non-nil if STYLE's value is safe for file-local variables."
@@ -165,23 +158,13 @@ the list of RULEs doesn't need to contain the language
symbol."
:safe 'c-ts-indent-style-safep
:group 'c)
-(defun c-ts-mode--get-indent-style (mode)
- "Helper function to set indentation style.
-MODE is either `c' or `cpp'."
- (let ((style
- (if (functionp c-ts-mode-indent-style)
- (funcall c-ts-mode-indent-style)
- (alist-get c-ts-mode-indent-style (c-ts-mode--indent-styles
mode)))))
- `((,mode ,@style))))
-
(defun c-ts-mode--prompt-for-style ()
"Prompt for an indent style and return the symbol for it."
- (let ((mode (if (derived-mode-p 'c-ts-mode) 'c 'c++)))
- (intern
- (completing-read
- "Style: "
- (mapcar #'car (c-ts-mode--indent-styles mode))
- nil t nil nil "gnu"))))
+ (intern
+ (completing-read
+ "Style: "
+ '(gnu k&r linux bsd)
+ nil t nil nil "gnu")))
(defun c-ts-mode-set-global-style (style)
"Set the indent style of C/C++ modes globally to STYLE.
@@ -202,9 +185,11 @@ To set the default indent style globally, use
(user-error "The current buffer is not in `c-ts-mode' nor `c++-ts-mode'")
(setq-local c-ts-mode-indent-style style)
(setq treesit-simple-indent-rules
- (treesit--indent-rules-optimize
- (c-ts-mode--get-indent-style
- (if (derived-mode-p 'c-ts-mode) 'c 'cpp))))))
+ (if (functionp style)
+ (funcall style)
+ (c-ts-mode--simple-indent-rules
+ (if (derived-mode-p 'c-ts-mode) 'c 'c++)
+ style)))))
(defcustom c-ts-mode-emacs-sources-support t
"Whether to enable Emacs source-specific C features.
@@ -374,8 +359,7 @@ PARENT is the parent of the current node."
(defun c-ts-mode--standalone-grandparent (_node parent bol &rest args)
"Like the standalone-parent anchor but pass it the grandparent.
-PARENT is NODE's parent, BOL is the beginning of non-whitespace
-characters of the current line."
+NODE, PARENT, BOL, ARGS are the same as in other anchors."
(apply (alist-get 'standalone-parent treesit-simple-indent-presets)
parent (treesit-node-parent parent) bol args))
@@ -409,6 +393,167 @@ The top-level compound is the {} that immediately follows
the function
signature."
(not (equal "function_definition" (treesit-node-type (treesit-node-parent
parent)))))
+(defun c-ts-mode--for-loop-indent-rule (node parent &rest _)
+ "Indentation rule for the for-loop.
+
+NODE and PARENT as usual."
+ (when (treesit-node-match-p parent "for_statement")
+ (pcase (treesit-node-field-name node)
+ ("initializer"
+ ;; Anchor is the opening paren.
+ (cons (treesit-node-start (treesit-node-child parent 1)) 1))
+ ((or "condition" "update")
+ (cons (treesit-node-start (treesit-node-prev-sibling node 'named))
+ 0))
+ ("body"
+ (cons (c-ts-common--standalone-parent parent)
+ c-ts-mode-indent-offset))
+ (_ (if (treesit-node-match-p node ")")
+ ;; Anchor is the opening paren.
+ (cons (treesit-node-start (treesit-node-child parent 1)) 0)
+ nil)))))
+
+(defvar c-ts-mode--preproc-indent-rules
+ `(((node-is "preproc") column-0 0)
+ ((node-is "#endif") column-0 0)
+ ((match "preproc_call" "compound_statement") column-0 0)
+ ((prev-line-is "#endif") c-ts-mode--anchor-prev-sibling 0)
+ ;; Top-level things under a preproc directive. Note that
+ ;; "preproc" matches more than one type: it matches
+ ;; preproc_if, preproc_elif, etc.
+ ((n-p-gp nil "preproc" "translation_unit") column-0 0)
+ ;; Indent rule for an empty line after a preproc directive.
+ ((and no-node (parent-is ,(rx (or "\n" "preproc"))))
+ c-ts-mode--standalone-parent-skip-preproc c-ts-mode--preproc-offset)
+ ;; Statement under a preproc directive, the first statement
+ ;; indents against parent, the rest statements indent to
+ ;; their prev-sibling.
+ ((match nil ,(rx "preproc_" (or "if" "elif")) nil 3 3)
+ c-ts-mode--standalone-parent-skip-preproc c-ts-mode-indent-offset)
+ ((match nil "preproc_ifdef" nil 2 2)
+ c-ts-mode--standalone-parent-skip-preproc c-ts-mode-indent-offset)
+ ((match nil "preproc_else" nil 1 1)
+ c-ts-mode--standalone-parent-skip-preproc c-ts-mode-indent-offset)
+ ((parent-is "preproc") c-ts-mode--anchor-prev-sibling 0))
+ "Indent rules for preprocessors.")
+
+(defun c-ts-mode--macro-heuristic-rules (node parent &rest _)
+ "Heuristic indent rule for control flow macros.
+
+Eg,
+
+ #define IOTA(var, n) for (int var = 0; var != (n); ++var)
+
+ int main()
+ {
+ IOTA (v, 10) {
+ printf(\"%d \", v); <-- Here we want to indent
+ counter++; <-- Use baseline rule to align
+ } to prev sibling
+
+Checked by \"Compound Statement after code (Bug#74507)\" test.
+
+NODE and PARENT are the same as other indent rules."
+ (when (and (treesit-node-match-p parent "compound_statement")
+ (treesit-node-match-p (treesit-node-prev-sibling parent)
+ "expression_statement"))
+ (let ((parent-bol
+ (lambda () (save-excursion
+ (goto-char (treesit-node-start parent))
+ (back-to-indentation)
+ (point)))))
+ (cond
+ ;; Closing brace.
+ ((treesit-node-match-p node "}")
+ (cons (funcall parent-bol) 0))
+ ;; First sibling.
+ ((treesit-node-eq (treesit-node-child parent 0 'named) node)
+ (cons (funcall parent-bol)
+ c-ts-mode-indent-offset))))))
+
+(defun c-ts-mode--simple-indent-rules (mode style)
+ "Return the indent rules for MODE and STYLE.
+
+The returned value can be set to `treesit-simple-indent-rules'.
+MODE can be `c' or `c++'. STYLE can be `gnu', `k&r', `linux', `bsd'."
+ (let ((rules
+ `((c-ts-mode--for-each-tail-body-matcher
+ prev-line c-ts-mode-indent-offset)
+
+ ;; Misc overrides.
+ ((parent-is "translation_unit") column-0 0)
+ ((node-is ,(rx (or "else" "case"))) standalone-parent 0)
+ ;; Align the while keyword to the do keyword.
+ ((match "while" "do_statement") parent 0)
+ c-ts-mode--parenthesized-expression-indent-rule
+ ;; Thanks to tree-sitter-c's weird for-loop grammar, we can't
+ ;; use the baseline indent rule for it.
+ c-ts-mode--for-loop-indent-rule
+ c-ts-mode--label-indent-rules
+ ,@c-ts-mode--preproc-indent-rules
+ c-ts-mode--macro-heuristic-rules
+
+ ;; Make sure type and function definition components align and
+ ;; don't indent. Also takes care of GNU style opening braces.
+ ((parent-is ,(rx (or "function_definition"
+ "struct_specifier"
+ "enum_specifier"
+ "function_declarator"
+ "template_declaration")))
+ parent 0)
+ ;; This is for the trailing-star stype: int *
+ ;; func()
+ ((match "function_declarator" nil "declarator") parent-bol 0)
+ ;; ((match nil "function_definition" "declarator") parent 0)
+ ;; ((match nil "struct_specifier" "name") parent 0)
+ ;; ((match nil "function_declarator" "parameters") parent 0)
+ ;; ((parent-is "template_declaration") parent 0)
+
+ ;; `c-ts-common-looking-at-star' has to come before
+ ;; `c-ts-common-comment-2nd-line-matcher'.
+ ;; FIXME: consolidate into a single rule.
+ ((and (parent-is "comment") c-ts-common-looking-at-star)
+ c-ts-common-comment-start-after-first-star -1)
+ (c-ts-common-comment-2nd-line-matcher
+ c-ts-common-comment-2nd-line-anchor
+ 1)
+ ((parent-is "comment") prev-adaptive-prefix 0)
+
+ ;; Labels.
+ ((node-is "labeled_statement") standalone-parent 0)
+ ((parent-is "labeled_statement")
+ c-ts-mode--standalone-grandparent c-ts-mode-indent-offset)
+
+ ;; Preproc directives
+ ((node-is "preproc_arg") no-indent)
+ ((node-is "preproc") column-0 0)
+ ((node-is "#endif") column-0 0)
+
+ ;; C++
+ ((node-is "access_specifier") parent-bol 0)
+ ((prev-line-is "access_specifier")
+ parent-bol c-ts-mode-indent-offset)
+
+ c-ts-common-baseline-indent-rule)))
+ (setq rules
+ (pcase style
+ ('gnu rules)
+ ('k&r rules)
+ ('linux
+ ;; Reference:
+ ;;
https://www.kernel.org/doc/html/latest/process/coding-style.html,
+ ;; and script/Lindent in Linux kernel repository.
+ `(((node-is "labeled_statement") column-0 0)
+ ,@rules))
+ ('bsd
+ `(((match "compound_statement" "compound_statement")
+ standalone-parent c-ts-mode-indent-offset)
+ ((node-is "compound_statement") standalone-parent 0)
+ ,@rules))))
+ (pcase mode
+ ('c `((c . ,rules)))
+ ('c++ `((cpp . ,rules))))))
+
(defun c-ts-mode--indent-styles (mode)
"Indent rules supported by `c-ts-mode'.
MODE is either `c' or `cpp'."
@@ -558,6 +703,56 @@ MODE is either `c' or `cpp'."
((match "compound_statement" "do_statement") standalone-parent 0)
,@common))))
+(defun c-ts-mode--parenthesized-expression-indent-rule (_node parent &rest _)
+ "Indent rule that indents aprenthesized expression.
+
+Aligns the next line to the first sibling
+
+return (a && b
+ && c)
+
+return ( a && b
+ && c
+ )
+
+Same for if/while statements
+
+if (a && b
+ && c)
+
+NODE, PARENT are the same as other indent rules."
+ (when (treesit-node-match-p
+ parent (rx (or "binary" "conditional") "_expression"))
+ (while (and parent
+ (not (treesit-node-match-p
+ parent "parenthesized_expression")))
+ (setq parent (treesit-node-parent parent)))
+ (when parent
+ (cons (treesit-node-start
+ (treesit-node-child parent 0 'named))
+ 0))))
+
+(defun c-ts-mode--label-indent-rules (node parent &rest _)
+ "Handles indentation around labels.
+NODE, PARENT are as usual."
+ (cond
+ ;; Matches the top-level labels for GNU-style.
+ ((and (eq c-ts-mode-indent-style 'gnu)
+ (treesit-node-match-p node "labeled_statement")
+ (treesit-node-match-p (treesit-node-parent parent)
+ "function_definition"))
+ (cons (pos-bol) 1))
+ ;; If previous sibling is a labeled_statement, align to it's
+ ;; children, which is the previous statement.
+ ((and (not (treesit-node-match-p node "}"))
+ (treesit-node-match-p (treesit-node-prev-sibling node)
+ "labeled_statement"))
+ (cons (treesit-node-start
+ (treesit-node-child
+ (treesit-node-prev-sibling node) 1 'named))
+ 0))
+ (t nil)))
+
(defun c-ts-mode--top-level-label-matcher (node parent &rest _)
"A matcher that matches a top-level label.
NODE should be a labeled_statement. PARENT is its parent."
@@ -796,17 +991,17 @@ MODE is either `c' or `cpp'."
:feature 'delimiter
'((["," ":" ";"]) @font-lock-delimiter-face)
- :language mode
- :feature 'emacs-devel
- :override t
- `(((call_expression
- (call_expression function: (identifier) @fn)
- @c-ts-mode--fontify-DEFUN)
- (:match "\\`DEFUN\\'" @fn))
+:language mode
+:feature 'emacs-devel
+:override t
+`(((call_expression
+ (call_expression function: (identifier) @fn)
+ @c-ts-mode--fontify-DEFUN)
+ (:match "\\`DEFUN\\'" @fn))
- ((function_definition type: (_) @for-each-tail)
- @c-ts-mode--fontify-for-each-tail
- (:match ,c-ts-mode--for-each-tail-regexp @for-each-tail)))))
+ ((function_definition type: (_) @for-each-tail)
+ @c-ts-mode--fontify-for-each-tail
+ (:match ,c-ts-mode--for-each-tail-regexp @for-each-tail)))))
;;; Font-lock helpers
@@ -1367,49 +1562,49 @@ in your init files."
(treesit-parser-create 'c nil nil 'for-each)))
(let ((primary-parser (treesit-parser-create 'c)))
- ;; Comments.
- (setq-local comment-start "/* ")
- (setq-local comment-end " */")
- ;; Indent.
- (setq-local treesit-simple-indent-rules
- (c-ts-mode--get-indent-style 'c))
- ;; Font-lock.
- (setq-local treesit-font-lock-settings
- (c-ts-mode--font-lock-settings 'c))
- ;; Navigation.
- ;;
- ;; Nodes like struct/enum/union_specifier can appear in
- ;; function_definitions, so we need to find the top-level node.
- (setq-local treesit-defun-tactic 'top-level)
- (treesit-major-mode-setup)
-
- ;; Emacs source support: handle DEFUN and FOR_EACH_* gracefully.
- (when c-ts-mode-emacs-sources-support
- (setq-local add-log-current-defun-function
- #'c-ts-mode--emacs-current-defun-name)
+ ;; Comments.
+ (setq-local comment-start "/* ")
+ (setq-local comment-end " */")
+ ;; Indent.
+ (setq-local treesit-simple-indent-rules
+ (c-ts-mode--simple-indent-rules
+ 'c c-ts-mode-indent-style))
+ ;; (setq-local treesit-simple-indent-rules
+ ;; `((c . ,(alist-get 'gnu (c-ts-mode--indent-styles 'c)))))
+ ;; Font-lock.
+ (setq-local treesit-font-lock-settings
+ (c-ts-mode--font-lock-settings 'c))
+ ;; Navigation.
+ (setq-local treesit-defun-tactic 'top-level)
+ (treesit-major-mode-setup)
- (setq-local treesit-range-settings
- (treesit-range-rules 'c-ts-mode--emacs-set-ranges))
+ ;; Emacs source support: handle DEFUN and FOR_EACH_* gracefully.
+ (when c-ts-mode-emacs-sources-support
+ (setq-local add-log-current-defun-function
+ #'c-ts-mode--emacs-current-defun-name)
- (setq-local treesit-language-at-point-function
- (lambda (_pos) 'c))
- (treesit-font-lock-recompute-features '(emacs-devel)))
+ (setq-local treesit-range-settings
+ (treesit-range-rules 'c-ts-mode--emacs-set-ranges))
- ;; Inject doxygen parser for comment.
- (when (and c-ts-mode-enable-doxygen (treesit-ready-p 'doxygen t))
- (setq-local treesit-primary-parser primary-parser)
- (setq-local treesit-font-lock-settings
- (append
- treesit-font-lock-settings
- c-ts-mode-doxygen-comment-font-lock-settings))
- (setq-local treesit-range-settings
- (treesit-range-rules
- :embed 'doxygen
- :host 'c
- :local t
- `(((comment) @cap
- (:match
- ,c-ts-mode--doxygen-comment-regex @cap)))))))))
+ (setq-local treesit-language-at-point-function
+ (lambda (_pos) 'c))
+ (treesit-font-lock-recompute-features '(emacs-devel)))
+
+ ;; Inject doxygen parser for comment.
+ (when (and c-ts-mode-enable-doxygen (treesit-ready-p 'doxygen t))
+ (setq-local treesit-primary-parser primary-parser)
+ (setq-local treesit-font-lock-settings
+ (append
+ treesit-font-lock-settings
+ c-ts-mode-doxygen-comment-font-lock-settings))
+ (setq-local treesit-range-settings
+ (treesit-range-rules
+ :embed 'doxygen
+ :host 'c
+ :local t
+ `(((comment) @cap
+ (:match
+ ,c-ts-mode--doxygen-comment-regex @cap)))))))))
(derived-mode-add-parents 'c-ts-mode '(c-mode))
@@ -1445,7 +1640,8 @@ recommended to enable `electric-pair-mode' with this
mode."
;; Indent.
(setq-local treesit-simple-indent-rules
- (c-ts-mode--get-indent-style 'cpp))
+ (c-ts-mode--simple-indent-rules
+ 'c++ c-ts-mode-indent-style))
;; Font-lock.
(setq-local treesit-font-lock-settings
- master updated (4afd1eca366 -> de98b5a24f2), Yuan Fu, 2024/12/01
- master c65c5d02224 01/11: Improve c-ts-mode compound indents (bug#74507), Yuan Fu, 2024/12/01
- master d0b918d8f3c 04/11: Add treesit-explore command, Yuan Fu, 2024/12/01
- master 4afdb7e80fe 05/11: ; Minor simplification for tree-sitter indent preset column-0, Yuan Fu, 2024/12/01
- master 63d69bd1549 07/11: Use new baseline indent rule in c-ts-mode,
Yuan Fu <=
- master 9acf6eff01a 10/11: Standardize and promote c-ts-mode's custom matcher and anchor, Yuan Fu, 2024/12/01
- master de98b5a24f2 11/11: ; Indent by 8 in BSD indent tests for c-ts-mode, Yuan Fu, 2024/12/01
- master 1e44c63fcad 03/11: Allow treesit-simple-indent's rule to be a single function, Yuan Fu, 2024/12/01
- master 1d425125694 02/11: Refactor treesit--indent-1, Yuan Fu, 2024/12/01
- master e37cd4fa597 06/11: Add baseline tree-sitter indent rule for C-like languages, Yuan Fu, 2024/12/01
- master 44fcd37a486 08/11: Add more c-ts-mode indent tests, Yuan Fu, 2024/12/01
- master 994258f5567 09/11: Remove unused functions in c-ts-mode, Yuan Fu, 2024/12/01