unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Tree sitter support for C-like languages
@ 2022-11-10 17:45 Theodor Thornhill via Emacs development discussions.
  2022-11-10 18:03 ` Stefan Monnier
                   ` (3 more replies)
  0 siblings, 4 replies; 83+ messages in thread
From: Theodor Thornhill via Emacs development discussions. @ 2022-11-10 17:45 UTC (permalink / raw)
  To: emacs-devel; +Cc: casouri

[-- Attachment #1: Type: text/plain, Size: 500 bytes --]



Hi all,

See the attached patch for support for several C-like languages.

They all support:
- Font locking
- Indentation (with styles for c/c++)
- Movement
- Imenu
- Which-func

These modes are meant as a supplement to tree-sitter.

I'm hopeful for some constructive criticism, and some testing.  This
patch needs to be applied to the feature/tree-sitter branch, and should
hopefully be applied there before we merge the branch to master, well
before Emacs 29 is cut.

I hope you like it,

Theo



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-Tree-sitter-modes-for-C-like-languages.patch --]
[-- Type: text/x-diff, Size: 54077 bytes --]

From 5f00f6c0f28f9113aded33a968c3694e9e698f98 Mon Sep 17 00:00:00 2001
From: Theodor Thornhill <theo@thornhill.no>
Date: Thu, 10 Nov 2022 17:15:49 +0100
Subject: [PATCH] Add Tree sitter modes for C-like languages

* etc/NEWS: Mention the new modes
* lisp/progmodes/c++-ts-mode.el: New major mode with Tree Sitter support.
* lisp/progmodes/c-ts-mode.el: New major mode with Tree Sitter support.
* lisp/progmodes/java-ts-mode.el: New major mode with Tree Sitter support.
* lisp/progmodes/json-ts-mode.el: New major mode with Tree Sitter support.
* lisp/progmodes/css-ts-mode.el: New major mode with Tree Sitter support.
---
 etc/NEWS                       |  28 ++-
 lisp/progmodes/c++-ts-mode.el  | 407 +++++++++++++++++++++++++++++++++
 lisp/progmodes/c-ts-mode.el    | 396 ++++++++++++++++++++++++++++++++
 lisp/progmodes/css-ts-mode.el  | 122 ++++++++++
 lisp/progmodes/java-ts-mode.el | 282 +++++++++++++++++++++++
 lisp/progmodes/json-ts-mode.el | 141 ++++++++++++
 6 files changed, 1374 insertions(+), 2 deletions(-)
 create mode 100644 lisp/progmodes/c++-ts-mode.el
 create mode 100644 lisp/progmodes/c-ts-mode.el
 create mode 100644 lisp/progmodes/css-ts-mode.el
 create mode 100644 lisp/progmodes/java-ts-mode.el
 create mode 100644 lisp/progmodes/json-ts-mode.el

diff --git a/etc/NEWS b/etc/NEWS
index 9ed78bc6b3..3ce9810ece 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2786,8 +2786,32 @@ when visiting JSON files.
 ** New mode ts-mode'.
 Support is added for TypeScript, based on the new integration with
 Tree-Sitter. There's support for font-locking, indentation and
-navigation.  Tree-Sitter is required for this mode to function, but if
-it is not available, we will default to use 'js-mode'.
+navigation.  Tree-Sitter is required for this mode to function.
+
+** New mode c-ts-mode'.
+Support is added for C, based on the new integration with
+Tree-Sitter. There's support for font-locking, indentation and
+navigation.  Tree-Sitter is required for this mode to function.
+
+** New mode c++-ts-mode'.
+Support is added for c++, based on the new integration with
+Tree-Sitter. There's support for font-locking, indentation and
+navigation.  Tree-Sitter is required for this mode to function.
+
+** New mode java-ts-mode'.
+Support is added for Java, based on the new integration with
+Tree-Sitter. There's support for font-locking, indentation and
+navigation.  Tree-Sitter is required for this mode to function.
+
+** New mode css-ts-mode'.
+Support is added for CSS, based on the new integration with
+Tree-Sitter. There's support for font-locking, indentation and
+navigation.  Tree-Sitter is required for this mode to function.
+
+** New mode json-ts-mode'.
+Support is added for JSON, based on the new integration with
+Tree-Sitter. There's support for font-locking, indentation and
+navigation.  Tree-Sitter is required for this mode to function.
 
 \f
 * Incompatible Lisp Changes in Emacs 29.1
