aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuan Fu2023-04-14 12:52:46 -0700
committerYuan Fu2023-04-14 13:09:05 -0700
commitb3603b84bd9c0e2f0fbd6cc49d4d086e7b08735a (patch)
tree3a4856661647c8f9ab41cd0b0ad1ad09075b1692
parent14e809ddff1e60fb53d8570d9b5cfa3cb134858f (diff)
downloademacs-b3603b84bd9c0e2f0fbd6cc49d4d086e7b08735a.tar.gz
emacs-b3603b84bd9c0e2f0fbd6cc49d4d086e7b08735a.zip
Partial support for DEFUN in c-ts-mode (bug#62825)
The DEFUN is hard to incorporate because it's made of two nodes rather than one, and most tree-sitter functionalities assume a defun is one node. I fixed the indent-defun and add-log functionality, but beginning/end-of-defun and imenu still don't recognize DEFUN. * lisp/progmodes/c-ts-mode.el (c-ts-mode-emacs-devel): New variable. (c-ts-mode--defun-name): Support DEFUN. (c-ts-mode--defun-valid-p): Support DEFUN. (c-ts-mode--emacs-defun-p) (c-ts-mode--emacs-defun-at-point): New functions. (c-ts-mode-indent-defun): Use c-ts-mode--emacs-defun-at-point. (c-ts-mode--emacs-current-defun-name): New function. (c-ts-mode, c++-ts-mode): Optionally setup custom defun-name function.
-rw-r--r--lisp/progmodes/c-ts-mode.el126
1 files changed, 101 insertions, 25 deletions
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 2d54577cb09..f3c1dde10c0 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -192,6 +192,10 @@ To set the default indent style globally, use
192 (c-ts-mode--get-indent-style 192 (c-ts-mode--get-indent-style
193 (if (derived-mode-p 'c-ts-mode) 'c 'cpp)))))) 193 (if (derived-mode-p 'c-ts-mode) 'c 'cpp))))))
194 194
195(defvar c-ts-mode-emacs-devel nil
196 "If the value is t, enable Emacs source-specific features.
197This needs to be set before enabling `c-ts-mode'.")
198
195;;; Syntax table 199;;; Syntax table
196 200
197(defvar c-ts-mode--syntax-table 201(defvar c-ts-mode--syntax-table
@@ -802,7 +806,14 @@ Return nil if NODE is not a defun node or doesn't have a name."
802 ((or "struct_specifier" "enum_specifier" 806 ((or "struct_specifier" "enum_specifier"
803 "union_specifier" "class_specifier" 807 "union_specifier" "class_specifier"
804 "namespace_definition") 808 "namespace_definition")
805 (treesit-node-child-by-field-name node "name"))) 809 (treesit-node-child-by-field-name node "name"))
810 ;; DEFUNs in Emacs source.
811 ("expression_statement"
812 (let* ((call-exp-1 (treesit-node-child node 0))
813 (call-exp-2 (treesit-node-child call-exp-1 0))
814 (arg-list (treesit-node-child call-exp-2 1))
815 (name (treesit-node-child arg-list 1 t)))
816 name)))
806 t)) 817 t))
807 818
808;;; Defun navigation 819;;; Defun navigation
@@ -810,28 +821,29 @@ Return nil if NODE is not a defun node or doesn't have a name."
810(defun c-ts-mode--defun-valid-p (node) 821(defun c-ts-mode--defun-valid-p (node)
811 "Return non-nil if NODE is a valid defun node. 822 "Return non-nil if NODE is a valid defun node.
812Ie, NODE is not nested." 823Ie, NODE is not nested."
813 (not (or (and (member (treesit-node-type node) 824 (or (c-ts-mode--emacs-defun-p node)
814 '("struct_specifier" 825 (not (or (and (member (treesit-node-type node)
815 "enum_specifier" 826 '("struct_specifier"
816 "union_specifier"
817 "declaration"))
818 ;; If NODE's type is one of the above, make sure it is
819 ;; top-level.
820 (treesit-node-top-level
821 node (rx (or "function_definition"
822 "type_definition"
823 "struct_specifier"
824 "enum_specifier" 827 "enum_specifier"
825 "union_specifier" 828 "union_specifier"
826 "declaration")))) 829 "declaration"))
830 ;; If NODE's type is one of the above, make sure it is
831 ;; top-level.
832 (treesit-node-top-level
833 node (rx (or "function_definition"
834 "type_definition"
835 "struct_specifier"
836 "enum_specifier"
837 "union_specifier"
838 "declaration"))))
827 839
828 (and (equal (treesit-node-type node) "declaration") 840 (and (equal (treesit-node-type node) "declaration")
829 ;; If NODE is a declaration, make sure it is not a 841 ;; If NODE is a declaration, make sure it is not a
830 ;; function declaration. 842 ;; function declaration.
831 (equal (treesit-node-type 843 (equal (treesit-node-type
832 (treesit-node-child-by-field-name 844 (treesit-node-child-by-field-name
833 node "declarator")) 845 node "declarator"))
834 "function_declarator"))))) 846 "function_declarator"))))))
835 847
836(defun c-ts-mode--defun-for-class-in-imenu-p (node) 848(defun c-ts-mode--defun-for-class-in-imenu-p (node)
837 "Check if NODE is a valid entry for the Class subindex. 849 "Check if NODE is a valid entry for the Class subindex.
@@ -871,17 +883,73 @@ the semicolon. This function skips the semicolon."
871 (setq parent (treesit-node-parent smallest-node))))) 883 (setq parent (treesit-node-parent smallest-node)))))
872 (list node parent bol))) 884 (list node parent bol)))
873 885
886(defun c-ts-mode--emacs-defun-p (node)
887 "Return non-nil if NODE is a DEFUN in Emacs source files."
888 (and (equal (treesit-node-type node) "expression_statement")
889 (equal (treesit-node-text
890 (treesit-node-child-by-field-name
891 (treesit-node-child
892 (treesit-node-child node 0) 0)
893 "function")
894 t)
895 "DEFUN")))
896
897(defun c-ts-mode--emacs-defun-at-point (&optional range)
898 "Return the current defun node.
899
900This function recognizes DEFUNs in Emacs source files.
901
902Note that for the case of a DEFUN, it is made of two separate
903nodes, one for the declaration and one for the body, this
904function returns the declaration node.
905
906If RANGE is non-nil, return (BEG . END) where BEG end END
907encloses the whole defun. This solves the problem of only
908returning the declaration part for DEFUN."
909 (or (when-let ((node (treesit-defun-at-point)))
910 (if range
911 (cons (treesit-node-start node)
912 (treesit-node-end node))
913 node))
914 (and c-ts-mode-emacs-devel
915 (let ((candidate-1 ; For when point is in the DEFUN statement.
916 (treesit-node-prev-sibling
917 (treesit-node-top-level
918 (treesit-node-at (point))
919 "compound_statement")))
920 (candidate-2 ; For when point is in the body.
921 (treesit-node-top-level
922 (treesit-node-at (point))
923 "expression_statement")))
924 (when-let
925 ((node (or (and (c-ts-mode--emacs-defun-p candidate-1)
926 candidate-1)
927 (and (c-ts-mode--emacs-defun-p candidate-2)
928 candidate-2))))
929 (if range
930 (cons (treesit-node-start node)
931 (treesit-node-end
932 (treesit-node-next-sibling node)))
933 node))))))
934
874(defun c-ts-mode-indent-defun () 935(defun c-ts-mode-indent-defun ()
875 "Indent the current top-level declaration syntactically. 936 "Indent the current top-level declaration syntactically.
876 937
877`treesit-defun-type-regexp' defines what constructs to indent." 938`treesit-defun-type-regexp' defines what constructs to indent."
878 (interactive "*") 939 (interactive "*")
879 (when-let ((orig-point (point-marker)) 940 (when-let ((orig-point (point-marker))
880 (node (treesit-defun-at-point))) 941 (range (c-ts-mode--emacs-defun-at-point t)))
881 (indent-region (treesit-node-start node) 942 (indent-region (car range) (cdr range))
882 (treesit-node-end node))
883 (goto-char orig-point))) 943 (goto-char orig-point)))
884 944
945(defun c-ts-mode--emacs-current-defun-name ()
946 "Return the name of the current defun.
947This is used for `add-log-current-defun-function'. This
948recognizes DEFUN in Emacs sources, in addition to normal function
949definitions."
950 (or (treesit-add-log-current-defun)
951 (c-ts-mode--defun-name (c-ts-mode--emacs-defun-at-point))))
952
885;;; Modes 953;;; Modes
886 954
887(defvar-keymap c-ts-base-mode-map 955(defvar-keymap c-ts-base-mode-map
@@ -996,7 +1064,11 @@ in your configuration."
996 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c)) 1064 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c))
997 ;; Navigation. 1065 ;; Navigation.
998 (setq-local treesit-defun-tactic 'top-level) 1066 (setq-local treesit-defun-tactic 'top-level)
999 (treesit-major-mode-setup))) 1067 (treesit-major-mode-setup)
1068
1069 (when c-ts-mode-emacs-devel
1070 (setq-local add-log-current-defun-function
1071 #'c-ts-mode--emacs-current-defun-name))))
1000 1072
1001;;;###autoload 1073;;;###autoload
1002(define-derived-mode c++-ts-mode c-ts-base-mode "C++" 1074(define-derived-mode c++-ts-mode c-ts-base-mode "C++"
@@ -1031,7 +1103,11 @@ recommended to enable `electric-pair-mode' with this mode."
1031 (c-ts-mode--get-indent-style 'cpp)) 1103 (c-ts-mode--get-indent-style 'cpp))
1032 ;; Font-lock. 1104 ;; Font-lock.
1033 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp)) 1105 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp))
1034 (treesit-major-mode-setup))) 1106 (treesit-major-mode-setup)
1107
1108 (when c-ts-mode-emacs-devel
1109 (setq-local add-log-current-defun-function
1110 #'c-ts-mode--emacs-current-defun-name))))
1035 1111
1036(easy-menu-define c-ts-mode-menu (list c-ts-mode-map c++-ts-mode-map) 1112(easy-menu-define c-ts-mode-menu (list c-ts-mode-map c++-ts-mode-map)
1037 "Menu for `c-ts-mode' and `c++-ts-mode'." 1113 "Menu for `c-ts-mode' and `c++-ts-mode'."