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
|
||||
@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 defvar
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ MODE is either `c' or `cpp'."
|
|||
((node-is "case") parent-bol 0)
|
||||
((node-is "preproc_arg") no-indent)
|
||||
((and (parent-is "comment") comment-end) comment-start -1)
|
||||
((parent-is "comment") prev-adaptive-prefix 0)
|
||||
((node-is "labeled_statement") parent-bol 0)
|
||||
((parent-is "labeled_statement") parent-bol c-ts-mode-indent-offset)
|
||||
((match "preproc_ifdef" "compound_statement") point-min 0)
|
||||
|
@ -562,6 +563,70 @@ the semicolon. This function skips the semicolon."
|
|||
(treesit-node-end node))
|
||||
(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
|
||||
:doc "Keymap for the C language with tree-sitter"
|
||||
:parent prog-mode-map
|
||||
|
@ -593,6 +658,37 @@ the semicolon. This function skips the semicolon."
|
|||
(when (eq c-ts-mode-indent-style 'linux)
|
||||
(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
|
||||
(setq-local electric-indent-chars
|
||||
(append "{}():;," electric-indent-chars))
|
||||
|
|
|
@ -1107,6 +1107,22 @@ See `treesit-simple-indent-presets'.")
|
|||
(re-search-forward comment-start-skip)
|
||||
(skip-syntax-backward "-")
|
||||
(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.
|
||||
(cons 'grand-parent
|
||||
(lambda (_n parent &rest _)
|
||||
|
@ -1229,7 +1245,14 @@ comment-start
|
|||
|
||||
Goes to the position that `comment-start-skip' would return,
|
||||
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)
|
||||
"Evaluate EXP.
|
||||
|
|
Loading…
Add table
Reference in a new issue