diff --git a/lisp/progmodes/c++-ts-mode.el b/lisp/progmodes/c++-ts-mode.el
new file mode 100644
index 0000000000..09457ec368
--- /dev/null
+++ b/lisp/progmodes/c++-ts-mode.el
@@ -0,0 +1,407 @@
+;;; c++-ts-mode.el --- tree sitter support for C++  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : c++ languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+
+(defcustom c++-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `c++-ts-mode'."
+  :type 'integer
+  :safe 'integerp
+  :group 'cpp)
+
+
+(defcustom c++-ts-mode-indent-style 'gnu
+  "Style used for indentation.
+
+The selected style could be one of GNU, K&R, LINUX or BSD.  If
+one of the supplied styles doesn't suffice a function could be
+set instead.  This function is expected return a list that
+follows the form of `treesit-simple-indent-rules'."
+  :type '(choice (symbol :tag "Gnu" 'gnu)
+                 (symbol :tag "K&R" 'k&r)
+                 (symbol :tag "Linux" 'linux)
+                 (symbol :tag "BSD" 'bsd)
+                 (function :tag "A function for user customized style" ignore))
+  :group 'cpp)
+
+(defvar c++-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?\' "\""    table)
+    (modify-syntax-entry ?\240 "."   table)
+    (modify-syntax-entry ?/  ". 124b" table)
+    (modify-syntax-entry ?*  ". 23"   table)
+    table)
+  "Syntax table for `c++-ts-mode'.")
+
+(defvar c++-ts-mode--indent-styles
+  (let ((common
+         `(((parent-is "translation_unit") parent-bol 0)
+           ((node-is ")") parent 1)
+           ((node-is "]") parent-bol 0)
+           ((node-is "else") parent-bol 0)
+           ((node-is "case") parent-bol 0)
+           ((node-is "comment") no-indent)
+           ((parent-is "comment") no-indent)
+           ((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)
+           ((match "#endif" "preproc_ifdef") point-min 0)
+           ((match "preproc_if" "compound_statement") point-min 0)
+           ((match "#endif" "preproc_if") point-min 0)
+           ((match "preproc_function_def" "compound_statement") point-min 0)
+           ((match "preproc_call" "compound_statement") point-min 0)
+           ((parent-is "field_declaration_list") parent-bol c++-ts-mode-indent-offset)
+           ((node-is "field_initializer_list") parent-bol ,(* c++-ts-mode-indent-offset 2))
+           ((parent-is "function_definition") parent-bol 0)
+           ((parent-is "conditional_expression") first-sibling 0)
+           ((parent-is "assignment_expression") parent-bol c++-ts-mode-indent-offset)
+           ((parent-is "comma_expression") first-sibling 0)
+           ((parent-is "init_declarator") parent-bol c++-ts-mode-indent-offset)
+           ((parent-is "parenthesized_expression") first-sibling 1)
+           ((parent-is "argument_list") first-sibling 1)
+           ((parent-is "parameter_list") first-sibling 1)
+           ((parent-is "binary_expression") parent 0)
+           ((query "(for_statement initializer: (_) @indent)") parent-bol 5)
+           ((query "(for_statement condition: (_) @indent)") parent-bol 5)
+           ((query "(for_statement update: (_) @indent)") parent-bol 5)
+           ((query "(call_expression arguments: (_) @indent)") parent c++-ts-mode-indent-offset)
+           ((parent-is "call_expression") parent 0)
+           ((parent-is "enumerator_list") parent-bol c++-ts-mode-indent-offset)
+           ((parent-is "initializer_list") parent-bol c++-ts-mode-indent-offset))))
+    `((gnu
+       ,@common
+       ((node-is "}") parent-bol 0)
+       ((parent-is "compound_statement") parent c++-ts-mode-indent-offset)
+       ((parent-is "if_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "for_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "while_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "switch_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "case_statement") parent-bol c++-ts-mode-indent-offset)
+       ((match "while" "do_statement") parent 0)
+       ((parent-is "do_statement") parent-bol c++-ts-mode-indent-offset)
+       (no-node parent-bol c++-ts-mode-indent-offset))
+      (k&r
+       ,@common
+       ((node-is "}") grand-parent 0)
+       ((parent-is "compound_statement") grand-parent c++-ts-mode-indent-offset)
+       ((parent-is "if_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "for_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "while_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "switch_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "case_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "do_statement") parent-bol c++-ts-mode-indent-offset)
+       (no-node parent-bol c++-ts-mode-indent-offset))
+      (linux
+       ,@common
+       ((node-is "}") grand-parent 0)
+       ((parent-is "compound_statement") grand-parent c++-ts-mode-indent-offset)
+       ((parent-is "if_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "for_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "while_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "switch_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "case_statement") parent-bol c++-ts-mode-indent-offset)
+       ((parent-is "do_statement") parent-bol c++-ts-mode-indent-offset)
+       (no-node parent-bol c++-ts-mode-indent-offset))
+      (bsd
+       ,@common
+       ((node-is "}") parent-bol 0)
+       ((parent-is "compound_statement") parent 0)
+       ((parent-is "if_statement") parent-bol 0)
+       ((parent-is "for_statement") parent-bol 0)
+       ((parent-is "while_statement") parent-bol 0)
+       ((parent-is "switch_statement") parent-bol 0)
+       ((parent-is "case_statement") parent-bol 0)
+       ((parent-is "do_statement") parent-bol 0)
+       (no-node parent-bol c++-ts-mode-indent-offset))))
+  "Indent rules supported by `c++-ts-mode'.")
+
+(defun c++-ts-mode--set-indent-style ()
+  "Helper function to set indentation style."
+  (let ((style
+         (if (functionp c++-ts-mode-indent-style)
+             (funcall c++-ts-mode-indent-style)
+           (pcase c++-ts-mode-indent-style
+             ('gnu   (alist-get 'gnu c++-ts-mode--indent-styles))
+             ('k&r   (alist-get 'k&r c++-ts-mode--indent-styles))
+             ('bsd   (alist-get 'bsd c++-ts-mode--indent-styles))
+             ('linux (alist-get 'linux c++-ts-mode--indent-styles))))))
+    `((cpp ,@style))))
+
+(defvar c++-ts-mode--keywords
+  '("and" "and_eq" "bitand" "bitor" "catch" "class" "co_await"
+    "co_return" "co_yield" "compl" "concept" "consteval" "constexpr"
+    "constinit" "decltype" "delete" "else" "explicit" "final" "for"
+    "friend" "friend" "if" "mutable" "namespace" "new" "noexcept"
+    "not" "not_eq" "operator" "or" "or_eq" "override" "private"
+    "protected" "public" "requires" "return" "static" "struct"
+    "template" "throw" "try" "typename" "using" "virtual" "xor" "xor_eq"
+    "switch" "case")
+  "C++ keywords for tree-sitter font-locking.")
+
+(defvar c++-ts-mode--preproc-keywords
+  '("#define" "#if" "#ifdef" "#ifndef" "#else" "#elif" "#endif" "#include")
+  "C keywords for tree-sitter font-locking.")
+
+(defvar c++-ts-mode--operators
+  '("=" "-" "*" "/" "+" "%" "~" "|" "&" "^" "<<" ">>" "->"
+    "." "<" "<=" ">=" ">" "==" "!=" "!" "&&" "||" "-="
+    "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++")
+  "C operators for tree-sitter font-locking.")
+
+(defvar c++-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'cpp
+   :override t
+   :feature 'comment
+   `((comment) @font-lock-comment-face)
+   :language 'cpp
+   :override t
+   :feature 'preprocessor
+   `((preproc_directive) @font-lock-preprocessor-face
+
+     (preproc_def
+      name: (identifier) @font-lock-variable-name-face)
+
+     (preproc_ifdef
+      name: (identifier) @font-lock-variable-name-face)
+
+     (preproc_function_def
+      name: (identifier) @font-lock-function-name-face)
+
+     (preproc_params
+      (identifier) @font-lock-variable-name-face)
+
+     (preproc_defined) @font-lock-preprocessor-face
+     (preproc_defined (identifier) @font-lock-variable-name-face)
+     [,@c++-ts-mode--preproc-keywords] @font-lock-preprocessor-face
+     )
+   :language 'cpp
+   :override t
+   :feature 'constant
+   `((true) @font-lock-constant-face
+     (false) @font-lock-constant-face
+     (null) @font-lock-constant-face
+     (this) @font-lock-constant-face)
+   :language 'cpp
+   :override t
+   :feature 'keyword
+   `([,@c++-ts-mode--keywords] @font-lock-keyword-face
+     (auto) @font-lock-keyword-face)
+   :language 'cpp
+   :override t
+   :feature 'operator
+   `([,@c++-ts-mode--operators] @font-lock-builtin-face)
+   :language 'cpp
+   :override t
+   :feature 'string
+   `((string_literal) @font-lock-string-face
+     (system_lib_string) @font-lock-string-face
+     (escape_sequence) @font-lock-string-face)
+   :language 'cpp
+   :override t
+   :feature 'literal
+   `((number_literal) @font-lock-constant-face
+     (char_literal) @font-lock-constant-face)
+   :language 'cpp
+   :override t
+   :feature 'type
+   '((primitive_type) @font-lock-type-face
+     (type_qualifier) @font-lock-type-face
+
+     (qualified_identifier
+      scope: (namespace_identifier) @font-lock-type-face)
+
+     (operator_cast)  type: (type_identifier) @font-lock-type-face)
+   :language 'cpp
+   :override t
+   :feature 'definition
+   `((declaration
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (declaration
+      type: (type_identifier) @font-lock-type-face)
+
+     (field_declaration
+      declarator: (field_identifier) @font-lock-variable-name-face)
+
+     (parameter_declaration
+      type: (type_identifier) @font-lock-type-face)
+
+     (function_definition
+      type: (type_identifier) @font-lock-function-name-face)
+
+     (function_declarator
+      declarator: (identifier) @font-lock-function-name-face)
+
+     (array_declarator
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (init_declarator
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (struct_specifier
+      name: (type_identifier) @font-lock-type-face)
+
+     (sized_type_specifier) @font-lock-type-face
+
+     (enum_specifier
+      name: (type_identifier) @font-lock-type-face)
+
+     (enumerator
+      name: (identifier) @font-lock-variable-name-face)
+
+     (parameter_declaration
+      type: (_) @font-lock-type-face
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (pointer_declarator
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (pointer_declarator
+      declarator: (field_identifier) @font-lock-variable-name-face))
+   :language 'cpp
+   :override t
+   :feature 'expression
+   '((assignment_expression
+      left: (identifier) @font-lock-variable-name-face)
+
+     (call_expression
+      function: (identifier) @font-lock-function-name-face)
+
+     (field_expression
+      field: (field_identifier) @font-lock-variable-name-face)
+
+     (field_expression
+      argument: (identifier) @font-lock-variable-name-face
+      field: (field_identifier) @font-lock-variable-name-face)
+
+     (pointer_expression
+      argument: (identifier) @font-lock-variable-name-face))
+   :language 'cpp
+   :override t
+   :feature 'statement
+   '((expression_statement (identifier) @font-lock-variable-name-face)
+     (labeled_statement
+      label: (statement_identifier) @font-lock-type-face))
+   :language 'cpp
+   :override t
+   :feature 'error
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings.")
+
+(defun c++-ts-mode--imenu-1 (node)
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'c++-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (or (treesit-node-text
+                      (or (treesit-node-child-by-field-name
+                           ts-node "declarator")
+                          (treesit-node-child-by-field-name
+                           ts-node "name"))
+                      t)
+                     "Unnamed node")))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) 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))
+         (tree (treesit-induce-sparse-tree
+                node (rx (or "function_definition"
+                             "struct_specifier")))))
+    (c++-ts-mode--imenu-1 tree)))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.hpp\\'" . c++-ts-mode))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.cpp\\'" . c++-ts-mode))
+
+;;;###autoload
+(define-derived-mode c++-ts-mode prog-mode "C++"
+  "Major mode for editing C++, powered by Tree Sitter."
+  :group 'cpp
+  :syntax-table c++-ts-mode--syntax-table
+
+  (unless (treesit-ready-p nil 'cpp)
+    (error "Tree Sitter for C++ isn't "))
+
+  (treesit-parser-create 'cpp)
+
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+
+  ;; Indent.
+  (when (eq c++-ts-mode-indent-style 'linux)
+    (setq-local indent-tabs-mode t))
+  (setq-local treesit-simple-indent-rules
+              (c++-ts-mode--set-indent-style))
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "specifier"
+                      "definition")))
+
+  ;; Electric
+  (setq-local electric-indent-chars
+	      (append "{}():;," electric-indent-chars))
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings c++-ts-mode--font-lock-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((comment preprocessor operator constant string literal keyword)
+                (type definition expression statement)
+                (error)))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'c++-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+  (treesit-major-mode-setup))
+
+(provide 'c++-ts-mode)
+
+;;; c++-ts-mode.el ends here
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
new file mode 100644
index 0000000000..9da20485af
--- /dev/null
+++ b/lisp/progmodes/c-ts-mode.el
@@ -0,0 +1,396 @@
+;;; c-ts-mode.el --- tree sitter support for C  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : c languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+(require 'pcase)
+
+(defcustom c-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `c-ts-mode'."
+  :type 'integer
+  :safe 'integerp
+  :group 'c)
+
+(defcustom c-ts-mode-indent-style 'gnu
+  "Style used for indentation.
+
+The selected style could be one of GNU, K&R, LINUX or BSD.  If
+one of the supplied styles doesn't suffice a function could be
+set instead.  This function is expected return a list that
+follows the form of `treesit-simple-indent-rules'."
+  :type '(choice (symbol :tag "Gnu" 'gnu)
+                 (symbol :tag "K&R" 'k&r)
+                 (symbol :tag "Linux" 'linux)
+                 (symbol :tag "BSD" 'bsd)
+                 (function :tag "A function for user customized style" ignore))
+  :group 'c)
+
+(defvar c-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?\' "\""    table)
+    (modify-syntax-entry ?\240 "."   table)
+    (modify-syntax-entry ?/  ". 124b" table)
+    (modify-syntax-entry ?*  ". 23"   table)
+    table)
+  "Syntax table for `c-ts-mode'.")
+
+(defvar c-ts-mode--indent-styles
+  (let ((common
+         `(((parent-is "translation_unit") parent-bol 0)
+           ((node-is ")") parent 1)
+           ((node-is "]") parent-bol 0)
+           ((node-is "else") parent-bol 0)
+           ((node-is "case") parent-bol 0)
+           ((node-is "comment") no-indent)
+           ((parent-is "comment") no-indent)
+           ((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)
+           ((match "#endif" "preproc_ifdef") point-min 0)
+           ((match "preproc_if" "compound_statement") point-min 0)
+           ((match "#endif" "preproc_if") point-min 0)
+           ((match "preproc_function_def" "compound_statement") point-min 0)
+           ((match "preproc_call" "compound_statement") point-min 0)
+           ((parent-is "function_definition") parent-bol 0)
+           ((parent-is "conditional_expression") first-sibling 0)
+           ((parent-is "assignment_expression") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "comma_expression") first-sibling 0)
+           ((parent-is "init_declarator") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "parenthesized_expression") first-sibling 1)
+           ((parent-is "argument_list") first-sibling 1)
+           ((parent-is "parameter_list") first-sibling 1)
+           ((parent-is "binary_expression") parent 0)
+           ((query "(for_statement initializer: (_) @indent)") parent-bol 5)
+           ((query "(for_statement condition: (_) @indent)") parent-bol 5)
+           ((query "(for_statement update: (_) @indent)") parent-bol 5)
+           ((query "(call_expression arguments: (_) @indent)") parent c-ts-mode-indent-offset)
+           ((parent-is "call_expression") parent 0)
+           ((parent-is "enumerator_list") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "field_declaration_list") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "initializer_list") parent-bol c-ts-mode-indent-offset))))
+    `((gnu
+       ,@common
+       ((node-is "}") parent-bol 0)
+       ((parent-is "compound_statement") parent c-ts-mode-indent-offset)
+       ((parent-is "if_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "for_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "while_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "switch_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "case_statement") parent-bol c-ts-mode-indent-offset)
+       ((match "while" "do_statement") parent 0)
+       ((parent-is "do_statement") parent-bol c-ts-mode-indent-offset)
+       (no-node parent-bol c-ts-mode-indent-offset))
+      (k&r
+       ,@common
+       ((node-is "}") grand-parent 0)
+       ((parent-is "compound_statement") grand-parent c-ts-mode-indent-offset)
+       ((parent-is "if_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "for_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "while_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "switch_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "case_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "do_statement") parent-bol c-ts-mode-indent-offset)
+       (no-node parent-bol c-ts-mode-indent-offset))
+      (linux
+       ,@common
+       ((node-is "}") grand-parent 0)
+       ((parent-is "compound_statement") grand-parent c-ts-mode-indent-offset)
+       ((parent-is "if_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "for_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "while_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "switch_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "case_statement") parent-bol c-ts-mode-indent-offset)
+       ((parent-is "do_statement") parent-bol c-ts-mode-indent-offset)
+       (no-node parent-bol c-ts-mode-indent-offset))
+      (bsd
+       ,@common
+       ((node-is "}") parent-bol 0)
+       ((parent-is "compound_statement") parent 0)
+       ((parent-is "if_statement") parent-bol 0)
+       ((parent-is "for_statement") parent-bol 0)
+       ((parent-is "while_statement") parent-bol 0)
+       ((parent-is "switch_statement") parent-bol 0)
+       ((parent-is "case_statement") parent-bol 0)
+       ((parent-is "do_statement") parent-bol 0)
+       (no-node parent-bol c-ts-mode-indent-offset))))
+  "Indent rules supported by `c-ts-mode'.")
+
+(defun c-ts-mode--set-indent-style ()
+  "Helper function to set indentation style."
+  (let ((style
+         (if (functionp c-ts-mode-indent-style)
+             (funcall c-ts-mode-indent-style)
+           (pcase c-ts-mode-indent-style
+             ('gnu   (alist-get 'gnu c-ts-mode--indent-styles))
+             ('k&r   (alist-get 'k&r c-ts-mode--indent-styles))
+             ('bsd   (alist-get 'bsd c-ts-mode--indent-styles))
+             ('linux (alist-get 'linux c-ts-mode--indent-styles))))))
+    `((c ,@style))))
+
+(defvar c-ts-mode--keywords
+  '("const" "default" "enum" "extern" "inline" "static"
+    "struct" "typedef" "union" "volatile" "goto" "register"
+    "sizeof" "return"
+    "while" "for" "do" "continue" "break"
+    "if" "else" "case" "switch")
+  "C keywords for tree-sitter font-locking.")
+
+(defvar c-ts-mode--preproc-keywords
+  '("#define" "#if" "#ifdef" "#ifndef" "#else" "#elif" "#endif"
+    "#include")
+  "C keywords for tree-sitter font-locking.")
+
+(defvar c-ts-mode--operators
+  '("=" "-" "*" "/" "+" "%" "~" "|" "&" "^" "<<" ">>" "->"
+    "." "<" "<=" ">=" ">" "==" "!=" "!" "&&" "||" "-="
+    "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++")
+  "C operators for tree-sitter font-locking.")
+
+(defvar c-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'c
+   :override t
+   :feature 'comment
+   `((comment) @font-lock-comment-face)
+   :language 'c
+   :override t
+   :feature 'preprocessor
+   `((preproc_directive) @font-lock-preprocessor-face
+
+     (preproc_def
+      name: (identifier) @font-lock-variable-name-face)
+
+     (preproc_ifdef
+      name: (identifier) @font-lock-variable-name-face)
+
+     (preproc_function_def
+      name: (identifier) @font-lock-function-name-face)
+
+     (preproc_params
+      (identifier) @font-lock-variable-name-face)
+
+     (preproc_defined) @font-lock-preprocessor-face
+     (preproc_defined (identifier) @font-lock-variable-name-face)
+     [,@c-ts-mode--preproc-keywords] @font-lock-preprocessor-face)
+   :language 'c
+   :override t
+   :feature 'constant
+   `((true) @font-lock-constant-face
+     (false) @font-lock-constant-face
+     (null) @font-lock-constant-face)
+   :language 'c
+   :override t
+   :feature 'keyword
+   `([,@c-ts-mode--keywords] @font-lock-keyword-face)
+   :language 'c
+   :override t
+   :feature 'operator
+   `([,@c-ts-mode--operators] @font-lock-builtin-face)
+   :language 'c
+   :override t
+   :feature 'string
+   `((string_literal) @font-lock-string-face
+     (system_lib_string) @font-lock-string-face
+     (escape_sequence) @font-lock-string-face)
+   :language 'c
+   :override t
+   :feature 'literal
+   `((number_literal) @font-lock-constant-face
+     (char_literal) @font-lock-constant-face)
+   :language 'c
+   :override t
+   :feature 'type
+   '((primitive_type) @font-lock-type-face)
+   :language 'c
+   :override t
+   :feature 'definition
+   `((declaration
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (declaration
+      type: (type_identifier) @font-lock-type-face)
+
+     (field_declaration
+      declarator: (field_identifier) @font-lock-variable-name-face)
+
+     (field_declaration
+      type: (type_identifier) @font-lock-type-face)
+
+     (parameter_declaration
+      type: (type_identifier) @font-lock-type-face)
+
+     (function_definition
+      type: (type_identifier) @font-lock-type-face)
+
+     (function_declarator
+      declarator: (identifier) @font-lock-function-name-face)
+
+     (array_declarator
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (init_declarator
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (struct_specifier
+      name: (type_identifier) @font-lock-type-face)
+
+     (sized_type_specifier) @font-lock-type-face
+
+     (enum_specifier
+      name: (type_identifier) @font-lock-type-face)
+
+     (enumerator
+      name: (identifier) @font-lock-variable-name-face)
+
+     (parameter_declaration
+      type: (_) @font-lock-type-face
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (pointer_declarator
+      declarator: (identifier) @font-lock-variable-name-face)
+
+     (pointer_declarator
+      declarator: (field_identifier) @font-lock-variable-name-face))
+   :language 'c
+   :override t
+   :feature 'expression
+   '((assignment_expression
+      left: (identifier) @font-lock-variable-name-face)
+
+     (call_expression
+      function: (identifier) @font-lock-function-name-face)
+
+     (field_expression
+      field: (field_identifier) @font-lock-variable-name-face)
+
+     (field_expression
+      argument: (identifier) @font-lock-variable-name-face
+      field: (field_identifier) @font-lock-variable-name-face)
+
+     (pointer_expression
+      argument: (identifier) @font-lock-variable-name-face))
+   :language 'c
+   :override t
+   :feature 'statement
+   '((expression_statement (identifier) @font-lock-variable-name-face))
+   :language 'c
+   :override t
+   :feature 'error
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings.")
+
+(defun c-ts-mode--imenu-1 (node)
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'c-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (or (treesit-node-text
+                      (or (treesit-node-child-by-field-name
+                           ts-node "declarator")
+                          (treesit-node-child-by-field-name
+                           ts-node "name"))
+                      t)
+                     "Unnamed node")))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) 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))
+         (tree (treesit-induce-sparse-tree
+                node (rx (or "function_definition"
+                             "struct_specifier")))))
+    (c-ts-mode--imenu-1 tree)))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.h\\'" . c-ts-mode))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.c\\'" . c-ts-mode))
+
+;;;###autoload
+(define-derived-mode c-ts-mode prog-mode "C"
+  "Major mode for editing C, powered by Tree Sitter."
+  :group 'c
+  :syntax-table c-ts-mode--syntax-table
+
+  (unless (treesit-ready-p nil 'c)
+    (error "Tree Sitter for C isn't available"))
+
+  (treesit-parser-create 'c)
+
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+
+  ;; Indent.
+  (when (eq c-ts-mode-indent-style 'linux)
+    (setq-local indent-tabs-mode t))
+  (setq-local treesit-simple-indent-rules
+              (c-ts-mode--set-indent-style))
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "specifier"
+                      "definition")))
+
+  ;; Electric
+  (setq-local electric-indent-chars
+	      (append "{}():;," electric-indent-chars))
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings c-ts-mode--font-lock-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((comment preprocessor operator constant string literal keyword)
+                (type definition expression statement)
+                (error)))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'c-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+  (treesit-major-mode-setup))
+
+(provide 'c-ts-mode)
+
+;;; c-ts-mode.el ends here
diff --git a/lisp/progmodes/css-ts-mode.el b/lisp/progmodes/css-ts-mode.el
new file mode 100644
index 0000000000..0cf2fbb689
--- /dev/null
+++ b/lisp/progmodes/css-ts-mode.el
@@ -0,0 +1,122 @@
+;;; css-ts-mode.el --- tree sitter support for CSS  -*- lexical-binding: t; -*-
+
+;; Copyright (C) Theodor Thornhill
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+(require 'treesit)
+(require 'css-mode)
+
+(defcustom css-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `ts-mode'."
+  :type 'integer
+  :safe 'integerp
+  :group 'css)
+
+(defvar css-ts-mode--indent-rules
+  `((css
+     ((node-is "}") parent-bol 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+
+     ((parent-is "block") parent-bol css-ts-mode-indent-offset)
+     ((parent-is "arguments") parent-bol css-ts-mode-indent-offset)
+     ((parent-is "declaration") parent-bol css-ts-mode-indent-offset))))
+
+(defvar css-ts-mode--settings
+  (treesit-font-lock-rules
+   :language 'css
+   :feature 'basic
+   :override t
+   `(
+     (unit) @font-lock-constant-face
+     (integer_value) @font-lock-builtin-face
+     (float_value) @font-lock-builtin-face
+     (plain_value) @font-lock-variable-name-face
+     (comment) @font-lock-comment-face
+     (class_selector) @css-selector
+     (child_selector) @css-selector
+     (id_selector) @css-selector
+     (tag_name) @css-selector
+     (property_name) @css-property
+     (class_name) @css-selector
+     (function_name) @font-lock-function-name-face)))
+
+(defun css-ts-mode--imenu-1 (node)
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'css-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (if (equal (treesit-node-type ts-node) "tag_name")
+                     (treesit-node-text ts-node)
+                   (treesit-node-text (treesit-node-child ts-node 1) t))))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun css-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (tree (treesit-induce-sparse-tree
+                node (rx (or "class_selector"
+                             "id_selector"
+                             "tag_name")))))
+    (css-ts-mode--imenu-1 tree)))
+
+(define-derived-mode css-ts-mode prog-mode "CSS"
+  "Major mode for editing CSS"
+  :group 'css
+  :syntax-table css-mode-syntax-table
+
+  (unless (treesit-ready-p nil 'css)
+    (error "Tree Sitter for CSS isn't available."))
+
+  (treesit-parser-create 'css)
+
+  ;; Comments
+  (setq-local comment-start "/*")
+  (setq-local comment-start-skip "/\\*+[ \t]*")
+  (setq-local comment-end "*/")
+  (setq-local comment-end-skip "[ \t]*\\*+/")
+
+  ;; Indent.
+  (setq-local treesit-simple-indent-rules css-ts-mode--indent-rules)
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "rule_set")))
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings css-ts-mode--settings)
+  (setq treesit-font-lock-feature-list '((basic) () ()))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'css-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+
+  (treesit-major-mode-setup))
+
+(provide 'css-ts-mode)
+
+;;; css-ts-mode.el ends here
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
new file mode 100644
index 0000000000..0a930f1a65
--- /dev/null
+++ b/lisp/progmodes/java-ts-mode.el
@@ -0,0 +1,282 @@
+;;; java-ts-mode.el --- tree sitter support for Java  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : java languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+
+(defcustom java-ts-mode-indent-offset 4
+  "Number of spaces for each indentation step in `java-ts-mode'."
+  :type 'integer
+  :safe 'integerp
+  :group 'java)
+
+(defvar java-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?\' "\""    table)
+    (modify-syntax-entry ?\240 "."   table)
+    table)
+  "Syntax table for `java-ts-mode'.")
+
+(defvar java-ts-mode--indent-rules
+  `((java
+     ((parent-is "program") parent-bol 0)
+     ((node-is "}") (and parent parent-bol) 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+     ((parent-is "class_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "interface_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "constructor_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "enum_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "switch_block") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "record_declaration_body") parent-bol java-ts-mode-indent-offset)
+     ((query "(method_declaration (block _ @indent))") parent-bol java-ts-mode-indent-offset)
+     ((query "(method_declaration (block (_) @indent))") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "variable_declarator") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "method_invocation") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "switch_rule") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "ternary_expression") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "element_value_array_initializer") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "function_definition") parent-bol 0)
+     ((parent-is "conditional_expression") first-sibling 0)
+     ((parent-is "assignment_expression") parent-bol 2)
+     ((parent-is "binary_expression") parent 0)
+     ((parent-is "parenthesized_expression") first-sibling 1)
+     ((parent-is "argument_list") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "annotation_argument_list") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "modifiers") parent-bol 0)
+     ((parent-is "formal_parameters") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "formal_parameter") parent-bol 0)
+     ((parent-is "init_declarator") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "if_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "for_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "while_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "switch_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "case_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "labeled_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "do_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "block") (and parent parent-bol) java-ts-mode-indent-offset)))
+  "Tree-sitter indent rules.")
+
+(defvar java-ts-mode--keywords
+  '("abstract" "assert" "break" "case" "catch"
+    "class" "continue" "default" "do" "else"
+    "enum" "exports" "extends" "final" "finally"
+    "for" "if" "implements" "import" "instanceof"
+    "interface" "module" "native" "new" "non-sealed"
+    "open" "opens" "package" "private" "protected"
+    "provides" "public" "requires" "return" "sealed"
+    "static" "strictfp" "switch" "synchronized"
+    "throw" "throws" "to" "transient" "transitive"
+    "try" "uses" "volatile" "while" "with" "record")
+  "C keywords for tree-sitter font-locking.")
+
+(defvar java-ts-mode--operators
+  '("@" "+" ":" "++" "-" "--" "&" "&&" "|" "||"
+    "!=" "==" "*" "/" "%" "<" "<=" ">" ">=" "="
+    "-=" "+=" "*=" "/=" "%=" "->" "^" "^=" "&="
+    "|=" "~" ">>" ">>>" "<<" "::" "?")
+  "C operators for tree-sitter font-locking.")
+
+(defvar java-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'java
+   :override t
+   :feature 'basic
+   '((identifier) @font-lock-variable-name-face)
+   :language 'java
+   :override t
+   :feature 'comment
+   `((line_comment) @font-lock-comment-face
+     (block_comment) @font-lock-comment-face)
+   :language 'java
+   :override t
+   :feature 'constant
+   `(((identifier) @font-lock-constant-face
+      (:match "^[A-Z_][A-Z_\\d]*$" @font-lock-constant-face))
+     (true) @font-lock-constant-face
+     (false) @font-lock-constant-face)
+   :language 'java
+   :override t
+   :feature 'keyword
+   `([,@java-ts-mode--keywords] @font-lock-keyword-face
+     (labeled_statement
+      (identifier) @font-lock-keyword-face))
+   :language 'java
+   :override t
+   :feature 'operator
+   `([,@java-ts-mode--operators] @font-lock-builtin-face)
+   :language 'java
+   :override t
+   :feature 'annotation
+   `((annotation
+      name: (identifier) @font-lock-constant-face)
+
+     (marker_annotation
+      name: (identifier) @font-lock-constant-face))
+   :language 'java
+   :override t
+   :feature 'string
+   `((string_literal) @font-lock-string-face)
+   :language 'java
+   :override t
+   :feature 'literal
+   `((null_literal) @font-lock-constant-face
+     (decimal_floating_point_literal)  @font-lock-constant-face
+     (hex_floating_point_literal) @font-lock-constant-face)
+   :language 'java
+   :override t
+   :feature 'type
+   '((interface_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (class_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (record_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (enum_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (constructor_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (field_access
+      object: (identifier) @font-lock-type-face)
+
+     (method_reference (identifier) @font-lock-type-face)
+
+     ((scoped_identifier name: (identifier) @font-lock-type-face)
+      (:match "^[A-Z]" @font-lock-type-face))
+
+     (type_identifier) @font-lock-type-face
+
+     [(boolean_type)
+      (integral_type)
+      (floating_point_type)
+      (void_type)] @font-lock-type-face)
+   :language 'java
+   :override t
+   :feature 'definition
+   `((method_declaration
+      name: (identifier) @font-lock-function-name-face)
+
+     (formal_parameter
+      name: (identifier) @font-lock-variable-name-face)
+
+     (catch_formal_parameter
+      name: (identifier) @font-lock-variable-name-face))
+   :language 'java
+   :override t
+   :feature 'expression
+   '((method_invocation
+      object: (identifier) @font-lock-variable-name-face)
+
+     (method_invocation
+      name: (identifier) @font-lock-function-name-face)
+
+     (argument_list (identifier) @font-lock-variable-name-face)))
+  "Tree-sitter font-lock settings.")
+
+(defun java-ts-mode--imenu-1 (node)
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'java-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (or (treesit-node-text
+                      (or (treesit-node-child-by-field-name
+                           ts-node "name"))
+                      t)
+                     "Unnamed node")))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun java-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (tree (treesit-induce-sparse-tree
+                node (rx (or "class_declaration"
+                             "interface_declaration"
+                             "enum_declaration"
+                             "record_declaration"
+                             "method_declaration")))))
+    (java-ts-mode--imenu-1 tree)))
+
+;;;###autoload
+(define-derived-mode java-ts-mode prog-mode "Java"
+  "Major mode for editing Java, powered by Tree Sitter."
+  :group 'c
+  :syntax-table java-ts-mode--syntax-table
+
+  (unless (treesit-ready-p nil 'java)
+    (error "Tree-sitter for Java isn't available."))
+
+  (treesit-parser-create 'java)
+
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+
+  ;; Indent.
+  (setq-local treesit-simple-indent-rules java-ts-mode--indent-rules)
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "declaration")))
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings java-ts-mode--font-lock-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((basic comment keyword constant string operator)
+                (type definition expression literal annotation)
+                ()))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'java-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+  (treesit-major-mode-setup))
+
+(provide 'java-ts-mode)
+
+;;; java-ts-mode.el ends here
diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el
new file mode 100644
index 0000000000..ab693f513a
--- /dev/null
+++ b/lisp/progmodes/json-ts-mode.el
@@ -0,0 +1,141 @@
+;;; json-ts-mode.el --- tree sitter support for JSON  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : json languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+
+(defcustom json-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `json-ts-mode'."
+  :type 'integer
+  :safe 'integerp
+  :group 'json)
+
+(defvar json-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?$ "_"      table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?` "\""     table)
+    (modify-syntax-entry ?\240 "."   table)
+    table)
+  "Syntax table for `json-ts-mode'.")
+
+
+(defvar json-ts--indent-rules
+  `((json
+     ((node-is "}") parent-bol 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+     ((parent-is "object") parent-bol json-ts-mode-indent-offset))))
+
+(defvar json-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'json
+   :feature 'minimal
+   :override t
+   `(
+     (pair
+      key: (_) @font-lock-string-face)
+
+     (string) @font-lock-string-face
+
+     (number) @font-lock-constant-face
+
+     [(null) (true) (false)] @font-lock-constant-face
+
+     (escape_sequence) @font-lock-constant-face
+
+     (comment) @font-lock-comment-face))
+  "Font-lock settings for JSON.")
+
+(defun json-ts-mode--imenu-1 (node)
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'json-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (treesit-node-text
+                  (or (treesit-node-child-by-field-name
+                       ts-node "key"))
+                  t)))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun json-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (tree (treesit-induce-sparse-tree
+                node (rx (or "pair")))))
+    (json-ts-mode--imenu-1 tree)))
+
+;;;###autoload
+(define-derived-mode json-ts-mode prog-mode "JSON"
+  "Major mode for editing JSON, powered by Tree Sitter."
+  :group 'json
+  :syntax-table json-ts-mode--syntax-table
+
+  (unless (treesit-ready-p nil 'json)
+    (error "Tree Sitter for JSON isn't available."))
+
+  (treesit-parser-create 'json)
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+  ;; Indent.
+  (setq-local treesit-simple-indent-rules json-ts--indent-rules)
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "pair"
+                      "object")))
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings json-ts-mode--font-lock-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((minimal) () ()))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'json-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+
+  (treesit-major-mode-setup))
+
+(provide 'json-ts-mode)
+
+;;; json-ts-mode.el ends here
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 83+ messages in thread

end of thread, other threads:[~2022-11-23  1:55 UTC | newest]

Thread overview: 83+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-10 17:45 Tree sitter support for C-like languages Theodor Thornhill via Emacs development discussions.
2022-11-10 18:03 ` Stefan Monnier
2022-11-10 18:18   ` Eli Zaretskii
2022-11-10 18:19   ` Theodor Thornhill
2022-11-10 22:58 ` Yuan Fu
2022-11-11  5:48   ` Theodor Thornhill
2022-11-11  6:01   ` Theodor Thornhill via Emacs development discussions.
2022-11-12  5:43     ` Yuan Fu
2022-11-12  6:13       ` Po Lu
2022-11-12  6:17         ` Yuan Fu
2022-11-12  6:43           ` Po Lu
2022-11-12  6:16       ` Theodor Thornhill
2022-11-12  6:25         ` Yuan Fu
2022-11-12  6:37           ` Theodor Thornhill
2022-11-12  8:08         ` Eli Zaretskii
2022-11-12  8:42           ` Theodor Thornhill
2022-11-12  7:22       ` Theodor Thornhill via Emacs development discussions.
2022-11-12  8:05       ` Eli Zaretskii
2022-11-12  8:43         ` Theodor Thornhill
2022-11-12 12:21     ` Eli Zaretskii
2022-11-12 19:38       ` Theodor Thornhill via Emacs development discussions.
2022-11-12 19:46         ` Stefan Kangas
2022-11-12 20:03           ` Theodor Thornhill
2022-11-12 19:51         ` Eli Zaretskii
2022-11-12 20:05           ` Theodor Thornhill via Emacs development discussions.
2022-11-12 20:08             ` Yuan Fu
2022-11-12 20:14               ` Theodor Thornhill
2022-11-13  9:13                 ` Eli Zaretskii
2022-11-13  9:40                   ` Theodor Thornhill
2022-11-13  9:56                     ` Eli Zaretskii
2022-11-13 10:13                       ` Theodor Thornhill
2022-11-13 12:55                         ` Eli Zaretskii
2022-11-13 13:02                           ` Theodor Thornhill
2022-11-13 13:08                             ` Eli Zaretskii
2022-11-13 13:37                               ` Theodor Thornhill
2022-11-14  1:23                             ` Dmitry Gutov
2022-11-14  0:22                       ` Yuan Fu
2022-11-14  1:26                         ` Dmitry Gutov
2022-11-14  8:35                           ` Yuan Fu
2022-11-14 13:24                             ` Eli Zaretskii
2022-11-14 18:31                               ` Yuan Fu
2022-11-14 19:54                             ` Dmitry Gutov
2022-11-15 10:56                               ` Yuan Fu
2022-11-15 12:30                                 ` Dmitry Gutov
2022-11-14  3:48                         ` Stefan Monnier
2022-11-14  8:23                           ` Yuan Fu
2022-11-14 12:46                             ` Stefan Monnier
2022-11-14 13:20                             ` Eli Zaretskii
2022-11-14 18:29                               ` Yuan Fu
2022-11-14 18:45                                 ` Eli Zaretskii
2022-11-14 19:51                                   ` Yuan Fu
2022-11-14 20:10                                     ` Eli Zaretskii
2022-11-14 21:57                                       ` Yuan Fu
2022-11-15  3:27                                         ` Eli Zaretskii
2022-11-15 10:51                                           ` Yuan Fu
2022-11-15 11:37                                             ` Theodor Thornhill
2022-11-15 15:03                                             ` Eli Zaretskii
2022-11-15 16:01                                               ` Stefan Monnier
2022-11-15 16:59                                                 ` Eli Zaretskii
2022-11-15 18:18                                                   ` Yuan Fu
2022-11-15 18:38                                                     ` Eli Zaretskii
2022-11-16  7:58                                                       ` Yuan Fu
2022-11-16 13:16                                                         ` Eli Zaretskii
2022-11-16 13:29                                                           ` Po Lu
2022-11-16 17:29                                                             ` Yuan Fu
2022-11-15 18:27                                                   ` Visuwesh
2022-11-15 18:36                                                     ` Yuan Fu
2022-11-14 12:55                         ` Eli Zaretskii
2022-11-11  0:43 ` Randy Taylor
2022-11-11  5:50   ` Theodor Thornhill
2022-11-11 13:37     ` Stefan Monnier
2022-11-11 15:09       ` Theodor Thornhill
2022-11-11 15:54     ` Randy Taylor
2022-11-13  8:37       ` Theodor Thornhill
2022-11-13 13:03         ` Randy Taylor
2022-11-16 17:51 ` Yuan Fu
2022-11-16 20:02   ` Theodor Thornhill
2022-11-16 20:10     ` Yuan Fu
2022-11-16 20:25       ` Theodor Thornhill
2022-11-16 20:58     ` Yuan Fu
2022-11-21  9:28       ` Yuan Fu
2022-11-21 11:15         ` Theodor Thornhill
2022-11-23  1:55           ` Yuan Fu

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).