summaryrefslogtreecommitdiffstats
path: root/.emacs.d/csharp-mode.el
diff options
context:
space:
mode:
Diffstat (limited to '.emacs.d/csharp-mode.el')
-rw-r--r--.emacs.d/csharp-mode.el1977
1 files changed, 1977 insertions, 0 deletions
diff --git a/.emacs.d/csharp-mode.el b/.emacs.d/csharp-mode.el
new file mode 100644
index 0000000..9cd7914
--- /dev/null
+++ b/.emacs.d/csharp-mode.el
@@ -0,0 +1,1977 @@
+;;; csharp-mode.el --- C# mode derived mode
+
+;; Author: Dylan R. E. Moonfire
+;; Maintainer: Dylan R. E. Moonfire <contact@mfgames.com>
+;; Created: Feburary 2005
+;; Modified: February 2010
+;; Version: 0.7.4 - Dino Chiesa <dpchiesa@hotmail.com>
+;; Keywords: c# languages oop 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 2 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; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+;;
+;; This is a separate mode to implement the C# constructs and
+;; font-locking. It is based on the java-mode example from cc-mode.
+;;
+;; csharp-mode requires CC Mode 5.30 or later. It works with
+;; cc-mode 5.31.3, which is current at this time.
+;;
+;; Features:
+;;
+;; - font-lock and indent of C# syntax including:
+;; all c# keywords and major syntax
+;; attributes that decorate methods, classes, fields, properties
+;; enum types
+;; #if/#endif #region/#endregion
+;; instance initializers
+;; anonymous functions and methods
+;; verbatim literal strings (those that begin with @)
+;; generics
+;;
+;; - automagic code-doc generation when you type three slashes.
+;;
+;; - intelligent inserttion of matched pairs of curly braces.
+;;
+;; - sets the compiler regex for next-error, for csc.exe output.
+;;
+;;
+
+
+;;; To use:
+;;
+;; put this in your .emacs:
+;;
+;; (autoload 'csharp-mode "csharp-mode" "Major mode for editing C# code." t)
+;;
+;; or:
+;;
+;; (require 'csharp-mode)
+;;
+;;
+;; AND:
+;;
+;; (setq auto-mode-alist
+;; (append '(("\\.cs$" . csharp-mode)) auto-mode-alist))
+;; (defun my-csharp-mode-fn ()
+;; "function that runs when csharp-mode is initialized for a buffer."
+;; ...insert your code here...
+;; ...most commonly, your custom key bindings ...
+;; )
+;; (add-hook 'csharp-mode-hook 'my-csharp-mode-fn t)
+;;
+;;
+
+
+;;; Bugs:
+;;
+;; Namespaces in the using statements are not fontified. Should do in
+;; c-basic-matchers-before or c-basic-matchers-after.
+;;
+;; Method names with a preceding attribute are not fontified.
+;;
+;; Field/Prop names inside object initializers are fontified only
+;; if the null constructor is used, with no parens.
+;;
+;; This code doesn't seem to work when you compile it, then
+;; load/require in the emacs file. You will get an error (error
+;; "`c-lang-defconst' must be used in a file") which happens because
+;; cc-mode doesn't think it is in a buffer while loading directly
+;; from the init. However, if you call it based on a file extension,
+;; it works properly. Interestingly enough, this doesn't happen if
+;; you don't byte-compile cc-mode.
+;;
+;;
+;;
+;; Todo:
+;;
+;; Get csharp-mode.el accepted as part of the emacs standard distribution.
+;; Must contact monnier at iro.umontreal.ca to make this happen.
+;;
+;;
+;;
+;; Acknowledgements:
+;;
+;; Thanks to Alan Mackenzie and Stefan Monnier for answering questions
+;; and making suggestions.
+;;
+;;
+
+;;; Versions:
+;;
+;; 0.1.0 - Initial release.
+;; 0.2.0 - Fixed the identification on the "enum" keyword.
+;; - Fixed the font-lock on the "base" keyword
+;; 0.3.0 - Added a regex to fontify attributes. It isn't the
+;; the best method, but it handles single-like attributes
+;; well.
+;; - Got "super" not to fontify as a keyword.
+;; - Got extending classes and interfaces to fontify as something.
+;; 0.4.0 - Removed the attribute matching because it broke more than
+;; it fixed.
+;; - Corrected a bug with namespace not being properly identified
+;; and treating the class level as an inner object, which screwed
+;; up formatting.
+;; - Added "partial" to the keywords.
+;; 0.5.0 - Found bugs with compiled cc-mode and loading from init files.
+;; - Updated the eval-when-compile to code to let the mode be
+;; compiled.
+;; 0.6.0 - Added the c-filter-ops patch for 5.31.1 which made that
+;; function in cc-langs.el unavailable.
+;; - Added a csharp-lineup-region for indention #region and
+;; #endregion block differently.
+;; 0.7.0 - Added autoload so update-directory-autoloads works
+;; (Thank you, Nikolaj Schumacher)
+;; - Fontified the entire #region and #endregion lines.
+;; - Initial work to get get, set, add, remove font-locked.
+;; 0.7.1 - Added option to indent #if/endif with code
+;; - Fixed c-opt-cpp-prefix defn (it must not include the BOL
+;; char (^).
+;; - proper fontification and indent of classes that inherit
+;; (previously the colon was confusing the parser)
+;; - reclassified namespace as a block beginner
+;; - removed $ as a legal symbol char - not legal in C#.
+;; - added struct to c-class-decl-kwds so indent is correct
+;; within a struct.
+;; 0.7.2 - Added automatic codedoc insertion.
+;; 0.7.3 - Instance initializers (new Type { ... } ) and
+;; (new Type() { ...} ) are now indented properly.
+;; - proper fontification and indent of enums as brace-list-*,
+;; including special treatment for enums that explicitly
+;; inherit from an int type. Previously the colon was
+;; confusing the parser.
+;; - proper fontification of verbatim literal strings,
+;; including those that end in slash. This edge case was not
+;; handled at all before; it is now handled correctly.
+;; - code cleanup and organization; removed the linefeed.
+;; - intelligent curly-brace insertion
+;; 0.7.4 - added a C# style
+;; - using is now a keyword and gets fontified
+;; - fixed a bug that had crept into the codedoc insertion
+;;
+
+
+(require 'cc-mode)
+
+(message (concat "Loading " load-file-name))
+
+
+;; ==================================================================
+;; c# upfront stuff
+;; ==================================================================
+
+;; This is a copy of the function in cc-mode which is used to handle
+;; the eval-when-compile which is needed during other times.
+(defun c-filter-ops (ops opgroup-filter op-filter &optional xlate)
+ ;; See cc-langs.el, a direct copy.
+ (unless (listp (car-safe ops))
+ (setq ops (list ops)))
+ (cond ((eq opgroup-filter t)
+ (setq opgroup-filter (lambda (opgroup) t)))
+ ((not (functionp opgroup-filter))
+ (setq opgroup-filter `(lambda (opgroup)
+ (memq opgroup ',opgroup-filter)))))
+ (cond ((eq op-filter t)
+ (setq op-filter (lambda (op) t)))
+ ((stringp op-filter)
+ (setq op-filter `(lambda (op)
+ (string-match ,op-filter op)))))
+ (unless xlate
+ (setq xlate 'identity))
+ (c-with-syntax-table (c-lang-const c-mode-syntax-table)
+ (delete-duplicates
+ (mapcan (lambda (opgroup)
+ (when (if (symbolp (car opgroup))
+ (when (funcall opgroup-filter (car opgroup))
+ (setq opgroup (cdr opgroup))
+ t)
+ t)
+ (mapcan (lambda (op)
+ (when (funcall op-filter op)
+ (let ((res (funcall xlate op)))
+ (if (listp res) res (list res)))))
+ opgroup)))
+ ops)
+ :test 'equal)))
+
+
+
+;; These are only required at compile time to get the sources for the
+;; language constants. (The cc-fonts require and the font-lock
+;; related constants could additionally be put inside an
+;; (eval-after-load "font-lock" ...) but then some trickery is
+;; necessary to get them compiled.)
+(eval-when-compile
+ (let ((load-path
+ (if (and (boundp 'byte-compile-dest-file)
+ (stringp byte-compile-dest-file))
+ (cons (file-name-directory byte-compile-dest-file) load-path)
+ load-path)))
+ (load "cc-mode" nil t)
+ (load "cc-fonts" nil t)
+ (load "cc-langs" nil t)))
+
+(eval-and-compile
+ ;; Make our mode known to the language constant system. Use Java
+ ;; mode as the fallback for the constants we don't change here.
+ ;; This needs to be done also at compile time since the language
+ ;; constants are evaluated then.
+ (c-add-language 'csharp-mode 'java-mode))
+
+;; ==================================================================
+;; end of c# upfront stuff
+;; ==================================================================
+
+
+
+
+
+;; ==================================================================
+;; csharp-mode utility and feature defuns
+;; ==================================================================
+
+;; Indention: csharp-mode follows normal indention rules except for
+;; when indenting the #region and #endregion blocks. This function
+;; defines a custom indention to indent the #region blocks properly
+;;
+
+(defun csharp-lineup-region (langelem)
+ "Indent all #region and #endregion blocks inline with code while
+retaining normal column-zero indention for #if and the other
+processing blocks.
+
+To use this indenting just put the following in your emacs file:
+ (c-set-offset 'cpp-macro 'csharp-lineup-region)
+
+An alternative is to use `csharp-lineup-if-and-region'.
+"
+
+ (save-excursion
+ (back-to-indentation)
+ (if (re-search-forward "#\\(end\\)?region" (c-point 'eol) [0]) 0 [0])))
+
+
+
+(defun csharp-lineup-if-and-region (langelem)
+
+"Indent all #region/endregion blocks and #if/endif blocks inline
+with code while retaining normal column-zero indention for any
+other processing blocks.
+
+To use this indenting just put the following in your emacs file:
+ (c-set-offset 'cpp-macro 'csharp-lineup-if-and-region)
+
+Another option is to use `csharp-lineup-region'.
+
+"
+ (save-excursion
+ (back-to-indentation)
+ (if (re-search-forward "#\\(\\(end\\)?\\(if\\|region\\)\\|else\\)" (c-point 'eol) [0]) 0 [0])))
+
+
+
+
+
+(defun csharp-insert-open-brace ()
+ "Intelligently insert a pair of curly braces. This fn is most
+often bound to the open-curly brace, with
+
+ (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
+
+The default binding for an open curly brace in cc-modes is often
+`c-electric-brace' or `skeleton-pair-insert-maybe'. The former
+can be configured to insert newlines around braces in various
+syntactic positions. The latter inserts a pair of braces and
+then does not insert a newline, and does not indent.
+
+This fn provides another option, with some additional
+intelligence for csharp-mode. When you type an open curly, the
+appropriate pair of braces appears, with spacing and indent set
+in a context-sensitive manner.
+
+Within a string literal, you just get a pair of braces, and point
+is set between them. Following an equals sign, you get a pair of
+braces, with a semincolon appended. Otherwise, you
+get the open brace on a new line, with the closing brace on the
+line following.
+
+There may be another way to get this to happen appropriately just within emacs,
+but I could not figure out how to do it. So I wrote this alternative.
+"
+ (interactive)
+ (let
+ (tpoint
+ (in-string (string= (csharp-in-literal) "string"))
+ (preceding3
+ (save-excursion
+ (and
+ (skip-chars-backward " ")
+ (> (- (point) 2) (point-min))
+ (buffer-substring-no-properties (point) (- (point) 3)))))
+ (one-word-back
+ (save-excursion
+ (backward-word 2)
+ (thing-at-point 'word))))
+
+ (cond
+
+ ;; Case 1: inside a string literal?
+ ;; --------------------------------------------
+ ;; If so, then just insert a pair of braces and put the point
+ ;; between them. The most common case is a format string for
+ ;; String.Format() or Console.WriteLine().
+ (in-string
+ (self-insert-command 1)
+ (insert "}")
+ (backward-char))
+
+ ;; Case 2: the open brace starts an array initializer.
+ ;; --------------------------------------------
+ ;; When the last non-space was an equals sign or square brackets,
+ ;; then it's an initializer.
+ ((save-excursion
+ (backward-sexp)
+ (looking-at "\\(\\w+\\b *=\\|[[]]+\\)"))
+ (self-insert-command 1)
+ (insert " };")
+ (backward-char 3))
+
+ ;; Case 3: the open brace starts an instance initializer
+ ;; --------------------------------------------
+ ;; If one-word-back was "new", then it's an object initializer.
+ ((string= one-word-back "new")
+ (save-excursion
+ (message "object initializer")
+ (setq tpoint (point)) ;; prepare to indent-region later
+ (newline)
+ (self-insert-command 1)
+ (newline-and-indent)
+ (newline)
+ (insert "};")
+ (c-indent-region tpoint (point))
+ (previous-line)
+ (indent-according-to-mode)
+ (end-of-line)
+ (setq tpoint (point)))
+ (goto-char tpoint))
+
+ ;; Case 4: a lambda initialier.
+ ;; --------------------------------------------
+ ;; If the open curly follows =>, then it's a lambda initializer.
+ ((string= (substring preceding3 -2) "=>")
+ (message "lambda init")
+ (self-insert-command 1)
+ (insert " }")
+ (backward-char 2))
+
+ ;; else, it's a new scope. (if, while, class, etc)
+ (t
+ (save-excursion
+ (message "new scope")
+ (set-mark (point)) ;; prepare to indent-region later
+ ;; check if the prior sexp is on the same line
+ (if (save-excursion
+ (let ((curline (line-number-at-pos))
+ (aftline (progn
+ (backward-sexp)
+ (line-number-at-pos))))
+ (= curline aftline)))
+ (newline-and-indent))
+ (self-insert-command 1)
+ (c-indent-line-or-region)
+ (end-of-line)
+ (newline)
+ (insert "}")
+ ;;(c-indent-command) ;; not sure of the difference here
+ (c-indent-line-or-region)
+ (previous-line)
+ (end-of-line)
+ (newline-and-indent)
+ ;; point ends up on an empty line, within the braces, properly indented
+ (setq tpoint (point)))
+
+ (goto-char tpoint)))))
+
+
+
+
+;; ==================================================================
+;; end of csharp-mode utility and feature defuns
+;; ==================================================================
+
+
+
+
+
+
+;; ==================================================================
+;; c# values for "language constants" defined in cc-langs.el
+;; ==================================================================
+
+
+;; Java uses a series of regexes to change the font-lock for class
+;; references. The problem comes in because Java uses Pascal (leading
+;; space in names, SomeClass) for class and package names, but
+;; Camel-casing (initial lowercase, upper case in words,
+;; i.e. someVariable) for variables. The notation suggested by EMCA for C# is
+;; to use Pascal notation for everything, except inner variables. So,
+;; the Java regex and formatting produces very wrong results in C#.
+;;(error (byte-compile-dest-file))
+;;(error (c-get-current-file))
+(c-lang-defconst c-opt-after-id-concat-key
+ csharp (if (c-lang-const c-opt-identifier-concat-key)
+ (c-lang-const c-symbol-start)))
+
+(c-lang-defconst c-basic-matchers-before
+ csharp `(
+ ;;;; Font-lock the attributes by searching for the
+ ;;;; appropriate regex and marking it as TODO.
+ ;;,`(,(concat "\\(" csharp-attribute-regex "\\)")
+ ;; 0 font-lock-function-name-face)
+
+ ;; Put a warning face on the opener of unclosed strings that
+ ;; can't span lines. Later font
+ ;; lock packages have a `font-lock-syntactic-face-function' for
+ ;; this, but it doesn't give the control we want since any
+ ;; fontification done inside the function will be
+ ;; unconditionally overridden.
+ ,(c-make-font-lock-search-function
+ ;; Match a char before the string starter to make
+ ;; `c-skip-comments-and-strings' work correctly.
+ (concat ".\\(" c-string-limit-regexp "\\)")
+ '((c-font-lock-invalid-string)))
+
+ ;; Fontify 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)))))
+
+ ;; Fontify all keywords except the primitive types.
+ ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
+ 1 font-lock-keyword-face)
+
+ ;; Fontify leading identifiers in fully qualified names like
+ ;; "Foo.Bar".
+ ,@(when (c-lang-const c-opt-identifier-concat-key)
+ `((,(byte-compile
+ `(lambda (limit)
+ (while (re-search-forward
+ ,(concat "\\(\\<" ; 1
+ "\\(" (c-lang-const c-symbol-key)
+ "\\)" ; 2
+ "[ \t\n\r\f\v]*"
+ (c-lang-const
+ c-opt-identifier-concat-key)
+ "[ \t\n\r\f\v]*"
+ "\\)"
+ "\\("
+ (c-lang-const
+ c-opt-after-id-concat-key)
+ "\\)")
+ limit t)
+ (unless (progn
+ (goto-char (match-beginning 0))
+ (c-skip-comments-and-strings limit))
+ (or (get-text-property (match-beginning 2) 'face)
+ (c-put-font-lock-face (match-beginning 2)
+ (match-end 2)
+ c-reference-face-name))
+ (goto-char (match-end 1)))))))))
+ ))
+
+
+
+;; C# does not allow a leading qualifier operator. It also doesn't
+;; allow the ".*" construct of Java. So, we redo this regex without
+;; the "\\|\\*" regex.
+(c-lang-defconst c-identifier-key
+ csharp (concat "\\(" (c-lang-const c-symbol-key) "\\)" ; 1
+ (concat "\\("
+ "[ \t\n\r\f\v]*"
+ (c-lang-const c-opt-identifier-concat-key)
+ "[ \t\n\r\f\v]*"
+ (concat "\\("
+ "\\(" (c-lang-const c-symbol-key) "\\)"
+ "\\)")
+ "\\)*")))
+
+;; C# has a few rules that are slightly different than Java for
+;; operators. This also removed the Java's "super" and replaces it
+;; with the C#'s "base".
+(c-lang-defconst c-operators
+ csharp `((prefix "base")))
+
+
+;; C# uses CPP-like prefixes to mark #define, #region/endregion,
+;; #if/else/endif, and #pragma. This regexp matches the prefix,
+;; not including the beginning-of-line (BOL), and not including
+;; the term after the prefix (define, pragma, etc). This regexp says
+;; whitespace, followed by the prefix, followed by maybe more whitespace.
+
+(c-lang-defconst c-opt-cpp-prefix
+ csharp "\\s *#\\s *")
+
+
+;; there are no message directives in C#
+(c-lang-defconst c-cpp-message-directives
+ csharp nil)
+
+(c-lang-defconst c-cpp-expr-directives
+ csharp '("if"))
+
+(c-lang-defconst c-opt-cpp-macro-define
+ csharp "define")
+
+;; $ is not a legal char in an identifier in C#. So we need to
+;; create a csharp-specific definition of this constant.
+(c-lang-defconst c-symbol-chars
+ csharp (concat c-alnum "_"))
+
+
+(c-lang-defconst c-colon-type-list-kwds
+ csharp '("class"))
+
+(c-lang-defconst c-block-prefix-disallowed-chars
+
+ ;; Allow ':' for inherit list starters.
+ csharp (set-difference (c-lang-const c-block-prefix-disallowed-chars)
+ '(?: ?,)))
+
+
+(c-lang-defconst c-assignment-operators
+ csharp '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|="))
+
+(c-lang-defconst c-primitive-type-kwds
+ ;; ECMA-344, S8
+ csharp '("object" "string" "sbyte" "short" "int" "long" "byte"
+ "ushort" "uint" "ulong" "float" "double" "bool" "char"
+ "decimal" "void"))
+
+;; The keywords that define that the following is a type, such as a
+;; class definition.
+(c-lang-defconst c-type-prefix-kwds
+ ;; ECMA-344, S?
+ csharp '("class" "interface" "struct")) ;; no enum here.
+ ;; we want enum to be a brace list.
+
+
+;; Type modifier keywords. They appear anywhere in types, but modify
+;; instead of create one.
+(c-lang-defconst c-type-modifier-kwds
+ ;; EMCA-344, S?
+ csharp '("readonly" "const"))
+
+
+;; Tue, 20 Apr 2010 16:02
+;; need to vverify that this works for lambdas...
+(c-lang-defconst c-special-brace-lists
+ csharp '((?{ . ?}) ))
+
+
+
+;; dinoch
+;; Thu, 22 Apr 2010 18:54
+;;
+;; No idea why this isn't getting set properly in the first place.
+;; In cc-langs.el, it is set to the union of a bunch of things, none
+;; of which include "new", or "enum".
+;;
+;; But somehow both of those show up in the resulting derived regexp.
+;; This breaks indentation of instance initializers, such as
+;;
+;; var x = new Foo { ... };
+;;
+;; Based on my inspection, the existing c-lang-defconst should work!
+;; I don't know how to fix this c-lang-defconst, so I am re-setting this
+;; variable here, to provide the regex explicitly.
+;;
+(c-lang-defconst c-decl-block-key
+
+ csharp '"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)"
+ )
+
+
+
+;; Thu, 22 Apr 2010 14:29
+;; I want this to handle var x = new Foo[] { ... };
+;; not sure if necessary.
+(c-lang-defconst c-inexpr-brace-list-kwds
+ csharp '("new"))
+
+
+;; ;;(c-lang-defconst c-inexpr-class-kwds
+;; ;; csharp '("new"))
+
+
+
+(c-lang-defconst c-class-decl-kwds
+ ;; EMCA-344, S?
+ csharp '("class" "interface" "struct" )) ;; no "enum"!!
+
+
+;; The various modifiers used for class and method descriptions.
+(c-lang-defconst c-modifier-kwds
+ csharp '("public" "partial" "private" "const" "abstract"
+ "protected" "ref" "out" "static" "virtual"
+ "override" "params" "internal"))
+
+
+;; Thu, 22 Apr 2010 23:02
+;; Based on inspection of the cc-mode code, the c-protection-kwds
+;; c-lang-const is used only for objective-c. So the value is
+;; irrelevant for csharp.
+(c-lang-defconst c-protection-kwds
+ csharp nil
+ ;; csharp '("private" "protected" "public" "internal")
+)
+
+
+;; Define the keywords that can have something following after them.
+(c-lang-defconst c-type-list-kwds
+ csharp '("struct" "class" "interface" "is" "as"
+ "delegate" "event" "set" "get" "add" "remove"))
+
+
+;; This allows the classes after the : in the class declartion to be
+;; fontified.
+(c-lang-defconst c-typeless-decl-kwds
+ csharp '(":"))
+
+;; Sets up the enum to handle the list properly, and also the new
+;; keyword to handle object initializers. This requires a modified
+;; c-basic-matchers-after (see above) in order to correctly fontify C#
+;; 3.0 object initializers.
+(c-lang-defconst c-brace-list-decl-kwds
+ csharp '("enum" "new"))
+
+
+;; Statement keywords followed directly by a substatement.
+;; catch is not one of them.
+(c-lang-defconst c-block-stmt-1-kwds
+ csharp '("do" "try" "finally"))
+
+
+;; Statement keywords followed by a paren sexp and then by a substatement.
+(c-lang-defconst c-block-stmt-2-kwds
+ csharp '("for" "if" "switch" "while" "catch" "foreach" "using"
+ "checked" "unchecked" "lock"))
+
+
+;; Statements that break out of braces
+(c-lang-defconst c-simple-stmt-kwds
+ csharp '("return" "continue" "break" "throw" "goto" ))
+
+;; Statements that allow a label
+;; TODO?
+(c-lang-defconst c-before-label-kwds
+ csharp nil)
+
+;; Constant keywords
+(c-lang-defconst c-constant-kwds
+ csharp '("true" "false" "null"))
+
+;; Keywords that start "primary expressions."
+(c-lang-defconst c-primary-expr-kwds
+ csharp '("this" "base"))
+
+;; Treat namespace as an outer block so class indenting
+;; works properly.
+(c-lang-defconst c-other-block-decl-kwds
+ csharp '("namespace"))
+
+(c-lang-defconst c-other-kwds
+ csharp '("in" "sizeof" "typeof" "is" "as" "yield"
+ "where" "select" "from"))
+
+(c-lang-defconst c-overloadable-operators
+ ;; EMCA-344, S14.2.1
+ csharp '("+" "-" "*" "/" "%" "&" "|" "^"
+ "<<" ">>" "==" "!=" ">" "<" ">=" "<="))
+
+
+;; This c-cpp-matchers stuff is used for fontification.
+;; see cc-font.el
+;;
+
+;; There's no preprocessor in C#, but there are still compiler
+;; directives to fontify: "#pragma", #region/endregion, #define, #undef,
+;; #if/else/endif. (The definitions for the extra keywords above are
+;; enough to incorporate them into the fontification regexps for types
+;; and keywords, so no additional font-lock patterns are required for
+;; keywords.)
+
+(c-lang-defconst c-cpp-matchers
+ csharp (cons
+ ;; Use the eval form for `font-lock-keywords' to be able to use
+ ;; the `c-preprocessor-face-name' variable that maps to a
+ ;; suitable face depending on the (X)Emacs version.
+ '(eval . (list "^\\s *\\(#pragma\\|undef\\|define\\)\\>\\(.*\\)"
+ (list 1 c-preprocessor-face-name)
+ '(2 font-lock-string-face)))
+ ;; There are some other things in `c-cpp-matchers' besides the
+ ;; preprocessor support, so include it.
+ (c-lang-const c-cpp-matchers)))
+
+(defcustom csharp-font-lock-extra-types nil
+ "*List of extra types (aside from the type keywords) to recognize in C# mode.
+Each list item should be a regexp matching a single identifier."
+ :type 'list :group 'csharp)
+
+(defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
+ "Minimal highlighting for C# mode.")
+
+(defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
+ "Fast normal highlighting for C# mode.")
+
+(defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
+ "Accurate normal highlighting for C# mode.")
+
+(defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
+ "Default expressions to highlight in C# mode.")
+
+(defvar csharp-mode-syntax-table nil
+ "Syntax table used in csharp-mode buffers.")
+(or csharp-mode-syntax-table
+ (setq csharp-mode-syntax-table
+ (funcall (c-lang-const c-make-mode-syntax-table csharp))))
+
+(defvar csharp-mode-abbrev-table nil
+ "Abbreviation table used in csharp-mode buffers.")
+(c-define-abbrev-table 'csharp-mode-abbrev-table
+ ;; Keywords that if they occur first on a line might alter the
+ ;; syntactic context, and which therefore should trig reindentation
+ ;; when they are completed.
+ '(("else" "else" c-electric-continued-statement 0)
+ ("while" "while" c-electric-continued-statement 0)
+ ("catch" "catch" c-electric-continued-statement 0)
+ ("finally" "finally" c-electric-continued-statement 0)))
+
+(defvar csharp-mode-map (let ((map (c-make-inherited-keymap)))
+ ;; Add bindings which are only useful for C#
+ map)
+ "Keymap used in csharp-mode buffers.")
+
+
+;; TODO
+;; Defines our constant for finding attributes.
+;;(defconst csharp-attribute-regex "\\[\\([XmlType]+\\)(")
+;;(defconst csharp-attribute-regex "\\[\\(.\\)")
+;; This doesn't work because the string regex happens before this point
+;; and getting the font-locking to work before and after is fairly difficult
+;;(defconst csharp-attribute-regex
+;; (concat
+;; "\\[[a-zA-Z][ \ta-zA-Z0-9.]+"
+;; "\\((.*\\)?"
+;;))
+
+
+;; ==================================================================
+;; end of c# values for "language constants" defined in cc-langs.el
+;; ==================================================================
+
+
+
+
+;; ==================================================================
+;; C# code-doc insertion magic
+;; ==================================================================
+;;
+;; In Visual Studio, if you type three slashes, it immediately expands into
+;; an inline code-documentation fragment. The following method does the
+;; same thing.
+;;
+;; This is the kind of thing that could be handled by YASnippet or
+;; another similarly flexible snippet framework. But I don't want to
+;; introduce a dependency on yasnippet to csharp-mode. So the capability
+;; must live within csharp-mode itself.
+
+(defun csharp-maybe-insert-codedoc (arg)
+
+ "Insert an xml code documentation template as appropriate, when
+typing slashes. This fn gets bound to / (the slash key), in
+csharp-mode. If the slash being inserted is not the third
+consecutive slash, the slash is inserted as normal. If it is the
+third consecutive slash, then a xml code documentation template
+may be inserted in some cases. For example,
+
+ a <summary> template is inserted if the prior line is empty,
+ or contains only an open curly brace;
+ a <remarks> template is inserted if the prior word
+ closes the <summary> element;
+ a <returns> template is inserted if the prior word
+ closes the <remarks> element;
+ an <example> template is inserted if the prior word closes
+ the <returns> element;
+ a <para> template is inserted if the prior word closes
+ a <para> element.
+
+In all other cases the slash is inserted as normal.
+
+If you want the default cc-mode behavior, which implies no automatic
+insertion of xml code documentation templates, then use this in
+your `csharp-mode-hook' function:
+
+ (local-set-key (kbd \"/\") 'c-electric-slash)
+
+ "
+ (interactive "*p")
+ ;;(message "csharp-maybe-insert-codedoc")
+ (let (
+ (cur-point (point))
+ (char last-command-char)
+ (cb0 (char-before (- (point) 0)))
+ (cb1 (char-before (- (point) 1)))
+ is-first-non-whitespace
+ did-auto-insert
+ )
+
+ ;; check if two prior chars were slash
+ (if (and
+ (= char ?/)
+ cb0 (= ?/ cb0)
+ cb1 (= ?/ cb1)
+ )
+
+ (progn
+ ;;(message "yes - this is the third consecutive slash")
+ (setq is-first-non-whitespace
+ (save-excursion
+ (back-to-indentation)
+ (= cur-point (+ (point) 2))))
+
+ (if is-first-non-whitespace
+ ;; This is a 3-slash sequence. It is the first non-whitespace text
+ ;; on the line. Now we need to examine the surrounding context
+ ;; in order to determine which xml cod doc template to insert.
+ (let (word-back char0 char1
+ word-fore char-0 char-1
+ text-to-insert ;; text to insert in lieu of slash
+ fn-to-call ;; func to call after inserting text
+ (preceding-line-is-empty (or
+ (= (line-number-at-pos) 1)
+ (save-excursion
+ (previous-line)
+ (beginning-of-line)
+ (looking-at "[ \t]*$\\|[ \t]*{[ \t]*$"))))
+ (flavor 0) ;; used only for diagnostic purposes
+ )
+
+ ;;(message "starting a 3-slash comment")
+ ;; get the prior word, and the 2 chars preceding it.
+ (backward-word)
+
+ (setq word-back (thing-at-point 'word)
+ char0 (char-before (- (point) 0))
+ char1 (char-before (- (point) 1)))
+
+ ;; restore prior position
+ (goto-char cur-point)
+
+ ;; get the following word, and the 2 chars preceding it.
+ (forward-word)
+ (backward-word)
+ (setq word-fore (thing-at-point 'word)
+ char-0 (char-before (- (point) 0))
+ char-1 (char-before (- (point) 1)))
+
+ ;; restore prior position again
+ (goto-char cur-point)
+
+ (cond
+ ;; The preceding line is empty, or all whitespace, or
+ ;; contains only an open-curly. In this case, insert a
+ ;; summary element pair.
+ (preceding-line-is-empty
+ (setq text-to-insert "/ <summary>\n/// \n/// </summary>"
+ flavor 1) )
+
+ ;; The preceding word closed a summary element. In this case,
+ ;; if the forward word does not open a remarks element, then
+ ;; insert a remarks element.
+ ((and (string-equal word-back "summary") (eq char0 ?/) (eq char1 ?<))
+ (if (not (and (string-equal word-fore "remarks") (eq char-0 ?<)))
+ (setq text-to-insert "/ <remarks>\n/// <para>\n/// \n/// </para>\n/// </remarks>"
+ flavor 2)))
+
+ ;; The preceding word closed the remarks section. In this case,
+ ;; insert an example element.
+ ((and (string-equal word-back "remarks") (eq char0 ?/) (eq char1 ?<))
+ (setq text-to-insert "/ <example>\n/// \n/// </example>"
+ flavor 3))
+
+ ;; The preceding word closed the example section. In this
+ ;; case, insert an returns element. This isn't always
+ ;; correct, because sometimes the xml code doc is attached to
+ ;; a class or a property, neither of which has a return
+ ;; value. A more intelligent implementation would inspect the
+ ;; syntax state and only inject a returns element if
+ ;; appropriate.
+ ((and (string-equal word-back "example") (eq char0 ?/) (eq char1 ?<))
+ (setq text-to-insert "/ <returns></returns>"
+ fn-to-call (lambda ()
+ (backward-word)
+ (backward-char)
+ (backward-char)
+ (c-indent-line-or-region)
+ )
+ flavor 4))
+
+ ;; The preceding word opened the remarks section, or it
+ ;; closed a para section. In this case, insert a para
+ ;; element, using appropriate indentation with respect to the
+ ;; prior tag.
+ ((or
+ (and (string-equal word-back "remarks") (eq char0 ?<) (or (eq char1 32) (eq char1 9)))
+ (and (string-equal word-back "para") (eq char0 ?/) (eq char1 ?<)))
+
+ (let (prior-point spacer)
+ (save-excursion
+ (backward-word)
+ (backward-char)
+ (backward-char)
+ (setq prior-point (point))
+ (skip-chars-backward "\t ")
+ (setq spacer (buffer-substring (point) prior-point))
+ ;;(message (format "pt(%d) prior(%d) spacer(%s)" (point) prior-point spacer))
+ )
+
+ (if (string-equal word-back "remarks")
+ (setq spacer (concat spacer " ")))
+
+ (setq text-to-insert (format "/%s<para>\n///%s \n///%s</para>"
+ spacer spacer spacer)
+ flavor 6)))
+
+ ;; The preceding word opened a para element. In this case, if
+ ;; the forward word does not close the para element, then
+ ;; close the para element.
+ ;; --
+ ;; This is a nice idea but flawed. Suppose I have a para element with some
+ ;; text in it. If I position the cursor at the first line, then type 3 slashes,
+ ;; I get a close-element, and that would be inappropriate. Not sure I can
+ ;; easily solve that problem, so the best thing might be to simply punt, and
+ ;; require people to close their own elements.
+ ;;
+ ;; ( (and (string-equal word-back "para") (eq char0 60) (or (eq char1 32) (eq char1 9)))
+ ;; (if (not (and (string-equal word-fore "para") (eq char-0 47) (eq char-1 60) ))
+ ;; (setq text-to-insert "/ \n/// </para>\n///"
+ ;; fn-to-call (lambda ()
+ ;; (previous-line)
+ ;; (end-of-line)
+ ;; )
+ ;; flavor 7) )
+ ;; )
+
+ ;; the default case - do nothing
+ (t nil))
+
+ (if text-to-insert
+ (progn
+ ;;(message (format "inserting special text (f(%d))" flavor))
+
+ ;; set the flag, that we actually inserted text
+ (setq did-auto-insert t)
+
+ ;; save point of beginning of insertion
+ (setq cur-point (point))
+
+ ;; actually insert the text
+ (insert text-to-insert)
+
+ ;; indent the inserted string, and re-position point, either through
+ ;; the case-specific fn, or via the default progn.
+ (if fn-to-call
+ (funcall fn-to-call)
+
+ (let ((newline-count 0) (pos 0) ix)
+
+ ;; count the number of newlines in the inserted string
+ (while (string-match "\n" text-to-insert pos)
+ (setq pos (match-end 0)
+ newline-count (+ newline-count 1) )
+ )
+
+ ;; indent what we just inserted
+ (c-indent-region cur-point (point) t)
+
+ ;; move up n/2 lines. This assumes that the
+ ;; inserted text is ~symmetric about the halfway point.
+ ;; The assumption holds if the xml code doc uses a
+ ;; begin-elt and end-elt on a new line all by themselves,
+ ;; and a blank line in between them where the point should be.
+ ;; A more intelligent implementation would use a specific
+ ;; marker string, like @@DOT, to note the desired point.
+ (previous-line (/ newline-count 2))
+ (end-of-line)))))))))
+
+ (if (not did-auto-insert)
+ (self-insert-command (prefix-numeric-value arg)))))
+
+;; ==================================================================
+;; end of c# code-doc insertion magic
+;; ==================================================================
+
+
+
+
+;; ==================================================================
+;; c# fontification extensions
+;; ==================================================================
+;; Commentary:
+;;
+;; The purpose of the following code is to fix font-lock for C#,
+;; specifically for the verbatim-literal strings. C# is a cc-mode
+;; language and strings are handled mostly like other c-based
+;; languages. The one exception is the verbatim-literal string, which
+;; uses the syntax @"...".
+;;
+;; `parse-partial-sexp' treats those strings as just regular strings,
+;; with the @ a non-string character. This is fine, except when the
+;; verblit string ends in a slash, in which case, font-lock breaks from
+;; that point onward in the buffer.
+;;
+;; This is an attempt to fix that.
+;;
+;; The idea is to scan the buffer in full for verblit strings, and apply the
+;; appropriate syntax-table text properties for verblit strings. Also setting
+;; `parse-sexp-lookup-properties' to t tells `parse-partial-sexp'
+;; to use the syntax-table text properties set up by the scan as it does
+;; its parse.
+;;
+;; Also need to re-scan after any changes in the buffer, but on a more
+;; limited region.
+;;
+
+
+;; ;; I don't remember what this is supposed to do,
+;; ;; or how I figured out the value.
+;; ;;
+;; (defconst csharp-font-lock-syntactic-keywords
+;; '(("\\(@\\)\\(\"\\)[^\"]*\\(\"\\)\\(\"\\)[^\"]*\\(\"\\)[^\"]"
+;; (1 '(6)) (2 '(7)) (3 '(1)) (4 '(1)) (5 '(7))
+;; ))
+;; "Highlighting of verbatim literal strings. See also the variable
+;; `font-lock-keywords'.")
+
+
+
+;; Allow this:
+;; (csharp-log 3 "csharp: scan...'%s'" state)
+
+(defvar csharp-log-level 0
+ "The current log level for CSharp-specific operations.
+This is used in particular by the verbatim-literal
+string scanning.
+
+Most other csharp functions are not instrumented.
+0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG, 4 = SHUTUP ALREADY. ")
+
+(defun csharp-log (level text &rest args)
+ "Log a message at level LEVEL.
+If LEVEL is higher than `csharp-log-level', the message is
+ignored. Otherwise, it is printed using `message'.
+TEXT is a format control string, and the remaining arguments ARGS
+are the string substitutions (see `format')."
+ (if (<= level csharp-log-level)
+ (let* ((msg (apply 'format text args)))
+ (message "%s" msg)
+ )))
+
+
+
+(defun csharp-max-beginning-of-stmt ()
+ "Return the greater of `c-beginning-of-statement-1' and
+`c-beginning-of-statement' . I don't understand why both of
+these methods are necessary or why they differ. But they do."
+
+ (let (dash
+ nodash
+ (curpos (point)))
+
+ ;; I think this may need a save-excursion...
+ ;; Calling c-beginning-of-statement-1 resets the point!
+
+ (setq dash (progn (c-beginning-of-statement-1) (point)))
+ (csharp-log 3 "C#: max-bostmt dash(%d)" dash)
+ (goto-char curpos)
+
+ (setq nodash (progn (c-beginning-of-statement 1) (point)))
+ (csharp-log 3 "C#: max-bostmt nodash(%d)" nodash)
+ (goto-char curpos)
+
+ (max dash nodash)))
+
+
+(defun csharp-in-literal (&optional lim detect-cpp)
+ "Return the type of literal point is in, if any.
+Basically this works like `c-in-literal' except it doesn't
+use or fill the cache (`c-in-literal-cache').
+
+The return value is `c' if in a C-style comment, `c++' if in a C++
+style comment, `string' if in a string literal, `pound' if DETECT-CPP
+is non-nil and in a preprocessor line, or nil if somewhere else.
+Optional LIM is used as the backward limit of the search. If omitted,
+or nil, `c-beginning-of-syntax' is used.
+
+Note that this function might do hidden buffer changes. See the
+comment at the start of cc-engine.el for more info."
+
+ (let ((rtn
+ (save-excursion
+ (let* ((pos (point))
+ (lim (or lim (progn
+ (c-beginning-of-syntax)
+ (point))))
+ (state (parse-partial-sexp lim pos)))
+ (csharp-log 4 "C#: parse lim(%d) state: %s" lim (prin1-to-string state))
+ (cond
+ ((elt state 3)
+ (csharp-log 4 "C#: in literal string (%d)" pos)
+ 'string)
+ ((elt state 4)
+ (csharp-log 4 "C#: in literal comment (%d)" pos)
+ (if (elt state 7) 'c++ 'c))
+ ((and detect-cpp (c-beginning-of-macro lim)) 'pound)
+ (t nil))))))
+ rtn))
+
+
+(defun csharp-set-vliteral-syntax-table-properties (beg end)
+ "Scan the buffer text between BEG and END, a verbatim literal
+string, setting and clearing syntax-table text properties where
+necessary.
+
+We need to modify the default syntax-table text property in these cases:
+ (backslash) - is not an escape inside a verbatim literal string.
+ (double-quote) - can be a literal quote, when doubled.
+
+BEG is the @ delimiter. END is the 'old' position of the ending quote.
+
+see http://www.sunsite.ualberta.ca/Documentation/Gnu/emacs-lisp-ref-21-2.7/html_node/elisp_592.html
+for the list of syntax table numeric codes.
+
+"
+
+ (csharp-log 3 "C#: set-vlit-syntax-table: beg(%d) end(%d)" beg end)
+
+ (if (and (> beg 0) (> end 0))
+
+ (let ((curpos beg)
+ (state 0))
+
+ (c-clear-char-properties beg end 'syntax-table)
+
+ (while (<= curpos end)
+
+ (cond
+ ((= state 0)
+ (if (= (char-after curpos) ?@)
+ (progn
+ (c-put-char-property curpos 'syntax-table '(3)) ; (6) = expression prefix, (3) = symbol
+ ;;(message (format "C#: set-s-t: prefix pos(%d) chr(%c)" beg (char-after beg)))
+ )
+ )
+ (setq state (+ 1 state)))
+
+ ((= state 1)
+ (if (= (char-after curpos) ?\")
+ (progn
+ (c-put-char-property curpos 'syntax-table '(7)) ; (7) = string quote
+ ;;(message (format "C#: set-s-t: open quote pos(%d) chr(%c)"
+ ;; curpos (char-after curpos)))
+ ))
+ (setq state (+ 1 state)))
+
+ ((= state 2)
+ (cond
+ ;; handle backslash
+ ((= (char-after curpos) ?\\)
+ (c-put-char-property curpos 'syntax-table '(2)) ; (1) = punctuation, (2) = word
+ ;;(message (format "C#: set-s-t: backslash word pos(%d) chr(%c)" curpos (char-after curpos)))
+ )
+
+ ;; doubled double-quote
+ ((and
+ (= (char-after curpos) ?\")
+ (= (char-after (+ 1 curpos)) ?\"))
+ (c-put-char-property curpos 'syntax-table '(2)) ; (1) = punctuation, (2) = word
+ (c-put-char-property (+ 1 curpos) 'syntax-table '(2)) ; (1) = punctuation
+ ;;(message (format "C#: set-s-t: double doublequote pos(%d) chr(%c)" curpos (char-after curpos)))
+ (setq curpos (+ curpos 1))
+ )
+
+ ;; a single double-quote, which should be a string terminator
+ ((= (char-after curpos) ?\")
+ (c-put-char-property curpos 'syntax-table '(7)) ; (7) = string quote
+ ;;(message (format "C#: set-s-t: close quote pos(%d) chr(%c)" curpos (char-after curpos)))
+ ;;go no further
+ (setq state (+ 1 state)))
+
+ ;; everything else
+ (t
+ ;;(message (format "C#: set-s-t: none pos(%d) chr(%c)" curpos (char-after curpos)))
+ nil))))
+ ;; next char
+ (setq curpos (+ curpos 1))))))
+
+
+
+(defun csharp-end-of-verbatim-literal-string (&optional lim)
+ "Moves to and returns the position of the end quote of the verbatim literal
+string. When calling, point should be on the @ of the verblit string.
+If it is not, then no movement is performed and `point' is returned.
+
+This function ignores text properties. In fact it is the
+underlying scanner used to set the text properties in a C# buffer.
+"
+
+ (csharp-log 3 "C#: end-of-vlit-string: point(%d) c(%c)" (point) (char-after))
+
+ (let (curpos
+ (max (or lim (point-max))))
+
+ (if (not (looking-at "@\""))
+ (point)
+ (forward-char 2) ;; pass up the @ sign and first quote
+ (setq curpos (point))
+
+ ;; Within a verbatim literal string, a doubled double-quote
+ ;; escapes the double-quote."
+ (while (and ;; process characters...
+ (or ;; while...
+ (not (eq (char-after curpos) ?\")) ;; it's not a quote
+ (eq (char-after (+ curpos 1)) ?\")) ;; or, its a double (double) quote
+ (< curpos max)) ;; and we're not done yet
+
+ (cond
+ ((and (eq (char-after curpos) ?\") ;; it's a double-quote.
+ (eq (char-after (+ curpos 1)) ?\"))
+ (setq curpos (+ 2 curpos))) ;; Skip 2
+ (t ;; anything else
+ (setq curpos (+ 1 curpos))))) ;; skip fwd 1
+ curpos)))
+
+
+
+
+(defun csharp-scan-for-verbatim-literals-and-set-props (&optional beg end)
+
+"Scans the buffer, between BEG and END, for verbatim literal
+strings, and sets override text properties on each string to
+allow proper syntax highlighting, indenting, and cursor movement.
+
+BEG and END define the limits of the scan. When nil, they
+default to `point-min' and `point-max' respectively.
+
+Setting text properties generally causes the buffer to be marked
+as modified, but this fn suppresses that via the
+`c-buffer-save-state' macro, for any changes in text properties
+that it makes. This fn also ignores the read-only setting on a
+buffer, using the same macro.
+
+This fn is called when a csharp-mode buffer is loaded, with BEG
+and END set to nil, to do a full scan. It is also called on
+every buffer change, with the BEG and END set to the values for
+the change.
+
+The return value is nil if the buffer was not a csharp-mode
+buffer. Otherwise it is the last cursor position examined by the
+scan.
+"
+
+ (if (not (c-major-mode-is 'csharp-mode)) ;; don't scan if not csharp mode
+ nil
+ (save-excursion
+ (c-save-buffer-state
+ ((curpos (or beg (point-min)))
+ (lastpos (or end (point-max)))
+ (state 0) (start 0) (cycle 0)
+ literal eos limits)
+
+ (csharp-log 3 "C#: scan")
+ (goto-char curpos)
+
+ (while (and (< curpos lastpos) (< cycle 10000))
+ (cond
+
+ ;; Case 1: current char is a @ sign
+ ;; --------------------------------------------
+ ;; Check to see if it demarks the beginning of a verblit
+ ;; string.
+ ((= ?@ (char-after curpos))
+
+ ;; are we in a comment? a string? Maybe the @ is a prefix
+ ;; to allow the use of a reserved word as a symbol. Let's find out.
+
+ ;; not sure why I need both of the following.
+ (syntax-ppss-flush-cache 1)
+ (parse-partial-sexp 1 curpos)
+ (goto-char curpos)
+ (setq literal (csharp-in-literal))
+ (cond
+
+ ;; Case 1.A: it's a @ within a string.
+ ;; --------------------------------------------
+ ;; This should never happen, because this scanner hops over strings.
+ ;; But it might happen if the scan starts at an odd place.
+ ((eq literal 'string) nil)
+
+ ;; Case 1.B: The @ is within a comment. Hop over it.
+ ((and (memq literal '(c c++))
+ ;; This is a kludge for XEmacs where we use
+ ;; `buffer-syntactic-context', which doesn't correctly
+ ;; recognize "\*/" to end a block comment.
+ ;; `parse-partial-sexp' which is used by
+ ;; `c-literal-limits' will however do that in most
+ ;; versions, which results in that we get nil from
+ ;; `c-literal-limits' even when `c-in-literal' claims
+ ;; we're inside a comment.
+ ;;(setq limits (c-literal-limits start)))
+ (setq limits (c-literal-limits)))
+
+ ;; advance to the end of the comment
+ (if limits
+ (progn
+ (csharp-log 4 "C#: scan: jump end comment A (%d)" (cdr limits))
+ (setq curpos (cdr limits)))))
+
+
+ ;; Case 1.B: curpos is at least 2 chars before the last
+ ;; position to examine, and, the following char is a
+ ;; double-quote (ASCII 34).
+ ;; --------------------------------------------
+ ;; This looks like the beginning of a verbatim string
+ ;; literal.
+ ((and (< (+ 2 curpos) lastpos)
+ (= ?\" (char-after (+ 1 curpos))))
+
+ (setq eos (csharp-end-of-verbatim-literal-string))
+ ;; set override syntax properties on the verblit string
+ (csharp-set-vliteral-syntax-table-properties curpos eos)
+
+ (csharp-log 4 "C#: scan: jump end verblit string (%d)" eos)
+ (setq curpos eos))))
+
+
+ ;; Case 2: current char is a double-quote.
+ ;; --------------------------------------------
+ ;; If this is a string, we hop over it, on the assumption that
+ ;; this scanner need not bother with regular literal strings, which
+ ;; get the proper syntax with the generic approach.
+ ;; If in a comment, hop over the comment.
+ ((= ?\" (char-after curpos))
+ (goto-char curpos)
+ (setq literal (c-in-literal))
+ (cond
+
+ ;; Case 2.A: a quote within a string
+ ;; --------------------------------------------
+ ;; This shouldn't happen, because we hop over strings.
+ ;; But it might.
+ ((eq literal 'string) nil)
+
+ ;; Case 2.B: a quote within a comment
+ ;; --------------------------------------------
+ ((and (memq literal '(c c++))
+ ;; This is a kludge for XEmacs where we use
+ ;; `buffer-syntactic-context', which doesn't correctly
+ ;; recognize "\*/" to end a block comment.
+ ;; `parse-partial-sexp' which is used by
+ ;; `c-literal-limits' will however do that in most
+ ;; versions, which results in that we get nil from
+ ;; `c-literal-limits' even when `c-in-literal' claims
+ ;; we're inside a comment.
+ ;;(setq limits (c-literal-limits start)))
+ (setq limits (c-literal-limits)))
+
+ ;; advance to the end of the comment
+ (if limits
+ (progn
+ (setq curpos (cdr limits))
+ (csharp-log 3 "C#: scan: jump end comment B (%s)" curpos))))
+
+
+ ;; Case 2.C: Not in a comment, and not in a string.
+ ;; --------------------------------------------
+ ;; This is the beginning of a literal (but not verbatim) string.
+ (t
+ (forward-char 1) ;; pass up the quote
+ (if (consp (setq limits (c-literal-limits)))
+ (progn
+ (csharp-log 4 "C#: scan: jump end literal (%d)" (cdr limits))
+ (setq curpos (cdr limits))))))))
+
+ (setq cycle (+ 1 cycle))
+ (setq curpos (+ 1 curpos))
+ (c-safe (goto-char curpos)))))))
+
+
+(defun csharp-before-font-lock (beg end old-len)
+ "Adjust`syntax-table' properties on the region affected by the change
+in a csharp-mode buffer.
+
+This function is the C# value for `c-before-font-lock-function'.
+It intended to be called only by the cc-mode runtime.
+
+It prepares the buffer for font locking, hence must get called
+before `font-lock-after-change-function'.
+
+It does hidden buffer changes.
+
+BEG, END and OLD-LEN have the same meaning here as for any
+after-change function.
+
+Point is undefined both before and after this function call.
+The return value is meaningless, and is ignored by cc-mode.
+"
+ (let ((start-scan (progn
+ (c-beginning-of-statement 1)
+ (point))))
+ (csharp-scan-for-verbatim-literals-and-set-props start-scan end)))
+
+
+
+(c-lang-defconst c-before-font-lock-function
+ csharp 'csharp-before-font-lock)
+
+;; ==================================================================
+;; end of c# fontification extensions
+;; ==================================================================
+
+
+
+
+
+;; ==================================================================
+;; C#-specific optimizations of cc-mode funcs
+;; ==================================================================
+
+
+;; There's never a need to check for C-style macro definitions in
+;; a C# buffer.
+(defadvice c-beginning-of-macro (around
+ csharp-mode-advice-1
+ compile activate)
+ (if (c-major-mode-is 'csharp-mode)
+ nil
+ ad-do-it)
+ )
+
+
+;; There's never a need to move over an Obj-C directive in csharp mode
+(defadvice c-forward-objc-directive (around
+ csharp-mode-advice-2
+ compile activate)
+ (if (c-major-mode-is 'csharp-mode)
+ nil
+ ad-do-it)
+ )
+
+;; ==================================================================
+;; end of C#-specific optimizations of cc-mode funcs
+;; ==================================================================
+
+
+
+
+
+
+
+
+;; ==================================================================
+;; c# - monkey-patching of basic parsing logic
+;; ==================================================================
+;;
+;; Here, the model redefines two defuns to add special cases for csharp
+;; mode. These primarily deal with indentation of instance
+;; initializers, which are somewhat unique to C#. I couldn't figure out
+;; how to get cc-mode to do what C# needs, without modifying these
+;; defuns.
+;;
+
+(defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
+ ;; Return non-nil if we're looking at the beginning of a block
+ ;; inside an expression. The value returned is actually a cons of
+ ;; either 'inlambda, 'inexpr-statement or 'inexpr-class and the
+ ;; position of the beginning of the construct.
+ ;;
+ ;; LIM limits the backward search. CONTAINING-SEXP is the start
+ ;; position of the closest containing list. If it's nil, the
+ ;; containing paren isn't used to decide whether we're inside an
+ ;; expression or not. If both LIM and CONTAINING-SEXP are used, LIM
+ ;; needs to be farther back.
+ ;;
+ ;; If CHECK-AT-END is non-nil then extra checks at the end of the
+ ;; brace block might be done. It should only be used when the
+ ;; construct can be assumed to be complete, i.e. when the original
+ ;; starting position was further down than that.
+ ;;
+ ;; This function might do hidden buffer changes.
+
+ (save-excursion
+ (let ((res 'maybe) passed-paren
+ (closest-lim (or containing-sexp lim (point-min)))
+ ;; Look at the character after point only as a last resort
+ ;; when we can't disambiguate.
+ (block-follows (and (eq (char-after) ?{) (point))))
+
+ (while (and (eq res 'maybe)
+ (progn (c-backward-syntactic-ws)
+ (> (point) closest-lim))
+ (not (bobp))
+ (progn (backward-char)
+ (looking-at "[\]\).]\\|\\w\\|\\s_"))
+ (c-safe (forward-char)
+ (goto-char (scan-sexps (point) -1))))
+
+ (setq res
+ (if (looking-at c-keywords-regexp)
+ (let ((kw-sym (c-keyword-sym (match-string 1))))
+ (cond
+ ((and block-follows
+ (c-keyword-member kw-sym 'c-inexpr-class-kwds))
+ (and (not (eq passed-paren ?\[))
+
+ ;; dinoch Thu, 22 Apr 2010 18:20
+ ;; ============================================
+ ;; looking at new MyType() { ... }
+ ;; means this is a brace list, so, return nil,
+ ;; implying NOT looking-at-inexpr-block
+ (not
+ (and (c-major-mode-is 'csharp-mode)
+ (looking-at "new\s+\\([[:alnum:]_]+\\)\\b")))
+
+ (or (not (looking-at c-class-key))
+ ;; If the class instantiation is at the start of
+ ;; a statement, we don't consider it an
+ ;; in-expression class.
+ (let ((prev (point)))
+ (while (and
+ (= (c-backward-token-2 1 nil closest-lim) 0)
+ (eq (char-syntax (char-after)) ?w))
+ (setq prev (point)))
+ (goto-char prev)
+ (not (c-at-statement-start-p)))
+ ;; Also, in Pike we treat it as an
+ ;; in-expression class if it's used in an
+ ;; object clone expression.
+ (save-excursion
+ (and check-at-end
+ (c-major-mode-is 'pike-mode)
+ (progn (goto-char block-follows)
+ (zerop (c-forward-token-2 1 t)))
+ (eq (char-after) ?\())))
+ (cons 'inexpr-class (point))))
+ ((c-keyword-member kw-sym 'c-inexpr-block-kwds)
+ (when (not passed-paren)
+ (cons 'inexpr-statement (point))))
+ ((c-keyword-member kw-sym 'c-lambda-kwds)
+ (when (or (not passed-paren)
+ (eq passed-paren ?\())
+ (cons 'inlambda (point))))
+ ((c-keyword-member kw-sym 'c-block-stmt-kwds)
+ nil)
+ (t
+ 'maybe)))
+
+ (if (looking-at "\\s(")
+ (if passed-paren
+ (if (and (eq passed-paren ?\[)
+ (eq (char-after) ?\[))
+ ;; Accept several square bracket sexps for
+ ;; Java array initializations.
+ 'maybe)
+ (setq passed-paren (char-after))
+ 'maybe)
+ 'maybe))))
+
+ (if (eq res 'maybe)
+ (when (and c-recognize-paren-inexpr-blocks
+ block-follows
+ containing-sexp
+ (eq (char-after containing-sexp) ?\())
+ (goto-char containing-sexp)
+ (if (or (save-excursion
+ (c-backward-syntactic-ws lim)
+ (and (> (point) (or lim (point-min)))
+ (c-on-identifier)))
+ (and c-special-brace-lists
+ (c-looking-at-special-brace-list)))
+ nil
+ (cons 'inexpr-statement (point))))
+
+ res))))
+
+
+
+
+(defconst csharp-enum-decl-re
+ (concat
+ "\\<enum\\>\s+\\([[:alnum:]_]+\\)\s*:\s*"
+ "\\("
+ (c-make-keywords-re nil
+ (list "sbyte" "byte" "short" "ushort" "int" "uint" "long" "ulong"))
+ "\\)")
+ "Regex that captures an enum declaration in C#"
+ )
+
+
+
+(defun c-inside-bracelist-p (containing-sexp paren-state)
+ ;; return the buffer position of the beginning of the brace list
+ ;; statement if we're inside a brace list, otherwise return nil.
+ ;; CONTAINING-SEXP is the buffer pos of the innermost containing
+ ;; paren. PAREN-STATE is the remainder of the state of enclosing
+ ;; braces
+ ;;
+ ;; N.B.: This algorithm can potentially get confused by cpp macros
+ ;; placed in inconvenient locations. It's a trade-off we make for
+ ;; speed.
+ ;;
+ ;; This function might do hidden buffer changes.
+ (or
+ ;; This will pick up brace list declarations.
+ (c-safe
+ (save-excursion
+ (goto-char containing-sexp)
+ (c-forward-sexp -1)
+ (let (bracepos)
+ (if (and (or (looking-at c-brace-list-key)
+
+ (progn (c-forward-sexp -1)
+ (looking-at c-brace-list-key))
+
+ ;; dinoch Thu, 22 Apr 2010 18:20
+ ;; ============================================
+ ;; looking enum Foo : int
+ ;; means this is a brace list, so, return nil,
+ ;; implying NOT looking-at-inexpr-block
+
+ (and (c-major-mode-is 'csharp-mode)
+ (progn
+ (c-forward-sexp -1)
+ (looking-at csharp-enum-decl-re))))
+
+ (setq bracepos (c-down-list-forward (point)))
+ (not (c-crosses-statement-barrier-p (point)
+ (- bracepos 2))))
+ (point)))))
+ ;; this will pick up array/aggregate init lists, even if they are nested.
+ (save-excursion
+ (let ((class-key
+ ;; Pike can have class definitions anywhere, so we must
+ ;; check for the class key here.
+ (and (c-major-mode-is 'pike-mode)
+ c-decl-block-key))
+ bufpos braceassignp lim next-containing)
+ (while (and (not bufpos)
+ containing-sexp)
+ (when paren-state
+ (if (consp (car paren-state))
+ (setq lim (cdr (car paren-state))
+ paren-state (cdr paren-state))
+ (setq lim (car paren-state)))
+ (when paren-state
+ (setq next-containing (car paren-state)
+ paren-state (cdr paren-state))))
+ (goto-char containing-sexp)
+ (if (c-looking-at-inexpr-block next-containing next-containing)
+ ;; We're in an in-expression block of some kind. Do not
+ ;; check nesting. We deliberately set the limit to the
+ ;; containing sexp, so that c-looking-at-inexpr-block
+ ;; doesn't check for an identifier before it.
+ (setq containing-sexp nil)
+ ;; see if the open brace is preceded by = or [...] in
+ ;; this statement, but watch out for operator=
+ (setq braceassignp 'dontknow)
+ (c-backward-token-2 1 t lim)
+ ;; Checks to do only on the first sexp before the brace.
+ (when (and c-opt-inexpr-brace-list-key
+ (eq (char-after) ?\[))
+ ;; In Java, an initialization brace list may follow
+ ;; directly after "new Foo[]", so check for a "new"
+ ;; earlier.
+ (while (eq braceassignp 'dontknow)
+ (setq braceassignp
+ (cond ((/= (c-backward-token-2 1 t lim) 0) nil)
+ ((looking-at c-opt-inexpr-brace-list-key) t)
+ ((looking-at "\\sw\\|\\s_\\|[.[]")
+ ;; Carry on looking if this is an
+ ;; identifier (may contain "." in Java)
+ ;; or another "[]" sexp.
+ 'dontknow)
+ (t nil)))))
+ ;; Checks to do on all sexps before the brace, up to the
+ ;; beginning of the statement.
+ (while (eq braceassignp 'dontknow)
+ (cond ((eq (char-after) ?\;)
+ (setq braceassignp nil))
+ ((and class-key
+ (looking-at class-key))
+ (setq braceassignp nil))
+ ((eq (char-after) ?=)
+ ;; We've seen a =, but must check earlier tokens so
+ ;; that it isn't something that should be ignored.
+ (setq braceassignp 'maybe)
+ (while (and (eq braceassignp 'maybe)
+ (zerop (c-backward-token-2 1 t lim)))
+ (setq braceassignp
+ (cond
+ ;; Check for operator =
+ ((and c-opt-op-identifier-prefix
+ (looking-at c-opt-op-identifier-prefix))
+ nil)
+ ;; Check for `<opchar>= in Pike.
+ ((and (c-major-mode-is 'pike-mode)
+ (or (eq (char-after) ?`)
+ ;; Special case for Pikes
+ ;; `[]=, since '[' is not in
+ ;; the punctuation class.
+ (and (eq (char-after) ?\[)
+ (eq (char-before) ?`))))
+ nil)
+ ((looking-at "\\s.") 'maybe)
+ ;; make sure we're not in a C++ template
+ ;; argument assignment
+ ((and
+ (c-major-mode-is 'c++-mode)
+ (save-excursion
+ (let ((here (point))
+ (pos< (progn
+ (skip-chars-backward "^<>")
+ (point))))
+ (and (eq (char-before) ?<)
+ (not (c-crosses-statement-barrier-p
+ pos< here))
+ (not (c-in-literal))
+ ))))
+ nil)
+ (t t))))))
+ (if (and (eq braceassignp 'dontknow)
+ (/= (c-backward-token-2 1 t lim) 0))
+ (setq braceassignp nil)))
+ (if (not braceassignp)
+ (if (eq (char-after) ?\;)
+ ;; Brace lists can't contain a semicolon, so we're done.
+ (setq containing-sexp nil)
+ ;; Go up one level.
+ (setq containing-sexp next-containing
+ lim nil
+ next-containing nil))
+ ;; we've hit the beginning of the aggregate list
+ (c-beginning-of-statement-1
+ (c-most-enclosing-brace paren-state))
+ (setq bufpos (point))))
+ )
+ bufpos))
+ ))
+
+;; ==================================================================
+;; end of monkey-patching of basic parsing logic
+;; ==================================================================
+
+
+
+
+;;(easy-menu-define csharp-menu csharp-mode-map "C# Mode Commands"
+;; ;; Can use `csharp' as the language for `c-mode-menu'
+;; ;; since its definition covers any language. In
+;; ;; this case the language is used to adapt to the
+;; ;; nonexistence of a cpp pass and thus removing some
+;; ;; irrelevant menu alternatives.
+;; (cons "C#" (c-lang-const c-mode-menu csharp)))
+
+;;; Autoload mode trigger
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.cs$" . csharp-mode))
+
+
+
+(c-add-style "C#"
+ '("Java"
+ (c-basic-offset . 4)
+ (c-comment-only-line-offset . (0 . 0))
+ (c-offsets-alist . (
+ (access-label . -)
+ (arglist-close . c-lineup-arglist)
+ (arglist-cont . 0)
+ (arglist-cont-nonempty . c-lineup-arglist)
+ (arglist-intro . c-lineup-arglist-intro-after-paren)
+ (block-close . 0)
+ (block-open . 0)
+ (brace-entry-open . 0)
+ (brace-list-close . 0)
+ (brace-list-entry . 0)
+ (brace-list-intro . +)
+ (brace-list-open . +)
+ (c . c-lineup-C-comments)
+ (case-label . +)
+ (catch-clause . 0)
+ (class-close . 0)
+ (class-open . 0)
+ (comment-intro . c-lineup-comment)
+ (cpp-macro . 0)
+ (cpp-macro-cont . c-lineup-dont-change)
+ (defun-block-intro . +)
+ (defun-close . 0)
+ (defun-open . 0)
+ (do-while-closure . 0)
+ (else-clause . 0)
+ (extern-lang-close . 0)
+ (extern-lang-open . 0)
+ (friend . 0)
+ (func-decl-cont . +)
+ (inclass . +)
+ (inexpr-class . +)
+ (inexpr-statement . 0)
+ (inextern-lang . +)
+ (inher-cont . c-lineup-multi-inher)
+ (inher-intro . +)
+ (inlambda . c-lineup-inexpr-block)
+ (inline-close . 0)
+ (inline-open . 0)
+ (innamespace . +)
+ (knr-argdecl . 0)
+ (knr-argdecl-intro . 5)
+ (label . 0)
+ (lambda-intro-cont . +)
+ (member-init-cont . c-lineup-multi-inher)
+ (member-init-intro . +)
+ (namespace-close . 0)
+ (namespace-open . 0)
+ (statement . 0)
+ (statement-block-intro . +)
+ (statement-case-intro . +)
+ (statement-case-open . +)
+ (statement-cont . +)
+ (stream-op . c-lineup-streamop)
+ (string . c-lineup-dont-change)
+ (substatement . +)
+ (substatement-open . 0)
+ (template-args-cont c-lineup-template-args +)
+ (topmost-intro . 0)
+ (topmost-intro-cont . 0)
+ ))
+ ))
+
+
+
+
+;; Custom variables
+;;;###autoload
+(defcustom csharp-mode-hook nil
+ "*Hook called by `csharp-mode'."
+ :type 'hook
+ :group 'c)
+
+
+
+;;; The entry point into the mode
+;;;###autoload
+(defun csharp-mode ()
+ "Major mode for editing C# code. This mode is derived from CC Mode to
+support C#.
+
+The hook `c-mode-common-hook' is run with no args at mode
+initialization, then `csharp-mode-hook'.
+
+This mode will automatically add a regexp for Csc.exe error and warning
+messages to the `compilation-error-regexp-alist'.
+
+Key bindings:
+\\{csharp-mode-map}"
+ (interactive)
+ (kill-all-local-variables)
+ (make-local-variable 'beginning-of-defun-function)
+ (make-local-variable 'end-of-defun-function)
+ (c-initialize-cc-mode t)
+ (set-syntax-table csharp-mode-syntax-table)
+
+ ;; define underscore as part of a word in the Csharp syntax table
+ (modify-syntax-entry ?_ "w" csharp-mode-syntax-table)
+
+ ;; define @ as an expression prefix in Csharp syntax table
+ (modify-syntax-entry ?@ "'" csharp-mode-syntax-table)
+
+ (setq major-mode 'csharp-mode
+ mode-name "C#"
+ local-abbrev-table csharp-mode-abbrev-table
+ abbrev-mode t)
+ (use-local-map csharp-mode-map)
+
+ ;; `c-init-language-vars' is a macro that is expanded at compile
+ ;; time to a large `setq' with all the language variables and their
+ ;; customized values for our language.
+ (c-init-language-vars csharp-mode)
+
+
+ ;; `c-common-init' initializes most of the components of a CC Mode
+ ;; buffer, including setup of the mode menu, font-lock, etc.
+ ;; There's also a lower level routine `c-basic-common-init' that
+ ;; only makes the necessary initialization to get the syntactic
+ ;; analysis and similar things working.
+ (c-common-init 'csharp-mode)
+
+
+ ;; csc.exe, the C# Compiler, produces errors like this:
+ ;; file.cs(6,18): error SC1006: Name of constructor must match name of class
+
+ (add-hook 'compilation-mode-hook
+ (lambda ()
+ (setq compilation-error-regexp-alist
+ (cons ' ("^[ \t]*\\([A-Za-z0-9][^(]+\\.cs\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) ?: \\(error\\|warning\\) CS[0-9]+:" 1 2 3)
+ compilation-error-regexp-alist))))
+
+ ;; to allow next-error to work with csc.exe:
+ (setq compilation-scroll-output t)
+
+ ;; allow fill-paragraph to work on xml code doc
+ (set (make-local-variable 'paragraph-separate)
+ "[ \t]*\\(//+\\|\\**\\)\\([ \t]+\\|[ \t]+<.+?>\\)$\\|^\f")
+
+
+ (c-run-mode-hooks 'c-mode-common-hook 'csharp-mode-hook)
+
+
+ ;; Need the following for parse-partial-sexp to work properly with
+ ;; verbatim literal strings Setting this var to non-nil tells
+ ;; `parse-partial-sexp' to pay attention to the syntax text
+ ;; properties on the text in the buffer. If csharp-mode attaches
+ ;; text syntax to @"..." then, `parse-partial-sexp' will treat those
+ ;; strings accordingly.
+ (set (make-local-variable 'parse-sexp-lookup-properties)
+ t)
+
+ ;; scan the entire buffer for verblit strings
+ (csharp-scan-for-verbatim-literals-and-set-props nil nil)
+
+
+ (local-set-key (kbd "/") 'csharp-maybe-insert-codedoc)
+ (local-set-key (kbd "{") 'csharp-insert-open-brace)
+
+ (c-update-modeline))
+
+
+
+(message (concat "Done loading " load-file-name))
+
+
+(provide 'csharp-mode)
+
+;;; csharp-mode.el ends here
+;;MD5: 4EDCB2ECE38841F407C7ED3DA8354E15