From 1670ce27679f323282c68a21f6e29eac3cc2532e Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Thu, 15 May 2025 12:46:58 +0200 Subject: [PATCH] feat!(cc): add treesit support BREAKING CHANGE: Besides treesit support, this removes a few fontification enhancements (in favor of tree-sitter). --- modules/lang/cc/autoload.el | 61 +++++++-------- modules/lang/cc/config.el | 113 +++++++++++++++++++--------- modules/lang/cc/packages.el | 8 +- modules/tools/tree-sitter/config.el | 4 - 4 files changed, 110 insertions(+), 76 deletions(-) diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index e809ed225..24e5af191 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -54,32 +54,34 @@ preceded by the opening brace or a comma (disregarding whitespace in between)." This is meant to replace `c-or-c++-mode' (introduced in Emacs 26.1), which doesn't support specification of the fallback mode and whose heuristics are simpler." - (let ((base (file-name-sans-extension (buffer-file-name (buffer-base-buffer))))) - (cond ((file-exists-p! (or (concat base ".cpp") - (concat base ".cc"))) - (c++-mode)) - ((or (file-exists-p! (or (concat base ".m") - (concat base ".mm"))) - (+cc--re-search-for - (concat "^[ \t\r]*\\(?:" - "@\\(?:class\\|interface\\|property\\|end\\)\\_>" - "\\|#import +" - "\\|[-+] ([a-zA-Z0-9_]+)" + (funcall + (major-mode-remap + (let ((base (file-name-sans-extension (buffer-file-name (buffer-base-buffer))))) + (cond ((file-exists-p! (or (concat base ".cpp") + (concat base ".cc"))) + 'c++-mode) + ((or (file-exists-p! (or (concat base ".m") + (concat base ".mm"))) + (+cc--re-search-for + (concat "^[ \t\r]*\\(?:" + "@\\(?:class\\|interface\\|property\\|end\\)\\_>" + "\\|#import +" + "\\|[-+] ([a-zA-Z0-9_]+)" + "\\)"))) + 'objc-mode) + ((+cc--re-search-for + (let ((id "[a-zA-Z0-9_]+") (ws "[ \t\r]+") (ws-maybe "[ \t\r]*")) + (concat "^" ws-maybe "\\(?:" + "using" ws "\\(?:namespace" ws "std;\\|std::\\)" + "\\|" "namespace" "\\(?:" ws id "\\)?" ws-maybe "{" + "\\|" "class" ws id ws-maybe "[:{\n]" + "\\|" "template" ws-maybe "<.*>" + "\\|" "#include" ws-maybe "<\\(?:string\\|iostream\\|map\\)>" "\\)"))) - (objc-mode)) - ((+cc--re-search-for - (let ((id "[a-zA-Z0-9_]+") (ws "[ \t\r]+") (ws-maybe "[ \t\r]*")) - (concat "^" ws-maybe "\\(?:" - "using" ws "\\(?:namespace" ws "std;\\|std::\\)" - "\\|" "namespace" "\\(?:" ws id "\\)?" ws-maybe "{" - "\\|" "class" ws id ws-maybe "[:{\n]" - "\\|" "template" ws-maybe "<.*>" - "\\|" "#include" ws-maybe "<\\(?:string\\|iostream\\|map\\)>" - "\\)"))) - (c++-mode)) - ((functionp +cc-default-header-file-mode) - (funcall +cc-default-header-file-mode)) - ((c-mode))))) + 'c++-mode) + ((functionp +cc-default-header-file-mode) + +cc-default-header-file-mode) + ('c-mode)))))) (defun +cc-resolve-include-paths () (cl-loop with path = (or buffer-file-name default-directory) @@ -137,15 +139,6 @@ the children of class at point." ;; ;; Hooks -;;;###autoload -(defun +cc-fontify-constants-h () - "Better fontification for preprocessor constants" - (when (memq major-mode '(c-mode c++-mode)) - (font-lock-add-keywords - nil '(("\\<[A-Z]*_[0-9A-Z_]+\\>" . font-lock-constant-face) - ("\\<[A-Z]\\{3,\\}\\>" . font-lock-constant-face)) - t))) - (defvar +cc--project-includes-alist nil) ;;;###autoload (defun +cc-init-ffap-integration-h () diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index a1b797271..ca98b4719 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -25,17 +25,26 @@ This is ignored by ccls.") ;; set up before lsp is initialized. Also, we use local-vars hooks to ensure ;; these only run in their respective major modes, and not derived modes. :hook ((c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) . +cc-init-ffap-integration-h) + :hook ((c-ts-mode-local-vars c++-ts-mode-local-vars) . +cc-init-ffap-integration-h) ;;; Improve fontification in C/C++ (also see `modern-cpp-font-lock') :hook (c-mode-common . rainbow-delimiters-mode) - :hook ((c-mode c++-mode) . +cc-fontify-constants-h) + :init + (when (modulep! +tree-sitter) + (set-tree-sitter! 'c-mode 'c-ts-mode + '((c :url "https://github.com/tree-sitter/tree-sitter-c"))) + (set-tree-sitter! 'c++-mode 'c++-ts-mode + '((cpp :url "https://github.com/tree-sitter/tree-sitter-cpp")))) + :config - (set-docsets! 'c-mode "C") - (set-docsets! 'c++-mode "C++" "Boost") - (set-electric! '(c-mode c++-mode objc-mode java-mode) :chars '(?\n ?\} ?\{)) + (set-docsets! '(c-mode c-ts-mode) "C") + (set-docsets! '(c++-mode c++-ts-mode) "C++" "Boost") + (set-electric! '(c-mode c++-mode objc-mode java-mode + c-ts-mode c++-ts-mode java-ts-mode) + :chars '(?\n ?\} ?\{)) (set-rotate-patterns! 'c++-mode :symbols '(("public" "protected" "private") ("class" "struct"))) - (set-ligatures! '(c-mode c++-mode) + (set-ligatures! '(c-mode c-ts-mode c++-mode c++-ts-mode) ;; Functional ;; :def "void " ;; Types @@ -54,10 +63,14 @@ This is ignored by ccls.") (add-to-list 'find-sibling-rules '("/\\([^/]+\\)\\.c\\(c\\|pp\\)?\\'" "\\1.h\\(h\\|pp\\)?\\'")) (add-to-list 'find-sibling-rules '("/\\([^/]+\\)\\.h\\(h\\|pp\\)?\\'" "\\1.c\\(c\\|pp\\)?\\'")) - (when (modulep! +tree-sitter) - (add-hook! '(c-mode-local-vars-hook - c++-mode-local-vars-hook) - :append #'tree-sitter!)) + ;; Delete all the default remappings created by the cc-mode package. We define + ;; better ones with `set-tree-sitter!' further below, otherwise there should + ;; be no remapping if the user hasn't explicitly asked for tree-sitter + ;; integration. + (dolist (mode '((c++-mode . c++-ts-mode) + (c-mode . c-ts-mode) + (c-or-c++-mode . c-or-c++-ts-mode))) + (cl-callf2 delete mode major-mode-remap-defaults)) ;; HACK Suppress 'Args out of range' error in when multiple modifications are ;; performed at once in a `c++-mode' buffer, e.g. with `iedit' or @@ -101,52 +114,82 @@ This is ignored by ccls.") (label . 0)))) (when (listp c-default-style) - (setf (alist-get 'other c-default-style) "doom")) - - (after! ffap - (add-to-list 'ffap-alist '(c-mode . ffap-c-mode)))) - - -(use-package! modern-cpp-font-lock - :unless (modulep! +tree-sitter) - :hook (c++-mode . modern-c++-font-lock-mode)) + (setf (alist-get 'other c-default-style) "doom"))) ;; ;; Major modes -(after! cmake-mode - (set-docsets! 'cmake-mode "CMake") +(use-package! cmake-mode + :defer t + :init + (when (and (modulep! +tree-sitter) + (boundp 'cmake-ts-mode)) ; 29+ only + (set-tree-sitter! 'cmake-mode 'cmake-ts-mode + '((cmake :url "https://github.com/uyha/tree-sitter-cmake")))) + :config + (set-docsets! '(cmake-mode cmake-ts-mode) "CMake") (set-popup-rule! "^\\*CMake Help\\*" :size 0.4 :ttl t) - (set-lookup-handlers! 'cmake-mode - :documentation '+cc-cmake-lookup-documentation-fn)) + (set-lookup-handlers! '(cmake-mode cmake-ts-mode) + :documentation '+cc-cmake-lookup-documentation-fn) + (when (require 'company-cmake nil t) + (set-company-backend! '(cmake-mode cmake-ts-mode) 'company-cmake)) + (when (modulep! +lsp) + (add-hook 'cmake-mode-local-vars-hook #'lsp! 'append) + (add-hook 'cmake-ts-mode-local-vars-hook #'lsp! 'append))) -(use-package! company-cmake ; for `cmake-mode' - :when (modulep! :completion company) - :after cmake-mode - :config (set-company-backend! 'cmake-mode 'company-cmake)) +(use-package! glsl-mode + :defer t + :init + (when (modulep! +tree-sitter) ; 29+ only + (set-tree-sitter! 'glsl-mode 'glsl-ts-mode + '((glsl :url "https://github.com/tree-sitter-grammars/tree-sitter-glsl")))) + :config + (when (require 'company-glsl nil t) + (set-company-backend! 'glsl-mode 'company-glsl)) + (when (modulep! +lsp) + (add-hook 'glsl-mode-local-vars-hook #'lsp! 'append) + (add-hook 'glsl-ts-mode-local-vars-hook #'lsp! 'append))) + + +(use-package! cuda-mode + :defer t + :config + (when (modulep! +lsp) + (add-hook 'cuda-mode-local-vars-hook #'lsp! 'append)) + ) + + +(use-package! cuda-ts-mode + :when (modulep! +tree-sitter) + :defer t + :init + (set-tree-sitter! 'cuda-mode 'cuda-ts-mode + '((cuda :url "https://github.com/tree-sitter-grammars/tree-sitter-cuda"))) + :config + (when (modulep! +lsp) + (add-hook 'cuda-ts-mode-local-vars-hook #'lsp! 'append)) + ;; HACK: Remove redundant entries so we can rely solely on + ;; `major-mode-remap-defaults' et co. + (rassq-delete-all 'cuda-ts-mode auto-mode-alist) + (cl-callf2 delete '(cuda "https://github.com/tree-sitter-grammars/tree-sitter-cuda" nil nil nil nil) + treesit-language-source-alist)) (use-package! demangle-mode :hook llvm-mode) -(use-package! company-glsl ; for `glsl-mode' - :when (modulep! :completion company) - :after glsl-mode - :config (set-company-backend! 'glsl-mode 'company-glsl)) - - ;; ;;; LSP (when (modulep! +lsp) (add-hook! '(c-mode-local-vars-hook + c-ts-mode-local-vars-hook c++-mode-local-vars-hook - objc-mode-local-vars-hook - cmake-mode-local-vars-hook - cuda-mode-local-vars-hook) + c++-ts-mode-local-vars-hook + objc-mode-local-vars-hook) :append #'lsp!) (if (modulep! :tools lsp -eglot) diff --git a/modules/lang/cc/packages.el b/modules/lang/cc/packages.el index cb969ca48..d54dfca01 100644 --- a/modules/lang/cc/packages.el +++ b/modules/lang/cc/packages.el @@ -4,13 +4,15 @@ (package! cmake-mode :recipe (:host github :repo "emacsmirror/cmake-mode" :files (:defaults "*")) :pin "b08b5d9045308362a623a4f576896d55ffecfd52") -(package! cuda-mode :pin "c3dae31b3d1abedf4d0b98840127e2cac73d6ad8") (package! demangle-mode :pin "04f545adab066708d6151f13da65aaf519f8ac4e") (package! disaster :pin "8b445913221feb0c196e943106643040118bcd77") (package! opencl-mode :pin "204d5d9e0f5cb2cbe810f2933230eb08fe2c7695") -(unless (modulep! +tree-sitter) - (package! modern-cpp-font-lock :pin "43c6b68ff58fccdf9deef11674a172e4eaa8455c")) +(when (package! cuda-mode :pin "c3dae31b3d1abedf4d0b98840127e2cac73d6ad8") + (when (modulep! +tree-sitter) + (package! cuda-ts-mode + :recipe (:host github :repo "Ergus/cuda-ts-mode") + :pin "807f15150deb3a3060bc36a0e135a27876d7e239"))) (when (package! glsl-mode :pin "86e6bb6cf28d1053366039683a4498401bab9c47") (when (modulep! :completion company) diff --git a/modules/tools/tree-sitter/config.el b/modules/tools/tree-sitter/config.el index 55197da50..78c32cc03 100644 --- a/modules/tools/tree-sitter/config.el +++ b/modules/tools/tree-sitter/config.el @@ -20,17 +20,13 @@ (dolist (map '((awk "https://github.com/Beaglefoot/tree-sitter-awk" nil nil nil nil) (bibtex "https://github.com/latex-lsp/tree-sitter-bibtex" nil nil nil nil) (blueprint "https://github.com/huanie/tree-sitter-blueprint" nil nil nil nil) - (c "https://github.com/tree-sitter/tree-sitter-c" nil nil nil nil) (c-sharp "https://github.com/tree-sitter/tree-sitter-c-sharp" nil nil nil nil) (clojure "https://github.com/sogaiu/tree-sitter-clojure" nil nil nil nil) - (cmake "https://github.com/uyha/tree-sitter-cmake" nil nil nil nil) (commonlisp "https://github.com/tree-sitter-grammars/tree-sitter-commonlisp" nil nil nil nil) - (cpp "https://github.com/tree-sitter/tree-sitter-cpp" nil nil nil nil) (css "https://github.com/tree-sitter/tree-sitter-css" nil nil nil nil) (dart "https://github.com/ast-grep/tree-sitter-dart" nil nil nil nil) (dockerfile "https://github.com/camdencheek/tree-sitter-dockerfile" nil nil nil nil) (elixir "https://github.com/elixir-lang/tree-sitter-elixir" nil nil nil nil) - (glsl "https://github.com/tree-sitter-grammars/tree-sitter-glsl" nil nil nil nil) (go "https://github.com/tree-sitter/tree-sitter-go" nil nil nil nil) (gomod "https://github.com/camdencheek/tree-sitter-go-mod" nil nil nil nil) (heex "https://github.com/phoenixframework/tree-sitter-heex" nil nil nil nil)