initial commit
[emacs-init.git] / nxhtml / related / csharp-mode.el
1 ;;; csharp-mode.el --- C# mode derived mode
2
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
9
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.
14 ;;
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.
19 ;;
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.
24
25 ;;; Commentary:
26 ;;
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.
29 ;;
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.
32 ;;
33 ;; Features:
34 ;;
35 ;;   - font-lock and indent of C# syntax including:
36 ;;       all c# keywords and major syntax
37 ;;       attributes that decorate methods, classes, fields, properties
38 ;;       enum types
39 ;;       #if/#endif  #region/#endregion
40 ;;       instance initializers
41 ;;       anonymous functions and methods
42 ;;       verbatim literal strings (those that begin with @)
43 ;;       generics
44 ;;
45 ;;   - automagic code-doc generation when you type three slashes.
46 ;;
47 ;;   - intelligent inserttion of matched pairs of curly braces.
48 ;;
49 ;;   - sets the compiler regex for next-error, for csc.exe output.
50 ;;
51 ;;
52
53
54 ;;; To use:
55 ;;
56 ;; put this in your .emacs:
57 ;;
58 ;;   (autoload 'csharp-mode "csharp-mode" "Major mode for editing C# code." t)
59 ;;
60 ;; or:
61 ;;
62 ;;   (require 'csharp-mode)
63 ;;
64 ;;
65 ;; AND:
66 ;;
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 ...
73 ;;   )
74 ;;   (add-hook  'csharp-mode-hook 'my-csharp-mode-fn t)
75 ;;
76 ;;
77
78
79 ;;; Bugs:
80 ;;
81 ;;   Namespaces in the using statements are not fontified. Should do in
82 ;;   c-basic-matchers-before or c-basic-matchers-after.
83 ;;
84 ;;   Method names with a preceding attribute are not fontified.
85 ;;
86 ;;   Field/Prop names inside object initializers are fontified only
87 ;;   if the null constructor is used, with no parens.
88 ;;
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.
96 ;;
97 ;;
98 ;;
99 ;;  Todo:
100 ;;
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.
103 ;;
104 ;;
105 ;;
106 ;;  Acknowledgements:
107 ;;
108 ;;    Thanks to Alan Mackenzie and Stefan Monnier for answering questions
109 ;;    and making suggestions.
110 ;;
111 ;;
112
113 ;;; Versions:
114 ;;
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
120 ;;            well.
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
124 ;;            it fixed.
125 ;;          - Corrected a bug with namespace not being properly identified
126 ;;            and treating the class level as an inner object, which screwed
127 ;;            up formatting.
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
131 ;;            compiled.
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
142 ;;            char (^).
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
148 ;;            within a struct.
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
164 ;;
165
166
167 (require 'cc-mode)
168
169 (message  (concat "Loading " load-file-name))
170
171
172 ;; ==================================================================
173 ;; c# upfront stuff
174 ;; ==================================================================
175
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)))
189         ((stringp op-filter)
190          (setq op-filter `(lambda (op)
191                             (string-match ,op-filter op)))))
192   (unless xlate
193     (setq xlate 'identity))
194   (c-with-syntax-table (c-lang-const c-mode-syntax-table)
195     (delete-duplicates
196      (mapcan (lambda (opgroup)
197                (when (if (symbolp (car opgroup))
198                          (when (funcall opgroup-filter (car opgroup))
199                            (setq opgroup (cdr opgroup))
200                            t)
201                        t)
202                  (mapcan (lambda (op)
203                            (when (funcall op-filter op)
204                              (let ((res (funcall xlate op)))
205                                (if (listp res) res (list res)))))
206                          opgroup)))
207              ops)
208      :test 'equal)))
209
210
211
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.)
217 (eval-when-compile
218   (let ((load-path
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)
222            load-path)))
223     (load "cc-mode" nil t)
224     (load "cc-fonts" nil t)
225     (load "cc-langs" nil t)))
226
227 (eval-and-compile
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))
233
234 ;; ==================================================================
235 ;; end of c# upfront stuff
236 ;; ==================================================================
237
238
239
240
241
242 ;; ==================================================================
243 ;; csharp-mode utility and feature defuns
244 ;; ==================================================================
245
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
249 ;;
250
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
254 processing blocks.
255
256 To use this indenting just put the following in your emacs file:
257    (c-set-offset 'cpp-macro 'csharp-lineup-region)
258
259 An alternative is to use `csharp-lineup-if-and-region'.
260 "
261
262   (save-excursion
263     (back-to-indentation)
264     (if (re-search-forward "#\\(end\\)?region" (c-point 'eol) [0]) 0  [0])))
265
266
267
268 (defun csharp-lineup-if-and-region (langelem)
269
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.
273
274 To use this indenting just put the following in your emacs file:
275   (c-set-offset 'cpp-macro 'csharp-lineup-if-and-region)
276
277 Another option is to use `csharp-lineup-region'.
278
279 "
280   (save-excursion
281     (back-to-indentation)
282     (if (re-search-forward "#\\(\\(end\\)?\\(if\\|region\\)\\|else\\)" (c-point 'eol) [0]) 0  [0])))
283
284
285
286
287
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
291
292     (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
293
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.
299
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.
304
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
309 line following.
310
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.
313 "
314   (interactive)
315   (let
316       (tpoint
317        (in-string (string= (csharp-in-literal) "string"))
318        (preceding3
319         (save-excursion
320           (and
321            (skip-chars-backward " ")
322            (> (- (point) 2) (point-min))
323            (buffer-substring-no-properties (point) (- (point) 3)))))
324        (one-word-back
325         (save-excursion
326           (backward-word 2)
327           (thing-at-point 'word))))
328
329     (cond
330
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().
336      (in-string
337       (self-insert-command 1)
338       (insert "}")
339       (backward-char))
340
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.
345      ((save-excursion
346         (backward-sexp)
347         (looking-at "\\(\\w+\\b *=\\|[[]]+\\)"))
348       (self-insert-command 1)
349       (insert "  };")
350       (backward-char 3))
351
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")
356       (save-excursion
357         (message "object initializer")
358         (setq tpoint (point)) ;; prepare to indent-region later
359         (newline)
360         (self-insert-command 1)
361         (newline-and-indent)
362         (newline)
363         (insert "};")
364         (c-indent-region tpoint (point))
365         (previous-line)
366         (indent-according-to-mode)
367         (end-of-line)
368         (setq tpoint (point)))
369       (goto-char tpoint))
370
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)
377       (insert "  }")
378       (backward-char 2))
379
380      ;; else, it's a new scope. (if, while, class, etc)
381      (t
382       (save-excursion
383         (message "new scope")
384         (set-mark (point)) ;; prepare to indent-region later
385         ;; check if the prior sexp is on the same line
386         (if (save-excursion
387               (let ((curline (line-number-at-pos))
388                     (aftline (progn
389                                (backward-sexp)
390                                (line-number-at-pos))))
391                 (= curline aftline)))
392             (newline-and-indent))
393         (self-insert-command 1)
394         (c-indent-line-or-region)
395         (end-of-line)
396         (newline)
397         (insert "}")
398         ;;(c-indent-command) ;; not sure of the difference here
399         (c-indent-line-or-region)
400         (previous-line)
401         (end-of-line)
402         (newline-and-indent)
403         ;; point ends up on an empty line, within the braces, properly indented
404         (setq tpoint (point)))
405
406       (goto-char tpoint)))))
407
408
409
410
411 ;; ==================================================================
412 ;; end of csharp-mode utility and feature defuns
413 ;; ==================================================================
414
415
416
417
418
419
420 ;; ==================================================================
421 ;; c# values for "language constants" defined in cc-langs.el
422 ;; ==================================================================
423
424
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)))
437
438 (c-lang-defconst c-basic-matchers-before
439   csharp `(
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)
444
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)))
456
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)))))
463
464            ;; Fontify all keywords except the primitive types.
465            ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
466               1 font-lock-keyword-face)
467
468            ;; Fontify leading identifiers in fully qualified names like
469            ;; "Foo.Bar".
470            ,@(when (c-lang-const c-opt-identifier-concat-key)
471                `((,(byte-compile
472                     `(lambda (limit)
473                        (while (re-search-forward
474                                ,(concat "\\(\\<" ; 1
475                                         "\\(" (c-lang-const c-symbol-key)
476                                         "\\)" ; 2
477                                         "[ \t\n\r\f\v]*"
478                                         (c-lang-const
479                                          c-opt-identifier-concat-key)
480                                         "[ \t\n\r\f\v]*"
481                                         "\\)"
482                                         "\\("
483                                         (c-lang-const
484                                          c-opt-after-id-concat-key)
485                                         "\\)")
486                                limit t)
487                          (unless (progn
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)
492                                                      (match-end 2)
493                                                      c-reference-face-name))
494                            (goto-char (match-end 1)))))))))
495            ))
496
497
498
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
504                  (concat "\\("
505                          "[ \t\n\r\f\v]*"
506                          (c-lang-const c-opt-identifier-concat-key)
507                          "[ \t\n\r\f\v]*"
508                          (concat "\\("
509                                  "\\(" (c-lang-const c-symbol-key) "\\)"
510                                  "\\)")
511                          "\\)*")))
512
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")))
518
519
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.
525
526 (c-lang-defconst c-opt-cpp-prefix
527   csharp "\\s *#\\s *")
528
529
530 ;; there are no message directives in C#
531 (c-lang-defconst c-cpp-message-directives
532   csharp nil)
533
534 (c-lang-defconst c-cpp-expr-directives
535   csharp '("if"))
536
537 (c-lang-defconst c-opt-cpp-macro-define
538   csharp "define")
539
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 "_"))
544
545
546 (c-lang-defconst c-colon-type-list-kwds
547   csharp '("class"))
548
549 (c-lang-defconst c-block-prefix-disallowed-chars
550
551   ;; Allow ':' for inherit list starters.
552   csharp (set-difference (c-lang-const c-block-prefix-disallowed-chars)
553                          '(?: ?,)))
554
555
556 (c-lang-defconst c-assignment-operators
557   csharp '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|="))
558
559 (c-lang-defconst c-primitive-type-kwds
560   ;; ECMA-344, S8
561   csharp '("object" "string" "sbyte" "short" "int" "long" "byte"
562            "ushort" "uint" "ulong" "float" "double" "bool" "char"
563            "decimal" "void"))
564
565 ;; The keywords that define that the following is a type, such as a
566 ;; class definition.
567 (c-lang-defconst c-type-prefix-kwds
568   ;; ECMA-344, S?
569   csharp '("class" "interface" "struct"))  ;; no enum here.
570                                            ;; we want enum to be a brace list.
571
572
573 ;; Type modifier keywords. They appear anywhere in types, but modify
574 ;; instead of create one.
575 (c-lang-defconst c-type-modifier-kwds
576   ;; EMCA-344, S?
577   csharp '("readonly" "const"))
578
579
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 '((?{ . ?}) ))
584
585
586
587 ;; dinoch
588 ;; Thu, 22 Apr 2010  18:54
589 ;;
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".
593 ;;
594 ;; But somehow both of those show up in the resulting derived regexp.
595 ;; This breaks indentation of instance initializers, such as
596 ;;
597 ;;         var x = new Foo { ... };
598 ;;
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.
602 ;;
603 (c-lang-defconst c-decl-block-key
604
605   csharp '"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)"
606   )
607
608
609
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
614   csharp '("new"))
615
616
617 ;; ;;(c-lang-defconst c-inexpr-class-kwds
618 ;; ;; csharp '("new"))
619
620
621
622 (c-lang-defconst c-class-decl-kwds
623   ;; EMCA-344, S?
624   csharp '("class" "interface" "struct" ))  ;; no "enum"!!
625
626
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"))
632
633
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
639   csharp nil
640   ;; csharp '("private" "protected" "public" "internal")
641 )
642
643
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"))
648
649
650 ;; This allows the classes after the : in the class declartion to be
651 ;; fontified.
652 (c-lang-defconst c-typeless-decl-kwds
653   csharp '(":"))
654
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"))
661
662
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"))
667
668
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"))
673
674
675 ;; Statements that break out of braces
676 (c-lang-defconst c-simple-stmt-kwds
677   csharp '("return" "continue" "break" "throw" "goto" ))
678
679 ;; Statements that allow a label
680 ;; TODO?
681 (c-lang-defconst c-before-label-kwds
682   csharp nil)
683
684 ;; Constant keywords
685 (c-lang-defconst c-constant-kwds
686   csharp '("true" "false" "null"))
687
688 ;; Keywords that start "primary expressions."
689 (c-lang-defconst c-primary-expr-kwds
690   csharp '("this" "base"))
691
692 ;; Treat namespace as an outer block so class indenting
693 ;; works properly.
694 (c-lang-defconst c-other-block-decl-kwds
695   csharp '("namespace"))
696
697 (c-lang-defconst c-other-kwds
698   csharp '("in" "sizeof" "typeof" "is" "as" "yield"
699            "where" "select" "from"))
700
701 (c-lang-defconst c-overloadable-operators
702   ;; EMCA-344, S14.2.1
703   csharp '("+" "-" "*" "/" "%" "&" "|" "^"
704            "<<" ">>" "==" "!=" ">" "<" ">=" "<="))
705
706
707 ;; This c-cpp-matchers stuff is used for fontification.
708 ;; see cc-font.el
709 ;;
710
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
716 ;; keywords.)
717
718 (c-lang-defconst c-cpp-matchers
719   csharp (cons
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)))
729
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)
734
735 (defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
736   "Minimal highlighting for C# mode.")
737
738 (defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
739   "Fast normal highlighting for C# mode.")
740
741 (defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
742   "Accurate normal highlighting for C# mode.")
743
744 (defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
745   "Default expressions to highlight in C# mode.")
746
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))))
752
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)))
763
764 (defvar csharp-mode-map (let ((map (c-make-inherited-keymap)))
765                       ;; Add bindings which are only useful for C#
766                       map)
767   "Keymap used in csharp-mode buffers.")
768
769
770 ;; TODO
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
777 ;;  (concat
778 ;;   "\\[[a-zA-Z][ \ta-zA-Z0-9.]+"
779 ;;   "\\((.*\\)?"
780 ;;))
781
782
783 ;; ==================================================================
784 ;; end of c# values for "language constants" defined in cc-langs.el
785 ;; ==================================================================
786
787
788
789
790 ;; ==================================================================
791 ;; C# code-doc insertion magic
792 ;; ==================================================================
793 ;;
794 ;; In Visual Studio, if you type three slashes, it immediately expands into
795 ;; an inline code-documentation fragment.  The following method does the
796 ;; same thing.
797 ;;
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.
802
803 (defun csharp-maybe-insert-codedoc (arg)
804
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,
811
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
821         a <para> element.
822
823 In all other cases the slash is inserted as normal.
824
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:
828
829      (local-set-key (kbd \"/\") 'c-electric-slash)
830
831  "
832   (interactive "*p")
833   ;;(message "csharp-maybe-insert-codedoc")
834   (let (
835         (cur-point (point))
836         (char last-command-char)
837         (cb0 (char-before (- (point) 0)))
838         (cb1 (char-before (- (point) 1)))
839         is-first-non-whitespace
840         did-auto-insert
841         )
842
843     ;; check if two prior chars were slash
844     (if (and
845          (= char ?/)
846          cb0 (= ?/ cb0)
847          cb1 (= ?/ cb1)
848          )
849
850         (progn
851           ;;(message "yes - this is the third consecutive slash")
852           (setq is-first-non-whitespace
853                 (save-excursion
854                   (back-to-indentation)
855                   (= cur-point (+ (point) 2))))
856
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)
867                                               (save-excursion
868                                                (previous-line)
869                                                (beginning-of-line)
870                                                (looking-at "[ \t]*$\\|[ \t]*{[ \t]*$"))))
871                     (flavor 0) ;; used only for diagnostic purposes
872                     )
873
874                 ;;(message "starting a 3-slash comment")
875                 ;; get the prior word, and the 2 chars preceding it.
876                 (backward-word)
877
878                 (setq word-back (thing-at-point 'word)
879                       char0 (char-before (- (point) 0))
880                       char1 (char-before (- (point) 1)))
881
882                 ;; restore prior position
883                 (goto-char cur-point)
884
885                 ;; get the following word, and the 2 chars preceding it.
886                 (forward-word)
887                 (backward-word)
888                 (setq word-fore (thing-at-point 'word)
889                       char-0 (char-before (- (point) 0))
890                       char-1 (char-before (- (point) 1)))
891
892                 ;; restore prior position again
893                 (goto-char cur-point)
894
895                 (cond
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>"
901                         flavor 1) )
902
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>"
909                             flavor 2)))
910
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>"
915                         flavor 3))
916
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
923                  ;; appropriate.
924                  ((and (string-equal word-back "example")  (eq char0 ?/)  (eq char1 ?<))
925                   (setq text-to-insert "/ <returns></returns>"
926                         fn-to-call (lambda ()
927                                      (backward-word)
928                                      (backward-char)
929                                      (backward-char)
930                                      (c-indent-line-or-region)
931                                      )
932                         flavor 4))
933
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
937                  ;; prior tag.
938                  ((or
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 ?<)))
941
942                   (let (prior-point spacer)
943                     (save-excursion
944                       (backward-word)
945                       (backward-char)
946                       (backward-char)
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))
951                       )
952
953                     (if (string-equal word-back "remarks")
954                         (setq spacer (concat spacer "   ")))
955
956                     (setq text-to-insert (format "/%s<para>\n///%s  \n///%s</para>"
957                                                  spacer spacer spacer)
958                           flavor 6)))
959
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.
963                  ;; --
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.
969                  ;;
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 ()
974                  ;;                                       (previous-line)
975                  ;;                                       (end-of-line)
976                  ;;                                       )
977                  ;;                          flavor 7) )
978                  ;;                )
979
980                  ;; the default case - do nothing
981                  (t nil))
982
983                 (if text-to-insert
984                     (progn
985                       ;;(message (format "inserting special text (f(%d))" flavor))
986
987                       ;; set the flag, that we actually inserted text
988                       (setq did-auto-insert t)
989
990                       ;; save point of beginning of insertion
991                       (setq cur-point (point))
992
993                       ;; actually insert the text
994                       (insert text-to-insert)
995
996                       ;; indent the inserted string, and re-position point, either through
997                       ;; the case-specific fn, or via the default progn.
998                       (if fn-to-call
999                           (funcall fn-to-call)
1000
1001                         (let ((newline-count 0) (pos 0) ix)
1002
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) )
1007                             )
1008
1009                           ;; indent what we just inserted
1010                           (c-indent-region cur-point (point) t)
1011
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)))))))))
1021
1022     (if (not did-auto-insert)
1023         (self-insert-command (prefix-numeric-value arg)))))
1024
1025 ;; ==================================================================
1026 ;; end of c# code-doc insertion magic
1027 ;; ==================================================================
1028
1029
1030
1031
1032 ;; ==================================================================
1033 ;; c# fontification extensions
1034 ;; ==================================================================
1035 ;; Commentary:
1036 ;;
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 @"...".
1042 ;;
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.
1047 ;;
1048 ;; This is an attempt to fix that.
1049 ;;
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
1054 ;; its parse.
1055 ;;
1056 ;; Also need to re-scan after any changes in the buffer, but on a more
1057 ;; limited region.
1058 ;;
1059
1060
1061 ;; ;; I don't remember what this is supposed to do,
1062 ;; ;; or how I figured out the value.
1063 ;; ;;
1064 ;; (defconst csharp-font-lock-syntactic-keywords
1065 ;;   '(("\\(@\\)\\(\"\\)[^\"]*\\(\"\\)\\(\"\\)[^\"]*\\(\"\\)[^\"]"
1066 ;;      (1 '(6)) (2 '(7)) (3 '(1)) (4 '(1)) (5 '(7))
1067 ;;                  ))
1068 ;;   "Highlighting of verbatim literal strings. See also the variable
1069 ;;   `font-lock-keywords'.")
1070
1071
1072
1073 ;; Allow this:
1074 ;;    (csharp-log 3 "csharp: scan...'%s'" state)
1075
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
1079 string scanning.
1080
1081 Most other csharp functions are not instrumented.
1082 0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG, 4 = SHUTUP ALREADY. ")
1083
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)))
1092         (message "%s" msg)
1093         )))
1094
1095
1096
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."
1101
1102   (let (dash
1103         nodash
1104         (curpos (point)))
1105
1106     ;; I think this may need a save-excursion...
1107     ;; Calling c-beginning-of-statement-1 resets the point!
1108
1109     (setq dash (progn (c-beginning-of-statement-1) (point)))
1110     (csharp-log 3 "C#: max-bostmt dash(%d)" dash)
1111     (goto-char curpos)
1112
1113     (setq nodash (progn (c-beginning-of-statement 1) (point)))
1114     (csharp-log 3 "C#: max-bostmt nodash(%d)" nodash)
1115     (goto-char curpos)
1116
1117     (max dash nodash)))
1118
1119
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').
1124
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.
1130
1131 Note that this function might do hidden buffer changes.  See the
1132 comment at the start of cc-engine.el for more info."
1133
1134   (let ((rtn
1135         (save-excursion
1136           (let* ((pos (point))
1137                  (lim (or lim (progn
1138                                 (c-beginning-of-syntax)
1139                                 (point))))
1140                  (state (parse-partial-sexp lim pos)))
1141             (csharp-log 4 "C#: parse lim(%d) state: %s" lim (prin1-to-string state))
1142             (cond
1143              ((elt state 3)
1144               (csharp-log 4 "C#: in literal string (%d)" pos)
1145               'string)
1146              ((elt state 4)
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)
1150              (t nil))))))
1151     rtn))
1152
1153
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
1157 necessary.
1158
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.
1162
1163 BEG is the @ delimiter. END is the 'old' position of the ending quote.
1164
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.
1167
1168 "
1169
1170   (csharp-log 3 "C#: set-vlit-syntax-table:  beg(%d) end(%d)" beg end)
1171
1172   (if (and (> beg 0) (> end 0))
1173
1174       (let ((curpos beg)
1175             (state 0))
1176
1177         (c-clear-char-properties beg end 'syntax-table)
1178
1179         (while (<= curpos end)
1180
1181           (cond
1182            ((= state 0)
1183             (if (= (char-after curpos) ?@)
1184                 (progn
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)))
1187                   )
1188               )
1189             (setq state (+ 1 state)))
1190
1191            ((= state 1)
1192             (if (= (char-after curpos) ?\")
1193                 (progn
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)))
1197                   ))
1198             (setq state (+ 1 state)))
1199
1200            ((= state 2)
1201             (cond
1202              ;; handle backslash
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)))
1206               )
1207
1208              ;; doubled double-quote
1209              ((and
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))
1216               )
1217
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)))
1222               ;;go no further
1223               (setq state (+ 1 state)))
1224
1225              ;; everything else
1226              (t
1227               ;;(message (format "C#: set-s-t: none pos(%d) chr(%c)" curpos (char-after curpos)))
1228               nil))))
1229           ;; next char
1230           (setq curpos (+ curpos 1))))))
1231
1232
1233
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.
1238
1239 This function ignores text properties. In fact it is the
1240 underlying scanner used to set the text properties in a C# buffer.
1241 "
1242
1243   (csharp-log 3 "C#: end-of-vlit-string: point(%d) c(%c)" (point) (char-after))
1244
1245   (let (curpos
1246         (max (or lim (point-max))))
1247
1248     (if (not (looking-at "@\""))
1249         (point)
1250     (forward-char 2) ;; pass up the @ sign and first quote
1251     (setq curpos (point))
1252
1253     ;; Within a verbatim literal string, a doubled double-quote
1254     ;; escapes the double-quote."
1255     (while (and                                  ;; process characters...
1256             (or                                  ;; while...
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
1260
1261       (cond
1262        ((and (eq (char-after curpos) ?\")        ;; it's a double-quote.
1263              (eq (char-after (+ curpos 1)) ?\"))
1264         (setq curpos (+ 2 curpos)))              ;; Skip 2
1265        (t                                        ;; anything else
1266         (setq curpos (+ 1 curpos)))))            ;; skip fwd 1
1267     curpos)))
1268
1269
1270
1271
1272 (defun csharp-scan-for-verbatim-literals-and-set-props (&optional beg end)
1273
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.
1277
1278 BEG and END define the limits of the scan.  When nil, they
1279 default to `point-min' and `point-max' respectively.
1280
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.
1286
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
1290 the change.
1291
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
1294 scan.
1295 "
1296
1297   (if (not (c-major-mode-is 'csharp-mode)) ;; don't scan if not csharp mode
1298       nil
1299     (save-excursion
1300       (c-save-buffer-state
1301           ((curpos (or beg (point-min)))
1302            (lastpos (or end (point-max)))
1303            (state 0) (start 0) (cycle 0)
1304            literal eos limits)
1305
1306         (csharp-log 3 "C#: scan")
1307         (goto-char curpos)
1308
1309         (while (and (< curpos lastpos) (< cycle 10000))
1310           (cond
1311
1312            ;; Case 1: current char is a @ sign
1313            ;; --------------------------------------------
1314            ;; Check to see if it demarks the beginning of a verblit
1315            ;; string.
1316            ((= ?@ (char-after curpos))
1317
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.
1320
1321             ;; not sure why I need both of the following.
1322             (syntax-ppss-flush-cache 1)
1323             (parse-partial-sexp 1 curpos)
1324             (goto-char curpos)
1325             (setq literal (csharp-in-literal))
1326             (cond
1327
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)
1333
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)))
1346
1347               ;; advance to the end of the comment
1348               (if limits
1349                   (progn
1350                     (csharp-log 4 "C#: scan: jump end comment A (%d)" (cdr limits))
1351                     (setq curpos (cdr limits)))))
1352
1353
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
1359              ;; literal.
1360              ((and (< (+ 2 curpos) lastpos)
1361                    (= ?\" (char-after (+ 1 curpos))))
1362
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)
1366
1367               (csharp-log 4 "C#: scan: jump end verblit string (%d)" eos)
1368               (setq curpos eos))))
1369
1370
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))
1378             (goto-char curpos)
1379             (setq literal (c-in-literal))
1380             (cond
1381
1382              ;; Case 2.A: a quote within a string
1383              ;; --------------------------------------------
1384              ;; This shouldn't happen, because we hop over strings.
1385              ;; But it might.
1386              ((eq literal 'string) nil)
1387
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)))
1401
1402               ;; advance to the end of the comment
1403               (if limits
1404                   (progn
1405                     (setq curpos (cdr limits))
1406                     (csharp-log 3 "C#: scan: jump end comment B (%s)" curpos))))
1407
1408
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.
1412              (t
1413               (forward-char 1) ;; pass up the quote
1414               (if (consp (setq limits (c-literal-limits)))
1415                   (progn
1416                     (csharp-log 4 "C#: scan: jump end literal (%d)" (cdr limits))
1417                     (setq curpos (cdr limits))))))))
1418
1419           (setq cycle (+ 1 cycle))
1420           (setq curpos (+ 1 curpos))
1421           (c-safe (goto-char curpos)))))))
1422
1423
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.
1427
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.
1430
1431 It prepares the buffer for font locking, hence must get called
1432 before `font-lock-after-change-function'.
1433
1434 It does hidden buffer changes.
1435
1436 BEG, END and OLD-LEN have the same meaning here as for any
1437 after-change function.
1438
1439 Point is undefined both before and after this function call.
1440 The return value is meaningless, and is ignored by cc-mode.
1441 "
1442     (let ((start-scan (progn
1443                         (c-beginning-of-statement 1)
1444                         (point))))
1445       (csharp-scan-for-verbatim-literals-and-set-props start-scan end)))
1446
1447
1448
1449 (c-lang-defconst c-before-font-lock-function
1450   csharp 'csharp-before-font-lock)
1451
1452 ;; ==================================================================
1453 ;; end of c# fontification extensions
1454 ;; ==================================================================
1455
1456
1457
1458
1459
1460 ;; ==================================================================
1461 ;; C#-specific optimizations of cc-mode funcs
1462 ;; ==================================================================
1463
1464
1465 ;; There's never a need to check for C-style macro definitions in
1466 ;; a C# buffer.
1467 (defadvice c-beginning-of-macro (around
1468                                  csharp-mode-advice-1
1469                                  compile activate)
1470   (if (c-major-mode-is 'csharp-mode)
1471       nil
1472     ad-do-it)
1473   )
1474
1475
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
1479                                  compile activate)
1480   (if (c-major-mode-is 'csharp-mode)
1481       nil
1482     ad-do-it)
1483   )
1484
1485 ;; ==================================================================
1486 ;; end of C#-specific optimizations of cc-mode funcs
1487 ;; ==================================================================
1488
1489
1490
1491
1492
1493
1494
1495
1496 ;; ==================================================================
1497 ;; c# - monkey-patching of basic parsing logic
1498 ;; ==================================================================
1499 ;;
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
1504 ;; defuns.
1505 ;;
1506
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.
1512   ;;
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.
1518   ;;
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.
1523   ;;
1524   ;; This function might do hidden buffer changes.
1525
1526   (save-excursion
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))))
1532
1533       (while (and (eq res 'maybe)
1534                   (progn (c-backward-syntactic-ws)
1535                          (> (point) closest-lim))
1536                   (not (bobp))
1537                   (progn (backward-char)
1538                          (looking-at "[\]\).]\\|\\w\\|\\s_"))
1539                   (c-safe (forward-char)
1540                           (goto-char (scan-sexps (point) -1))))
1541
1542         (setq res
1543               (if (looking-at c-keywords-regexp)
1544                   (let ((kw-sym (c-keyword-sym (match-string 1))))
1545                     (cond
1546                      ((and block-follows
1547                            (c-keyword-member kw-sym 'c-inexpr-class-kwds))
1548                       (and (not (eq passed-paren ?\[))
1549
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
1555                            (not
1556                             (and (c-major-mode-is 'csharp-mode)
1557                                  (looking-at "new\s+\\([[:alnum:]_]+\\)\\b")))
1558
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)))
1564                                  (while (and
1565                                          (= (c-backward-token-2 1 nil closest-lim) 0)
1566                                          (eq (char-syntax (char-after)) ?w))
1567                                    (setq prev (point)))
1568                                  (goto-char prev)
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.
1573                                (save-excursion
1574                                  (and check-at-end
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)
1588                       nil)
1589                      (t
1590                       'maybe)))
1591
1592                 (if (looking-at "\\s(")
1593                     (if passed-paren
1594                         (if (and (eq passed-paren ?\[)
1595                                  (eq (char-after) ?\[))
1596                             ;; Accept several square bracket sexps for
1597                             ;; Java array initializations.
1598                             'maybe)
1599                       (setq passed-paren (char-after))
1600                       'maybe)
1601                   'maybe))))
1602
1603       (if (eq res 'maybe)
1604           (when (and c-recognize-paren-inexpr-blocks
1605                      block-follows
1606                      containing-sexp
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)))
1612                            (c-on-identifier)))
1613                     (and c-special-brace-lists
1614                          (c-looking-at-special-brace-list)))
1615                 nil
1616               (cons 'inexpr-statement (point))))
1617
1618         res))))
1619
1620
1621
1622
1623 (defconst csharp-enum-decl-re
1624   (concat
1625    "\\<enum\\>\s+\\([[:alnum:]_]+\\)\s*:\s*"
1626    "\\("
1627    (c-make-keywords-re nil
1628      (list "sbyte" "byte" "short" "ushort" "int" "uint" "long" "ulong"))
1629    "\\)")
1630   "Regex that captures an enum declaration in C#"
1631   )
1632
1633
1634
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
1640   ;; braces
1641   ;;
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
1644   ;; speed.
1645   ;;
1646   ;; This function might do hidden buffer changes.
1647   (or
1648    ;; This will pick up brace list declarations.
1649    (c-safe
1650     (save-excursion
1651       (goto-char containing-sexp)
1652       (c-forward-sexp -1)
1653       (let (bracepos)
1654         (if (and (or (looking-at c-brace-list-key)
1655
1656                      (progn (c-forward-sexp -1)
1657                             (looking-at c-brace-list-key))
1658
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
1664
1665                      (and (c-major-mode-is 'csharp-mode)
1666                           (progn
1667                             (c-forward-sexp -1)
1668                             (looking-at csharp-enum-decl-re))))
1669
1670                  (setq bracepos (c-down-list-forward (point)))
1671                  (not (c-crosses-statement-barrier-p (point)
1672                                                      (- bracepos 2))))
1673             (point)))))
1674    ;; this will pick up array/aggregate init lists, even if they are nested.
1675    (save-excursion
1676      (let ((class-key
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)
1680                  c-decl-block-key))
1681            bufpos braceassignp lim next-containing)
1682        (while (and (not bufpos)
1683                    containing-sexp)
1684            (when paren-state
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)))
1689              (when 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"
1708                ;; earlier.
1709                (while (eq braceassignp 'dontknow)
1710                  (setq braceassignp
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.
1717                               'dontknow)
1718                              (t nil)))))
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))
1724                      ((and class-key
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)))
1733                         (setq braceassignp
1734                               (cond
1735                                ;; Check for operator =
1736                                ((and c-opt-op-identifier-prefix
1737                                      (looking-at c-opt-op-identifier-prefix))
1738                                 nil)
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) ?`))))
1747                                 nil)
1748                                ((looking-at "\\s.") 'maybe)
1749                                ;; make sure we're not in a C++ template
1750                                ;; argument assignment
1751                                ((and
1752                                  (c-major-mode-is 'c++-mode)
1753                                  (save-excursion
1754                                    (let ((here (point))
1755                                          (pos< (progn
1756                                                  (skip-chars-backward "^<>")
1757                                                  (point))))
1758                                      (and (eq (char-before) ?<)
1759                                           (not (c-crosses-statement-barrier-p
1760                                                 pos< here))
1761                                           (not (c-in-literal))
1762                                           ))))
1763                                 nil)
1764                                (t t))))))
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)
1772                    ;; Go up one level.
1773                    (setq containing-sexp next-containing
1774                          lim nil
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))))
1780            )
1781        bufpos))
1782    ))
1783
1784 ;; ==================================================================
1785 ;; end of monkey-patching of basic parsing logic
1786 ;; ==================================================================
1787
1788
1789
1790
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)))
1798
1799 ;;; Autoload mode trigger
1800 ;;;###autoload
1801 (add-to-list 'auto-mode-alist '("\\.cs$" . csharp-mode))
1802
1803
1804
1805 (c-add-style "C#"
1806  '("Java"
1807    (c-basic-offset . 4)
1808    (c-comment-only-line-offset . (0 . 0))
1809    (c-offsets-alist . (
1810        (access-label          . -)
1811        (arglist-close         . c-lineup-arglist)
1812        (arglist-cont          . 0)
1813        (arglist-cont-nonempty . c-lineup-arglist)
1814        (arglist-intro         . c-lineup-arglist-intro-after-paren)
1815        (block-close           . 0)
1816        (block-open            . 0)
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)
1823        (case-label            . +)
1824        (catch-clause          . 0)
1825        (class-close           . 0)
1826        (class-open            . 0)
1827        (comment-intro         . c-lineup-comment)
1828        (cpp-macro             . 0)
1829        (cpp-macro-cont        . c-lineup-dont-change)
1830        (defun-block-intro     . +)
1831        (defun-close           . 0)
1832        (defun-open            . 0)
1833        (do-while-closure      . 0)
1834        (else-clause           . 0)
1835        (extern-lang-close     . 0)
1836        (extern-lang-open      . 0)
1837        (friend                . 0)
1838        (func-decl-cont        . +)
1839        (inclass               . +)
1840        (inexpr-class          . +)
1841        (inexpr-statement      . 0)
1842        (inextern-lang         . +)
1843        (inher-cont            . c-lineup-multi-inher)
1844        (inher-intro           . +)
1845        (inlambda              . c-lineup-inexpr-block)
1846        (inline-close          . 0)
1847        (inline-open           . 0)
1848        (innamespace           . +)
1849        (knr-argdecl           . 0)
1850        (knr-argdecl-intro     . 5)
1851        (label                 . 0)
1852        (lambda-intro-cont     . +)
1853        (member-init-cont      . c-lineup-multi-inher)
1854        (member-init-intro     . +)
1855        (namespace-close       . 0)
1856        (namespace-open        . 0)
1857        (statement             . 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)
1864        (substatement          . +)
1865        (substatement-open     . 0)
1866        (template-args-cont c-lineup-template-args +)
1867        (topmost-intro         . 0)
1868        (topmost-intro-cont    . 0)
1869        ))
1870    ))
1871
1872
1873
1874
1875 ;; Custom variables
1876 ;;;###autoload
1877 (defcustom csharp-mode-hook nil
1878   "*Hook called by `csharp-mode'."
1879   :type 'hook
1880   :group 'c)
1881
1882
1883
1884 ;;; The entry point into the mode
1885 ;;;###autoload
1886 (defun csharp-mode ()
1887   "Major mode for editing C# code. This mode is derived from CC Mode to
1888 support C#.
1889
1890 The hook `c-mode-common-hook' is run with no args at mode
1891 initialization, then `csharp-mode-hook'.
1892
1893 This mode will automatically add a regexp for Csc.exe error and warning
1894 messages to the `compilation-error-regexp-alist'.
1895
1896 Key bindings:
1897 \\{csharp-mode-map}"
1898   (interactive)
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)
1904
1905   ;; define underscore as part of a word in the Csharp syntax table
1906   (modify-syntax-entry ?_ "w" csharp-mode-syntax-table)
1907
1908   ;; define @ as an expression prefix in Csharp syntax table
1909   (modify-syntax-entry ?@ "'" csharp-mode-syntax-table)
1910
1911   (setq major-mode 'csharp-mode
1912         mode-name "C#"
1913         local-abbrev-table csharp-mode-abbrev-table
1914         abbrev-mode t)
1915   (use-local-map csharp-mode-map)
1916
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)
1921
1922
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)
1929
1930
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
1933
1934   (add-hook 'compilation-mode-hook
1935             (lambda ()
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))))
1939
1940   ;; to allow next-error to work with csc.exe:
1941   (setq compilation-scroll-output t)
1942
1943   ;; allow fill-paragraph to work on xml code doc
1944   (set (make-local-variable 'paragraph-separate)
1945        "[ \t]*\\(//+\\|\\**\\)\\([ \t]+\\|[ \t]+<.+?>\\)$\\|^\f")
1946
1947
1948   (c-run-mode-hooks 'c-mode-common-hook 'csharp-mode-hook)
1949
1950
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)
1958        t)
1959
1960   ;; scan the entire buffer for verblit strings
1961   (csharp-scan-for-verbatim-literals-and-set-props nil nil)
1962
1963
1964   (local-set-key (kbd "/") 'csharp-maybe-insert-codedoc)
1965   (local-set-key (kbd "{") 'csharp-insert-open-brace)
1966
1967   (c-update-modeline))
1968
1969
1970
1971 (message  (concat "Done loading " load-file-name))
1972
1973
1974 (provide 'csharp-mode)
1975
1976 ;;; csharp-mode.el ends here
1977 ;;MD5: 4EDCB2ECE38841F407C7ED3DA8354E15