Fix block comment indent and filling for c-ts-mode (bug#59763)
Now indent and filling works like in c-mode. The only noticeable missing piece is that the "*/" is not attached to the last sentence when filling. c-mode does it by replacing whitespaces between the "*/" and the end of the last sentence with xxx, fill it, then change the xxx back. I don't know if we should do that in c-ts-mode's filling. * doc/lispref/modes.texi (Parser-based Indentation): Add new preset. * lisp/progmodes/c-ts-mode.el (c-ts-mode--indent-styles): Add new indent rule. (c-ts-mode--fill-paragraph): New function. (c-ts-base-mode): Setup paragraph-start, adaptive-fill, etc. * lisp/treesit.el (treesit-simple-indent-presets): Add new preset.
This commit is contained in:
parent
e492c21e81
commit
6a43af5880
3 changed files with 128 additions and 1 deletions
|
@ -5024,6 +5024,14 @@ comment-start token. Comment-start tokens are defined by regular
|
||||||
expression @code{comment-start-skip}. This function assumes
|
expression @code{comment-start-skip}. This function assumes
|
||||||
@var{parent} is the comment node.
|
@var{parent} is the comment node.
|
||||||
|
|
||||||
|
@item prev-adaptive-prefix
|
||||||
|
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.
|
||||||
|
|
||||||
@end ftable
|
@end ftable
|
||||||
@end defvar
|
@end defvar
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ MODE is either `c' or `cpp'."
|
||||||
((node-is "case") parent-bol 0)
|
((node-is "case") parent-bol 0)
|
||||||
((node-is "preproc_arg") no-indent)
|
((node-is "preproc_arg") no-indent)
|
||||||
((and (parent-is "comment") comment-end) comment-start -1)
|
((and (parent-is "comment") comment-end) comment-start -1)
|
||||||
|
((parent-is "comment") prev-adaptive-prefix 0)
|
||||||
((node-is "labeled_statement") parent-bol 0)
|
((node-is "labeled_statement") parent-bol 0)
|
||||||
((parent-is "labeled_statement") parent-bol c-ts-mode-indent-offset)
|
((parent-is "labeled_statement") parent-bol c-ts-mode-indent-offset)
|
||||||
((match "preproc_ifdef" "compound_statement") point-min 0)
|
((match "preproc_ifdef" "compound_statement") point-min 0)
|
||||||
|
@ -562,6 +563,70 @@ the semicolon. This function skips the semicolon."
|
||||||
(treesit-node-end node))
|
(treesit-node-end node))
|
||||||
(goto-char orig-point)))
|
(goto-char orig-point)))
|
||||||
|
|
||||||
|
(defun c-ts-mode--fill-paragraph (&optional arg)
|
||||||
|
"Fillling function for `c-ts-mode'.
|
||||||
|
ARG is passed to `fill-paragraph'."
|
||||||
|
(interactive "*P")
|
||||||
|
(save-restriction
|
||||||
|
(widen)
|
||||||
|
(let* ((node (treesit-node-at (point)))
|
||||||
|
(start (treesit-node-start node))
|
||||||
|
(end (treesit-node-end node))
|
||||||
|
;; Bind to nil to avoid infinite recursion.
|
||||||
|
(fill-paragraph-function nil)
|
||||||
|
(orig-point (point-marker))
|
||||||
|
(start-marker nil)
|
||||||
|
(end-marker nil)
|
||||||
|
(end-len 0))
|
||||||
|
(when (equal (treesit-node-type node) "comment")
|
||||||
|
;; We mask "/*" and the space before "*/" like
|
||||||
|
;; `c-fill-paragraph' does.
|
||||||
|
(atomic-change-group
|
||||||
|
;; Mask "/*".
|
||||||
|
(goto-char start)
|
||||||
|
(when (looking-at (rx (* (syntax whitespace))
|
||||||
|
(group "/") "*"))
|
||||||
|
(goto-char (match-beginning 1))
|
||||||
|
(setq start-marker (point-marker))
|
||||||
|
(replace-match " " nil nil nil 1))
|
||||||
|
;; Mask spaces before "*/" if it is attached at the end
|
||||||
|
;; of a sentence rather than on its own line.
|
||||||
|
(goto-char end)
|
||||||
|
(when (looking-back (rx (not (syntax whitespace))
|
||||||
|
(group (+ (syntax whitespace)))
|
||||||
|
"*/")
|
||||||
|
(line-beginning-position))
|
||||||
|
(goto-char (match-beginning 1))
|
||||||
|
(setq end-marker (point-marker))
|
||||||
|
(setq end-len (- (match-end 1) (match-beginning 1)))
|
||||||
|
(replace-match (make-string end-len ?x)
|
||||||
|
nil nil nil 1))
|
||||||
|
;; If "*/" is on its own line, don't included it in the
|
||||||
|
;; filling region.
|
||||||
|
(when (not end-marker)
|
||||||
|
(goto-char end)
|
||||||
|
(when (looking-back "*/" 2)
|
||||||
|
(backward-char 2)
|
||||||
|
(skip-syntax-backward "-")
|
||||||
|
(setq end (point))))
|
||||||
|
;; Let `fill-paragraph' do its thing.
|
||||||
|
(goto-char orig-point)
|
||||||
|
(narrow-to-region start end)
|
||||||
|
(funcall #'fill-paragraph arg)
|
||||||
|
;; Unmask.
|
||||||
|
(when start-marker
|
||||||
|
(goto-char start-marker)
|
||||||
|
(delete-char 1)
|
||||||
|
(insert "/"))
|
||||||
|
(when end-marker
|
||||||
|
(goto-char end-marker)
|
||||||
|
(delete-region (point) (+ end-len (point)))
|
||||||
|
(insert (make-string end-len ?\s))))
|
||||||
|
(goto-char orig-point))
|
||||||
|
;; Return t so `fill-paragraph' doesn't attempt to fill by
|
||||||
|
;; itself.
|
||||||
|
t)))
|
||||||
|
|
||||||
(defvar-keymap c-ts-mode-map
|
(defvar-keymap c-ts-mode-map
|
||||||
:doc "Keymap for the C language with tree-sitter"
|
:doc "Keymap for the C language with tree-sitter"
|
||||||
:parent prog-mode-map
|
:parent prog-mode-map
|
||||||
|
@ -593,6 +658,37 @@ the semicolon. This function skips the semicolon."
|
||||||
(when (eq c-ts-mode-indent-style 'linux)
|
(when (eq c-ts-mode-indent-style 'linux)
|
||||||
(setq-local indent-tabs-mode t))
|
(setq-local indent-tabs-mode t))
|
||||||
|
|
||||||
|
(setq-local adaptive-fill-mode t)
|
||||||
|
;; This matches (1) empty spaces (the default), (2) "//", (3) "*",
|
||||||
|
;; but do not match "/*", because we don't want to use "/*" as
|
||||||
|
;; prefix when filling. (Actually, it doesn't matter, because
|
||||||
|
;; `comment-start-skip' matches "/*" which will cause
|
||||||
|
;; `fill-context-prefix' to use "/*" as a prefix for filling, that's
|
||||||
|
;; why we mask the "/*" in `c-ts-mode--fill-paragraph'.)
|
||||||
|
(setq-local adaptive-fill-regexp
|
||||||
|
(concat (rx (* (syntax whitespace))
|
||||||
|
(group (or (seq "/" (+ "/")) (* "*"))))
|
||||||
|
adaptive-fill-regexp))
|
||||||
|
;; Same as `adaptive-fill-regexp'.
|
||||||
|
(setq-local adaptive-fill-first-line-regexp
|
||||||
|
(rx bos
|
||||||
|
(seq (* (syntax whitespace))
|
||||||
|
(group (or (seq "/" (+ "/")) (* "*")))
|
||||||
|
(* (syntax whitespace)))
|
||||||
|
eos))
|
||||||
|
;; Same as `adaptive-fill-regexp'.
|
||||||
|
(setq-local paragraph-start
|
||||||
|
(rx (or (seq (* (syntax whitespace))
|
||||||
|
(group (or (seq "/" (+ "/")) (* "*")))
|
||||||
|
(* (syntax whitespace))
|
||||||
|
;; Add this eol so that in
|
||||||
|
;; `fill-context-prefix', `paragraph-start'
|
||||||
|
;; doesn't match the prefix.
|
||||||
|
eol)
|
||||||
|
"\f")))
|
||||||
|
(setq-local paragraph-separate paragraph-start)
|
||||||
|
(setq-local fill-paragraph-function #'c-ts-mode--fill-paragraph)
|
||||||
|
|
||||||
;; Electric
|
;; Electric
|
||||||
(setq-local electric-indent-chars
|
(setq-local electric-indent-chars
|
||||||
(append "{}():;," electric-indent-chars))
|
(append "{}():;," electric-indent-chars))
|
||||||
|
|
|
@ -1107,6 +1107,22 @@ See `treesit-simple-indent-presets'.")
|
||||||
(re-search-forward comment-start-skip)
|
(re-search-forward comment-start-skip)
|
||||||
(skip-syntax-backward "-")
|
(skip-syntax-backward "-")
|
||||||
(point))))
|
(point))))
|
||||||
|
(cons 'prev-adaptive-prefix
|
||||||
|
(lambda (_n parent &rest _)
|
||||||
|
(save-excursion
|
||||||
|
(re-search-backward
|
||||||
|
(rx (not (or " " "\t" "\n"))) nil t)
|
||||||
|
(beginning-of-line)
|
||||||
|
(and (>= (point) (treesit-node-start parent))
|
||||||
|
;; `adaptive-fill-regexp' will not match "/*",
|
||||||
|
;; so we need to also try `comment-start-skip'.
|
||||||
|
(or (and adaptive-fill-regexp
|
||||||
|
(looking-at adaptive-fill-regexp)
|
||||||
|
(> (- (match-end 0) (match-beginning 0)) 0)
|
||||||
|
(match-end 0))
|
||||||
|
(and comment-start-skip
|
||||||
|
(looking-at comment-start-skip)
|
||||||
|
(match-end 0)))))))
|
||||||
;; TODO: Document.
|
;; TODO: Document.
|
||||||
(cons 'grand-parent
|
(cons 'grand-parent
|
||||||
(lambda (_n parent &rest _)
|
(lambda (_n parent &rest _)
|
||||||
|
@ -1229,7 +1245,14 @@ comment-start
|
||||||
|
|
||||||
Goes to the position that `comment-start-skip' would return,
|
Goes to the position that `comment-start-skip' would return,
|
||||||
skips whitespace backwards, and returns the resulting
|
skips whitespace backwards, and returns the resulting
|
||||||
position. Assumes PARENT is a comment node.")
|
position. Assumes PARENT is a comment node.
|
||||||
|
|
||||||
|
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.")
|
||||||
|
|
||||||
(defun treesit--simple-indent-eval (exp)
|
(defun treesit--simple-indent-eval (exp)
|
||||||
"Evaluate EXP.
|
"Evaluate EXP.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue