Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Tree sitter support for csharp mode #204

Merged
merged 39 commits into from
Dec 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c1489db
First small tree-sitter query
theothornhill Dec 11, 2020
998a022
Strings
theothornhill Dec 11, 2020
4aedd56
More basic highlighting
theothornhill Dec 11, 2020
b27a3e4
More highlighting
theothornhill Dec 11, 2020
9a9c92a
Many, many more things
theothornhill Dec 12, 2020
383f020
Attributes and more interpolation
theothornhill Dec 13, 2020
658adb2
Add support for namespace-declarations.
josteink Dec 13, 2020
1bd2d62
Make attribute-fontification consistent
josteink Dec 13, 2020
2bf76fd
Fix csharp-mode initialization without forked tree-sitter
josteink Dec 13, 2020
11d92b9
Various fixes
theothornhill Dec 13, 2020
b6b7661
Add this expression
theothornhill Dec 13, 2020
0ebd21a
Remove CC Mode and add tree-sitter-indent :O
theothornhill Dec 14, 2020
5374fe3
Add some indent rules
theothornhill Dec 14, 2020
58928af
Indent object creation expression
theothornhill Dec 14, 2020
a90b875
Add yield and object init indentation rules
theothornhill Dec 14, 2020
eb3e9de
Almost functional indentation
theothornhill Dec 15, 2020
512c858
Start extracting defcustoms
theothornhill Dec 15, 2020
f94e5a7
More extracting
theothornhill Dec 15, 2020
323a028
More cleaning
theothornhill Dec 15, 2020
84a5daa
Should not need to autoload c_sharp
theothornhill Dec 17, 2020
878c671
Try add better support for interfaces.
josteink Dec 18, 2020
4651004
Hassle with the defcustoms
theothornhill Dec 18, 2020
5ac7484
Add back better support for interfaces
theothornhill Dec 18, 2020
e30ed5e
Indent feature parity with previous implementation
theothornhill Dec 20, 2020
4eef364
Absorb tree-sitter-indent
theothornhill Dec 20, 2020
45d1e9f
These are passing again
theothornhill Dec 20, 2020
5a3d4a9
More indentation fixes
theothornhill Dec 20, 2020
8fdeedf
Indentation tweaking
theothornhill Dec 20, 2020
0b1df32
Add tree-sitter as optional feature
theothornhill Dec 21, 2020
3d0b60c
Add a little documentation
theothornhill Dec 21, 2020
0de2bbc
Fix byte compilation errors
theothornhill Dec 21, 2020
5730c99
Split functionality to two files
theothornhill Dec 21, 2020
4eeccbd
Remove newly added tests
theothornhill Dec 21, 2020
f0b5efa
Use code with same arrangement as before rework
theothornhill Dec 22, 2020
53fa8f1
Use dedicated major mode for tree-sitter
theothornhill Dec 22, 2020
ea1718e
Use defvar instead of defcustom for indentation rules
theothornhill Dec 22, 2020
1e37eaf
Add dependency to tree-sitter-indent
theothornhill Dec 29, 2020
f937426
Merge branch 'master' into tree-sitter
theothornhill Dec 29, 2020
00a3cd4
Add new url to csharp-tree-sitter.el
theothornhill Dec 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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

Expand Down
291 changes: 291 additions & 0 deletions csharp-tree-sitter.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
;;; csharp-tree-sitter.el --- tree sitter support for C# -*- lexical-binding: t; -*-

;; Author : Theodor Thornhill <theo@thornhill.no>
;; Maintainer : Jostein Kjønigsen <jostein@gmail.com>
;; : Theodor Thornhill <theo@thornhill.no>
;; 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
2 changes: 0 additions & 2 deletions test-files/indentation-tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ public void Test()
PropB = 2
};

// Commented out for rework -- Theodor Thornhill
// yield return new InnerA.InnerB {
// PropA = 1,
// PropB = 2
Expand All @@ -131,7 +130,6 @@ public void Test()
May = "Yay"
};

// Commented out for rework -- Theodor Thornhill
// yield return new InnerA.InnerB
// {
// Boo = "Foo",
Expand Down