1 ;;; csharp-mode.el --- C# mode derived mode
3 ;; Author: Dylan R. E. Moonfire
4 ;; Maintainer: Dylan R. E. Moonfire <contact@mfgames.com>
5 ;; Created: Feburary 2005
6 ;; Modified: February 2010
7 ;; Version: 0.7.4 - Dino Chiesa <dpchiesa@hotmail.com>
8 ;; Keywords: c# languages oop mode
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2 of the License, or
13 ;; (at your option) any later version.
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program; see the file COPYING. If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
27 ;; This is a separate mode to implement the C# constructs and
28 ;; font-locking. It is based on the java-mode example from cc-mode.
30 ;; csharp-mode requires CC Mode 5.30 or later. It works with
31 ;; cc-mode 5.31.3, which is current at this time.
35 ;; - font-lock and indent of C# syntax including:
36 ;; all c# keywords and major syntax
37 ;; attributes that decorate methods, classes, fields, properties
39 ;; #if/#endif #region/#endregion
40 ;; instance initializers
41 ;; anonymous functions and methods
42 ;; verbatim literal strings (those that begin with @)
45 ;; - automagic code-doc generation when you type three slashes.
47 ;; - intelligent inserttion of matched pairs of curly braces.
49 ;; - sets the compiler regex for next-error, for csc.exe output.
56 ;; put this in your .emacs:
58 ;; (autoload 'csharp-mode "csharp-mode" "Major mode for editing C# code." t)
62 ;; (require 'csharp-mode)
67 ;; (setq auto-mode-alist
68 ;; (append '(("\\.cs$" . csharp-mode)) auto-mode-alist))
69 ;; (defun my-csharp-mode-fn ()
70 ;; "function that runs when csharp-mode is initialized for a buffer."
71 ;; ...insert your code here...
72 ;; ...most commonly, your custom key bindings ...
74 ;; (add-hook 'csharp-mode-hook 'my-csharp-mode-fn t)
81 ;; Namespaces in the using statements are not fontified. Should do in
82 ;; c-basic-matchers-before or c-basic-matchers-after.
84 ;; Method names with a preceding attribute are not fontified.
86 ;; Field/Prop names inside object initializers are fontified only
87 ;; if the null constructor is used, with no parens.
89 ;; This code doesn't seem to work when you compile it, then
90 ;; load/require in the emacs file. You will get an error (error
91 ;; "`c-lang-defconst' must be used in a file") which happens because
92 ;; cc-mode doesn't think it is in a buffer while loading directly
93 ;; from the init. However, if you call it based on a file extension,
94 ;; it works properly. Interestingly enough, this doesn't happen if
95 ;; you don't byte-compile cc-mode.
101 ;; Get csharp-mode.el accepted as part of the emacs standard distribution.
102 ;; Must contact monnier at iro.umontreal.ca to make this happen.
108 ;; Thanks to Alan Mackenzie and Stefan Monnier for answering questions
109 ;; and making suggestions.
115 ;; 0.1.0 - Initial release.
116 ;; 0.2.0 - Fixed the identification on the "enum" keyword.
117 ;; - Fixed the font-lock on the "base" keyword
118 ;; 0.3.0 - Added a regex to fontify attributes. It isn't the
119 ;; the best method, but it handles single-like attributes
121 ;; - Got "super" not to fontify as a keyword.
122 ;; - Got extending classes and interfaces to fontify as something.
123 ;; 0.4.0 - Removed the attribute matching because it broke more than
125 ;; - Corrected a bug with namespace not being properly identified
126 ;; and treating the class level as an inner object, which screwed
128 ;; - Added "partial" to the keywords.
129 ;; 0.5.0 - Found bugs with compiled cc-mode and loading from init files.
130 ;; - Updated the eval-when-compile to code to let the mode be
132 ;; 0.6.0 - Added the c-filter-ops patch for 5.31.1 which made that
133 ;; function in cc-langs.el unavailable.
134 ;; - Added a csharp-lineup-region for indention #region and
135 ;; #endregion block differently.
136 ;; 0.7.0 - Added autoload so update-directory-autoloads works
137 ;; (Thank you, Nikolaj Schumacher)
138 ;; - Fontified the entire #region and #endregion lines.
139 ;; - Initial work to get get, set, add, remove font-locked.
140 ;; 0.7.1 - Added option to indent #if/endif with code
141 ;; - Fixed c-opt-cpp-prefix defn (it must not include the BOL
143 ;; - proper fontification and indent of classes that inherit
144 ;; (previously the colon was confusing the parser)
145 ;; - reclassified namespace as a block beginner
146 ;; - removed $ as a legal symbol char - not legal in C#.
147 ;; - added struct to c-class-decl-kwds so indent is correct
149 ;; 0.7.2 - Added automatic codedoc insertion.
150 ;; 0.7.3 - Instance initializers (new Type { ... } ) and
151 ;; (new Type() { ...} ) are now indented properly.
152 ;; - proper fontification and indent of enums as brace-list-*,
153 ;; including special treatment for enums that explicitly
154 ;; inherit from an int type. Previously the colon was
155 ;; confusing the parser.
156 ;; - proper fontification of verbatim literal strings,
157 ;; including those that end in slash. This edge case was not
158 ;; handled at all before; it is now handled correctly.
159 ;; - code cleanup and organization; removed the linefeed.
160 ;; - intelligent curly-brace insertion
161 ;; 0.7.4 - added a C# style
162 ;; - using is now a keyword and gets fontified
163 ;; - fixed a bug that had crept into the codedoc insertion
169 (message (concat "Loading " load-file-name))
172 ;; ==================================================================
174 ;; ==================================================================
176 ;; This is a copy of the function in cc-mode which is used to handle
177 ;; the eval-when-compile which is needed during other times.
178 (defun c-filter-ops (ops opgroup-filter op-filter &optional xlate)
179 ;; See cc-langs.el, a direct copy.
180 (unless (listp (car-safe ops))
181 (setq ops (list ops)))
182 (cond ((eq opgroup-filter t)
183 (setq opgroup-filter (lambda (opgroup) t)))
184 ((not (functionp opgroup-filter))
185 (setq opgroup-filter `(lambda (opgroup)
186 (memq opgroup ',opgroup-filter)))))
187 (cond ((eq op-filter t)
188 (setq op-filter (lambda (op) t)))
190 (setq op-filter `(lambda (op)
191 (string-match ,op-filter op)))))
193 (setq xlate 'identity))
194 (c-with-syntax-table (c-lang-const c-mode-syntax-table)
196 (mapcan (lambda (opgroup)
197 (when (if (symbolp (car opgroup))
198 (when (funcall opgroup-filter (car opgroup))
199 (setq opgroup (cdr opgroup))
203 (when (funcall op-filter op)
204 (let ((res (funcall xlate op)))
205 (if (listp res) res (list res)))))
212 ;; These are only required at compile time to get the sources for the
213 ;; language constants. (The cc-fonts require and the font-lock
214 ;; related constants could additionally be put inside an
215 ;; (eval-after-load "font-lock" ...) but then some trickery is
216 ;; necessary to get them compiled.)
219 (if (and (boundp 'byte-compile-dest-file)
220 (stringp byte-compile-dest-file))
221 (cons (file-name-directory byte-compile-dest-file) load-path)
223 (load "cc-mode" nil t)
224 (load "cc-fonts" nil t)
225 (load "cc-langs" nil t)))
228 ;; Make our mode known to the language constant system. Use Java
229 ;; mode as the fallback for the constants we don't change here.
230 ;; This needs to be done also at compile time since the language
231 ;; constants are evaluated then.
232 (c-add-language 'csharp-mode 'java-mode))
234 ;; ==================================================================
235 ;; end of c# upfront stuff
236 ;; ==================================================================
242 ;; ==================================================================
243 ;; csharp-mode utility and feature defuns
244 ;; ==================================================================
246 ;; Indention: csharp-mode follows normal indention rules except for
247 ;; when indenting the #region and #endregion blocks. This function
248 ;; defines a custom indention to indent the #region blocks properly
251 (defun csharp-lineup-region (langelem)
252 "Indent all #region and #endregion blocks inline with code while
253 retaining normal column-zero indention for #if and the other
256 To use this indenting just put the following in your emacs file:
257 (c-set-offset 'cpp-macro 'csharp-lineup-region)
259 An alternative is to use `csharp-lineup-if-and-region'.
263 (back-to-indentation)
264 (if (re-search-forward "#\\(end\\)?region" (c-point 'eol) [0]) 0 [0])))
268 (defun csharp-lineup-if-and-region (langelem)
270 "Indent all #region/endregion blocks and #if/endif blocks inline
271 with code while retaining normal column-zero indention for any
272 other processing blocks.
274 To use this indenting just put the following in your emacs file:
275 (c-set-offset 'cpp-macro 'csharp-lineup-if-and-region)
277 Another option is to use `csharp-lineup-region'.
281 (back-to-indentation)
282 (if (re-search-forward "#\\(\\(end\\)?\\(if\\|region\\)\\|else\\)" (c-point 'eol) [0]) 0 [0])))
288 (defun csharp-insert-open-brace ()
289 "Intelligently insert a pair of curly braces. This fn is most
290 often bound to the open-curly brace, with
292 (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
294 The default binding for an open curly brace in cc-modes is often
295 `c-electric-brace' or `skeleton-pair-insert-maybe'. The former
296 can be configured to insert newlines around braces in various
297 syntactic positions. The latter inserts a pair of braces and
298 then does not insert a newline, and does not indent.
300 This fn provides another option, with some additional
301 intelligence for csharp-mode. When you type an open curly, the
302 appropriate pair of braces appears, with spacing and indent set
303 in a context-sensitive manner.
305 Within a string literal, you just get a pair of braces, and point
306 is set between them. Following an equals sign, you get a pair of
307 braces, with a semincolon appended. Otherwise, you
308 get the open brace on a new line, with the closing brace on the
311 There may be another way to get this to happen appropriately just within emacs,
312 but I could not figure out how to do it. So I wrote this alternative.
317 (in-string (string= (csharp-in-literal) "string"))
321 (skip-chars-backward " ")
322 (> (- (point) 2) (point-min))
323 (buffer-substring-no-properties (point) (- (point) 3)))))
327 (thing-at-point 'word))))
331 ;; Case 1: inside a string literal?
332 ;; --------------------------------------------
333 ;; If so, then just insert a pair of braces and put the point
334 ;; between them. The most common case is a format string for
335 ;; String.Format() or Console.WriteLine().
337 (self-insert-command 1)
341 ;; Case 2: the open brace starts an array initializer.
342 ;; --------------------------------------------
343 ;; When the last non-space was an equals sign or square brackets,
344 ;; then it's an initializer.
347 (looking-at "\\(\\w+\\b *=\\|[[]]+\\)"))
348 (self-insert-command 1)
352 ;; Case 3: the open brace starts an instance initializer
353 ;; --------------------------------------------
354 ;; If one-word-back was "new", then it's an object initializer.
355 ((string= one-word-back "new")
357 (message "object initializer")
358 (setq tpoint (point)) ;; prepare to indent-region later
360 (self-insert-command 1)
364 (c-indent-region tpoint (point))
366 (indent-according-to-mode)
368 (setq tpoint (point)))
371 ;; Case 4: a lambda initialier.
372 ;; --------------------------------------------
373 ;; If the open curly follows =>, then it's a lambda initializer.
374 ((string= (substring preceding3 -2) "=>")
375 (message "lambda init")
376 (self-insert-command 1)
380 ;; else, it's a new scope. (if, while, class, etc)
383 (message "new scope")
384 (set-mark (point)) ;; prepare to indent-region later
385 ;; check if the prior sexp is on the same line
387 (let ((curline (line-number-at-pos))
390 (line-number-at-pos))))
391 (= curline aftline)))
392 (newline-and-indent))
393 (self-insert-command 1)
394 (c-indent-line-or-region)
398 ;;(c-indent-command) ;; not sure of the difference here
399 (c-indent-line-or-region)
403 ;; point ends up on an empty line, within the braces, properly indented
404 (setq tpoint (point)))
406 (goto-char tpoint)))))
411 ;; ==================================================================
412 ;; end of csharp-mode utility and feature defuns
413 ;; ==================================================================
420 ;; ==================================================================
421 ;; c# values for "language constants" defined in cc-langs.el
422 ;; ==================================================================
425 ;; Java uses a series of regexes to change the font-lock for class
426 ;; references. The problem comes in because Java uses Pascal (leading
427 ;; space in names, SomeClass) for class and package names, but
428 ;; Camel-casing (initial lowercase, upper case in words,
429 ;; i.e. someVariable) for variables. The notation suggested by EMCA for C# is
430 ;; to use Pascal notation for everything, except inner variables. So,
431 ;; the Java regex and formatting produces very wrong results in C#.
432 ;;(error (byte-compile-dest-file))
433 ;;(error (c-get-current-file))
434 (c-lang-defconst c-opt-after-id-concat-key
435 csharp (if (c-lang-const c-opt-identifier-concat-key)
436 (c-lang-const c-symbol-start)))
438 (c-lang-defconst c-basic-matchers-before
440 ;;;; Font-lock the attributes by searching for the
441 ;;;; appropriate regex and marking it as TODO.
442 ;;,`(,(concat "\\(" csharp-attribute-regex "\\)")
443 ;; 0 font-lock-function-name-face)
445 ;; Put a warning face on the opener of unclosed strings that
446 ;; can't span lines. Later font
447 ;; lock packages have a `font-lock-syntactic-face-function' for
448 ;; this, but it doesn't give the control we want since any
449 ;; fontification done inside the function will be
450 ;; unconditionally overridden.
451 ,(c-make-font-lock-search-function
452 ;; Match a char before the string starter to make
453 ;; `c-skip-comments-and-strings' work correctly.
454 (concat ".\\(" c-string-limit-regexp "\\)")
455 '((c-font-lock-invalid-string)))
457 ;; Fontify keyword constants.
458 ,@(when (c-lang-const c-constant-kwds)
459 (let ((re (c-make-keywords-re nil
460 (c-lang-const c-constant-kwds))))
461 `((eval . (list ,(concat "\\<\\(" re "\\)\\>")
462 1 c-constant-face-name)))))
464 ;; Fontify all keywords except the primitive types.
465 ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
466 1 font-lock-keyword-face)
468 ;; Fontify leading identifiers in fully qualified names like
470 ,@(when (c-lang-const c-opt-identifier-concat-key)
473 (while (re-search-forward
474 ,(concat "\\(\\<" ; 1
475 "\\(" (c-lang-const c-symbol-key)
479 c-opt-identifier-concat-key)
484 c-opt-after-id-concat-key)
488 (goto-char (match-beginning 0))
489 (c-skip-comments-and-strings limit))
490 (or (get-text-property (match-beginning 2) 'face)
491 (c-put-font-lock-face (match-beginning 2)
493 c-reference-face-name))
494 (goto-char (match-end 1)))))))))
499 ;; C# does not allow a leading qualifier operator. It also doesn't
500 ;; allow the ".*" construct of Java. So, we redo this regex without
501 ;; the "\\|\\*" regex.
502 (c-lang-defconst c-identifier-key
503 csharp (concat "\\(" (c-lang-const c-symbol-key) "\\)" ; 1
506 (c-lang-const c-opt-identifier-concat-key)
509 "\\(" (c-lang-const c-symbol-key) "\\)"
513 ;; C# has a few rules that are slightly different than Java for
514 ;; operators. This also removed the Java's "super" and replaces it
515 ;; with the C#'s "base".
516 (c-lang-defconst c-operators
517 csharp `((prefix "base")))
520 ;; C# uses CPP-like prefixes to mark #define, #region/endregion,
521 ;; #if/else/endif, and #pragma. This regexp matches the prefix,
522 ;; not including the beginning-of-line (BOL), and not including
523 ;; the term after the prefix (define, pragma, etc). This regexp says
524 ;; whitespace, followed by the prefix, followed by maybe more whitespace.
526 (c-lang-defconst c-opt-cpp-prefix
527 csharp "\\s *#\\s *")
530 ;; there are no message directives in C#
531 (c-lang-defconst c-cpp-message-directives
534 (c-lang-defconst c-cpp-expr-directives
537 (c-lang-defconst c-opt-cpp-macro-define
540 ;; $ is not a legal char in an identifier in C#. So we need to
541 ;; create a csharp-specific definition of this constant.
542 (c-lang-defconst c-symbol-chars
543 csharp (concat c-alnum "_"))
546 (c-lang-defconst c-colon-type-list-kwds
549 (c-lang-defconst c-block-prefix-disallowed-chars
551 ;; Allow ':' for inherit list starters.
552 csharp (set-difference (c-lang-const c-block-prefix-disallowed-chars)
556 (c-lang-defconst c-assignment-operators
557 csharp '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|="))
559 (c-lang-defconst c-primitive-type-kwds
561 csharp '("object" "string" "sbyte" "short" "int" "long" "byte"
562 "ushort" "uint" "ulong" "float" "double" "bool" "char"
565 ;; The keywords that define that the following is a type, such as a
567 (c-lang-defconst c-type-prefix-kwds
569 csharp '("class" "interface" "struct")) ;; no enum here.
570 ;; we want enum to be a brace list.
573 ;; Type modifier keywords. They appear anywhere in types, but modify
574 ;; instead of create one.
575 (c-lang-defconst c-type-modifier-kwds
577 csharp '("readonly" "const"))
580 ;; Tue, 20 Apr 2010 16:02
581 ;; need to vverify that this works for lambdas...
582 (c-lang-defconst c-special-brace-lists
583 csharp '((?{ . ?}) ))
588 ;; Thu, 22 Apr 2010 18:54
590 ;; No idea why this isn't getting set properly in the first place.
591 ;; In cc-langs.el, it is set to the union of a bunch of things, none
592 ;; of which include "new", or "enum".
594 ;; But somehow both of those show up in the resulting derived regexp.
595 ;; This breaks indentation of instance initializers, such as
597 ;; var x = new Foo { ... };
599 ;; Based on my inspection, the existing c-lang-defconst should work!
600 ;; I don't know how to fix this c-lang-defconst, so I am re-setting this
601 ;; variable here, to provide the regex explicitly.
603 (c-lang-defconst c-decl-block-key
605 csharp '"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)"
610 ;; Thu, 22 Apr 2010 14:29
611 ;; I want this to handle var x = new Foo[] { ... };
612 ;; not sure if necessary.
613 (c-lang-defconst c-inexpr-brace-list-kwds
617 ;; ;;(c-lang-defconst c-inexpr-class-kwds
618 ;; ;; csharp '("new"))
622 (c-lang-defconst c-class-decl-kwds
624 csharp '("class" "interface" "struct" )) ;; no "enum"!!
627 ;; The various modifiers used for class and method descriptions.
628 (c-lang-defconst c-modifier-kwds
629 csharp '("public" "partial" "private" "const" "abstract"
630 "protected" "ref" "out" "static" "virtual"
631 "override" "params" "internal"))
634 ;; Thu, 22 Apr 2010 23:02
635 ;; Based on inspection of the cc-mode code, the c-protection-kwds
636 ;; c-lang-const is used only for objective-c. So the value is
637 ;; irrelevant for csharp.
638 (c-lang-defconst c-protection-kwds
640 ;; csharp '("private" "protected" "public" "internal")
644 ;; Define the keywords that can have something following after them.
645 (c-lang-defconst c-type-list-kwds
646 csharp '("struct" "class" "interface" "is" "as"
647 "delegate" "event" "set" "get" "add" "remove"))
650 ;; This allows the classes after the : in the class declartion to be
652 (c-lang-defconst c-typeless-decl-kwds
655 ;; Sets up the enum to handle the list properly, and also the new
656 ;; keyword to handle object initializers. This requires a modified
657 ;; c-basic-matchers-after (see above) in order to correctly fontify C#
658 ;; 3.0 object initializers.
659 (c-lang-defconst c-brace-list-decl-kwds
660 csharp '("enum" "new"))
663 ;; Statement keywords followed directly by a substatement.
664 ;; catch is not one of them.
665 (c-lang-defconst c-block-stmt-1-kwds
666 csharp '("do" "try" "finally"))
669 ;; Statement keywords followed by a paren sexp and then by a substatement.
670 (c-lang-defconst c-block-stmt-2-kwds
671 csharp '("for" "if" "switch" "while" "catch" "foreach" "using"
672 "checked" "unchecked" "lock"))
675 ;; Statements that break out of braces
676 (c-lang-defconst c-simple-stmt-kwds
677 csharp '("return" "continue" "break" "throw" "goto" ))
679 ;; Statements that allow a label
681 (c-lang-defconst c-before-label-kwds
685 (c-lang-defconst c-constant-kwds
686 csharp '("true" "false" "null"))
688 ;; Keywords that start "primary expressions."
689 (c-lang-defconst c-primary-expr-kwds
690 csharp '("this" "base"))
692 ;; Treat namespace as an outer block so class indenting
694 (c-lang-defconst c-other-block-decl-kwds
695 csharp '("namespace"))
697 (c-lang-defconst c-other-kwds
698 csharp '("in" "sizeof" "typeof" "is" "as" "yield"
699 "where" "select" "from"))
701 (c-lang-defconst c-overloadable-operators
703 csharp '("+" "-" "*" "/" "%" "&" "|" "^"
704 "<<" ">>" "==" "!=" ">" "<" ">=" "<="))
707 ;; This c-cpp-matchers stuff is used for fontification.
711 ;; There's no preprocessor in C#, but there are still compiler
712 ;; directives to fontify: "#pragma", #region/endregion, #define, #undef,
713 ;; #if/else/endif. (The definitions for the extra keywords above are
714 ;; enough to incorporate them into the fontification regexps for types
715 ;; and keywords, so no additional font-lock patterns are required for
718 (c-lang-defconst c-cpp-matchers
720 ;; Use the eval form for `font-lock-keywords' to be able to use
721 ;; the `c-preprocessor-face-name' variable that maps to a
722 ;; suitable face depending on the (X)Emacs version.
723 '(eval . (list "^\\s *\\(#pragma\\|undef\\|define\\)\\>\\(.*\\)"
724 (list 1 c-preprocessor-face-name)
725 '(2 font-lock-string-face)))
726 ;; There are some other things in `c-cpp-matchers' besides the
727 ;; preprocessor support, so include it.
728 (c-lang-const c-cpp-matchers)))
730 (defcustom csharp-font-lock-extra-types nil
731 "*List of extra types (aside from the type keywords) to recognize in C# mode.
732 Each list item should be a regexp matching a single identifier."
733 :type 'list :group 'csharp)
735 (defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
736 "Minimal highlighting for C# mode.")
738 (defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
739 "Fast normal highlighting for C# mode.")
741 (defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
742 "Accurate normal highlighting for C# mode.")
744 (defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
745 "Default expressions to highlight in C# mode.")
747 (defvar csharp-mode-syntax-table nil
748 "Syntax table used in csharp-mode buffers.")
749 (or csharp-mode-syntax-table
750 (setq csharp-mode-syntax-table
751 (funcall (c-lang-const c-make-mode-syntax-table csharp))))
753 (defvar csharp-mode-abbrev-table nil
754 "Abbreviation table used in csharp-mode buffers.")
755 (c-define-abbrev-table 'csharp-mode-abbrev-table
756 ;; Keywords that if they occur first on a line might alter the
757 ;; syntactic context, and which therefore should trig reindentation
758 ;; when they are completed.
759 '(("else" "else" c-electric-continued-statement 0)
760 ("while" "while" c-electric-continued-statement 0)
761 ("catch" "catch" c-electric-continued-statement 0)
762 ("finally" "finally" c-electric-continued-statement 0)))
764 (defvar csharp-mode-map (let ((map (c-make-inherited-keymap)))
765 ;; Add bindings which are only useful for C#
767 "Keymap used in csharp-mode buffers.")
771 ;; Defines our constant for finding attributes.
772 ;;(defconst csharp-attribute-regex "\\[\\([XmlType]+\\)(")
773 ;;(defconst csharp-attribute-regex "\\[\\(.\\)")
774 ;; This doesn't work because the string regex happens before this point
775 ;; and getting the font-locking to work before and after is fairly difficult
776 ;;(defconst csharp-attribute-regex
778 ;; "\\[[a-zA-Z][ \ta-zA-Z0-9.]+"
783 ;; ==================================================================
784 ;; end of c# values for "language constants" defined in cc-langs.el
785 ;; ==================================================================
790 ;; ==================================================================
791 ;; C# code-doc insertion magic
792 ;; ==================================================================
794 ;; In Visual Studio, if you type three slashes, it immediately expands into
795 ;; an inline code-documentation fragment. The following method does the
798 ;; This is the kind of thing that could be handled by YASnippet or
799 ;; another similarly flexible snippet framework. But I don't want to
800 ;; introduce a dependency on yasnippet to csharp-mode. So the capability
801 ;; must live within csharp-mode itself.
803 (defun csharp-maybe-insert-codedoc (arg)
805 "Insert an xml code documentation template as appropriate, when
806 typing slashes. This fn gets bound to / (the slash key), in
807 csharp-mode. If the slash being inserted is not the third
808 consecutive slash, the slash is inserted as normal. If it is the
809 third consecutive slash, then a xml code documentation template
810 may be inserted in some cases. For example,
812 a <summary> template is inserted if the prior line is empty,
813 or contains only an open curly brace;
814 a <remarks> template is inserted if the prior word
815 closes the <summary> element;
816 a <returns> template is inserted if the prior word
817 closes the <remarks> element;
818 an <example> template is inserted if the prior word closes
819 the <returns> element;
820 a <para> template is inserted if the prior word closes
823 In all other cases the slash is inserted as normal.
825 If you want the default cc-mode behavior, which implies no automatic
826 insertion of xml code documentation templates, then use this in
827 your `csharp-mode-hook' function:
829 (local-set-key (kbd \"/\") 'c-electric-slash)
833 ;;(message "csharp-maybe-insert-codedoc")
836 (char last-command-char)
837 (cb0 (char-before (- (point) 0)))
838 (cb1 (char-before (- (point) 1)))
839 is-first-non-whitespace
843 ;; check if two prior chars were slash
851 ;;(message "yes - this is the third consecutive slash")
852 (setq is-first-non-whitespace
854 (back-to-indentation)
855 (= cur-point (+ (point) 2))))
857 (if is-first-non-whitespace
858 ;; This is a 3-slash sequence. It is the first non-whitespace text
859 ;; on the line. Now we need to examine the surrounding context
860 ;; in order to determine which xml cod doc template to insert.
861 (let (word-back char0 char1
862 word-fore char-0 char-1
863 text-to-insert ;; text to insert in lieu of slash
864 fn-to-call ;; func to call after inserting text
865 (preceding-line-is-empty (or
866 (= (line-number-at-pos) 1)
870 (looking-at "[ \t]*$\\|[ \t]*{[ \t]*$"))))
871 (flavor 0) ;; used only for diagnostic purposes
874 ;;(message "starting a 3-slash comment")
875 ;; get the prior word, and the 2 chars preceding it.
878 (setq word-back (thing-at-point 'word)
879 char0 (char-before (- (point) 0))
880 char1 (char-before (- (point) 1)))
882 ;; restore prior position
883 (goto-char cur-point)
885 ;; get the following word, and the 2 chars preceding it.
888 (setq word-fore (thing-at-point 'word)
889 char-0 (char-before (- (point) 0))
890 char-1 (char-before (- (point) 1)))
892 ;; restore prior position again
893 (goto-char cur-point)
896 ;; The preceding line is empty, or all whitespace, or
897 ;; contains only an open-curly. In this case, insert a
898 ;; summary element pair.
899 (preceding-line-is-empty
900 (setq text-to-insert "/ <summary>\n/// \n/// </summary>"
903 ;; The preceding word closed a summary element. In this case,
904 ;; if the forward word does not open a remarks element, then
905 ;; insert a remarks element.
906 ((and (string-equal word-back "summary") (eq char0 ?/) (eq char1 ?<))
907 (if (not (and (string-equal word-fore "remarks") (eq char-0 ?<)))
908 (setq text-to-insert "/ <remarks>\n/// <para>\n/// \n/// </para>\n/// </remarks>"
911 ;; The preceding word closed the remarks section. In this case,
912 ;; insert an example element.
913 ((and (string-equal word-back "remarks") (eq char0 ?/) (eq char1 ?<))
914 (setq text-to-insert "/ <example>\n/// \n/// </example>"
917 ;; The preceding word closed the example section. In this
918 ;; case, insert an returns element. This isn't always
919 ;; correct, because sometimes the xml code doc is attached to
920 ;; a class or a property, neither of which has a return
921 ;; value. A more intelligent implementation would inspect the
922 ;; syntax state and only inject a returns element if
924 ((and (string-equal word-back "example") (eq char0 ?/) (eq char1 ?<))
925 (setq text-to-insert "/ <returns></returns>"
926 fn-to-call (lambda ()
930 (c-indent-line-or-region)
934 ;; The preceding word opened the remarks section, or it
935 ;; closed a para section. In this case, insert a para
936 ;; element, using appropriate indentation with respect to the
939 (and (string-equal word-back "remarks") (eq char0 ?<) (or (eq char1 32) (eq char1 9)))
940 (and (string-equal word-back "para") (eq char0 ?/) (eq char1 ?<)))
942 (let (prior-point spacer)
947 (setq prior-point (point))
948 (skip-chars-backward "\t ")
949 (setq spacer (buffer-substring (point) prior-point))
950 ;;(message (format "pt(%d) prior(%d) spacer(%s)" (point) prior-point spacer))
953 (if (string-equal word-back "remarks")
954 (setq spacer (concat spacer " ")))
956 (setq text-to-insert (format "/%s<para>\n///%s \n///%s</para>"
957 spacer spacer spacer)
960 ;; The preceding word opened a para element. In this case, if
961 ;; the forward word does not close the para element, then
962 ;; close the para element.
964 ;; This is a nice idea but flawed. Suppose I have a para element with some
965 ;; text in it. If I position the cursor at the first line, then type 3 slashes,
966 ;; I get a close-element, and that would be inappropriate. Not sure I can
967 ;; easily solve that problem, so the best thing might be to simply punt, and
968 ;; require people to close their own elements.
970 ;; ( (and (string-equal word-back "para") (eq char0 60) (or (eq char1 32) (eq char1 9)))
971 ;; (if (not (and (string-equal word-fore "para") (eq char-0 47) (eq char-1 60) ))
972 ;; (setq text-to-insert "/ \n/// </para>\n///"
973 ;; fn-to-call (lambda ()
980 ;; the default case - do nothing
985 ;;(message (format "inserting special text (f(%d))" flavor))
987 ;; set the flag, that we actually inserted text
988 (setq did-auto-insert t)
990 ;; save point of beginning of insertion
991 (setq cur-point (point))
993 ;; actually insert the text
994 (insert text-to-insert)
996 ;; indent the inserted string, and re-position point, either through
997 ;; the case-specific fn, or via the default progn.
1001 (let ((newline-count 0) (pos 0) ix)
1003 ;; count the number of newlines in the inserted string
1004 (while (string-match "\n" text-to-insert pos)
1005 (setq pos (match-end 0)
1006 newline-count (+ newline-count 1) )
1009 ;; indent what we just inserted
1010 (c-indent-region cur-point (point) t)
1012 ;; move up n/2 lines. This assumes that the
1013 ;; inserted text is ~symmetric about the halfway point.
1014 ;; The assumption holds if the xml code doc uses a
1015 ;; begin-elt and end-elt on a new line all by themselves,
1016 ;; and a blank line in between them where the point should be.
1017 ;; A more intelligent implementation would use a specific
1018 ;; marker string, like @@DOT, to note the desired point.
1019 (previous-line (/ newline-count 2))
1020 (end-of-line)))))))))
1022 (if (not did-auto-insert)
1023 (self-insert-command (prefix-numeric-value arg)))))
1025 ;; ==================================================================
1026 ;; end of c# code-doc insertion magic
1027 ;; ==================================================================
1032 ;; ==================================================================
1033 ;; c# fontification extensions
1034 ;; ==================================================================
1037 ;; The purpose of the following code is to fix font-lock for C#,
1038 ;; specifically for the verbatim-literal strings. C# is a cc-mode
1039 ;; language and strings are handled mostly like other c-based
1040 ;; languages. The one exception is the verbatim-literal string, which
1041 ;; uses the syntax @"...".
1043 ;; `parse-partial-sexp' treats those strings as just regular strings,
1044 ;; with the @ a non-string character. This is fine, except when the
1045 ;; verblit string ends in a slash, in which case, font-lock breaks from
1046 ;; that point onward in the buffer.
1048 ;; This is an attempt to fix that.
1050 ;; The idea is to scan the buffer in full for verblit strings, and apply the
1051 ;; appropriate syntax-table text properties for verblit strings. Also setting
1052 ;; `parse-sexp-lookup-properties' to t tells `parse-partial-sexp'
1053 ;; to use the syntax-table text properties set up by the scan as it does
1056 ;; Also need to re-scan after any changes in the buffer, but on a more
1061 ;; ;; I don't remember what this is supposed to do,
1062 ;; ;; or how I figured out the value.
1064 ;; (defconst csharp-font-lock-syntactic-keywords
1065 ;; '(("\\(@\\)\\(\"\\)[^\"]*\\(\"\\)\\(\"\\)[^\"]*\\(\"\\)[^\"]"
1066 ;; (1 '(6)) (2 '(7)) (3 '(1)) (4 '(1)) (5 '(7))
1068 ;; "Highlighting of verbatim literal strings. See also the variable
1069 ;; `font-lock-keywords'.")
1074 ;; (csharp-log 3 "csharp: scan...'%s'" state)
1076 (defvar csharp-log-level 0
1077 "The current log level for CSharp-specific operations.
1078 This is used in particular by the verbatim-literal
1081 Most other csharp functions are not instrumented.
1082 0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG, 4 = SHUTUP ALREADY. ")
1084 (defun csharp-log (level text &rest args)
1085 "Log a message at level LEVEL.
1086 If LEVEL is higher than `csharp-log-level', the message is
1087 ignored. Otherwise, it is printed using `message'.
1088 TEXT is a format control string, and the remaining arguments ARGS
1089 are the string substitutions (see `format')."
1090 (if (<= level csharp-log-level)
1091 (let* ((msg (apply 'format text args)))
1097 (defun csharp-max-beginning-of-stmt ()
1098 "Return the greater of `c-beginning-of-statement-1' and
1099 `c-beginning-of-statement' . I don't understand why both of
1100 these methods are necessary or why they differ. But they do."
1106 ;; I think this may need a save-excursion...
1107 ;; Calling c-beginning-of-statement-1 resets the point!
1109 (setq dash (progn (c-beginning-of-statement-1) (point)))
1110 (csharp-log 3 "C#: max-bostmt dash(%d)" dash)
1113 (setq nodash (progn (c-beginning-of-statement 1) (point)))
1114 (csharp-log 3 "C#: max-bostmt nodash(%d)" nodash)
1120 (defun csharp-in-literal (&optional lim detect-cpp)
1121 "Return the type of literal point is in, if any.
1122 Basically this works like `c-in-literal' except it doesn't
1123 use or fill the cache (`c-in-literal-cache').
1125 The return value is `c' if in a C-style comment, `c++' if in a C++
1126 style comment, `string' if in a string literal, `pound' if DETECT-CPP
1127 is non-nil and in a preprocessor line, or nil if somewhere else.
1128 Optional LIM is used as the backward limit of the search. If omitted,
1129 or nil, `c-beginning-of-syntax' is used.
1131 Note that this function might do hidden buffer changes. See the
1132 comment at the start of cc-engine.el for more info."
1136 (let* ((pos (point))
1138 (c-beginning-of-syntax)
1140 (state (parse-partial-sexp lim pos)))
1141 (csharp-log 4 "C#: parse lim(%d) state: %s" lim (prin1-to-string state))
1144 (csharp-log 4 "C#: in literal string (%d)" pos)
1147 (csharp-log 4 "C#: in literal comment (%d)" pos)
1148 (if (elt state 7) 'c++ 'c))
1149 ((and detect-cpp (c-beginning-of-macro lim)) 'pound)
1154 (defun csharp-set-vliteral-syntax-table-properties (beg end)
1155 "Scan the buffer text between BEG and END, a verbatim literal
1156 string, setting and clearing syntax-table text properties where
1159 We need to modify the default syntax-table text property in these cases:
1160 (backslash) - is not an escape inside a verbatim literal string.
1161 (double-quote) - can be a literal quote, when doubled.
1163 BEG is the @ delimiter. END is the 'old' position of the ending quote.
1165 see http://www.sunsite.ualberta.ca/Documentation/Gnu/emacs-lisp-ref-21-2.7/html_node/elisp_592.html
1166 for the list of syntax table numeric codes.
1170 (csharp-log 3 "C#: set-vlit-syntax-table: beg(%d) end(%d)" beg end)
1172 (if (and (> beg 0) (> end 0))
1177 (c-clear-char-properties beg end 'syntax-table)
1179 (while (<= curpos end)
1183 (if (= (char-after curpos) ?@)
1185 (c-put-char-property curpos 'syntax-table '(3)) ; (6) = expression prefix, (3) = symbol
1186 ;;(message (format "C#: set-s-t: prefix pos(%d) chr(%c)" beg (char-after beg)))
1189 (setq state (+ 1 state)))
1192 (if (= (char-after curpos) ?\")
1194 (c-put-char-property curpos 'syntax-table '(7)) ; (7) = string quote
1195 ;;(message (format "C#: set-s-t: open quote pos(%d) chr(%c)"
1196 ;; curpos (char-after curpos)))
1198 (setq state (+ 1 state)))
1203 ((= (char-after curpos) ?\\)
1204 (c-put-char-property curpos 'syntax-table '(2)) ; (1) = punctuation, (2) = word
1205 ;;(message (format "C#: set-s-t: backslash word pos(%d) chr(%c)" curpos (char-after curpos)))
1208 ;; doubled double-quote
1210 (= (char-after curpos) ?\")
1211 (= (char-after (+ 1 curpos)) ?\"))
1212 (c-put-char-property curpos 'syntax-table '(2)) ; (1) = punctuation, (2) = word
1213 (c-put-char-property (+ 1 curpos) 'syntax-table '(2)) ; (1) = punctuation
1214 ;;(message (format "C#: set-s-t: double doublequote pos(%d) chr(%c)" curpos (char-after curpos)))
1215 (setq curpos (+ curpos 1))
1218 ;; a single double-quote, which should be a string terminator
1219 ((= (char-after curpos) ?\")
1220 (c-put-char-property curpos 'syntax-table '(7)) ; (7) = string quote
1221 ;;(message (format "C#: set-s-t: close quote pos(%d) chr(%c)" curpos (char-after curpos)))
1223 (setq state (+ 1 state)))
1227 ;;(message (format "C#: set-s-t: none pos(%d) chr(%c)" curpos (char-after curpos)))
1230 (setq curpos (+ curpos 1))))))
1234 (defun csharp-end-of-verbatim-literal-string (&optional lim)
1235 "Moves to and returns the position of the end quote of the verbatim literal
1236 string. When calling, point should be on the @ of the verblit string.
1237 If it is not, then no movement is performed and `point' is returned.
1239 This function ignores text properties. In fact it is the
1240 underlying scanner used to set the text properties in a C# buffer.
1243 (csharp-log 3 "C#: end-of-vlit-string: point(%d) c(%c)" (point) (char-after))
1246 (max (or lim (point-max))))
1248 (if (not (looking-at "@\""))
1250 (forward-char 2) ;; pass up the @ sign and first quote
1251 (setq curpos (point))
1253 ;; Within a verbatim literal string, a doubled double-quote
1254 ;; escapes the double-quote."
1255 (while (and ;; process characters...
1257 (not (eq (char-after curpos) ?\")) ;; it's not a quote
1258 (eq (char-after (+ curpos 1)) ?\")) ;; or, its a double (double) quote
1259 (< curpos max)) ;; and we're not done yet
1262 ((and (eq (char-after curpos) ?\") ;; it's a double-quote.
1263 (eq (char-after (+ curpos 1)) ?\"))
1264 (setq curpos (+ 2 curpos))) ;; Skip 2
1266 (setq curpos (+ 1 curpos))))) ;; skip fwd 1
1272 (defun csharp-scan-for-verbatim-literals-and-set-props (&optional beg end)
1274 "Scans the buffer, between BEG and END, for verbatim literal
1275 strings, and sets override text properties on each string to
1276 allow proper syntax highlighting, indenting, and cursor movement.
1278 BEG and END define the limits of the scan. When nil, they
1279 default to `point-min' and `point-max' respectively.
1281 Setting text properties generally causes the buffer to be marked
1282 as modified, but this fn suppresses that via the
1283 `c-buffer-save-state' macro, for any changes in text properties
1284 that it makes. This fn also ignores the read-only setting on a
1285 buffer, using the same macro.
1287 This fn is called when a csharp-mode buffer is loaded, with BEG
1288 and END set to nil, to do a full scan. It is also called on
1289 every buffer change, with the BEG and END set to the values for
1292 The return value is nil if the buffer was not a csharp-mode
1293 buffer. Otherwise it is the last cursor position examined by the
1297 (if (not (c-major-mode-is 'csharp-mode)) ;; don't scan if not csharp mode
1300 (c-save-buffer-state
1301 ((curpos (or beg (point-min)))
1302 (lastpos (or end (point-max)))
1303 (state 0) (start 0) (cycle 0)
1306 (csharp-log 3 "C#: scan")
1309 (while (and (< curpos lastpos) (< cycle 10000))
1312 ;; Case 1: current char is a @ sign
1313 ;; --------------------------------------------
1314 ;; Check to see if it demarks the beginning of a verblit
1316 ((= ?@ (char-after curpos))
1318 ;; are we in a comment? a string? Maybe the @ is a prefix
1319 ;; to allow the use of a reserved word as a symbol. Let's find out.
1321 ;; not sure why I need both of the following.
1322 (syntax-ppss-flush-cache 1)
1323 (parse-partial-sexp 1 curpos)
1325 (setq literal (csharp-in-literal))
1328 ;; Case 1.A: it's a @ within a string.
1329 ;; --------------------------------------------
1330 ;; This should never happen, because this scanner hops over strings.
1331 ;; But it might happen if the scan starts at an odd place.
1332 ((eq literal 'string) nil)
1334 ;; Case 1.B: The @ is within a comment. Hop over it.
1335 ((and (memq literal '(c c++))
1336 ;; This is a kludge for XEmacs where we use
1337 ;; `buffer-syntactic-context', which doesn't correctly
1338 ;; recognize "\*/" to end a block comment.
1339 ;; `parse-partial-sexp' which is used by
1340 ;; `c-literal-limits' will however do that in most
1341 ;; versions, which results in that we get nil from
1342 ;; `c-literal-limits' even when `c-in-literal' claims
1343 ;; we're inside a comment.
1344 ;;(setq limits (c-literal-limits start)))
1345 (setq limits (c-literal-limits)))
1347 ;; advance to the end of the comment
1350 (csharp-log 4 "C#: scan: jump end comment A (%d)" (cdr limits))
1351 (setq curpos (cdr limits)))))
1354 ;; Case 1.B: curpos is at least 2 chars before the last
1355 ;; position to examine, and, the following char is a
1356 ;; double-quote (ASCII 34).
1357 ;; --------------------------------------------
1358 ;; This looks like the beginning of a verbatim string
1360 ((and (< (+ 2 curpos) lastpos)
1361 (= ?\" (char-after (+ 1 curpos))))
1363 (setq eos (csharp-end-of-verbatim-literal-string))
1364 ;; set override syntax properties on the verblit string
1365 (csharp-set-vliteral-syntax-table-properties curpos eos)
1367 (csharp-log 4 "C#: scan: jump end verblit string (%d)" eos)
1368 (setq curpos eos))))
1371 ;; Case 2: current char is a double-quote.
1372 ;; --------------------------------------------
1373 ;; If this is a string, we hop over it, on the assumption that
1374 ;; this scanner need not bother with regular literal strings, which
1375 ;; get the proper syntax with the generic approach.
1376 ;; If in a comment, hop over the comment.
1377 ((= ?\" (char-after curpos))
1379 (setq literal (c-in-literal))
1382 ;; Case 2.A: a quote within a string
1383 ;; --------------------------------------------
1384 ;; This shouldn't happen, because we hop over strings.
1386 ((eq literal 'string) nil)
1388 ;; Case 2.B: a quote within a comment
1389 ;; --------------------------------------------
1390 ((and (memq literal '(c c++))
1391 ;; This is a kludge for XEmacs where we use
1392 ;; `buffer-syntactic-context', which doesn't correctly
1393 ;; recognize "\*/" to end a block comment.
1394 ;; `parse-partial-sexp' which is used by
1395 ;; `c-literal-limits' will however do that in most
1396 ;; versions, which results in that we get nil from
1397 ;; `c-literal-limits' even when `c-in-literal' claims
1398 ;; we're inside a comment.
1399 ;;(setq limits (c-literal-limits start)))
1400 (setq limits (c-literal-limits)))
1402 ;; advance to the end of the comment
1405 (setq curpos (cdr limits))
1406 (csharp-log 3 "C#: scan: jump end comment B (%s)" curpos))))
1409 ;; Case 2.C: Not in a comment, and not in a string.
1410 ;; --------------------------------------------
1411 ;; This is the beginning of a literal (but not verbatim) string.
1413 (forward-char 1) ;; pass up the quote
1414 (if (consp (setq limits (c-literal-limits)))
1416 (csharp-log 4 "C#: scan: jump end literal (%d)" (cdr limits))
1417 (setq curpos (cdr limits))))))))
1419 (setq cycle (+ 1 cycle))
1420 (setq curpos (+ 1 curpos))
1421 (c-safe (goto-char curpos)))))))
1424 (defun csharp-before-font-lock (beg end old-len)
1425 "Adjust`syntax-table' properties on the region affected by the change
1426 in a csharp-mode buffer.
1428 This function is the C# value for `c-before-font-lock-function'.
1429 It intended to be called only by the cc-mode runtime.
1431 It prepares the buffer for font locking, hence must get called
1432 before `font-lock-after-change-function'.
1434 It does hidden buffer changes.
1436 BEG, END and OLD-LEN have the same meaning here as for any
1437 after-change function.
1439 Point is undefined both before and after this function call.
1440 The return value is meaningless, and is ignored by cc-mode.
1442 (let ((start-scan (progn
1443 (c-beginning-of-statement 1)
1445 (csharp-scan-for-verbatim-literals-and-set-props start-scan end)))
1449 (c-lang-defconst c-before-font-lock-function
1450 csharp 'csharp-before-font-lock)
1452 ;; ==================================================================
1453 ;; end of c# fontification extensions
1454 ;; ==================================================================
1460 ;; ==================================================================
1461 ;; C#-specific optimizations of cc-mode funcs
1462 ;; ==================================================================
1465 ;; There's never a need to check for C-style macro definitions in
1467 (defadvice c-beginning-of-macro (around
1468 csharp-mode-advice-1
1470 (if (c-major-mode-is 'csharp-mode)
1476 ;; There's never a need to move over an Obj-C directive in csharp mode
1477 (defadvice c-forward-objc-directive (around
1478 csharp-mode-advice-2
1480 (if (c-major-mode-is 'csharp-mode)
1485 ;; ==================================================================
1486 ;; end of C#-specific optimizations of cc-mode funcs
1487 ;; ==================================================================
1496 ;; ==================================================================
1497 ;; c# - monkey-patching of basic parsing logic
1498 ;; ==================================================================
1500 ;; Here, the model redefines two defuns to add special cases for csharp
1501 ;; mode. These primarily deal with indentation of instance
1502 ;; initializers, which are somewhat unique to C#. I couldn't figure out
1503 ;; how to get cc-mode to do what C# needs, without modifying these
1507 (defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
1508 ;; Return non-nil if we're looking at the beginning of a block
1509 ;; inside an expression. The value returned is actually a cons of
1510 ;; either 'inlambda, 'inexpr-statement or 'inexpr-class and the
1511 ;; position of the beginning of the construct.
1513 ;; LIM limits the backward search. CONTAINING-SEXP is the start
1514 ;; position of the closest containing list. If it's nil, the
1515 ;; containing paren isn't used to decide whether we're inside an
1516 ;; expression or not. If both LIM and CONTAINING-SEXP are used, LIM
1517 ;; needs to be farther back.
1519 ;; If CHECK-AT-END is non-nil then extra checks at the end of the
1520 ;; brace block might be done. It should only be used when the
1521 ;; construct can be assumed to be complete, i.e. when the original
1522 ;; starting position was further down than that.
1524 ;; This function might do hidden buffer changes.
1527 (let ((res 'maybe) passed-paren
1528 (closest-lim (or containing-sexp lim (point-min)))
1529 ;; Look at the character after point only as a last resort
1530 ;; when we can't disambiguate.
1531 (block-follows (and (eq (char-after) ?{) (point))))
1533 (while (and (eq res 'maybe)
1534 (progn (c-backward-syntactic-ws)
1535 (> (point) closest-lim))
1537 (progn (backward-char)
1538 (looking-at "[\]\).]\\|\\w\\|\\s_"))
1539 (c-safe (forward-char)
1540 (goto-char (scan-sexps (point) -1))))
1543 (if (looking-at c-keywords-regexp)
1544 (let ((kw-sym (c-keyword-sym (match-string 1))))
1547 (c-keyword-member kw-sym 'c-inexpr-class-kwds))
1548 (and (not (eq passed-paren ?\[))
1550 ;; dinoch Thu, 22 Apr 2010 18:20
1551 ;; ============================================
1552 ;; looking at new MyType() { ... }
1553 ;; means this is a brace list, so, return nil,
1554 ;; implying NOT looking-at-inexpr-block
1556 (and (c-major-mode-is 'csharp-mode)
1557 (looking-at "new\s+\\([[:alnum:]_]+\\)\\b")))
1559 (or (not (looking-at c-class-key))
1560 ;; If the class instantiation is at the start of
1561 ;; a statement, we don't consider it an
1562 ;; in-expression class.
1563 (let ((prev (point)))
1565 (= (c-backward-token-2 1 nil closest-lim) 0)
1566 (eq (char-syntax (char-after)) ?w))
1567 (setq prev (point)))
1569 (not (c-at-statement-start-p)))
1570 ;; Also, in Pike we treat it as an
1571 ;; in-expression class if it's used in an
1572 ;; object clone expression.
1575 (c-major-mode-is 'pike-mode)
1576 (progn (goto-char block-follows)
1577 (zerop (c-forward-token-2 1 t)))
1578 (eq (char-after) ?\())))
1579 (cons 'inexpr-class (point))))
1580 ((c-keyword-member kw-sym 'c-inexpr-block-kwds)
1581 (when (not passed-paren)
1582 (cons 'inexpr-statement (point))))
1583 ((c-keyword-member kw-sym 'c-lambda-kwds)
1584 (when (or (not passed-paren)
1585 (eq passed-paren ?\())
1586 (cons 'inlambda (point))))
1587 ((c-keyword-member kw-sym 'c-block-stmt-kwds)
1592 (if (looking-at "\\s(")
1594 (if (and (eq passed-paren ?\[)
1595 (eq (char-after) ?\[))
1596 ;; Accept several square bracket sexps for
1597 ;; Java array initializations.
1599 (setq passed-paren (char-after))
1604 (when (and c-recognize-paren-inexpr-blocks
1607 (eq (char-after containing-sexp) ?\())
1608 (goto-char containing-sexp)
1609 (if (or (save-excursion
1610 (c-backward-syntactic-ws lim)
1611 (and (> (point) (or lim (point-min)))
1613 (and c-special-brace-lists
1614 (c-looking-at-special-brace-list)))
1616 (cons 'inexpr-statement (point))))
1623 (defconst csharp-enum-decl-re
1625 "\\<enum\\>\s+\\([[:alnum:]_]+\\)\s*:\s*"
1627 (c-make-keywords-re nil
1628 (list "sbyte" "byte" "short" "ushort" "int" "uint" "long" "ulong"))
1630 "Regex that captures an enum declaration in C#"
1635 (defun c-inside-bracelist-p (containing-sexp paren-state)
1636 ;; return the buffer position of the beginning of the brace list
1637 ;; statement if we're inside a brace list, otherwise return nil.
1638 ;; CONTAINING-SEXP is the buffer pos of the innermost containing
1639 ;; paren. PAREN-STATE is the remainder of the state of enclosing
1642 ;; N.B.: This algorithm can potentially get confused by cpp macros
1643 ;; placed in inconvenient locations. It's a trade-off we make for
1646 ;; This function might do hidden buffer changes.
1648 ;; This will pick up brace list declarations.
1651 (goto-char containing-sexp)
1654 (if (and (or (looking-at c-brace-list-key)
1656 (progn (c-forward-sexp -1)
1657 (looking-at c-brace-list-key))
1659 ;; dinoch Thu, 22 Apr 2010 18:20
1660 ;; ============================================
1661 ;; looking enum Foo : int
1662 ;; means this is a brace list, so, return nil,
1663 ;; implying NOT looking-at-inexpr-block
1665 (and (c-major-mode-is 'csharp-mode)
1668 (looking-at csharp-enum-decl-re))))
1670 (setq bracepos (c-down-list-forward (point)))
1671 (not (c-crosses-statement-barrier-p (point)
1674 ;; this will pick up array/aggregate init lists, even if they are nested.
1677 ;; Pike can have class definitions anywhere, so we must
1678 ;; check for the class key here.
1679 (and (c-major-mode-is 'pike-mode)
1681 bufpos braceassignp lim next-containing)
1682 (while (and (not bufpos)
1685 (if (consp (car paren-state))
1686 (setq lim (cdr (car paren-state))
1687 paren-state (cdr paren-state))
1688 (setq lim (car paren-state)))
1690 (setq next-containing (car paren-state)
1691 paren-state (cdr paren-state))))
1692 (goto-char containing-sexp)
1693 (if (c-looking-at-inexpr-block next-containing next-containing)
1694 ;; We're in an in-expression block of some kind. Do not
1695 ;; check nesting. We deliberately set the limit to the
1696 ;; containing sexp, so that c-looking-at-inexpr-block
1697 ;; doesn't check for an identifier before it.
1698 (setq containing-sexp nil)
1699 ;; see if the open brace is preceded by = or [...] in
1700 ;; this statement, but watch out for operator=
1701 (setq braceassignp 'dontknow)
1702 (c-backward-token-2 1 t lim)
1703 ;; Checks to do only on the first sexp before the brace.
1704 (when (and c-opt-inexpr-brace-list-key
1705 (eq (char-after) ?\[))
1706 ;; In Java, an initialization brace list may follow
1707 ;; directly after "new Foo[]", so check for a "new"
1709 (while (eq braceassignp 'dontknow)
1711 (cond ((/= (c-backward-token-2 1 t lim) 0) nil)
1712 ((looking-at c-opt-inexpr-brace-list-key) t)
1713 ((looking-at "\\sw\\|\\s_\\|[.[]")
1714 ;; Carry on looking if this is an
1715 ;; identifier (may contain "." in Java)
1716 ;; or another "[]" sexp.
1719 ;; Checks to do on all sexps before the brace, up to the
1720 ;; beginning of the statement.
1721 (while (eq braceassignp 'dontknow)
1722 (cond ((eq (char-after) ?\;)
1723 (setq braceassignp nil))
1725 (looking-at class-key))
1726 (setq braceassignp nil))
1727 ((eq (char-after) ?=)
1728 ;; We've seen a =, but must check earlier tokens so
1729 ;; that it isn't something that should be ignored.
1730 (setq braceassignp 'maybe)
1731 (while (and (eq braceassignp 'maybe)
1732 (zerop (c-backward-token-2 1 t lim)))
1735 ;; Check for operator =
1736 ((and c-opt-op-identifier-prefix
1737 (looking-at c-opt-op-identifier-prefix))
1739 ;; Check for `<opchar>= in Pike.
1740 ((and (c-major-mode-is 'pike-mode)
1741 (or (eq (char-after) ?`)
1742 ;; Special case for Pikes
1743 ;; `[]=, since '[' is not in
1744 ;; the punctuation class.
1745 (and (eq (char-after) ?\[)
1746 (eq (char-before) ?`))))
1748 ((looking-at "\\s.") 'maybe)
1749 ;; make sure we're not in a C++ template
1750 ;; argument assignment
1752 (c-major-mode-is 'c++-mode)
1754 (let ((here (point))
1756 (skip-chars-backward "^<>")
1758 (and (eq (char-before) ?<)
1759 (not (c-crosses-statement-barrier-p
1761 (not (c-in-literal))
1765 (if (and (eq braceassignp 'dontknow)
1766 (/= (c-backward-token-2 1 t lim) 0))
1767 (setq braceassignp nil)))
1768 (if (not braceassignp)
1769 (if (eq (char-after) ?\;)
1770 ;; Brace lists can't contain a semicolon, so we're done.
1771 (setq containing-sexp nil)
1773 (setq containing-sexp next-containing
1775 next-containing nil))
1776 ;; we've hit the beginning of the aggregate list
1777 (c-beginning-of-statement-1
1778 (c-most-enclosing-brace paren-state))
1779 (setq bufpos (point))))
1784 ;; ==================================================================
1785 ;; end of monkey-patching of basic parsing logic
1786 ;; ==================================================================
1791 ;;(easy-menu-define csharp-menu csharp-mode-map "C# Mode Commands"
1792 ;; ;; Can use `csharp' as the language for `c-mode-menu'
1793 ;; ;; since its definition covers any language. In
1794 ;; ;; this case the language is used to adapt to the
1795 ;; ;; nonexistence of a cpp pass and thus removing some
1796 ;; ;; irrelevant menu alternatives.
1797 ;; (cons "C#" (c-lang-const c-mode-menu csharp)))
1799 ;;; Autoload mode trigger
1801 (add-to-list 'auto-mode-alist '("\\.cs$" . csharp-mode))
1807 (c-basic-offset . 4)
1808 (c-comment-only-line-offset . (0 . 0))
1809 (c-offsets-alist . (
1811 (arglist-close . c-lineup-arglist)
1813 (arglist-cont-nonempty . c-lineup-arglist)
1814 (arglist-intro . c-lineup-arglist-intro-after-paren)
1817 (brace-entry-open . 0)
1818 (brace-list-close . 0)
1819 (brace-list-entry . 0)
1820 (brace-list-intro . +)
1821 (brace-list-open . +)
1822 (c . c-lineup-C-comments)
1827 (comment-intro . c-lineup-comment)
1829 (cpp-macro-cont . c-lineup-dont-change)
1830 (defun-block-intro . +)
1833 (do-while-closure . 0)
1835 (extern-lang-close . 0)
1836 (extern-lang-open . 0)
1838 (func-decl-cont . +)
1841 (inexpr-statement . 0)
1843 (inher-cont . c-lineup-multi-inher)
1845 (inlambda . c-lineup-inexpr-block)
1850 (knr-argdecl-intro . 5)
1852 (lambda-intro-cont . +)
1853 (member-init-cont . c-lineup-multi-inher)
1854 (member-init-intro . +)
1855 (namespace-close . 0)
1856 (namespace-open . 0)
1858 (statement-block-intro . +)
1859 (statement-case-intro . +)
1860 (statement-case-open . +)
1861 (statement-cont . +)
1862 (stream-op . c-lineup-streamop)
1863 (string . c-lineup-dont-change)
1865 (substatement-open . 0)
1866 (template-args-cont c-lineup-template-args +)
1868 (topmost-intro-cont . 0)
1877 (defcustom csharp-mode-hook nil
1878 "*Hook called by `csharp-mode'."
1884 ;;; The entry point into the mode
1886 (defun csharp-mode ()
1887 "Major mode for editing C# code. This mode is derived from CC Mode to
1890 The hook `c-mode-common-hook' is run with no args at mode
1891 initialization, then `csharp-mode-hook'.
1893 This mode will automatically add a regexp for Csc.exe error and warning
1894 messages to the `compilation-error-regexp-alist'.
1897 \\{csharp-mode-map}"
1899 (kill-all-local-variables)
1900 (make-local-variable 'beginning-of-defun-function)
1901 (make-local-variable 'end-of-defun-function)
1902 (c-initialize-cc-mode t)
1903 (set-syntax-table csharp-mode-syntax-table)
1905 ;; define underscore as part of a word in the Csharp syntax table
1906 (modify-syntax-entry ?_ "w" csharp-mode-syntax-table)
1908 ;; define @ as an expression prefix in Csharp syntax table
1909 (modify-syntax-entry ?@ "'" csharp-mode-syntax-table)
1911 (setq major-mode 'csharp-mode
1913 local-abbrev-table csharp-mode-abbrev-table
1915 (use-local-map csharp-mode-map)
1917 ;; `c-init-language-vars' is a macro that is expanded at compile
1918 ;; time to a large `setq' with all the language variables and their
1919 ;; customized values for our language.
1920 (c-init-language-vars csharp-mode)
1923 ;; `c-common-init' initializes most of the components of a CC Mode
1924 ;; buffer, including setup of the mode menu, font-lock, etc.
1925 ;; There's also a lower level routine `c-basic-common-init' that
1926 ;; only makes the necessary initialization to get the syntactic
1927 ;; analysis and similar things working.
1928 (c-common-init 'csharp-mode)
1931 ;; csc.exe, the C# Compiler, produces errors like this:
1932 ;; file.cs(6,18): error SC1006: Name of constructor must match name of class
1934 (add-hook 'compilation-mode-hook
1936 (setq compilation-error-regexp-alist
1937 (cons ' ("^[ \t]*\\([A-Za-z0-9][^(]+\\.cs\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) ?: \\(error\\|warning\\) CS[0-9]+:" 1 2 3)
1938 compilation-error-regexp-alist))))
1940 ;; to allow next-error to work with csc.exe:
1941 (setq compilation-scroll-output t)
1943 ;; allow fill-paragraph to work on xml code doc
1944 (set (make-local-variable 'paragraph-separate)
1945 "[ \t]*\\(//+\\|\\**\\)\\([ \t]+\\|[ \t]+<.+?>\\)$\\|^\f")
1948 (c-run-mode-hooks 'c-mode-common-hook 'csharp-mode-hook)
1951 ;; Need the following for parse-partial-sexp to work properly with
1952 ;; verbatim literal strings Setting this var to non-nil tells
1953 ;; `parse-partial-sexp' to pay attention to the syntax text
1954 ;; properties on the text in the buffer. If csharp-mode attaches
1955 ;; text syntax to @"..." then, `parse-partial-sexp' will treat those
1956 ;; strings accordingly.
1957 (set (make-local-variable 'parse-sexp-lookup-properties)
1960 ;; scan the entire buffer for verblit strings
1961 (csharp-scan-for-verbatim-literals-and-set-props nil nil)
1964 (local-set-key (kbd "/") 'csharp-maybe-insert-codedoc)
1965 (local-set-key (kbd "{") 'csharp-insert-open-brace)
1967 (c-update-modeline))
1971 (message (concat "Done loading " load-file-name))
1974 (provide 'csharp-mode)
1976 ;;; csharp-mode.el ends here
1977 ;;MD5: 4EDCB2ECE38841F407C7ED3DA8354E15