683 lines
21 KiB
EmacsLisp
683 lines
21 KiB
EmacsLisp
;;; csharp-mode.el --- C# mode derived mode -*- 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
|
||
;; Package-Version: 20201104.743
|
||
;; Package-Commit: 49168293a28aaaf84b9655fe1b2abe402c5e2ee1
|
||
;; Keywords : c# languages oop mode
|
||
;; X-URL : https://github.com/josteink/csharp-mode
|
||
|
||
;; 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:
|
||
|
||
|
||
(when (version< emacs-version "25.1")
|
||
(require 'cl))
|
||
(require 'cc-mode)
|
||
(require 'cc-langs)
|
||
|
||
(eval-when-compile
|
||
(require 'cc-fonts))
|
||
|
||
(require 'compile)
|
||
|
||
(defgroup csharp nil
|
||
"Major mode for editing C# code."
|
||
:group 'prog-mode)
|
||
|
||
(eval-and-compile
|
||
(defconst csharp--regex-identifier
|
||
"[A-Za-z][A-Za-z0-9_]*"
|
||
"Regex describing an dentifier in C#.")
|
||
|
||
(defconst csharp--regex-identifier-matcher
|
||
(concat "\\(" csharp--regex-identifier "\\)")
|
||
"Regex matching an identifier in C#.")
|
||
|
||
(defconst csharp--regex-type-name
|
||
"[A-Z][A-Za-z0-9_]*"
|
||
"Regex describing a type identifier in C#.")
|
||
|
||
(defconst csharp--regex-type-name-matcher
|
||
(concat "\\(" csharp--regex-type-name "\\)")
|
||
"Regex matching a type identifier in C#.")
|
||
|
||
(defconst csharp--regex-using-or-namespace
|
||
(concat "using" "\\|" "namespace"
|
||
"\\s *"
|
||
csharp--regex-type-name-matcher)
|
||
"Regex matching identifiers after a using or namespace
|
||
declaration."))
|
||
|
||
(eval-and-compile
|
||
(c-add-language 'csharp-mode 'java-mode))
|
||
|
||
(c-lang-defconst c-make-mode-syntax-table
|
||
csharp `(lambda ()
|
||
(let ((table (make-syntax-table)))
|
||
(c-populate-syntax-table table)
|
||
(modify-syntax-entry ?@ "_" table)
|
||
table)))
|
||
|
||
(c-lang-defconst c-identifier-syntax-modifications
|
||
csharp (append '((?@ . "w"))
|
||
(c-lang-const c-identifier-syntax-modifications)))
|
||
|
||
(c-lang-defconst c-symbol-start
|
||
csharp (concat "[" c-alpha "_@]"))
|
||
|
||
(c-lang-defconst c-opt-type-suffix-key
|
||
csharp (concat "\\(\\[" (c-lang-const c-simple-ws) "*\\]\\|\\?\\)"))
|
||
|
||
(c-lang-defconst c-identifier-ops
|
||
csharp '((left-assoc ".")))
|
||
|
||
(c-lang-defconst c-overloadable-operators
|
||
csharp '("+" "-" "*" "/" "%" "&" "|" "^" "<<" ">>" "=="
|
||
"!=" ">" "<" ">=" "<="))
|
||
|
||
(c-lang-defconst c-multiline-string-start-char
|
||
csharp ?@)
|
||
|
||
(c-lang-defconst c-type-prefix-kwds
|
||
csharp '("class" "interface" "struct"))
|
||
|
||
(c-lang-defconst c-class-decl-kwds
|
||
csharp '("class" "interface" "struct"))
|
||
|
||
;;; Keyword lists
|
||
|
||
(c-lang-defconst c-primitive-type-kwds
|
||
csharp '("bool" "byte" "sbyte" "char" "decimal" "double" "float" "int" "uint"
|
||
"long" "ulong" "short" "ushort" "void" "object" "string" "var"))
|
||
|
||
(c-lang-defconst c-other-decl-kwds
|
||
csharp nil)
|
||
|
||
(c-lang-defconst c-type-list-kwds
|
||
csharp nil)
|
||
|
||
(c-lang-defconst c-other-block-decl-kwds
|
||
csharp nil)
|
||
|
||
(c-lang-defconst c-return-kwds
|
||
csharp '("return"))
|
||
|
||
(c-lang-defconst c-typedef-kwds
|
||
csharp nil)
|
||
|
||
(c-lang-defconst c-typeof-kwds
|
||
csharp '("typeof" "is" "as"))
|
||
|
||
(c-lang-defconst c-type-modifier-prefix-kwds
|
||
csharp '("volatile"))
|
||
|
||
(c-lang-defconst c-type-modifier-kwds
|
||
csharp '("readonly" "new"))
|
||
|
||
(c-lang-defconst c-brace-list-decl-kwds
|
||
csharp '("enum" "new"))
|
||
|
||
(c-lang-defconst c-recognize-post-brace-list-type-p
|
||
csharp t)
|
||
|
||
(c-lang-defconst c-ref-list-kwds
|
||
csharp nil)
|
||
|
||
(c-lang-defconst c-using-kwds
|
||
csharp '("using"))
|
||
|
||
(c-lang-defconst c-equals-type-clause-kwds
|
||
csharp '("using"))
|
||
|
||
(defun csharp-at-vsemi-p (&optional pos)
|
||
(if pos (goto-char pos))
|
||
(save-excursion
|
||
(beginning-of-line)
|
||
(c-forward-syntactic-ws)
|
||
(looking-at "using\\s *(")))
|
||
|
||
(c-lang-defconst c-at-vsemi-p-fn
|
||
csharp 'csharp-at-vsemi-p)
|
||
|
||
(defun csharp-vsemi-status-unknown () t)
|
||
|
||
(c-lang-defconst c-vsemi-status-unknown-p-fn
|
||
csharp 'csharp-vsemi-status-unknown-p)
|
||
|
||
|
||
(c-lang-defconst c-modifier-kwds
|
||
csharp '("abstract" "default" "final" "native" "private" "protected"
|
||
"public" "partial" "internal" "readonly" "static" "event" "transient"
|
||
"volatile" "sealed" "ref" "out" "virtual" "implicit" "explicit"
|
||
"fixed" "override" "params" "async" "await" "extern" "unsafe"
|
||
"get" "set" "this" "const" "delegate"))
|
||
|
||
(c-lang-defconst c-other-kwds
|
||
csharp '("select" "from" "where" "join" "in" "on" "equals" "into"
|
||
"orderby" "ascending" "descending" "group" "when"
|
||
"let" "by" "namespace"))
|
||
|
||
(c-lang-defconst c-colon-type-list-kwds
|
||
csharp '("class" "struct" "interface"))
|
||
|
||
(c-lang-defconst c-block-stmt-1-kwds
|
||
csharp '("do" "else" "finally" "try"))
|
||
|
||
(c-lang-defconst c-block-stmt-1-2-kwds
|
||
csharp '("try"))
|
||
|
||
(c-lang-defconst c-block-stmt-2-kwds
|
||
csharp '("for" "if" "switch" "while" "catch" "foreach" "fixed" "checked"
|
||
"unchecked" "using" "lock"))
|
||
|
||
(c-lang-defconst c-simple-stmt-kwds
|
||
csharp '("break" "continue" "goto" "throw" "return" "yield"))
|
||
|
||
(c-lang-defconst c-constant-kwds
|
||
csharp '("true" "false" "null" "value"))
|
||
|
||
(c-lang-defconst c-primary-expr-kwds
|
||
csharp '("this" "base" "operator"))
|
||
|
||
(c-lang-defconst c-inexpr-class-kwds
|
||
csharp nil)
|
||
|
||
(c-lang-defconst c-class-decl-kwds
|
||
csharp '("class" "struct" "interface"))
|
||
|
||
(c-lang-defconst c-std-abbrev-keywords
|
||
csharp (append (c-lang-const c-std-abbrev-keywords) '("catch" "finally")))
|
||
|
||
(c-lang-defconst c-decl-prefix-re
|
||
csharp "\\([{}(;,<]+\\)")
|
||
|
||
(c-lang-defconst c-recognize-typeless-decls
|
||
csharp t)
|
||
|
||
(c-lang-defconst c-recognize-<>-arglists
|
||
csharp t)
|
||
|
||
(c-lang-defconst c-opt-cpp-prefix
|
||
csharp "\\s *#\\s *")
|
||
|
||
(c-lang-defconst c-opt-cpp-macro-define
|
||
csharp (if (c-lang-const c-opt-cpp-prefix)
|
||
"define"))
|
||
|
||
(c-lang-defconst c-cpp-message-directives
|
||
csharp '("error" "warning" "region"))
|
||
|
||
(c-lang-defconst c-cpp-expr-directives
|
||
csharp '("if" "elif"))
|
||
|
||
(c-lang-defconst c-other-op-syntax-tokens
|
||
csharp (append '("#")
|
||
(c-lang-const c-other-op-syntax-tokens)))
|
||
|
||
(c-lang-defconst c-line-comment-starter
|
||
csharp "//")
|
||
|
||
(c-lang-defconst c-doc-comment-start-regexp
|
||
csharp "///")
|
||
|
||
(c-add-style "csharp"
|
||
'("java"
|
||
(c-basic-offset . 4)
|
||
(c-comment-only-line-offset . (0 . 0))
|
||
(c-offsets-alist . ((inline-open . 0)
|
||
(arglist-intro . +)
|
||
(arglist-close . 0)
|
||
(inexpr-class . 0)
|
||
(case-label . +)
|
||
(cpp-macro . c-lineup-dont-change)
|
||
(substatement-open . 0)))))
|
||
|
||
(eval-and-compile
|
||
(unless (assoc 'csharp-mode c-default-style)
|
||
(setq c-default-style
|
||
(cons '(csharp-mode . "csharp")
|
||
c-default-style))))
|
||
|
||
(defun csharp--color-forwards (font-lock-face)
|
||
(let (id-beginning)
|
||
(goto-char (match-beginning 0))
|
||
(forward-word)
|
||
(while (and (not (or (eq (char-after) ?\;)
|
||
(eq (char-after) ?\{)))
|
||
(progn
|
||
(forward-char)
|
||
(c-forward-syntactic-ws)
|
||
(setq id-beginning (point))
|
||
(> (skip-chars-forward
|
||
(c-lang-const c-symbol-chars))
|
||
0))
|
||
(not (get-text-property (point) 'face)))
|
||
(c-put-font-lock-face id-beginning (point) font-lock-face)
|
||
(c-forward-syntactic-ws))))
|
||
|
||
(c-lang-defconst c-basic-matchers-before
|
||
csharp `(
|
||
;; Warning face on unclosed strings
|
||
,@(if (version< emacs-version "27.0")
|
||
;; Taken from 26.1 branch
|
||
`(,(c-make-font-lock-search-function
|
||
(concat ".\\(" c-string-limit-regexp "\\)")
|
||
'((c-font-lock-invalid-string))))
|
||
`(("\\s|" 0 font-lock-warning-face t nil)))
|
||
|
||
;; Invalid single quotes
|
||
c-font-lock-invalid-single-quotes
|
||
|
||
;; Keyword constants
|
||
,@(when (c-lang-const c-constant-kwds)
|
||
(let ((re (c-make-keywords-re nil (c-lang-const c-constant-kwds))))
|
||
`((eval . (list ,(concat "\\<\\(" re "\\)\\>")
|
||
1 c-constant-face-name)))))
|
||
|
||
;; Keywords except the primitive types.
|
||
,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
|
||
1 font-lock-keyword-face)
|
||
|
||
;; Chained identifiers in using/namespace statements
|
||
,`(,(c-make-font-lock-search-function
|
||
csharp--regex-using-or-namespace
|
||
`((csharp--color-forwards font-lock-variable-name-face)
|
||
nil
|
||
(goto-char (match-end 0)))))
|
||
|
||
|
||
;; Negation character
|
||
(eval . (list "\\(!\\)[^=]" 1 c-negation-char-face-name))
|
||
|
||
;; Types after 'new'
|
||
(eval . (list (concat "\\<new\\> *" csharp--regex-type-name-matcher)
|
||
1 font-lock-type-face))
|
||
|
||
;; Single identifier in attribute
|
||
(eval . (list (concat "\\[" csharp--regex-type-name-matcher "\\][^;]")
|
||
1 font-lock-variable-name-face t))
|
||
|
||
;; Function names
|
||
(eval . (list "\\([A-Za-z0-9_]+\\)\\(<[a-zA-Z0-9, ]+>\\)?("
|
||
1 font-lock-function-name-face))
|
||
|
||
;; Nameof
|
||
(eval . (list (concat "\\(\\<nameof\\>\\) *(")
|
||
1 font-lock-function-name-face))
|
||
|
||
(eval . (list (concat "\\<nameof\\> *( *"
|
||
csharp--regex-identifier-matcher
|
||
" *) *")
|
||
1 font-lock-variable-name-face))
|
||
|
||
;; Catch statements with type only
|
||
(eval . (list (concat "\\<catch\\> *( *"
|
||
csharp--regex-type-name-matcher
|
||
" *) *")
|
||
1 font-lock-type-face))
|
||
))
|
||
|
||
(c-lang-defconst c-basic-matchers-after
|
||
csharp (append
|
||
;; Merge with cc-mode defaults - enables us to add more later
|
||
(c-lang-const c-basic-matchers-after)))
|
||
|
||
(defcustom csharp-codedoc-tag-face 'c-doc-markup-face-name
|
||
"Face to be used on the codedoc docstring tags.
|
||
|
||
Should be one of the font lock faces, such as
|
||
`font-lock-variable-name-face' and friends.
|
||
|
||
Needs to be set before `csharp-mode' is loaded, because of
|
||
compilation and evaluation time conflicts."
|
||
:type 'symbol
|
||
:group 'csharp)
|
||
|
||
(defcustom csharp-font-lock-extra-types
|
||
(list csharp--regex-type-name)
|
||
(c-make-font-lock-extra-types-blurb "C#" "csharp-mode" (concat))
|
||
:type 'c-extra-types-widget
|
||
:group 'c)
|
||
|
||
(defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
|
||
"Minimal font locking for C# mode.")
|
||
|
||
(defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
|
||
"Fast normal font locking for C# mode.")
|
||
|
||
(defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
|
||
"Accurate normal font locking for C# mode.")
|
||
|
||
(defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
|
||
"Default expressions to highlight in C# mode.")
|
||
|
||
(defun csharp-font-lock-keywords-2 ()
|
||
(c-compose-keywords-list csharp-font-lock-keywords-2))
|
||
(defun csharp-font-lock-keywords-3 ()
|
||
(c-compose-keywords-list csharp-font-lock-keywords-3))
|
||
(defun csharp-font-lock-keywords ()
|
||
(c-compose-keywords-list csharp-font-lock-keywords))
|
||
|
||
;;; Compilation support
|
||
;; When invoked by MSBuild, csc’s errors look like this:
|
||
;; subfolder\file.cs(6,18): error CS1006: Name of constructor must
|
||
;; match name of class [c:\Users\user\project.csproj]
|
||
|
||
(defun csharp--compilation-error-file-resolve ()
|
||
"Resolve an msbuild error to a (filename . dirname) cons cell."
|
||
;; http://stackoverflow.com/a/18049590/429091
|
||
(cons (match-string 1) (file-name-directory (match-string 4))))
|
||
|
||
(defconst csharp-compilation-re-msbuild-error
|
||
(concat
|
||
"^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
|
||
"\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?): "
|
||
"error [[:alnum:]]+: [^\r\n]+\\[\\([^]\r\n]+\\)\\]$")
|
||
"Regexp to match compilation error from msbuild.")
|
||
|
||
(defconst csharp-compilation-re-msbuild-warning
|
||
(concat
|
||
"^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
|
||
"\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?): "
|
||
"warning [[:alnum:]]+: [^\r\n]+\\[\\([^]\r\n]+\\)\\]$")
|
||
"Regexp to match compilation warning from msbuild.")
|
||
|
||
;; Notes on xbuild and devenv commonalities
|
||
;;
|
||
;; These regexes were tailored for xbuild, but apart from the concurrent
|
||
;; build-marker ("1>") they share exactly the same match-markers.
|
||
;;
|
||
;; If we don't exclude the match-markers explicitly, these regexes
|
||
;; will also be used to match for devenv as well, including the build-marker
|
||
;; in the file-name, causing the lookup to fail.
|
||
;;
|
||
;; So if we don't want devenv to fail, we actually need to handle it in our
|
||
;; xbuild-regexes, but then we automatically get devenv-support for free.
|
||
|
||
(defconst csharp-compilation-re-xbuild-error
|
||
(concat
|
||
"^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
|
||
"\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?"
|
||
;; handle weird devenv output format with 4 numbers, not 2 by having optional
|
||
;; extra capture-groups.
|
||
"\\(?:,\\([0-9]+\\)\\)*): "
|
||
"error [[:alnum:]]+: .+$")
|
||
"Regexp to match compilation error from xbuild.")
|
||
|
||
(defconst csharp-compilation-re-xbuild-warning
|
||
(concat
|
||
"^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
|
||
"\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?"
|
||
;; handle weird devenv output format with 4 numbers, not 2 by having optional
|
||
;; extra capture-groups.
|
||
"\\(?:,\\([0-9]+\\)\\)?*): "
|
||
"warning [[:alnum:]]+: .+$")
|
||
"Regexp to match compilation warning from xbuild.")
|
||
|
||
(defconst csharp-compilation-re-dotnet-error
|
||
"\\([^\r\n]+\\) : error [A-Z]+[0-9]+:")
|
||
|
||
(defconst csharp-compilation-re-dotnet-warning
|
||
"\\([^\r\n]+\\) : warning [A-Z]+[0-9]+:")
|
||
|
||
(defconst csharp-compilation-re-dotnet-testfail
|
||
(concat
|
||
"\\[[A-Za-z.]+[[:blank:]]+[0-9]+:[0-9]+:[0-9]+.[0-9]+\\][^(\n)]+ \\[FAIL\\]\n"
|
||
"[[:blank:]]+X \\(?:.+\n\\)"
|
||
"[[:blank:]]+Error Message:\n"
|
||
"[[:blank:]]+\\(?:.+\n\\)"
|
||
"[[:blank:]]+Stack Trace:\n"
|
||
"[[:blank:]]+at [^\n]+ in \\([^\n]+\\):line \\([0-9]+\\)"))
|
||
|
||
|
||
(eval-after-load 'compile
|
||
(lambda ()
|
||
(dolist
|
||
(regexp
|
||
`((dotnet-testfail
|
||
,csharp-compilation-re-dotnet-testfail
|
||
1 2)
|
||
(xbuild-error
|
||
,csharp-compilation-re-xbuild-error
|
||
1 2 3 2)
|
||
(xbuild-warning
|
||
,csharp-compilation-re-xbuild-warning
|
||
1 2 3 1)
|
||
(msbuild-error
|
||
,csharp-compilation-re-msbuild-error
|
||
csharp--compilation-error-file-resolve
|
||
2
|
||
3
|
||
2
|
||
nil
|
||
(1 compilation-error-face)
|
||
(4 compilation-error-face))
|
||
(msbuild-warning
|
||
,csharp-compilation-re-msbuild-warning
|
||
csharp--compilation-error-file-resolve
|
||
2
|
||
3
|
||
1
|
||
nil
|
||
(1 compilation-warning-face)
|
||
(4 compilation-warning-face))
|
||
(dotnet-error
|
||
,csharp-compilation-re-dotnet-error
|
||
1)
|
||
(dotnet-warning
|
||
,csharp-compilation-re-dotnet-warning
|
||
1 nil nil 1)))
|
||
(add-to-list 'compilation-error-regexp-alist-alist regexp)
|
||
(add-to-list 'compilation-error-regexp-alist (car regexp)))))
|
||
|
||
;;; Doc comments
|
||
|
||
(defconst codedoc-font-lock-doc-comments
|
||
;; Most of this is taken from the javadoc example, however, we don't use the
|
||
;; '@foo' syntax, so I removed that. Supports the XML tags only
|
||
`((,(concat "</?\\sw" ; XML tags.
|
||
"\\("
|
||
(concat "\\sw\\|\\s \\|[=\n\r*.:]\\|"
|
||
"\"[^\"]*\"\\|'[^']*'")
|
||
"\\)*/?>")
|
||
0 ,csharp-codedoc-tag-face prepend nil)
|
||
;; ("\\([a-zA-Z0-9_]+\\)=" 0 font-lock-variable-name-face prepend nil)
|
||
;; ("\".*\"" 0 font-lock-string-face prepend nil)
|
||
("&\\(\\sw\\|[.:]\\)+;" ; XML entities.
|
||
0 ,csharp-codedoc-tag-face prepend nil)))
|
||
|
||
(defconst codedoc-font-lock-keywords
|
||
`((,(lambda (limit)
|
||
(c-font-lock-doc-comments "///" limit
|
||
codedoc-font-lock-doc-comments)))))
|
||
|
||
;;; End of doc comments
|
||
|
||
;;; Adding syntax constructs
|
||
|
||
(advice-add 'c-looking-at-inexpr-block
|
||
:around 'csharp-looking-at-inexpr-block)
|
||
|
||
(defun csharp-looking-at-inexpr-block (orig-fun &rest args)
|
||
(let ((res (csharp-at-lambda-header)))
|
||
(if res
|
||
res
|
||
(apply orig-fun args))))
|
||
|
||
(defun csharp-at-lambda-header ()
|
||
(save-excursion
|
||
(c-backward-syntactic-ws)
|
||
(unless (bobp)
|
||
(backward-char)
|
||
(c-safe (goto-char (scan-sexps (point) -1)))
|
||
(when (or (looking-at "([[:alnum:][:space:]_,]*)[ \t\n]*=>[ \t\n]*{")
|
||
(looking-at "[[:alnum:]_]+[ \t\n]*=>[ \t\n]*{"))
|
||
;; If we are at a C# lambda header
|
||
(cons 'inexpr (point))))))
|
||
|
||
(advice-add 'c-guess-basic-syntax
|
||
:around 'csharp-guess-basic-syntax)
|
||
|
||
(defun csharp-guess-basic-syntax (orig-fun &rest args)
|
||
(cond
|
||
(;; Attributes
|
||
(save-excursion
|
||
(goto-char (c-point 'iopl))
|
||
(and
|
||
(eq (char-after) ?\[)
|
||
(save-excursion
|
||
(c-go-list-forward)
|
||
(and (eq (char-before) ?\])
|
||
(not (eq (char-after) ?\;))))))
|
||
`((annotation-top-cont ,(c-point 'iopl))))
|
||
|
||
((and
|
||
;; Heuristics to find object initializers
|
||
(save-excursion
|
||
;; Next non-whitespace character should be '{'
|
||
(goto-char (c-point 'boi))
|
||
(eq (char-after) ?{))
|
||
(save-excursion
|
||
;; 'new' should be part of the line
|
||
(goto-char (c-point 'iopl))
|
||
(looking-at ".*\\s *new\\s *.*"))
|
||
;; Line should not already be terminated
|
||
(save-excursion
|
||
(goto-char (c-point 'eopl))
|
||
(or (not (eq (char-before) ?\;))
|
||
(not (eq (char-before) ?\{)))))
|
||
(if (save-excursion
|
||
;; if we have a hanging brace on line before
|
||
(goto-char (c-point 'eopl))
|
||
(eq (char-before) ?\{))
|
||
`((brace-list-intro ,(c-point 'iopl)))
|
||
`((block-open) (statement ,(c-point 'iopl)))))
|
||
(t
|
||
(apply orig-fun args))))
|
||
|
||
;;; End of new syntax constructs
|
||
|
||
|
||
|
||
;;; Fix for strings on version 27.1
|
||
|
||
(when (version= emacs-version "27.1")
|
||
;; See:
|
||
;; https://github.com/josteink/csharp-mode/issues/175
|
||
;; https://github.com/josteink/csharp-mode/issues/151
|
||
;; for the full story.
|
||
(defun c-pps-to-string-delim (end)
|
||
(let* ((start (point))
|
||
(no-st-s `(0 nil nil ?\" nil nil 0 nil ,start nil nil))
|
||
(st-s `(0 nil nil t nil nil 0 nil ,start nil nil))
|
||
no-st-pos st-pos
|
||
)
|
||
(parse-partial-sexp start end nil nil no-st-s 'syntax-table)
|
||
(setq no-st-pos (point))
|
||
(goto-char start)
|
||
(while (progn
|
||
(parse-partial-sexp (point) end nil nil st-s 'syntax-table)
|
||
(unless (bobp)
|
||
(c-clear-syn-tab (1- (point))))
|
||
(setq st-pos (point))
|
||
(and (< (point) end)
|
||
(not (eq (char-before) ?\")))))
|
||
(goto-char (min no-st-pos st-pos))
|
||
nil))
|
||
|
||
(defun c-multiline-string-check-final-quote ()
|
||
(let (pos-ll pos-lt)
|
||
(save-excursion
|
||
(goto-char (point-max))
|
||
(skip-chars-backward "^\"")
|
||
(while
|
||
(and
|
||
(not (bobp))
|
||
(cond
|
||
((progn
|
||
(setq pos-ll (c-literal-limits)
|
||
pos-lt (c-literal-type pos-ll))
|
||
(memq pos-lt '(c c++)))
|
||
;; In a comment.
|
||
(goto-char (car pos-ll)))
|
||
((save-excursion
|
||
(backward-char) ; over "
|
||
(c-is-escaped (point)))
|
||
;; At an escaped string.
|
||
(backward-char)
|
||
t)
|
||
(t
|
||
;; At a significant "
|
||
(c-clear-syn-tab (1- (point)))
|
||
(setq pos-ll (c-literal-limits)
|
||
pos-lt (c-literal-type pos-ll))
|
||
nil)))
|
||
(skip-chars-backward "^\""))
|
||
(cond
|
||
((bobp))
|
||
((eq pos-lt 'string)
|
||
(c-put-syn-tab (1- (point)) '(15)))
|
||
(t nil))))))
|
||
|
||
;;; End of fix for strings on version 27.1
|
||
|
||
|
||
|
||
(defvar csharp-mode-syntax-table
|
||
(funcall (c-lang-const c-make-mode-syntax-table csharp))
|
||
"Syntax table used in csharp-mode buffers.")
|
||
|
||
(defvar csharp-mode-map
|
||
(let ((map (c-make-inherited-keymap)))
|
||
map)
|
||
"Keymap used in csharp-mode buffers.")
|
||
|
||
(easy-menu-define csharp-mode-menu csharp-mode-map "C# Mode Commands"
|
||
(cons "C#" (c-lang-const c-mode-menu csharp)))
|
||
|
||
;;;###autoload
|
||
(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode))
|
||
|
||
;; Custom variables
|
||
;;;###autoload
|
||
(defcustom csharp-mode-hook nil
|
||
"*Hook called by `csharp-mode'."
|
||
:type 'hook
|
||
:group 'csharp)
|
||
|
||
;;;###autoload
|
||
(define-derived-mode csharp-mode prog-mode "C#"
|
||
"Major mode for editing Csharp code.
|
||
|
||
Key bindings:
|
||
\\{csharp-mode-map}"
|
||
:after-hook (c-update-modeline)
|
||
(c-initialize-cc-mode t)
|
||
(c-init-language-vars csharp-mode)
|
||
(c-common-init 'csharp-mode)
|
||
(easy-menu-add csharp-mode-menu)
|
||
(setq-local c-doc-comment-style '((csharp-mode . codedoc)))
|
||
(c-run-mode-hooks 'c-mode-common-hook 'csharp-mode-hook))
|
||
|
||
(provide 'csharp-mode)
|
||
|
||
;;; csharp-mode.el ends here
|