branch: externals/csharp-mode
commit 3cff337b41f39eebe77185d8f07dcd1db2f2c405
Merge: 76bbf26 00a3cd4
Author: Theodor Thornhill <[email protected]>
Commit: GitHub <[email protected]>
Merge pull request #204 from emacs-csharp/tree-sitter
Tree sitter support for csharp mode
---
README.org | 26 +++-
csharp-tree-sitter.el | 291 ++++++++++++++++++++++++++++++++++++++++
test-files/indentation-tests.cs | 2 -
3 files changed, 311 insertions(+), 8 deletions(-)
diff --git a/README.org b/README.org
index 4b9fcb9..4e82552 100644
--- a/README.org
+++ b/README.org
@@ -4,8 +4,7 @@
* csharp-mode
-This is a mode for editing C# in emacs. It's based on cc-mode, v5.30.3 and
above.
-
+This is a mode for editing C# in emacs. It's using
[[https://github.com/ubolonton/emacs-tree-sitter][tree-sitter]] for
highlighting and indentation.
** Main features
- font-lock and indent of C# syntax including:
@@ -17,11 +16,26 @@ This is a mode for editing C# in emacs. It's based on
cc-mode, v5.30.3 and above
- anonymous functions and methods
- verbatim literal strings (those that begin with @)
- generics
-- automagic code-doc generation when you type three slashes.
- intelligent insertion of matched pairs of curly braces.
-- imenu indexing of C# source, for easy menu-based navigation.
- compilation-mode support for msbuild, devenv and xbuild.
+** tree-sitter support
+You can enable experimental tree sitter support for indentation and
highlighting using
+#+begin_src elisp
+ (use-package tree-sitter)
+ (use-package tree-sitter-langs)
+
+ (use-package csharp-mode
+ :straight
+ (csharp-mode :type git
+ :host github
+ :repo "emacs-csharp/csharp-mode"
+ :branch "tree-sitter")
+ :config
+ (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-tree-sitter-mode)))
+#+end_src
+If you are using this, clearly state so if you find any issues.
+
** Usage
This package is currently available on MELPA. Install using ~M-x
@@ -44,10 +58,10 @@ To do so, add the following to your .emacs-file:
(add-hook 'csharp-mode-hook 'my-csharp-mode-hook)
#+END_SRC
-For further mode-specific customization, ~M-x customize-group RET csharp RET~
will show available settings with documentation. For configuring ~cc-mode~
settings (which csharp-mode derives from) see the
[[https://www.gnu.org/software/emacs/manual/html_mono/ccmode.html][cc-mode
manual]].
+For further mode-specific customization, ~M-x customize-group RET csharp RET~
will show available settings with documentation.
For more advanced and IDE-like functionality we recommend using csharp-mode
together
-with [[https://github.com/OmniSharp/omnisharp-emacs][Omnisharp-Emacs]].
+with [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]] or
[[https://github.com/joaotavora/eglot][eglot]]
* Attribution
diff --git a/csharp-tree-sitter.el b/csharp-tree-sitter.el
new file mode 100644
index 0000000..90f633a
--- /dev/null
+++ b/csharp-tree-sitter.el
@@ -0,0 +1,291 @@
+;;; csharp-tree-sitter.el --- tree sitter support for C# -*- lexical-binding:
t; -*-
+
+;; Author : Theodor Thornhill <[email protected]>
+;; Maintainer : Jostein Kjønigsen <[email protected]>
+;; : Theodor Thornhill <[email protected]>
+;; Created : September 2020
+;; Modified : 2020
+;; Version : 0.10.0
+;; Keywords : c# languages oop mode
+;; X-URL : https://github.com/emacs-csharp/csharp-mode
+;; Package-Requires: ((emacs "26.1") (tree-sitter "0.12.1")
(tree-sitter-indent "0.1"))
+
+;; 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 'cl-lib)
+(require 'seq)
+(require 'tree-sitter)
+(require 'tree-sitter-hl)
+(require 'tree-sitter-indent)
+
+;;; Tree-sitter
+
+(defvar-local csharp-mode-tree-sitter-patterns
+ [ ;; Various constructs
+ (comment) @comment
+ (modifier) @keyword
+ (this_expression) @keyword
+
+ ;; Literals
+ [(real_literal) (integer_literal)] @number
+ (null_literal) @constant
+ (boolean_literal) @constant
+ (character_literal) @string
+
+ ;; Keywords
+ ["using" "namespace" "class" "if" "else" "throw" "new" "for"
+ "return" "await" "struct" "enum" "switch" "case"
+ "default" "typeof" "try" "catch" "finally" "break"
+ "foreach" "in" "yield" "get" "set" "when" "as" "out"
+ "is" "while" "continue" "this" "ref" "goto" "interface"
+ "from" "where" "select"
+ ] @keyword
+
+ ;; Linq
+ (from_clause (identifier) @variable)
+ (group_clause)
+ (order_by_clause)
+ (select_clause (identifier) @variable)
+ (query_continuation (identifier) @variable) @keyword
+
+ ;; String
+ (interpolation (identifier) (interpolation_format_clause) @variable)
+ (interpolation (identifier)* @variable)
+ [(string_literal) (verbatim_string_literal)
(interpolated_string_expression)] @string
+
+ ;; Enum
+ (enum_member_declaration (identifier) @variable)
+ (enum_declaration (identifier) @type)
+
+ ;; Interface
+ (interface_declaration
+ name: (identifier) @type)
+
+ ;; Struct
+ (struct_declaration (identifier) @type)
+
+ ;; Namespace
+ (namespace_declaration
+ name: (identifier) @type)
+
+ ;; Class
+ (base_list (identifier) @type)
+ (property_declaration
+ type: (nullable_type) @type
+ name: (identifier) @variable)
+ (property_declaration
+ type: (predefined_type) @type
+ name: (identifier) @variable)
+ (property_declaration
+ type: (identifier) @type
+ name: (identifier) @variable)
+ (class_declaration
+ name: (identifier) @type)
+ (constructor_declaration (identifier) @type)
+
+ ;; Method
+ (method_declaration (identifier) @type (identifier) @function)
+ (method_declaration (predefined_type) @type (identifier) @function)
+ (method_declaration (nullable_type) @type (identifier) @function)
+ (method_declaration (void_keyword) @type (identifier) @function)
+ (method_declaration (generic_name) (identifier) @function)
+
+ ;; Function
+ (local_function_statement (identifier) @type (identifier) @function)
+ (local_function_statement (predefined_type) @type (identifier) @function)
+ (local_function_statement (nullable_type) @type (identifier) @function)
+ (local_function_statement (void_keyword) @type (identifier) @function)
+ (local_function_statement (generic_name) (identifier) @function)
+
+ ;; Parameter
+ (parameter
+ type: (identifier) @type
+ name: (identifier) @variable)
+ (parameter (identifier) @variable)
+
+ ;; Array
+ (array_rank_specifier (identifier) @variable)
+ (array_type (identifier) @type)
+ (array_creation_expression)
+
+ ;; Attribute
+ (attribute (identifier) @variable (attribute_argument_list))
+ (attribute (identifier) @variable)
+
+ ;; Object init
+ (anonymous_object_creation_expression)
+ (object_creation_expression (identifier) @type)
+ (initializer_expression (identifier) @variable)
+
+ ;; Variable
+ (variable_declaration (identifier) @type)
+ (variable_declarator (identifier) @variable)
+
+ ;; Equals value
+ (equals_value_clause (identifier) @variable)
+
+ ;; Return
+ (return_statement (identifier) @variable)
+ (yield_statement (identifier) @variable)
+
+ ;; Type
+ (type_parameter
+ (identifier) @type)
+ (type_argument_list
+ (identifier) @type)
+ (generic_name
+ (identifier) @type)
+ (implicit_type) @type
+ (predefined_type) @type
+ (nullable_type) @type
+ ["operator"] @type
+
+ ;; Exprs
+ (binary_expression (identifier) @variable (identifier) @variable)
+ (binary_expression (identifier)* @variable)
+ (conditional_expression (identifier) @variable)
+ (prefix_unary_expression (identifier)* @variable)
+ (postfix_unary_expression (identifier)* @variable)
+ (type_of_expression (identifier) @variable)
+ (assignment_expression (identifier) @variable)
+ (cast_expression (identifier) @type)
+
+ ;; Preprocessor
+ (preprocessor_directive) @constant
+ (preprocessor_call (identifier) @string)
+
+ ;; Loop
+ (for_each_statement (identifier) @type (identifier) @variable)
+ (for_each_statement (implicit_type) @type (identifier) @variable)
+ (for_each_statement (predefined_type) @type (identifier) @variable)
+
+ ;; Exception
+ (catch_declaration (identifier) @type (identifier) @variable)
+ (catch_declaration (identifier) @type)
+
+ ;; Switch
+ (switch_statement (identifier) @variable)
+ (switch_expression (identifier) @variable)
+
+ ;; If
+ (if_statement (identifier) @variable)
+
+ ;; Declaration expression
+ (declaration_expression (implicit_type) (identifier) @variable)
+
+ ;; Arrow expression
+ (arrow_expression_clause (identifier) @variable)
+
+ ;; Other
+ (label_name) @variable
+ (qualified_name (identifier) @type)
+ (using_directive (identifier)* @type)
+ (await_expression (identifier)* @function)
+ (invocation_expression (identifier) @function)
+ (element_access_expression (identifier) @variable)
+ (conditional_access_expression (identifier) @variable)
+ (member_binding_expression (identifier) @variable)
+ (member_access_expression (identifier) @function)
+ (name_colon (identifier)* @variable)
+ (name_equals (identifier) @type)
+ (field_declaration)
+ (argument (identifier) @variable)
+ ]
+ "Default patterns for tree-sitter support.")
+
+;;; Tree-sitter indentation
+
+(defgroup csharp-mode-indent nil "Indent lines using Tree-sitter as backend"
+ :group 'tree-sitter)
+
+(defcustom csharp-mode-indent-offset 4
+ "Indent offset for csharp-mode"
+ :type 'integer
+ :group 'csharp)
+
+(defvar csharp-mode-indent-scopes
+ '((indent-all . ;; these nodes are always indented
+ (accessor_declaration
+ break_statement
+ arrow_expression_clause
+ parameter_list
+ conditional_expression
+ "."))
+ (indent-rest . ;; if parent node is one of these and node is not first →
indent
+ (
+ binary_expression
+ switch_section
+ ))
+ (indent-body . ;; if parent node is one of these and current node is in
middle → indent
+ (block
+ anonymous_object_creation_expression
+ enum_member_declaration_list
+ initializer_expression
+ expression_statement
+ declaration_list
+ attribute_argument_list
+ switch_body))
+
+ (paren-indent . ;; if parent node is one of these → indent to paren opener
+ (parenthesized_expression))
+ (align-char-to . ;; chaining char → node types we move parentwise to find
the first chaining char
+ ())
+ (aligned-siblings . ;; siblings (nodes with same parent) should be aligned
to the first child
+ (parameter))
+
+ (multi-line-text . ;; if node is one of these, then don't modify the indent
+ ;; this is basically a peaceful way out by saying "this
looks like something
+ ;; that cannot be indented using AST, so best I leave it
as-is"
+ (comment
+ preprocessor_call
+ labeled_statement))
+ (outdent . ;; these nodes always outdent (1 shift in opposite direction)
+ (;; "}"
+ case_switch_label
+ ))
+ )
+ "Scopes for indenting in C#.")
+
+;;;###autoload
+(define-derived-mode csharp-tree-sitter-mode prog-mode "C#"
+ "Major mode for editing Csharp code.
+
+Key bindings:
+\\{csharp-mode-map}"
+ :group 'csharp
+
+ (setq csharp-mode-syntax-table nil)
+ (setq csharp-mode-map nil)
+ (setq-local tree-sitter-indent-current-scopes csharp-mode-indent-scopes)
+ (setq-local tree-sitter-indent-offset csharp-mode-indent-offset)
+ (setq-local indent-line-function #'tree-sitter-indent-line)
+
+ ;; https://github.com/ubolonton/emacs-tree-sitter/issues/84
+ (unless font-lock-defaults
+ (setq font-lock-defaults '(nil)))
+ (setq-local tree-sitter-hl-default-patterns csharp-mode-tree-sitter-patterns)
+ ;; Comments
+ (setq-local comment-start "// ")
+ (setq-local comment-end "")
+
+ (tree-sitter-hl-mode))
+
+;;;###autoload
+(add-to-list 'tree-sitter-major-mode-language-alist '(csharp-tree-sitter-mode
. c-sharp))
+
+(provide 'csharp-tree-sitter)
+
+;;; csharp-tree-sitter.el ends here
diff --git a/test-files/indentation-tests.cs b/test-files/indentation-tests.cs
index e0355f9..650ac1e 100644
--- a/test-files/indentation-tests.cs
+++ b/test-files/indentation-tests.cs
@@ -119,7 +119,6 @@ namespace Boo
PropB = 2
};
- // Commented out for rework -- Theodor Thornhill
// yield return new InnerA.InnerB {
// PropA = 1,
// PropB = 2
@@ -131,7 +130,6 @@ namespace Boo
May = "Yay"
};
- // Commented out for rework -- Theodor Thornhill
// yield return new InnerA.InnerB
// {
// Boo = "Foo",