From: Theodor Thornhill via "Emacs development discussions." <emacs-devel@gnu.org>
To: Yuan Fu <casouri@gmail.com>
Cc: emacs-devel@gnu.org
Subject: Re: Tree sitter support for C-like languages
Date: Sat, 12 Nov 2022 08:22:29 +0100 [thread overview]
Message-ID: <87sfiozlfu.fsf@thornhill.no> (raw)
In-Reply-To: <D9801700-556F-4101-B041-388E4C5FC22B@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 440 bytes --]
>
> I noticed that with the default indent style, Emacs indents like this:
>
> int main () {
> for (int j = 0; j < 5; j++)
> a[j] = 3;
> int i = 1;
> swap(i, a[i+1]);
> Point p = {0, 1};
> }
>
> int main ()
> {
> for (int j = 0; j < 5; j++)
> a[j] = 3;
> int i = 1;
> swap(i, a[i+1]);
> Point p = {0, 1};
> }
>
> Is this expected?
Try this one instead:
[-- 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: 56651 bytes --]
From dbcd64db88a665393297996fba165b8adc66ba50 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 | 424 +++++++++++++++++++++++++++++++++
lisp/progmodes/c-ts-mode.el | 414 ++++++++++++++++++++++++++++++++
lisp/progmodes/css-ts-mode.el | 131 ++++++++++
lisp/progmodes/java-ts-mode.el | 289 ++++++++++++++++++++++
lisp/progmodes/json-ts-mode.el | 150 ++++++++++++
6 files changed, 1434 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..7e3bb7d141
--- /dev/null
+++ b/lisp/progmodes/c++-ts-mode.el
@@ -0,0 +1,424 @@
+;;; 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/>.
+
+
+;;; Commentary:
+;;
+
+;;; 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))))
+ `((gnu
+ ,@common
+ ((node-is "}") parent-bol 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)
+ ((parent-is "compound_statement") parent-bol 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 "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)
+ ((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 "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)
+ ((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 "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)
+ ((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
+ (comment) @contexual)
+ :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
+ ((string_literal)) @contextual
+ (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)
+ "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
+ (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)))))
+ ;; A struct_specifier could be inside a parameter list or another
+ ;; struct definition. In those cases we don't include it.
+ (cond
+ ((string-match-p
+ (rx (or "parameter" "field") "_declaration")
+ (or (treesit-node-type (treesit-node-parent ts-node))
+ ""))
+ nil)
+ ((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
+(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 available"))
+
+ (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..d99d863d60
--- /dev/null
+++ b/lisp/progmodes/c-ts-mode.el
@@ -0,0 +1,414 @@
+;;; 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/>.
+
+
+;;; Commentary:
+;;
+
+;;; 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 '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))))
+ `((gnu
+ ,@common
+ ((node-is "}") parent-bol 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)
+ ((parent-is "compound_statement") parent-bol 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 "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)
+ ((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 "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)
+ ((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 "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)
+ ((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
+ (comment) @contexual)
+ :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
+ ((string_literal)) @contextual
+ (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)
+ "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
+ (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)))))
+ ;; A struct_specifier could be inside a parameter list or another
+ ;; struct definition. In those cases we don't include it.
+ (cond
+ ((string-match-p
+ (rx (or "parameter" "field") "_declaration")
+ (or (treesit-node-type (treesit-node-parent ts-node))
+ ""))
+ nil)
+ ((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
+(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..c1a8d4e94d
--- /dev/null
+++ b/lisp/progmodes/css-ts-mode.el
@@ -0,0 +1,131 @@
+;;; css-ts-mode.el --- tree sitter support for CSS -*- 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 : css 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/>.
+
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+(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)
+ "Helper for `css-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+ (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 "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..734a8be471
--- /dev/null
+++ b/lisp/progmodes/java-ts-mode.el
@@ -0,0 +1,289 @@
+;;; 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/>.
+
+
+;;; Commentary:
+;;
+
+;;; 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)
+ "Helper for `java-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+ (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..13eb5b78a9
--- /dev/null
+++ b/lisp/progmodes/json-ts-mode.el
@@ -0,0 +1,150 @@
+;;; 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/>.
+
+
+;;; Commentary:
+;;
+
+;;; 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)
+ "Helper for `json-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+ (let* ((ts-node (car node))
+ (subtrees (mapcan #'json-ts-mode--imenu-1 (cdr node)))
+ (name (when ts-node
+ (treesit-node-text
+ (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 "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
next prev parent reply other threads:[~2022-11-12 7:22 UTC|newest]
Thread overview: 83+ messages / expand[flat|nested] mbox.gz Atom feed top
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. [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87sfiozlfu.fsf@thornhill.no \
--to=emacs-devel@gnu.org \
--cc=casouri@gmail.com \
--cc=theo@thornhill.no \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.