summaryrefslogtreecommitdiff
path: root/lisp/progmodes/c-ts-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/progmodes/c-ts-mode.el')
-rw-r--r--lisp/progmodes/c-ts-mode.el230
1 files changed, 95 insertions, 135 deletions
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 161e01d4411..73e488a8058 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -487,92 +487,44 @@ For NODE, OVERRIDE, START, and END, see
(defun c-ts-mode--defun-name (node)
"Return the name of the defun NODE.
-Return nil if NODE is not a defun node, return an empty string if
-NODE doesn't have a name."
+Return nil if NODE is not a defun node or doesn't have a name."
(treesit-node-text
(pcase (treesit-node-type node)
((or "function_definition" "declaration")
(c-ts-mode--declarator-identifier
(treesit-node-child-by-field-name node "declarator")))
- ("struct_specifier"
+ ((or "struct_specifier" "enum_specifier"
+ "union_specifier" "class_specifier")
(treesit-node-child-by-field-name node "name")))
t))
-(defun c-ts-mode--imenu-1 (node)
- "Helper for `c-ts-mode--imenu'.
-Find string representation for NODE and set marker, then recurse
-the subtrees."
- (let* ((ts-node (car node))
- (subtrees (mapcan #'c-ts-mode--imenu-1 (cdr node)))
- (name (when ts-node
- (treesit-defun-name ts-node)))
- (marker (when ts-node
- (set-marker (make-marker)
- (treesit-node-start ts-node)))))
- (cond
- ;; A struct_specifier could be inside a parameter list, another
- ;; struct definition, a variable declaration, a function
- ;; declaration. In those cases we don't include it.
- ((string-match-p
- (rx (or "parameter_declaration" "field_declaration"
- "declaration" "function_definition"))
- (or (treesit-node-type (treesit-node-parent ts-node))
- ""))
- nil)
- ;; Ignore function local variable declarations.
- ((and (equal (treesit-node-type ts-node) "declaration")
- (not (equal (treesit-node-type (treesit-node-parent ts-node))
- "translation_unit")))
- nil)
- ((or (null ts-node) (null name)) subtrees)
- (subtrees
- `((,name ,(cons name marker) ,@subtrees)))
- (t
- `((,name . ,marker))))))
-
-(defun c-ts-mode--imenu ()
- "Return Imenu alist for the current buffer."
- (let* ((node (treesit-buffer-root-node))
- (func-tree (treesit-induce-sparse-tree
- node "^function_definition$" nil 1000))
- (var-tree (treesit-induce-sparse-tree
- node "^declaration$" nil 1000))
- (struct-tree (treesit-induce-sparse-tree
- node "^struct_specifier$" nil 1000))
- (func-index (c-ts-mode--imenu-1 func-tree))
- (var-index (c-ts-mode--imenu-1 var-tree))
- (struct-index (c-ts-mode--imenu-1 struct-tree)))
- (append
- (when struct-index `(("Struct" . ,struct-index)))
- (when var-index `(("Variable" . ,var-index)))
- (when func-index `(("Function" . ,func-index))))))
-
;;; Defun navigation
-(defun c-ts-mode--end-of-defun ()
- "`end-of-defun-function' of `c-ts-mode'."
- ;; A struct/enum/union_specifier node doesn't include the ; at the
- ;; end, so we manually skip it.
- (treesit-end-of-defun)
- (when (looking-at (rx (* " ") ";"))
- (goto-char (match-end 0))
- ;; This part is copied from `end-of-defun'.
- (unless (bolp)
- (skip-chars-forward " \t")
- (if (looking-at "\\s<\\|\n")
- (forward-line 1)))))
-
(defun c-ts-mode--defun-valid-p (node)
- (if (string-match-p
- (rx (or "struct_specifier"
- "enum_specifier"
- "union_specifier"))
- (treesit-node-type node))
- (null
- (treesit-node-top-level
- node (rx (or "function_definition"
- "type_definition"))))
- t))
+ "Return non-nil if NODE is a valid defun node.
+Ie, NODE is not nested."
+ (not (or (and (member (treesit-node-type node)
+ '("struct_specifier"
+ "enum_specifier"
+ "union_specifier"
+ "declaration"))
+ ;; If NODE's type is one of the above, make sure it is
+ ;; top-level.
+ (treesit-node-top-level
+ node (rx (or "function_definition"
+ "type_definition"
+ "struct_specifier"
+ "enum_specifier"
+ "union_specifier"
+ "declaration"))))
+
+ (and (equal (treesit-node-type node) "declaration")
+ ;; If NODE is a declaration, make sure it is not a
+ ;; function declaration.
+ (equal (treesit-node-type
+ (treesit-node-child-by-field-name
+ node "declarator"))
+ "function_declarator")))))
(defun c-ts-mode--defun-skipper ()
"Custom defun skipper for `c-ts-mode' and friends.
@@ -660,6 +612,59 @@ ARG is passed to `fill-paragraph'."
;; itself.
t)))
+(defun c-ts-mode-comment-setup ()
+ "Set up local variables for C-like comment.
+
+Set up:
+ - `comment-start'
+ - `comment-end'
+ - `comment-start-skip'
+ - `comment-end-skip'
+ - `adaptive-fill-mode'
+ - `adaptive-fill-first-line-regexp'
+ - `paragraph-start'
+ - `paragraph-separate'
+ - `fill-paragraph-function'"
+ (setq-local comment-start "// ")
+ (setq-local comment-end "")
+ (setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
+ (seq "/" (+ "*")))
+ (* (syntax whitespace))))
+ (setq-local comment-end-skip
+ (rx (* (syntax whitespace))
+ (group (or (syntax comment-end)
+ (seq (+ "*") "/")))))
+ (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))
+
;;; Modes
(defvar-keymap c-ts-mode-map
@@ -694,44 +699,25 @@ ARG is passed to `fill-paragraph'."
(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)
+ ;; Comment
+ (c-ts-mode-comment-setup)
;; Electric
(setq-local electric-indent-chars
(append "{}():;," electric-indent-chars))
;; Imenu.
- (setq-local imenu-create-index-function #'c-ts-mode--imenu)
- (setq-local which-func-functions nil)
+ (setq-local treesit-simple-imenu-settings
+ (let ((pred #'c-ts-mode--defun-valid-p))
+ `(("Struct" ,(rx bos (or "struct" "enum" "union")
+ "_specifier" eos)
+ ,pred nil)
+ ("Variable" ,(rx bos "declaration" eos) ,pred nil)
+ ("Function" "\\`function_definition\\'" ,pred nil)
+ ("Class" ,(rx bos (or "class_specifier"
+ "function_definition")
+ eos)
+ ,pred nil))))
(setq-local treesit-font-lock-feature-list
'(( comment definition)
@@ -752,13 +738,6 @@ ARG is passed to `fill-paragraph'."
;; Comments.
(setq-local comment-start "/* ")
(setq-local comment-end " */")
- (setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
- (seq "/" (+ "*")))
- (* (syntax whitespace))))
- (setq-local comment-end-skip
- (rx (* (syntax whitespace))
- (group (or (syntax comment-end)
- (seq (+ "*") "/")))))
(setq-local treesit-simple-indent-rules
(c-ts-mode--set-indent-style 'c))
@@ -766,11 +745,7 @@ ARG is passed to `fill-paragraph'."
;; Font-lock.
(setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c))
- (treesit-major-mode-setup)
-
- ;; Override default value of end-of-defun-function set by
- ;; `treesit-major-mode-setup'.
- (setq-local end-of-defun-function #'c-ts-mode--end-of-defun))
+ (treesit-major-mode-setup))
;;;###autoload
(define-derived-mode c++-ts-mode c-ts-base-mode "C++"
@@ -781,17 +756,6 @@ ARG is passed to `fill-paragraph'."
(unless (treesit-ready-p 'cpp)
(error "Tree-sitter for C++ isn't available"))
- ;; Comments.
- (setq-local comment-start "// ")
- (setq-local comment-end "")
- (setq-local comment-start-skip (rx (or (seq "/" (+ "/"))
- (seq "/" (+ "*")))
- (* (syntax whitespace))))
- (setq-local comment-end-skip
- (rx (* (syntax whitespace))
- (group (or (syntax comment-end)
- (seq (+ "*") "/")))))
-
(setq-local treesit-text-type-regexp
(regexp-opt '("comment"
"raw_string_literal")))
@@ -804,11 +768,7 @@ ARG is passed to `fill-paragraph'."
;; Font-lock.
(setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp))
- (treesit-major-mode-setup)
-
- ;; Override default value of end-of-defun-function set by
- ;; `treesit-major-mode-setup'.
- (setq-local end-of-defun-function #'c-ts-mode--end-of-defun))
+ (treesit-major-mode-setup))
(provide 'c-ts-mode)