Fix c-ts-mode preproc directive indentation

Mentioned in bug#61893, although not the subject of that report.  This
change fixes indentation for nested directives.  For example, when the
directive involves elif and the like, the elif is nested in the if
directive, so simply using grand-parent and great-grand-parent for
anchor is insufficient, because the nesting can grow arbitrarily.

The test added also covers the last preproc fix.

* lisp/progmodes/c-ts-mode.el:
(c-ts-mode--standalone-parent-skip-preproc): New function.
(c-ts-mode--indent-styles): New rules.
* test/lisp/progmodes/c-ts-mode-resources/indent-preproc.erts: New test.
This commit is contained in:
Yuan Fu 2023-03-04 14:39:44 -08:00
parent 64980a59b6
commit 836044f329
No known key found for this signature in database
GPG key ID: 56E19BC57664A442
2 changed files with 68 additions and 2 deletions

View file

@ -299,6 +299,23 @@ PARENT and BOL are like other anchor functions."
;; prev-sibling doesn't have a child.
(treesit-node-start prev-sibling)))
(defun c-ts-mode--standalone-parent-skip-preproc (_n parent &rest _)
"Like the standalone-parent anchor but skips preproc nodes.
PARENT is the same as other anchor functions."
(save-excursion
(treesit-node-start
(treesit-parent-until
;; Use PARENT rather than NODE, to handle the case where NODE is
;; nil.
parent (lambda (node)
(and node
(not (string-match "preproc" (treesit-node-type node)))
(progn
(goto-char (treesit-node-start node))
(looking-back (rx bol (* whitespace))
(line-beginning-position)))))
t))))
(defun c-ts-mode--standalone-grandparent (_node parent bol &rest args)
"Like the standalone-parent anchor but pass it the grandparent.
PARENT, BOL, ARGS are the same as other anchor functions."
@ -330,13 +347,28 @@ MODE is either `c' or `cpp'."
((parent-is "labeled_statement")
c-ts-mode--standalone-grandparent c-ts-mode-indent-offset)
;; Preproc directives
((node-is "preproc") column-0 0)
((node-is "#endif") column-0 0)
((match "preproc_call" "compound_statement") column-0 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)
((n-p-gp nil "\n" "preproc") great-grand-parent c-ts-mode--preproc-offset)
((parent-is "preproc") grand-parent c-ts-mode-indent-offset)
;; 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)
((parent-is "function_definition") parent-bol 0)
((parent-is "conditional_expression") first-sibling 0)

View file

@ -44,3 +44,37 @@ static void
/* */
static void
=-=-=
Code:
(lambda ()
(c-ts-mode)
(setq-local indent-tabs-mode nil)
(setq-local c-ts-mode-indent-offset 2)
(c-ts-mode-set-style 'gnu)
(indent-region (point-min) (point-max)))
Name: Prev-Sibling When Prev-Sibling is Preproc
=-=
static void
free_glyph_pool (struct glyph_pool *pool)
{
if (pool)
{
#if defined GLYPH_DEBUG
int c = 1;
#endif
int check_this = 3;
#ifdef stuff
int c = 1;
#elif defined stuff
int e = 5;
#else
int d = 11;
int f = 11;
#endif
int check_this = 3;
}
}
=-=-=