diff options
| author | Yuan Fu | 2023-04-14 12:52:46 -0700 |
|---|---|---|
| committer | Yuan Fu | 2023-04-14 13:09:05 -0700 |
| commit | b3603b84bd9c0e2f0fbd6cc49d4d086e7b08735a (patch) | |
| tree | 3a4856661647c8f9ab41cd0b0ad1ad09075b1692 | |
| parent | 14e809ddff1e60fb53d8570d9b5cfa3cb134858f (diff) | |
| download | emacs-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.el | 126 |
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. | ||
| 197 | This 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. |
| 812 | Ie, NODE is not nested." | 823 | Ie, 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 | |||
| 900 | This function recognizes DEFUNs in Emacs source files. | ||
| 901 | |||
| 902 | Note that for the case of a DEFUN, it is made of two separate | ||
| 903 | nodes, one for the declaration and one for the body, this | ||
| 904 | function returns the declaration node. | ||
| 905 | |||
| 906 | If RANGE is non-nil, return (BEG . END) where BEG end END | ||
| 907 | encloses the whole defun. This solves the problem of only | ||
| 908 | returning 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. | ||
| 947 | This is used for `add-log-current-defun-function'. This | ||
| 948 | recognizes DEFUN in Emacs sources, in addition to normal function | ||
| 949 | definitions." | ||
| 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'." |