Attachment 'csharp-mode-0.8.5.el'
Download 1 ;;; csharp-mode.el --- C# mode derived mode
2
3 ;; Author : Dylan R. E. Moonfire (original)
4 ;; Maintainer : Dino Chiesa <dpchiesa@hotmail.com>
5 ;; Created : Feburary 2005
6 ;; Modified : May 2011
7 ;; Version : 0.8.6
8 ;; Keywords : c# languages oop mode
9 ;; X-URL : http://code.google.com/p/csharpmode/
10 ;; Last-saved : <2011-May-22 10:51:23>
11
12 ;;
13 ;; This program is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2 of the License, or
16 ;; (at your option) any later version.
17 ;;
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22 ;;
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with this program; see the file COPYING. If not, write to
25 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29 ;;
30 ;; This is a major mode for editing C# code. It performs automatic
31 ;; indentation of C# syntax; font locking; and integration with compile.el;
32 ;; flymake.el; yasnippet.el; and imenu.el.
33 ;;
34 ;; csharp-mode requires CC Mode 5.30 or later. It works with
35 ;; cc-mode 5.31.3, which is current at this time.
36 ;;
37 ;; Features:
38 ;;
39 ;; - font-lock and indent of C# syntax including:
40 ;; all c# keywords and major syntax
41 ;; attributes that decorate methods, classes, fields, properties
42 ;; enum types
43 ;; #if/#endif #region/#endregion
44 ;; instance initializers
45 ;; anonymous functions and methods
46 ;; verbatim literal strings (those that begin with @)
47 ;; generics
48 ;;
49 ;; - automagic code-doc generation when you type three slashes.
50 ;;
51 ;; - intelligent insertion of matched pairs of curly braces.
52 ;;
53 ;; - compile tweaks. Infers the compile command from special comments
54 ;; in the file header. Also, sets the regex for next-error, so that
55 ;; compile.el can handle csc.exe output.
56 ;;
57 ;; - flymake integration
58 ;; - select flymake command from code comments
59 ;; - infer flymake command otherwise (presence of makefile, etc)
60 ;; - Turn off query-on-exit-flag for the flymake process.
61 ;; - define advice to flymake-goto-line , to allow it to goto the
62 ;; appropriate column for the error on a given line. This works
63 ;; with `flymake-goto-next-error' etc.
64 ;;
65 ;; - yasnippet integration
66 ;; - preloaded snippets
67 ;;
68 ;; - imenu integration - generates an index of namespaces, classes,
69 ;; interfaces, methods, and properties for easy navigation within
70 ;; the buffer.
71 ;;
72
73
74 ;; Installation instructions
75 ;; --------------------------------
76 ;;
77 ;; Put csharp-mode.el somewhere in your load path, optionally byte-compile
78 ;; it, and add the following to your .emacs file:
79 ;;
80 ;; (autoload 'csharp-mode "csharp-mode" "Major mode for editing C# code." t)
81 ;; (setq auto-mode-alist
82 ;; (append '(("\\.cs$" . csharp-mode)) auto-mode-alist))
83 ;;
84 ;;
85 ;; Optionally, define and register a mode-hook function. To do so, use
86 ;; something like this in your .emacs file:
87 ;;
88 ;; (defun my-csharp-mode-fn ()
89 ;; "function that runs when csharp-mode is initialized for a buffer."
90 ;; (turn-on-auto-revert-mode)
91 ;; (setq indent-tabs-mode nil)
92 ;; (require 'flymake)
93 ;; (flymake-mode 1)
94 ;; (require 'yasnippet)
95 ;; (yas/minor-mode-on)
96 ;; (require 'rfringe)
97 ;; ...insert more code here...
98 ;; ...including any custom key bindings you might want ...
99 ;; )
100 ;; (add-hook 'csharp-mode-hook 'my-csharp-mode-fn t)
101 ;;
102 ;;
103 ;; General
104 ;; ----------------------------
105 ;;
106 ;; Mostly C# mode will "just work." Use `describe-mode' to see the
107 ;; default keybindings and the highlights of the mode.
108 ;;
109 ;;
110 ;; Flymake Integration
111 ;; ----------------------------
112 ;;
113 ;; You can use flymake with csharp mode to automatically check the
114 ;; syntax of your csharp code, and highlight errors. To do so, add a
115 ;; comment line like this to each .cs file that you use flymake with:
116 ;;
117 ;; // flymake: c:\.net3.5\csc.exe /t:module /nologo /R:Foo.dll @@FILE@@
118 ;;
119 ;; That lines specifies a command "stub". Flymake appends the name of
120 ;; the file to compile, and then runs the command to check
121 ;; syntax. Flymake assumes that syntax errors will be noted in the
122 ;; output of the command in a form that fits one of the regexs in the
123 ;; `compilation-error-regexp-alist-alist'. Check the flymake module for
124 ;; more information on that.
125 ;;
126 ;; Some rules for the command:
127 ;;
128 ;; 1. it must appear all on a single line.
129 ;;
130 ;; 2. csharp-mode generally looks for the marker line in the first N
131 ;; lines of the file, where N is set in
132 ;; `csharp-cmd-line-limit'. See the documentation on that
133 ;; variable for more information.
134 ;;
135 ;; 3. the command SHOULD use @@FILE@@ in place of the name of the
136 ;; source file to be compiled, normally the file being edited.
137 ;; This is because normally flymake saves a copy of the buffer
138 ;; into a temporary file with a unique name, and then compiles
139 ;; that temporary file. The token @@FILE@@ is replaced by
140 ;; csharp-mode with the name of the temporary file created by
141 ;; flymake, before invoking the command.
142 ;;
143 ;; 4. The command should include /R options specifying external
144 ;; libraries that the code depends on.
145 ;;
146 ;; If you have no external dependencies, then you need not specify any
147 ;; flymake command at all. csharp-mode will implicitly act as if you had
148 ;; specified the command:
149 ;;
150 ;; // flymake: c:\.net3.5\csc.exe /t:module /nologo @@FILE@@
151 ;;
152 ;;
153 ;; If you use csc.exe as the syntax check tool (as almost everyone
154 ;; will), the /t:module is important. csharp-mode assumes that the
155 ;; syntax-check compile command will produce a file named
156 ;; NAME.netmodule, which is the default when using /t:module. (Remember
157 ;; than NAME is dynamically generated). csharp-mode will remove the
158 ;; generated netmodule file after the syntax check is complete. If you
159 ;; don't specify /t:module, then csharp-mode won't know what file to
160 ;; delete.
161 ;;
162 ;; csharp-mode also fiddles with some other flymake things. In
163 ;; particular it: adds .cs to the flymake "allowed filename masks";
164 ;; adds parsing for csc error messages; and adds advice to the error
165 ;; parsing logic. This all should be pretty benign for all other
166 ;; flymake buffers. But it might not be.
167 ;;
168 ;; You can explicitly turn the flymake integration for C# off by
169 ;; setting `csharp-want-flymake-fixup' to nil.
170 ;;
171 ;;
172 ;; Compile Integration
173 ;; ----------------------------
174 ;;
175 ;; csharp-mode binds the function `csharp-invoke-compile-interactively'
176 ;; to "\C-x\C-e" . This function attempts to intellgently guess the
177 ;; format of the compile command to use for a buffer. It looks in the
178 ;; comments at the head of the buffer for a line that begins with
179 ;; compile: . If found, csharp-mode suggests the text that follows as
180 ;; the compilation command when running `compile' . If such a line is
181 ;; not found, csharp-mode falls back to a msbuild or nmake command.
182 ;; See the documentation on `csharp-cmd-line-limit' for further
183 ;; information.
184 ;;
185 ;; Also, csharp-mode installs an error regexp for csc.exe into
186 ;; `compilation-error-regexp-alist-alist', which allows `next-error'
187 ;; and `previous-error' (defined in compile.el) to navigate to the next
188 ;; and previous compile errors in the cs buffer, after you've run `compile'.
189 ;;
190 ;;
191 ;; YASnippet integration
192 ;; -----------------------------
193 ;;
194 ;; csharp-mode defines some built-in snippets for
195 ;; convenience. For example, if statements, for, foreach, and
196 ;; so on. You can see them on the YASnippet menu that is displayed
197 ;; when a csharp-mode buffer is opened. csharp-mode defines this
198 ;; snippets happens only if ya-snippet is available. (It is done in an
199 ;; `eval-after-load' clause.) The builtin snippets will not overwrite
200 ;; snippets that use the same name, if they are defined in the normal
201 ;; way (in a compiled bundle) with ya-snippet.
202 ;;
203 ;; You can explicitly turn off ya-snippet integration. See the var,
204 ;; `csharp-want-yasnippet-fixup'.
205 ;;
206 ;;
207 ;; imenu integration
208 ;; -----------------------------
209 ;;
210 ;; This should just work. For those who don't know what imenu is, it
211 ;; allows navigation to different points within the file from an
212 ;; "Index" menu, in the window's menubar. csharp-mode computes the
213 ;; menu containing the namespaces, classes, methods, and so on, in the
214 ;; buffer. This happens at the time the file is loaded; for large
215 ;; files it takes a bit of time to complete the scan. If you don't
216 ;; want this capability, set `csharp-want-imenu' to nil.
217 ;;
218 ;;
219
220
221 ;;; Known Bugs:
222 ;;
223 ;; The imenu scan is text-based and naive. For example, if you
224 ;; intersperse comments between the name of a class/method/namespace,
225 ;; and the curly brace, the scan will not recognize the thing being
226 ;; declared. This is fixable - would need to extract the buffer
227 ;; substring then remove comments before doing the regexp checks - but
228 ;; it would make the scan much slower. Also, the scan doesn't deal
229 ;; with preproc symbol definitions and #if/#else. Those things are
230 ;; invisible to the scanner csharp-mode uses to build the imenu menu.
231 ;;
232 ;; Leading identifiers are no longer being fontified, for some reason.
233 ;; See matchers-before. (Not sure this is still a problem - 19 may
234 ;; 2011 DPC)
235 ;;
236 ;; Method names with a preceding attribute are not fontified.
237 ;;
238 ;; The symbol followng #if is not fontified. It should be treated like
239 ;; define and get font-lock-variable-name-face .
240 ;;
241 ;; This code doesn't seem to work when you compile it, then
242 ;; load/require in the emacs file. You will get an error (error
243 ;; "`c-lang-defconst' must be used in a file") which happens because
244 ;; cc-mode doesn't think it is in a buffer while loading directly
245 ;; from the init. However, if you call it based on a file extension,
246 ;; it works properly. Interestingly enough, this doesn't happen if
247 ;; you don't byte-compile cc-mode.
248 ;;
249 ;;
250 ;;
251 ;; Todo:
252 ;;
253 ;; imenu should scan for and find delegates and events, in addition
254 ;; to the classes, structs, properties and methods it does currently.
255 ;;
256 ;; Get csharp-mode.el accepted as part of the emacs standard distribution.
257 ;; Must contact monnier at iro.umontreal.ca to make this happen.
258 ;;
259 ;; Add refactoring capabilities?
260 ;; - extract as method - extract a block of code into a method
261 ;; - extract as Func<> - extract a block of code into an Action<T>
262 ;;
263 ;; More code-gen power:
264 ;; - interface implementation - I think would require csharp-shell
265 ;;
266 ;;
267 ;; Acknowledgements:
268 ;;
269 ;; Thanks to Alan Mackenzie and Stefan Monnier for answering questions
270 ;; and making suggestions. And to Trey Jackson for sharing his
271 ;; knowledge of emacs lisp.
272 ;;
273 ;;
274
275 ;;; Versions:
276 ;;
277 ;; 0.1.0 - Initial release.
278 ;; 0.2.0 - Fixed the identification on the "enum" keyword.
279 ;; - Fixed the font-lock on the "base" keyword
280 ;; 0.3.0 - Added a regex to fontify attributes. It isn't the
281 ;; the best method, but it handles single-like attributes
282 ;; well.
283 ;; - Got "super" not to fontify as a keyword.
284 ;; - Got extending classes and interfaces to fontify as something.
285 ;; 0.4.0 - Removed the attribute matching because it broke more than
286 ;; it fixed.
287 ;; - Corrected a bug with namespace not being properly identified
288 ;; and treating the class level as an inner object, which screwed
289 ;; up formatting.
290 ;; - Added "partial" to the keywords.
291 ;; 0.5.0 - Found bugs with compiled cc-mode and loading from init files.
292 ;; - Updated the eval-when-compile to code to let the mode be
293 ;; compiled.
294 ;; 0.6.0 - Added the c-filter-ops patch for 5.31.1 which made that
295 ;; function in cc-langs.el unavailable.
296 ;; - Added a csharp-lineup-region for indention #region and
297 ;; #endregion block differently.
298 ;; 0.7.0 - Added autoload so update-directory-autoloads works
299 ;; (Thank you, Nikolaj Schumacher)
300 ;; - Fontified the entire #region and #endregion lines.
301 ;; - Initial work to get get, set, add, remove font-locked.
302 ;; 0.7.1 - Added option to indent #if/endif with code
303 ;; - Fixed c-opt-cpp-prefix defn (it must not include the BOL
304 ;; char (^).
305 ;; - proper fontification and indent of classes that inherit
306 ;; (previously the colon was confusing the parser)
307 ;; - reclassified namespace as a block beginner
308 ;; - removed $ as a legal symbol char - not legal in C#.
309 ;; - added struct to c-class-decl-kwds so indent is correct
310 ;; within a struct.
311 ;; 0.7.2 - Added automatic codedoc insertion.
312 ;; 0.7.3 - Instance initializers (new Type { ... } ) and
313 ;; (new Type() { ...} ) are now indented properly.
314 ;; - proper fontification and indent of enums as brace-list-*,
315 ;; including special treatment for enums that explicitly
316 ;; inherit from an int type. Previously the colon was
317 ;; confusing the parser.
318 ;; - proper fontification of verbatim literal strings,
319 ;; including those that end in slash. This edge case was not
320 ;; handled at all before; it is now handled correctly.
321 ;; - code cleanup and organization; removed the formfeed.
322 ;; - intelligent curly-brace insertion with
323 ;; `csharp-insert-open-brace'
324 ;; 0.7.4 - added a C# style
325 ;; - using is now a keyword and gets fontified correctly
326 ;; - fixed a bug that had crept into the codedoc insertion.
327 ;; 0.7.5 - now fontify namespaces in the using statements. This is
328 ;; done in the csharp value for c-basic-matchers-before .
329 ;; - also fontify the name following namespace decl.
330 ;; This is done in the csharp value for c-basic-matchers-after .
331 ;; - turn on recognition of generic types. They are now
332 ;; fontified correctly.
333 ;; - <> are now treated as syntactic parens and can be jumped
334 ;; over with c-forward-sexp.
335 ;; - Constructors are now fontified.
336 ;; - Field/Prop names inside object initializers are now fontified.
337 ;;
338 ;; 0.7.7 - relocate running c-run-mode-hooks to the end of
339 ;; csharp-mode, to allow user to modify key bindings in a
340 ;; hook if he doesn't like the defaults.
341 ;;
342 ;; 0.7.8 - redefine csharp-log to insert timestamp.
343 ;; - Fix byte-compile errors on emacs 23.2 ? Why was
344 ;; c-filter-ops duplicated here? What was the purpose of its
345 ;; presence here, I am not clear.
346 ;;
347 ;; 0.8.0 - include flymake magic into this module.
348 ;; - include yasnippet integration
349 ;;
350 ;; 0.8.2 2011 April DPC
351 ;; - small tweaks; now set a one-time bool for flymake installation
352 ;; - some doc updates on flymake
353 ;;
354 ;; 0.8.3 2011 May 17 DPC
355 ;; - better help on csharp-mode
356 ;; - csharp-move-* functions for manual navigation.
357 ;; - imenu integration for menu-driven navigation - navigate to
358 ;; named methods, classes, etc.
359 ;; - adjusted the flymake regexp to handle output from fxcopcmd,
360 ;; and extended the help to provide examples how to use this.
361 ;;
362 ;; 0.8.4 DPC 2011 May 18
363 ;; - fix a basic bug in the `csharp-yasnippet-fixup' fn.
364 ;;
365 ;; 0.8.5 DPC 2011 May 21
366 ;; - imenu: correctly parse Properties that are part of an
367 ;; explicitly specified interface. Probably need to do this
368 ;; for methods, too.
369 ;; - fontify the optional alias before namespace in a using (import).
370 ;; - Tweak open-curly magic insertion for object initializers.
371 ;; - better fontification of variables and references
372 ;; - "sealed" is now fontified as a keyword
373 ;; - imenu: correctly index ctors that call this or base.
374 ;; - imenu: correctly index Extension methods (this System.Enum e)
375 ;; - imenu: correctly scan method params tagged with out, ref, params
376 ;; - imenu scan: now handle curlies within strings.
377 ;; - imenu: split menus now have better labels, are sorted correctly.
378 ;;
379 ;; 0.8.6 DPC 2011 May ??
380 ;; -
381
382
383 (require 'cc-mode)
384
385 (message (concat "Loading " load-file-name))
386
387
388 ;; ==================================================================
389 ;; c# upfront stuff
390 ;; ==================================================================
391
392 ;; This is a copy of the function in cc-mode which is used to handle the
393 ;; eval-when-compile which is needed during other times.
394 ;;
395 ;; NB: I think this is needed to satisfy requirements when this module
396 ;; calls `c-lang-defconst'. (DPC)
397
398 ;; (defun c-filter-ops (ops opgroup-filter op-filter &optional xlate)
399 ;; ;; See cc-langs.el, a direct copy.
400 ;; (unless (listp (car-safe ops))
401 ;; (setq ops (list ops)))
402 ;; (cond ((eq opgroup-filter t)
403 ;; (setq opgroup-filter (lambda (opgroup) t)))
404 ;; ((not (functionp opgroup-filter))
405 ;; (setq opgroup-filter `(lambda (opgroup)
406 ;; (memq opgroup ',opgroup-filter)))))
407 ;; (cond ((eq op-filter t)
408 ;; (setq op-filter (lambda (op) t)))
409 ;; ((stringp op-filter)
410 ;; (setq op-filter `(lambda (op)
411 ;; (string-match ,op-filter op)))))
412 ;; (unless xlate
413 ;; (setq xlate 'identity))
414 ;; (c-with-syntax-table (c-lang-const c-mode-syntax-table)
415 ;; (delete-duplicates
416 ;; (mapcan (lambda (opgroup)
417 ;; (when (if (symbolp (car opgroup))
418 ;; (when (funcall opgroup-filter (car opgroup))
419 ;; (setq opgroup (cdr opgroup))
420 ;; t)
421 ;; t)
422 ;; (mapcan (lambda (op)
423 ;; (when (funcall op-filter op)
424 ;; (let ((res (funcall xlate op)))
425 ;; (if (listp res) res (list res)))))
426 ;; opgroup)))
427 ;; ops)
428 ;; :test 'equal)))
429
430
431
432 ;; These are only required at compile time to get the sources for the
433 ;; language constants. (The load of cc-fonts and the font-lock
434 ;; related constants could additionally be put inside an
435 ;; (eval-after-load "font-lock" ...) but then some trickery is
436 ;; necessary to get them compiled.)
437
438 (eval-when-compile
439 (let ((load-path
440 (if (and (boundp 'byte-compile-dest-file)
441 (stringp byte-compile-dest-file))
442 (cons (file-name-directory byte-compile-dest-file) load-path)
443 load-path)))
444 (load "cc-mode" nil t)
445 (load "cc-fonts" nil t)
446 (load "cc-langs" nil t)))
447
448 (eval-and-compile
449 ;; Make our mode known to the language constant system. Use Java
450 ;; mode as the fallback for the constants we don't change here.
451 ;; This needs to be done also at compile time since the language
452 ;; constants are evaluated then.
453 (c-add-language 'csharp-mode 'java-mode))
454
455 ;; ==================================================================
456 ;; end of c# upfront stuff
457 ;; ==================================================================
458
459
460
461
462
463 ;; ==================================================================
464 ;; constants used in this module
465 ;; ==================================================================
466
467 ;;(error (byte-compile-dest-file))
468 ;;(error (c-get-current-file))
469
470 (defconst csharp-aspnet-directive-re
471 "<%@.+?%>"
472 "Regex for matching directive blocks in ASP.NET files (.aspx, .ashx, .ascx)")
473
474
475 (defconst csharp-enum-decl-re
476 (concat
477 "\\<enum[ \t\n\r\f\v]+"
478 "\\([[:alpha:]_][[:alnum:]_]*\\)"
479 "[ \t\n\r\f\v]*"
480 "\\(:[ \t\n\r\f\v]*"
481 "\\("
482 (c-make-keywords-re nil
483 (list "sbyte" "byte" "short" "ushort" "int" "uint" "long" "ulong"))
484 "\\)"
485 "\\)?")
486 "Regex that captures an enum declaration in C#"
487 )
488
489 ;; ==================================================================
490
491
492
493
494
495
496 ;; ==================================================================
497 ;; csharp-mode utility and feature defuns
498 ;; ==================================================================
499
500 (defun csharp--at-vsemi-p (&optional pos)
501 "Determines if there is a virtual semicolon at POS or point.
502 It returns t if at a position where a virtual-semicolon is.
503 Otherwise nil.
504
505 This is the C# version of the function. It gets set into
506 the variable `c-at-vsemi-p-fn'.
507
508 A vsemi is a cc-mode concept implying the end of a statement,
509 where no actual end-of-statement signifier character ( semicolon,
510 close-brace) appears. The concept is used to allow proper
511 indenting of blocks of code: Where a vsemi appears, the following
512 line will not indent further.
513
514 A vsemi appears in 3 cases in C#:
515
516 - after an attribute that decorates a class, method, field, or
517 property.
518
519 - in an object initializer, before the open-curly?
520
521 - after an ASPNET directive, that appears in a aspx/ashx/ascx file
522
523 An example of the former is [WebMethod] or [XmlElement].
524 An example of the latter is something like this:
525
526 <%@ WebHandler Language=\"C#\" Class=\"Handler\" %>
527
528 Providing this function allows the indenting in csharp-mode
529 to work properly with code that includes attributes and ASPNET
530 directives.
531
532 "
533 (save-excursion
534 (let ((pos-or-point (progn (if pos (goto-char pos)) (point))))
535
536 (cond
537
538 ;; before open curly in object initializer. new Foo* { }
539 ((and (looking-back
540 (concat "\\<new[ \t\n\f\v\r]+"
541 "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
542 "[A-Za-z_][[:alnum:]]*[\ t\n\f\v\r]*"))
543 (looking-at "[ \t\n\f\v\r]*{"))
544 t)
545
546 ;; put a vsemi after an ASPNET directive, like
547 ;; <%@ WebHandler Language="C#" Class="Handler" %>
548 ((looking-back (concat csharp-aspnet-directive-re "$") nil t)
549 t)
550
551 ;; put a vsemi after an attribute, as with
552 ;; [XmlElement]
553 ;; Except when the attribute is used within a line of code, as
554 ;; specifying something for a parameter.
555 ((c-safe (backward-sexp) t)
556 (cond
557 ((re-search-forward
558 (concat
559 "\\(\\["
560 "[ \t\n\r\f\v]*"
561 "\\("
562 "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
563 "[A-Za-z_][[:alnum:]]*"
564 "\\)"
565 "[^]]*\\]\\)"
566 )
567 (1+ pos-or-point) t)
568
569 (c-safe (backward-sexp))
570 (c-backward-syntactic-ws)
571 (cond
572
573 ((eq (char-before) 93) ;; close sq brace (a previous attribute)
574 (csharp--at-vsemi-p (point))) ;; recurse
575
576 ((or
577 (eq (char-before) 59) ;; semicolon
578 (eq (char-before) 123) ;; open curly
579 (eq (char-before) 125)) ;; close curly
580 t)
581
582 ;; attr is used within a line of code
583 (t nil)))
584
585 (t nil)))
586
587 (t nil))
588 )))
589
590
591
592
593 (defun csharp-lineup-region (langelem)
594 "Indent all #region and #endregion blocks inline with code while
595 retaining normal column-zero indention for #if and the other
596 processing blocks.
597
598 To use this indenting just put the following in your emacs file:
599 (c-set-offset 'cpp-macro 'csharp-lineup-region)
600
601 An alternative is to use `csharp-lineup-if-and-region'.
602 "
603
604 (save-excursion
605 (back-to-indentation)
606 (if (re-search-forward "#\\(end\\)?region" (c-point 'eol) [0]) 0 [0])))
607
608
609
610
611
612 (defun csharp-lineup-if-and-region (langelem)
613
614 "Indent all #region/endregion blocks and #if/endif blocks inline
615 with code while retaining normal column-zero indention for any
616 other processing blocks.
617
618 To use this indenting just put the following in your emacs file:
619 (c-set-offset 'cpp-macro 'csharp-lineup-if-and-region)
620
621 Another option is to use `csharp-lineup-region'.
622
623 "
624 (save-excursion
625 (back-to-indentation)
626 (if (re-search-forward "#\\(\\(end\\)?\\(if\\|region\\)\\|else\\)" (c-point 'eol) [0]) 0 [0])))
627
628
629
630
631 (defun csharp-in-literal (&optional lim detect-cpp)
632 "Return the type of literal point is in, if any.
633 Basically this works like `c-in-literal' except it doesn't
634 use or fill the cache (`c-in-literal-cache').
635
636 The return value is a symbol: `c' if in a C-style comment, `c++'
637 if in a C++ style comment, `string' if in a string literal,
638 `pound' if DETECT-CPP is non-nil and in a preprocessor line, or
639 nil if somewhere else. Optional LIM is used as the backward
640 limit of the search. If omitted, or nil, `c-beginning-of-syntax'
641 is used.
642
643 Note that this function might do hidden buffer changes. See the
644 comment at the start of cc-engine.el for more info."
645
646 (let ((rtn
647 (save-excursion
648 (let* ((pos (point))
649 (lim (or lim (progn
650 (c-beginning-of-syntax)
651 (point))))
652 (state (parse-partial-sexp lim pos)))
653 (csharp-log 4 "parse lim(%d) state: %s" lim (prin1-to-string state))
654 (cond
655 ((elt state 3)
656 (csharp-log 4 "in literal string (%d)" pos)
657 'string)
658 ((elt state 4)
659 (csharp-log 4 "in literal comment (%d)" pos)
660 (if (elt state 7) 'c++ 'c))
661 ((and detect-cpp (c-beginning-of-macro lim)) 'pound)
662 (t nil))))))
663 rtn))
664
665
666
667 (defun csharp-insert-open-brace ()
668 "Intelligently insert a pair of curly braces. This fn should be
669 bound to the open-curly brace, with
670
671 (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
672
673 The default binding for an open curly brace in cc-modes is often
674 `c-electric-brace' or `skeleton-pair-insert-maybe'. The former
675 can be configured to insert newlines around braces in various
676 syntactic positions. The latter inserts a pair of braces and
677 then does not insert a newline, and does not indent.
678
679 This fn provides another option, with some additional
680 intelligence for csharp-mode. When you type an open curly, the
681 appropriate pair of braces appears, with spacing and indent set
682 in a context-sensitive manner:
683
684 - Within a string literal, you just get a pair of braces, and
685 point is set between them. This works for String.Format()
686 purposes.
687
688 - Following = or [], as in an array assignment, you get a pair
689 of braces, with two intervening spaces, with a semincolon
690 appended. Point is left between the braces.
691
692 - Following \"new Foo\", it's an object initializer. You get:
693 newline, open brace, newline, newline, close, semi. Point is
694 left on the blank line between the braces. Unless the object
695 initializer is within an array initializer, in which case, no
696 newlines, and the semi is replaced with a comma. (Try it to
697 see what this means).
698
699 - Following => , implying a lambda, you get an open/close pair,
700 with two intervening spaces, no semicolon, and point on the
701 2nd space.
702
703 - Otherwise, you get a newline, the open curly, followed by
704 an empty line and the closing curly on the line following,
705 with point on the empty line.
706
707
708 There may be another way to get this to happen appropriately just
709 within emacs, but I could not figure out how to do it. So I
710 wrote this alternative.
711
712 "
713 (interactive)
714 (let
715 (tpoint
716 (in-string (string= (csharp-in-literal) "string"))
717 (preceding3
718 (save-excursion
719 (and
720 (skip-chars-backward " \t")
721 (> (- (point) 2) (point-min))
722 (buffer-substring-no-properties (point) (- (point) 3)))))
723 (one-word-back
724 (save-excursion
725 (backward-word 2)
726 (thing-at-point 'word))))
727
728 (cond
729
730 ;; Case 1: inside a string literal?
731 ;; --------------------------------------------
732 ;; If so, then just insert a pair of braces and put the point
733 ;; between them. The most common case is a format string for
734 ;; String.Format() or Console.WriteLine().
735 (in-string
736 (self-insert-command 1)
737 (insert "}")
738 (backward-char))
739
740 ;; Case 2: the open brace starts an array initializer.
741 ;; --------------------------------------------
742 ;; When the last non-space was an equals sign or square brackets,
743 ;; then it's an initializer.
744 ((save-excursion
745 (and (c-safe (backward-sexp) t)
746 (looking-at "\\(\\w+\\b *=\\|[[]]+\\)")))
747 (self-insert-command 1)
748 (insert " };")
749 (backward-char 3))
750
751 ;; Case 3: the open brace starts an instance initializer
752 ;; --------------------------------------------
753 ;; If one-word-back was "new", then it's an object initializer.
754 ((string= one-word-back "new")
755 (csharp-log 2 "object initializer")
756 (setq tpoint (point)) ;; prepare to indent-region later
757 (backward-word 2)
758 (c-backward-syntactic-ws)
759 (if (or (eq (char-before) ?,) ;; comma
760 (and (eq (char-before) 123) ;; open curly
761 (progn (backward-char)
762 (c-backward-syntactic-ws)
763 (looking-back "\\[\\]"))))
764 (progn
765 ;; within an array - emit no newlines
766 (goto-char tpoint)
767 (self-insert-command 1)
768 (insert " },")
769 (backward-char 3))
770
771 (progn
772 (goto-char tpoint)
773 (newline)
774 (self-insert-command 1)
775 (newline-and-indent)
776 (newline)
777 (insert "};")
778 (c-indent-region tpoint (point))
779 (forward-line -1)
780 (indent-according-to-mode)
781 (end-of-line))))
782
783
784 ;; Case 4: a lambda initialier.
785 ;; --------------------------------------------
786 ;; If the open curly follows =>, then it's a lambda initializer.
787 ((string= (substring preceding3 -2) "=>")
788 (csharp-log 2 "lambda init")
789 (self-insert-command 1)
790 (insert " }")
791 (backward-char 2))
792
793 ;; else, it's a new scope. (if, while, class, etc)
794 (t
795 (save-excursion
796 (csharp-log 2 "new scope")
797 (set-mark (point)) ;; prepare to indent-region later
798 ;; check if the prior sexp is on the same line
799 (if (save-excursion
800 (let ((curline (line-number-at-pos))
801 (aftline (progn
802 (if (c-safe (backward-sexp) t)
803 (line-number-at-pos)
804 -1))))
805 (= curline aftline)))
806 (newline-and-indent))
807 (self-insert-command 1)
808 (c-indent-line-or-region)
809 (end-of-line)
810 (newline)
811 (insert "}")
812 ;;(c-indent-command) ;; not sure of the difference here
813 (c-indent-line-or-region)
814 (forward-line -1)
815 (end-of-line)
816 (newline-and-indent)
817 ;; point ends up on an empty line, within the braces, properly indented
818 (setq tpoint (point)))
819
820 (goto-char tpoint)))))
821
822
823 ;; ==================================================================
824 ;; end of csharp-mode utility and feature defuns
825 ;; ==================================================================
826
827
828
829 ;; ==================================================================
830 ;; c# values for "language constants" defined in cc-langs.el
831 ;; ==================================================================
832
833 (c-lang-defconst c-at-vsemi-p-fn
834 csharp 'csharp--at-vsemi-p)
835
836
837 ;; This c-opt-after-id-concat-key is a regexp that matches
838 ;; dot. In other words: "\\(\\.\\)"
839 ;; Not sure why this needs to be so complicated.
840 ;; This const is now internal (obsolete); need to move to
841 ;; c-after-id-concat-ops. I don't yet understand the meaning
842 ;; of that variable, so for now. . . .
843
844 ;; (c-lang-defconst c-opt-after-id-concat-key
845 ;; csharp (if (c-lang-const c-opt-identifier-concat-key)
846 ;; (c-lang-const c-symbol-start)))
847
848 (c-lang-defconst c-opt-after-id-concat-key
849 csharp "[[:alpha:]_]" )
850
851
852
853
854 ;; The matchers elements can be of many forms. It gets pretty
855 ;; complicated. Do a describe-variable on font-lock-keywords to get a
856 ;; description. (Why on font-lock-keywords? I don't know, but that's
857 ;; where you get the help.)
858 ;;
859 ;; Aside from the provided documentation, the other option of course, is
860 ;; to look in the source code as an example for what to do. The source
861 ;; in cc-fonts uses a defun c-make-font-lock-search-function to produce
862 ;; most of the matchers. Called this way:
863 ;;
864 ;; (c-make-font-lock-search-function regexp '(A B c))
865 ;;
866 ;; The REGEXP is used in re-search-forward, and if there's a match, then
867 ;; A is called within a save-match-data. If B and C are non-nil, they
868 ;; are called as pre and post blocks, respecitvely.
869 ;;
870 ;; Anyway the c-make-font-lock-search-function works for a single regex,
871 ;; but more complicated scenarios such as those intended to match and
872 ;; fontify object initializers, call for a hand-crafted lambda.
873 ;;
874 ;; The object initializer is special because matching on it must
875 ;; allow nesting.
876 ;;
877 ;; In c#, the object initializer block is used directly after a
878 ;; constructor, like this:
879 ;;
880 ;; new MyType
881 ;; {
882 ;; Prop1 = "foo"
883 ;; }
884 ;;
885 ;; csharp-mode needs to fontify the properties in the
886 ;; initializer block in font-lock-variable-name-face. The key thing is
887 ;; to set the text property on the open curly, using type c-type and
888 ;; value c-decl-id-start. This apparently allows `parse-partial-sexp' to
889 ;; do the right thing, later.
890 ;;
891 ;; This simple case is easy to handle in a regex, using the basic
892 ;; `c-make-font-lock-search-function' form. But the general syntax for a
893 ;; constructor + object initializer in C# is more complex:
894 ;;
895 ;; new MyType(..arglist..) {
896 ;; Prop1 = "foo"
897 ;; }
898 ;;
899 ;; A simple regex match won't satisfy here, because the ..arglist.. can
900 ;; be anything, including calls to other constructors, potentially with
901 ;; object initializer blocks. This may nest arbitrarily deeply, and the
902 ;; regex in emacs doesn't support balanced matching. Therefore there's
903 ;; no way to match on the "outside" pair of parens, to find the relevant
904 ;; open curly. What's necessary is to do the match on "new MyType" then
905 ;; skip over the sexp defined by the parens, then set the text property on
906 ;; the appropriate open-curly.
907 ;;
908 ;; To make that happen, it's good to have insight into what the matcher
909 ;; really does. The output of `c-make-font-lock-search-function' before
910 ;; byte-compiling, is:
911 ;;
912 ;; (lambda (limit)
913 ;; (let ((parse-sexp-lookup-properties
914 ;; (cc-eval-when-compile
915 ;; (boundp 'parse-sexp-lookup-properties))))
916 ;; (while (re-search-forward REGEX limit t)
917 ;; (unless
918 ;; (progn
919 ;; (goto-char (match-beginning 0))
920 ;; (c-skip-comments-and-strings limit))
921 ;; (goto-char (match-end 0))
922 ;; (progn
923 ;; B
924 ;; (save-match-data A)
925 ;; C ))))
926 ;; nil)
927 ;;
928 ;; csharp-mode uses this hand-crafted form of a matcher to handle the
929 ;; general case for constructor + object initializer, within
930 ;; `c-basic-matchers-after' .
931 ;;
932
933
934
935
936 ;; (defun c-make-font-lock-search-function (regexp &rest highlights)
937 ;; ;; This function makes a byte compiled function that works much like
938 ;; ;; a matcher element in `font-lock-keywords'. It cuts out a little
939 ;; ;; bit of the overhead compared to a real matcher. The main reason
940 ;; ;; is however to pass the real search limit to the anchored
941 ;; ;; matcher(s), since most (if not all) font-lock implementations
942 ;; ;; arbitrarily limits anchored matchers to the same line, and also
943 ;; ;; to insulate against various other irritating differences between
944 ;; ;; the different (X)Emacs font-lock packages.
945 ;; ;;
946 ;; ;; REGEXP is the matcher, which must be a regexp. Only matches
947 ;; ;; where the beginning is outside any comment or string literal are
948 ;; ;; significant.
949 ;; ;;
950 ;; ;; HIGHLIGHTS is a list of highlight specs, just like in
951 ;; ;; `font-lock-keywords', with these limitations: The face is always
952 ;; ;; overridden (no big disadvantage, since hits in comments etc are
953 ;; ;; filtered anyway), there is no "laxmatch", and an anchored matcher
954 ;; ;; is always a form which must do all the fontification directly.
955 ;; ;; `limit' is a variable bound to the real limit in the context of
956 ;; ;; the anchored matcher forms.
957 ;; ;;
958 ;; ;; This function does not do any hidden buffer changes, but the
959 ;; ;; generated functions will. (They are however used in places
960 ;; ;; covered by the font-lock context.)
961 ;;
962 ;; ;; Note: Replace `byte-compile' with `eval' to debug the generated
963 ;; ;; lambda easier.
964 ;; (byte-compile
965 ;; `(lambda (limit)
966 ;; (let (;; The font-lock package in Emacs is known to clobber
967 ;; ;; `parse-sexp-lookup-properties' (when it exists).
968 ;; (parse-sexp-lookup-properties
969 ;; (cc-eval-when-compile
970 ;; (boundp 'parse-sexp-lookup-properties))))
971 ;; (while (re-search-forward ,regexp limit t)
972 ;; (unless (progn
973 ;; (goto-char (match-beginning 0))
974 ;; (c-skip-comments-and-strings limit))
975 ;; (goto-char (match-end 0))
976 ;; ,@(mapcar
977 ;; (lambda (highlight)
978 ;; (if (integerp (car highlight))
979 ;; (progn
980 ;; (unless (eq (nth 2 highlight) t)
981 ;; (error
982 ;; "The override flag must currently be t in %s"
983 ;; highlight))
984 ;; (when (nth 3 highlight)
985 ;; (error
986 ;; "The laxmatch flag may currently not be set in %s"
987 ;; highlight))
988 ;; `(save-match-data
989 ;; (c-put-font-lock-face
990 ;; (match-beginning ,(car highlight))
991 ;; (match-end ,(car highlight))
992 ;; ,(elt highlight 1))))
993 ;; (when (nth 3 highlight)
994 ;; (error "Match highlights currently not supported in %s"
995 ;; highlight))
996 ;; `(progn
997 ;; ,(nth 1 highlight)
998 ;; (save-match-data ,(car highlight))
999 ;; ,(nth 2 highlight))))
1000 ;; highlights))))
1001 ;; nil))
1002 ;; )
1003
1004
1005 (c-lang-defconst c-basic-matchers-before
1006 csharp `(
1007 ;;;; Font-lock the attributes by searching for the
1008 ;;;; appropriate regex and marking it as TODO.
1009 ;;,`(,(concat "\\(" csharp-attribute-regex "\\)")
1010 ;; 0 font-lock-function-name-face)
1011
1012 ;; Put a warning face on the opener of unclosed strings that
1013 ;; can't span lines. Later font
1014 ;; lock packages have a `font-lock-syntactic-face-function' for
1015 ;; this, but it doesn't give the control we want since any
1016 ;; fontification done inside the function will be
1017 ;; unconditionally overridden.
1018 ,(c-make-font-lock-search-function
1019 ;; Match a char before the string starter to make
1020 ;; `c-skip-comments-and-strings' work correctly.
1021 (concat ".\\(" c-string-limit-regexp "\\)")
1022 '((c-font-lock-invalid-string)))
1023
1024
1025 ;; Fontify keyword constants.
1026 ,@(when (c-lang-const c-constant-kwds)
1027 (let ((re (c-make-keywords-re nil
1028 (c-lang-const c-constant-kwds))))
1029 `((eval . (list ,(concat "\\<\\(" re "\\)\\>")
1030 1 c-constant-face-name)))))
1031
1032
1033 ;; Fontify the namespaces that follow using statements.
1034 ;; This regex handles the optional alias, as well.
1035 ,`(,(concat
1036 "\\<\\(using\\)[ \t\n\f\v\r]+"
1037 "\\(?:"
1038 "\\([A-Za-z_][[:alnum:]]*\\)"
1039 "[ \t\n\f\v\r]*="
1040 "[ \t\n\f\v\r]*"
1041 "\\)?"
1042 "\\(\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*[A-Za-z_][[:alnum:]]*\\)"
1043 "[ \t\n\f\v\r]*;")
1044 (2 font-lock-constant-face t t)
1045 (3 font-lock-constant-face))
1046
1047
1048 ;; Fontify all keywords except the primitive types.
1049 ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
1050 1 font-lock-keyword-face)
1051
1052
1053 ;; Fontify leading identifiers as a reference? in fully
1054 ;; qualified names like "Foo.Bar".
1055 ,@(when (c-lang-const c-opt-identifier-concat-key)
1056 `((,(byte-compile
1057 `(lambda (limit)
1058 (csharp-log 3 "bmb reference? p(%d) L(%d)" (point) limit)
1059 (while (re-search-forward
1060 ,(concat "\\(\\<" ;; 1
1061 "\\(" ;; 2
1062 ;;"[A-Z]";; uppercase - assume upper = classname
1063 "[A-Za-z_]" ;; any old
1064 "[A-Za-z0-9_]*" ;; old: (c-lang-const c-symbol-key)
1065 "\\)"
1066 "[ \t\n\r\f\v]*"
1067 "\\." ;;(c-lang-const c-opt-identifier-concat-key)
1068 "[ \t\n\r\f\v]*"
1069 "\\)" ;; 1 ends
1070 "\\("
1071 "[[:alpha:]_][A-Za-z0-9_]*" ;; start of another symbolname
1072 "\\)" ;; 3 ends
1073 )
1074 limit t)
1075 (csharp-log 3 "bmb ref? B(%d)" (match-beginning 0))
1076 (unless (progn
1077 (goto-char (match-beginning 0))
1078 (c-skip-comments-and-strings limit))
1079 (let* ((prefix (match-string 2))
1080 (me1 (match-end 1))
1081 (first-char (string-to-char prefix))
1082 (is-upper (and (>= first-char 65)
1083 (<= first-char 90))))
1084 (csharp-log 3 " - class/intf ref (%s)" prefix)
1085 ;; only put face if not there already
1086 (or (get-text-property (match-beginning 2) 'face)
1087 (c-put-font-lock-face (match-beginning 2)
1088 (match-end 2)
1089 (if is-upper
1090 font-lock-type-face ;; it's a type!
1091 font-lock-variable-name-face)))
1092
1093 (goto-char (match-end 3))
1094 (c-forward-syntactic-ws limit)
1095
1096 ;; now, maybe fontify the thing afterwards, too
1097 (let ((c (char-after)))
1098 (csharp-log 3 " - now lkg at c(%c)" c)
1099
1100 (cond
1101
1102 ((= c 40) ;; open paren
1103 (or (get-text-property (match-beginning 3) 'face)
1104 (c-put-font-lock-face (match-beginning 3)
1105 (match-end 3)
1106 font-lock-function-name-face))
1107 (goto-char (match-end 3)))
1108
1109 ;; these all look like variables or properties
1110 ((or (= c 59) ;; semicolon
1111 (= c 91) ;; open sq brack
1112 (= c 41) ;; close paren
1113 (= c 44) ;; ,
1114 (= c 33) ;; !
1115 (= c 124) ;; |
1116 (= c 61) ;; =
1117 (= c 43) ;; +
1118 (= c 45) ;; -
1119 (= c 42) ;; *
1120 (= c 47)) ;; /
1121 (or (get-text-property (match-beginning 3) 'face)
1122 (c-put-font-lock-face (match-beginning 3)
1123 (match-end 3)
1124 font-lock-variable-name-face))
1125 (goto-char (match-end 3)))
1126
1127 (t
1128 (goto-char (match-end 1)))))))))))))
1129
1130 ))
1131
1132
1133
1134 (c-lang-defconst c-basic-matchers-after
1135 csharp `(
1136
1137 ;; option 1:
1138 ;; ,@(when condition
1139 ;; `((,(byte-compile
1140 ;; `(lambda (limit) ...
1141 ;;
1142 ;; option 2:
1143 ;; ,`((lambda (limit) ...
1144 ;;
1145 ;; I don't know how to avoid the (when condition ...) in the
1146 ;; byte-compiled version.
1147 ;;
1148 ;; X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+
1149
1150 ;; Case 1: invocation of constructor + maybe an object
1151 ;; initializer. Some possible examples that satisfy:
1152 ;;
1153 ;; new Foo ();
1154 ;;
1155 ;; new Foo () { };
1156 ;;
1157 ;; new Foo { };
1158 ;;
1159 ;; new Foo { Prop1= 7 };
1160 ;;
1161 ;; new Foo {
1162 ;; Prop1= 7
1163 ;; };
1164 ;;
1165 ;; new Foo {
1166 ;; Prop1= 7,
1167 ;; Prop2= "Fred"
1168 ;; };
1169 ;;
1170 ;; new Foo {
1171 ;; Prop1= new Bar()
1172 ;; };
1173 ;;
1174 ;; new Foo {
1175 ;; Prop1= new Bar { PropA = 5.6F }
1176 ;; };
1177 ;;
1178 ,@(when t
1179 `((,(byte-compile
1180 `(lambda (limit)
1181 (let ((parse-sexp-lookup-properties
1182 (cc-eval-when-compile
1183 (boundp 'parse-sexp-lookup-properties))))
1184
1185 (while (re-search-forward
1186 ,(concat "\\<new"
1187 "[ \t\n\r\f\v]+"
1188 "\\(\\(?:"
1189 (c-lang-const c-symbol-key)
1190 "\\.\\)*"
1191 (c-lang-const c-symbol-key)
1192 "\\)"
1193 )
1194 limit t)
1195 (unless
1196 (progn
1197 (goto-char (match-beginning 0))
1198 (c-skip-comments-and-strings limit))
1199
1200 (csharp-log 3 "ctor invoke? at %d" (match-beginning 1))
1201
1202 (save-match-data
1203 ;; next thing could be: [] () <> or {} or nothing (semicolon, comma).
1204
1205 ;; fontify the typename
1206 (c-put-font-lock-face (match-beginning 1)
1207 (match-end 1)
1208 'font-lock-type-face)
1209
1210 (goto-char (match-end 0))
1211 (c-forward-syntactic-ws limit)
1212 (if (eq (char-after) ?<) ;; ctor for generic type
1213 (progn
1214 (csharp-log 3 " - this is a generic type")
1215 ;; skip over <> safely
1216 (c-safe (c-forward-sexp 1) t)
1217 (c-forward-syntactic-ws)))
1218
1219 ;; now, could be [] or (..) or {..} or semicolon.
1220
1221 (csharp-log 3 " - looking for sexp")
1222
1223 (if (or
1224 (eq (char-after) ?{) ;; open curly
1225 (and (eq (char-after) 91) ;; open square
1226 (while (eq (char-after) 91)
1227 (c-safe (c-forward-sexp 1)))
1228 (eq (char-before) 93)) ;; close square
1229 (and (eq (char-after) 40) ;; open paren
1230 (c-safe (c-forward-sexp 1) t)))
1231
1232 (progn
1233 ;; at this point we've jumped over any intervening s-exp,
1234 ;; like sq brackets or parens.
1235 (c-forward-syntactic-ws)
1236 (csharp-log 3 " - after fwd-syn-ws point(%d)" (point))
1237 (csharp-log 3 " - next char: %c" (char-after))
1238 (if (eq (char-after) ?{)
1239 (let ((start (point))
1240 (end (if (c-safe (c-forward-sexp 1) t)
1241 (point) 0)))
1242 (csharp-log 3 " - open curly gets c-decl-id-start %d" start)
1243 (c-put-char-property start
1244 'c-type
1245 'c-decl-id-start)
1246 (goto-char start)
1247 (if (> end start)
1248 (progn
1249 (forward-char 1) ;; step over open curly
1250 (c-forward-syntactic-ws)
1251 (while (> end (point))
1252 ;; now, try to fontify/assign variables to any properties inside the curlies
1253 (csharp-log 3 " - inside open curly point(%d)" (point))
1254 (csharp-log 3 " - next char: %c" (char-after))
1255 ;; fontify each property assignment
1256 (if (re-search-forward
1257 (concat "\\(" (c-lang-const c-symbol-key) "\\)\\s*=")
1258 end t)
1259 (progn
1260 (csharp-log 3 " - found variable %d-%d"
1261 (match-beginning 1)
1262 (match-end 1))
1263 (c-put-font-lock-face (match-beginning 1)
1264 (match-end 1)
1265 'font-lock-variable-name-face)
1266 (goto-char (match-end 0))
1267 (c-forward-syntactic-ws)
1268 ;; advance to the next assignment, if possible
1269 (if (eq (char-after) ?@)
1270 (forward-char 1))
1271
1272 (if (c-safe (c-forward-sexp 1) t)
1273 (progn
1274 (forward-char 1)
1275 (c-forward-syntactic-ws))))
1276
1277 ;; else
1278 (csharp-log 3 " - no more assgnmts found")
1279 (goto-char end)))))
1280 )))))
1281
1282 (goto-char (match-end 0))
1283 )))
1284 nil))
1285 )))
1286
1287
1288 ;; Case 2: declaration of enum with or without an explicit
1289 ;; base type.
1290 ;;
1291 ;; Examples:
1292 ;;
1293 ;; public enum Foo { ... }
1294 ;;
1295 ;; public enum Foo : uint { ... }
1296 ;;
1297 ,@(when t
1298 `((,(byte-compile
1299 `(lambda (limit)
1300 (let ((parse-sexp-lookup-properties
1301 (cc-eval-when-compile
1302 (boundp 'parse-sexp-lookup-properties))))
1303 (while (re-search-forward
1304 ,(concat csharp-enum-decl-re
1305 "[ \t\n\r\f\v]*"
1306 "{")
1307 limit t)
1308
1309 (csharp-log 3 "enum? at %d" (match-beginning 0))
1310
1311 (unless
1312 (progn
1313 (goto-char (match-beginning 0))
1314 (c-skip-comments-and-strings limit))
1315 (progn
1316 (save-match-data
1317 (goto-char (match-end 0))
1318 (c-put-char-property (1- (point))
1319 'c-type
1320 'c-decl-id-start)
1321 (c-forward-syntactic-ws))
1322 (save-match-data
1323 (c-font-lock-declarators limit t nil))
1324 (goto-char (match-end 0))
1325 )
1326 )))
1327 nil))
1328 )))
1329
1330
1331 ;; Case 3: declaration of constructor
1332 ;;
1333 ;; Example:
1334 ;;
1335 ;; private Foo(...) {...}
1336 ;;
1337 ,@(when t
1338 `((,(byte-compile
1339 `(lambda (limit)
1340 (let ((parse-sexp-lookup-properties
1341 (cc-eval-when-compile
1342 (boundp 'parse-sexp-lookup-properties)))
1343 (found-it nil))
1344 (while (re-search-forward
1345 ,(concat
1346 "^[ \t\n\r\f\v]*"
1347 "\\(\\<\\(public\\|private\\|protected\\)\\)?[ \t\n\r\f\v]+"
1348 "\\(@?[[:alpha:]_][[:alnum:]_]*\\)" ;; name of constructor
1349 "[ \t\n\r\f\v]*"
1350 "\\("
1351 "("
1352 "\\)")
1353 limit t)
1354
1355 (unless
1356 (progn
1357 (goto-char (match-beginning 0))
1358 (c-skip-comments-and-strings limit))
1359
1360 (goto-char (match-end 0))
1361
1362 (csharp-log 3 "ctor decl? L(%d) B(%d) E(%d)"
1363 limit (match-beginning 0) (point))
1364
1365 (backward-char 1) ;; just left of the open paren
1366 (save-match-data
1367 ;; Jump over the parens, safely.
1368 ;; If it's an unbalanced paren, no problem,
1369 ;; do nothing.
1370 (if (c-safe (c-forward-sexp 1) t)
1371 (progn
1372 (c-forward-syntactic-ws)
1373 (cond
1374
1375 ;; invokes base or this constructor.
1376 ((re-search-forward
1377 ,(concat
1378 "\\(:[ \t\n\r\f\v]*\\(base\\|this\\)\\)"
1379 "[ \t\n\r\f\v]*"
1380 "("
1381 )
1382 limit t)
1383 (csharp-log 3 " - ctor with dependency?")
1384
1385 (goto-char (match-end 0))
1386 (backward-char 1) ;; just left of the open paren
1387 (csharp-log 3 " - before paren at %d" (point))
1388
1389 (if (c-safe (c-forward-sexp 1) t)
1390 (progn
1391 (c-forward-syntactic-ws)
1392 (csharp-log 3 " - skipped over paren pair %d" (point))
1393 (if (eq (char-after) ?{)
1394 (setq found-it t)))))
1395
1396 ;; open curly. no depedency on other ctor.
1397 ((eq (char-after) ?{)
1398 (csharp-log 3 " - no dependency, curly at %d" (point))
1399 (setq found-it t)))
1400
1401 )))
1402
1403 (if found-it
1404 ;; fontify the constructor symbol
1405 (c-put-font-lock-face (match-beginning 3)
1406 (match-end 3)
1407 'font-lock-function-name-face))
1408 (goto-char (match-end 0)))))
1409 nil)))))
1410
1411
1412 ;; Case 4: using clause. Without this, using (..) gets fontified as a fn.
1413 ,@(when t
1414 `((,(byte-compile
1415 `(lambda (limit)
1416 (let ((parse-sexp-lookup-properties
1417 (cc-eval-when-compile
1418 (boundp 'parse-sexp-lookup-properties))))
1419 (while (re-search-forward
1420 ,(concat "\\<\\(using\\)"
1421 "[ \t\n\r\f\v]*"
1422 "(")
1423 limit t)
1424
1425 (csharp-log 3 "using clause p(%d)" (match-beginning 0))
1426
1427 (unless
1428 (progn
1429 (goto-char (match-beginning 0))
1430 (c-skip-comments-and-strings limit))
1431
1432 (save-match-data
1433 (c-put-font-lock-face (match-beginning 1)
1434 (match-end 1)
1435 'font-lock-keyword-face)
1436 (goto-char (match-end 0))))))
1437 nil))
1438 )))
1439
1440 ;; Case 5: attributes
1441 ,`((lambda (limit)
1442 (let ((parse-sexp-lookup-properties
1443 (cc-eval-when-compile
1444 (boundp 'parse-sexp-lookup-properties))))
1445
1446 (while (re-search-forward
1447 ,(concat "[ \t\n\r\f\v]+"
1448 "\\(\\["
1449 "[ \t\n\r\f\v]*"
1450 "\\(?:\\(?:return\\|assembly\\)[ \t]*:[ \t]*\\)?"
1451 "\\("
1452 "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
1453 "[A-Za-z_][[:alnum:]]*"
1454 "\\)"
1455 "[^]]*\\]\\)"
1456 )
1457 limit t)
1458
1459 (csharp-log 3 "attribute? - %d limit(%d)" (match-beginning 1)
1460 limit)
1461
1462 (unless
1463 (progn
1464 (goto-char (match-beginning 1))
1465 (c-skip-comments-and-strings limit))
1466
1467 (let ((b2 (match-beginning 2))
1468 (e2 (match-end 2))
1469 (is-attr nil))
1470 (csharp-log 3 " - type match: %d - %d"
1471 b2 e2)
1472 (save-match-data
1473 (c-backward-syntactic-ws)
1474 (setq is-attr (or
1475 (eq (char-before) 59) ;; semicolon
1476 (eq (char-before) 93) ;; close square brace
1477 (eq (char-before) 123) ;; open curly
1478 (eq (char-before) 125) ;; close curly
1479 (save-excursion
1480 (c-beginning-of-statement-1)
1481 (looking-at
1482 "#\\(pragma\\|endregion\\|region\\|if\\|else\\|endif\\)"))
1483 )))
1484
1485 (if is-attr
1486 (progn
1487 (if (<= 3 csharp-log-level)
1488 (csharp-log 3 " - attribute: '%s'"
1489 (buffer-substring-no-properties b2 e2)))
1490 (c-put-font-lock-face b2 e2 'font-lock-type-face)))))
1491 (goto-char (match-end 0))
1492 ))
1493 nil))
1494
1495
1496 ;; Case 6: directive blocks for .aspx/.ashx/.ascx
1497 ,`((lambda (limit)
1498 (let ((parse-sexp-lookup-properties
1499 (cc-eval-when-compile
1500 (boundp 'parse-sexp-lookup-properties))))
1501
1502 (while (re-search-forward csharp-aspnet-directive-re limit t)
1503 (csharp-log 3 "aspnet template? - %d limit(%d)" (match-beginning 1)
1504 limit)
1505
1506 (unless
1507 (progn
1508 (goto-char (match-beginning 0))
1509 (c-skip-comments-and-strings limit))
1510
1511 (save-match-data
1512 (let ((end-open (+ (match-beginning 0) 3))
1513 (beg-close (- (match-end 0) 2)))
1514 (c-put-font-lock-face (match-beginning 0)
1515 end-open
1516 'font-lock-preprocessor-face)
1517
1518 (c-put-font-lock-face beg-close
1519 (match-end 0)
1520 'font-lock-preprocessor-face)
1521
1522 ;; fontify within the directive
1523 (while (re-search-forward
1524 ,(concat
1525 "\\("
1526 (c-lang-const c-symbol-key)
1527 "\\)"
1528 "=?"
1529 )
1530 beg-close t)
1531
1532 (c-put-font-lock-face (match-beginning 1)
1533 (match-end 1)
1534 'font-lock-keyword-face)
1535 (c-skip-comments-and-strings beg-close))
1536 ))
1537 (goto-char (match-end 0)))))
1538 nil))
1539
1540
1541 ;; ;; Case 5: #if
1542 ;; ,@(when t
1543 ;; `((,(byte-compile
1544 ;; `(lambda (limit)
1545 ;; (let ((parse-sexp-lookup-properties
1546 ;; (cc-eval-when-compile
1547 ;; (boundp 'parse-sexp-lookup-properties))))
1548 ;; (while (re-search-forward
1549 ;; "\\<\\(#if\\)[ \t\n\r\f\v]+\\([A-Za-z_][[:alnum:]]*\\)"
1550 ;; limit t)
1551 ;;
1552 ;; (csharp-log 3 "#if directive - %d" (match-beginning 1))
1553 ;;
1554 ;; (unless
1555 ;; (progn
1556 ;; (goto-char (match-beginning 0))
1557 ;; (c-skip-comments-and-strings limit))
1558 ;;
1559 ;; (save-match-data
1560 ;; (c-put-font-lock-face (match-beginning 2)
1561 ;; (match-end 2)
1562 ;; 'font-lock-variable-name-face)
1563 ;; (goto-char (match-end 0))))))
1564 ;; nil))
1565 ;; )))
1566
1567
1568 ;; ,`(,(c-make-font-lock-search-function
1569 ;; (concat "\\<new"
1570 ;; "[ \t\n\r\f\v]+"
1571 ;; "\\(\\(?:"
1572 ;; (c-lang-const c-symbol-key)
1573 ;; "\\.\\)*"
1574 ;; (c-lang-const c-symbol-key)
1575 ;; "\\)"
1576 ;; "[ \t\n\r\f\v]*"
1577 ;; "\\(?:"
1578 ;; "( *)[ \t\n\r\f\v]*" ;; optional ()
1579 ;; "\\)?"
1580 ;; "{")
1581 ;; '((c-font-lock-declarators limit t nil)
1582 ;; (save-match-data
1583 ;; (goto-char (match-end 0))
1584 ;; (c-put-char-property (1- (point)) 'c-type
1585 ;; 'c-decl-id-start)
1586 ;; (c-forward-syntactic-ws))
1587 ;; (goto-char (match-end 0)))))
1588
1589
1590
1591
1592 ;; Fontify labels after goto etc.
1593 ,@(when (c-lang-const c-before-label-kwds)
1594 `( ;; (Got three different interpretation levels here,
1595 ;; which makes it a bit complicated: 1) The backquote
1596 ;; stuff is expanded when compiled or loaded, 2) the
1597 ;; eval form is evaluated at font-lock setup (to
1598 ;; substitute c-label-face-name correctly), and 3) the
1599 ;; resulting structure is interpreted during
1600 ;; fontification.)
1601 (eval
1602 . ,(let* ((c-before-label-re
1603 (c-make-keywords-re nil
1604 (c-lang-const c-before-label-kwds))))
1605 `(list
1606 ,(concat "\\<\\(" c-before-label-re "\\)\\>"
1607 "\\s *"
1608 "\\(" ; identifier-offset
1609 (c-lang-const c-symbol-key)
1610 "\\)")
1611 (list ,(+ (regexp-opt-depth c-before-label-re) 2)
1612 c-label-face-name nil t))))))
1613
1614
1615
1616 ;; Fontify the clauses after various keywords.
1617 ,@(when (or (c-lang-const c-type-list-kwds)
1618 (c-lang-const c-ref-list-kwds)
1619 (c-lang-const c-colon-type-list-kwds)
1620 (c-lang-const c-paren-type-kwds))
1621 `((,(c-make-font-lock-search-function
1622 (concat "\\<\\("
1623 (c-make-keywords-re nil
1624 (append (c-lang-const c-type-list-kwds)
1625 (c-lang-const c-ref-list-kwds)
1626 (c-lang-const c-colon-type-list-kwds)
1627 (c-lang-const c-paren-type-kwds)))
1628 "\\)\\>")
1629 '((c-fontify-types-and-refs ((c-promote-possible-types t))
1630 (c-forward-keyword-clause 1)
1631 (if (> (point) limit) (goto-char limit))))))))
1632
1633
1634 ;; Fontify the name that follows each namespace declaration
1635 ;; this needs to be done in the matchers-after because
1636 ;; otherwise the namespace names get the font-lock-type-face,
1637 ;; due to the energetic efforts of c-forward-type.
1638 ,`("\\<\\(namespace\\)[ \t\n\r\f\v]+\\(\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*[A-Za-z_][[:alnum:]]*\\)"
1639 2 font-lock-constant-face t)
1640
1641
1642 ))
1643
1644
1645
1646 ;; C# does generics. Setting this to t tells the parser to put
1647 ;; parenthesis syntax on angle braces that surround a comma-separated
1648 ;; list.
1649 (c-lang-defconst c-recognize-<>-arglists
1650 csharp t)
1651
1652
1653 (c-lang-defconst c-identifier-key
1654 csharp (concat "\\([[:alpha:]_][[:alnum:]_]*\\)" ; 1
1655 "\\("
1656 "[ \t\n\r\f\v]*"
1657 "\\(\\.\\)" ;;(c-lang-const c-opt-identifier-concat-key)
1658 "[ \t\n\r\f\v]*"
1659 "\\(\\([[:alpha:]_][[:alnum:]_]*\\)\\)"
1660 "\\)*"))
1661
1662 ;; C# has a few rules that are slightly different than Java for
1663 ;; operators. This also removed the Java's "super" and replaces it
1664 ;; with the C#'s "base".
1665 (c-lang-defconst c-operators
1666 csharp `((prefix "base")))
1667
1668
1669 ;; C# uses CPP-like prefixes to mark #define, #region/endregion,
1670 ;; #if/else/endif, and #pragma. This regexp matches the prefix, not
1671 ;; including the beginning-of-line (BOL), and not including the term
1672 ;; after the prefix (define, pragma, region, etc). This regexp says
1673 ;; whitespace, followed by the prefix, followed by maybe more
1674 ;; whitespace.
1675
1676 (c-lang-defconst c-opt-cpp-prefix
1677 csharp "\\s *#\\s *")
1678
1679
1680 ;; there are no message directives in C#
1681 (c-lang-defconst c-cpp-message-directives
1682 csharp nil)
1683
1684 (c-lang-defconst c-cpp-expr-directives
1685 csharp '("if"))
1686
1687 (c-lang-defconst c-opt-cpp-macro-define
1688 csharp "define")
1689
1690 ;; $ is not a legal char in an identifier in C#. So we need to
1691 ;; create a csharp-specific definition of this constant.
1692 (c-lang-defconst c-symbol-chars
1693 csharp (concat c-alnum "_"))
1694
1695 ;; c-identifier-syntax-modifications by default defines $ as a word
1696 ;; syntax, which is not legal in C#. So, define our own lang-specific
1697 ;; value.
1698 (c-lang-defconst c-identifier-syntax-modifications
1699 csharp '((?_ . "w")))
1700
1701
1702
1703 (c-lang-defconst c-colon-type-list-kwds
1704 csharp '("class"))
1705
1706 (c-lang-defconst c-block-prefix-disallowed-chars
1707
1708 ;; Allow ':' for inherit list starters.
1709 csharp (set-difference (c-lang-const c-block-prefix-disallowed-chars)
1710 '(?: ?,)))
1711
1712
1713 (c-lang-defconst c-assignment-operators
1714 csharp '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|="))
1715
1716 (c-lang-defconst c-primitive-type-kwds
1717 ;; ECMA-344, S8
1718 csharp '("object" "string" "sbyte" "short" "int" "long" "byte"
1719 "ushort" "uint" "ulong" "float" "double" "bool" "char"
1720 "decimal" "void"))
1721
1722 ;; The keywords that define that the following is a type, such as a
1723 ;; class definition.
1724 (c-lang-defconst c-type-prefix-kwds
1725 ;; ECMA-344, S?
1726 csharp '("class" "interface" "struct")) ;; no enum here.
1727 ;; we want enum to be a brace list.
1728
1729
1730 ;; Type modifier keywords. They appear anywhere in types, but modify
1731 ;; instead of create one.
1732 (c-lang-defconst c-type-modifier-kwds
1733 ;; EMCA-344, S?
1734 csharp '("readonly" "const"))
1735
1736
1737 ;; Tue, 20 Apr 2010 16:02
1738 ;; need to verify that this works for lambdas...
1739 (c-lang-defconst c-special-brace-lists
1740 csharp '((?{ . ?}) ))
1741
1742
1743
1744 ;; dinoch
1745 ;; Thu, 22 Apr 2010 18:54
1746 ;;
1747 ;; No idea why this isn't getting set properly in the first place.
1748 ;; In cc-langs.el, it is set to the union of a bunch of things, none
1749 ;; of which include "new", or "enum".
1750 ;;
1751 ;; But somehow both of those show up in the resulting derived regexp.
1752 ;; This breaks indentation of instance initializers, such as
1753 ;;
1754 ;; var x = new Foo { ... };
1755 ;;
1756 ;; Based on my inspection, the existing c-lang-defconst should work!
1757 ;; I don't know how to fix this c-lang-defconst, so I am re-setting this
1758 ;; variable here, to provide the regex explicitly.
1759 ;;
1760 (c-lang-defconst c-decl-block-key
1761 csharp '"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)" )
1762
1763
1764 ;; Thu, 22 Apr 2010 14:29
1765 ;; I want this to handle var x = new Foo[] { ... };
1766 ;; not sure if necessary.
1767 (c-lang-defconst c-inexpr-brace-list-kwds
1768 csharp '("new"))
1769
1770
1771 ;; ;;(c-lang-defconst c-inexpr-class-kwds
1772 ;; ;; csharp '("new"))
1773
1774
1775
1776 (c-lang-defconst c-class-decl-kwds
1777 ;; EMCA-344, S?
1778 ;; don't include enum here, because we want it to be fontified as a brace
1779 ;; list, with commas delimiting the values. see c-brace-list-decl-kwds
1780 ;; below.
1781 csharp '("class" "interface" "struct" )) ;; no "enum"!!
1782
1783
1784 ;; The various modifiers used for class and method descriptions.
1785 (c-lang-defconst c-modifier-kwds
1786 csharp '("public" "partial" "private" "const" "abstract" "sealed"
1787 "protected" "ref" "out" "static" "virtual"
1788 "override" "params" "internal"))
1789
1790
1791 ;; Thu, 22 Apr 2010 23:02
1792 ;; Based on inspection of the cc-mode code, the c-protection-kwds
1793 ;; c-lang-const is used only for objective-c. So the value is
1794 ;; irrelevant for csharp.
1795 (c-lang-defconst c-protection-kwds
1796 csharp nil
1797 ;; csharp '("private" "protected" "public" "internal")
1798 )
1799
1800
1801 ;; Define the keywords that can have something following after them.
1802 (c-lang-defconst c-type-list-kwds
1803 csharp '("struct" "class" "interface" "is" "as"
1804 "delegate" "event" "set" "get" "add" "remove"))
1805
1806
1807 ;; This allows the classes after the : in the class declartion to be
1808 ;; fontified.
1809 (c-lang-defconst c-typeless-decl-kwds
1810 csharp '(":"))
1811
1812 ;; Sets up the enum to handle the list properly, and also the new
1813 ;; keyword to handle object initializers. This requires a modified
1814 ;; c-basic-matchers-after (see above) in order to correctly fontify C#
1815 ;; 3.0 object initializers.
1816 (c-lang-defconst c-brace-list-decl-kwds
1817 csharp '("enum" "new"))
1818
1819
1820 ;; Statement keywords followed directly by a substatement.
1821 ;; catch is not one of them, because catch has a paren (typically).
1822 (c-lang-defconst c-block-stmt-1-kwds
1823 csharp '("do" "try" "finally" "unsafe"))
1824
1825
1826 ;; Statement keywords followed by a paren sexp and then by a substatement.
1827 (c-lang-defconst c-block-stmt-2-kwds
1828 csharp '("for" "if" "switch" "while" "catch" "foreach" "using"
1829 "checked" "unchecked" "lock"))
1830
1831
1832 ;; Statements that break out of braces
1833 (c-lang-defconst c-simple-stmt-kwds
1834 csharp '("return" "continue" "break" "throw" "goto" ))
1835
1836 ;; Statements that allow a label
1837 ;; TODO?
1838 (c-lang-defconst c-before-label-kwds
1839 csharp nil)
1840
1841 ;; Constant keywords
1842 (c-lang-defconst c-constant-kwds
1843 csharp '("true" "false" "null"))
1844
1845 ;; Keywords that start "primary expressions."
1846 (c-lang-defconst c-primary-expr-kwds
1847 csharp '("this" "base"))
1848
1849 ;; Treat namespace as an outer block so class indenting
1850 ;; works properly.
1851 (c-lang-defconst c-other-block-decl-kwds
1852 csharp '("namespace"))
1853
1854 (c-lang-defconst c-other-kwds
1855 csharp '("sizeof" "typeof" "is" "as" "yield"
1856 "where" "select" "in" "from"))
1857
1858 (c-lang-defconst c-overloadable-operators
1859 ;; EMCA-344, S14.2.1
1860 csharp '("+" "-" "*" "/" "%" "&" "|" "^"
1861 "<<" ">>" "==" "!=" ">" "<" ">=" "<="))
1862
1863
1864 ;; This c-cpp-matchers stuff is used for fontification.
1865 ;; see cc-font.el
1866 ;;
1867
1868 ;; There's no preprocessor in C#, but there are still compiler
1869 ;; directives to fontify: "#pragma", #region/endregion, #define, #undef,
1870 ;; #if/else/endif. (The definitions for the extra keywords above are
1871 ;; enough to incorporate them into the fontification regexps for types
1872 ;; and keywords, so no additional font-lock patterns are required for
1873 ;; keywords.)
1874
1875 (c-lang-defconst c-cpp-matchers
1876 csharp (cons
1877 ;; Use the eval form for `font-lock-keywords' to be able to use
1878 ;; the `c-preprocessor-face-name' variable that maps to a
1879 ;; suitable face depending on the (X)Emacs version.
1880 '(eval . (list "^\\s *\\(#pragma\\|undef\\|define\\)\\>\\(.*\\)"
1881 (list 1 c-preprocessor-face-name)
1882 '(2 font-lock-string-face)))
1883 ;; There are some other things in `c-cpp-matchers' besides the
1884 ;; preprocessor support, so include it.
1885 (c-lang-const c-cpp-matchers)))
1886
1887
1888
1889 ;; Custom variables
1890 ;;;###autoload
1891 (defcustom csharp-mode-hook nil
1892 "*Hook called by `csharp-mode'."
1893 :type 'hook
1894 :group 'csharp)
1895
1896 ;; The following fn allows this:
1897 ;; (csharp-log 3 "scan result...'%s'" state)
1898
1899 (defcustom csharp-log-level 0
1900 "The current log level for CSharp-mode-specific operations.
1901 This is used in particular by the verbatim-literal
1902 string scanning.
1903
1904 Most other csharp functions are not instrumented.
1905 0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG, 4 = SHUTUP ALREADY. "
1906 :type 'integer
1907 :group 'csharp)
1908
1909
1910 ;;;###autoload
1911 (defcustom csharp-want-flymake-fixup t
1912 "*Whether to enable the builtin C# support for flymake. This is meaningful
1913 only if flymake is loaded."
1914 :type 'boolean :group 'csharp)
1915
1916 ;;;###autoload
1917 (defcustom csharp-want-yasnippet-fixup t
1918 "*Whether to enable the builtin C# support for yasnippet. This is meaningful
1919 only if flymake is loaded."
1920 :type 'boolean :group 'csharp)
1921
1922
1923 ;;;###autoload
1924 (defcustom csharp-want-imenu t
1925 "*Whether to generate a buffer index via imenu for C# buffers."
1926 :type 'boolean :group 'csharp)
1927
1928
1929 ;;;###autoload
1930 (defcustom csharp-make-tool "nmake.exe"
1931 "*The make tool to use. Defaults to nmake, found on path. Specify
1932 a full path or alternative program name, to tell csharp-mode to use
1933 a different make tool in compile commands.
1934
1935 See also, `csharp-msbuild-tool'.
1936
1937 "
1938 :type 'string :group 'csharp)
1939
1940
1941 ;;;###autoload
1942 (defcustom csharp-msbuild-tool "msbuild.exe"
1943 "*The tool to use to build .csproj files. Defaults to msbuild, found on
1944 path. Specify a full path or alternative program name, to tell csharp-mode
1945 to use a different make tool in compile commands.
1946
1947 See also, `csharp-make-tool'.
1948
1949 "
1950 :type 'string :group 'csharp)
1951
1952
1953 ;;;###autoload
1954 (defcustom csharp-cmd-line-limit 28
1955 "The number of lines at the top of the file to look in, to find
1956 the command that csharp-mode will use to compile the current
1957 buffer, or the command \"stub\" that csharp-mode will use to
1958 check the syntax of the current buffer via flymake.
1959
1960 If the value of this variable is zero, then csharp-mode looks
1961 everywhere in the file. If the value is positive, then only in
1962 the first N lines. If negative, then only in the final N lines.
1963
1964 The line should appear in a comment inside the C# buffer.
1965
1966
1967 Compile
1968 --------
1969
1970 In the case of compile, the compile command must be prefixed with
1971 \"compile:\". For example,
1972
1973 // compile: csc.exe /r:Hallo.dll Arfie.cs
1974
1975
1976 This command will be suggested as the compile command when the
1977 user invokes `compile' for the first time.
1978
1979
1980 Flymake
1981 --------
1982
1983 In the case of flymake, the command \"stub\" string must be
1984 prefixed with \"flymake:\". For example,
1985
1986 // flymake: DOTNETDIR\csc.exe /target:netmodule /r:foo.dll @@FILE@@
1987
1988 In the case of flymake, the string should NOT include the name of
1989 the file for the buffer being checked. Instead, use the token
1990 @@FILE@@ . csharp-mode will replace this token with the name of
1991 the source file to compile, before passing the command to flymake
1992 to run it.
1993
1994 If for some reason the command is invalid or illegal, flymake
1995 will report an error and disable itself.
1996
1997 It might be handy to run fxcop, for example, via flymake.
1998
1999 // flymake: fxcopcmd.exe /c /f:MyLibrary.dll
2000
2001
2002
2003 In all cases
2004 ------------
2005
2006 Be sure to specify the proper path for your csc.exe, whatever
2007 version that might be, or no path if you want to use the system
2008 PATH search.
2009
2010 If the buffer depends on external libraries, then you will want
2011 to include /R arguments to that csc.exe command.
2012
2013 To be clear, this variable sets the number of lines to search for
2014 the command. This cariable is an integer.
2015
2016 If the marker string (either \"compile:\" or \"flymake:\"
2017 is present in the given set of lines, csharp-mode will take
2018 anything after the marker string as the command to run.
2019
2020 "
2021 :type 'integer :group 'csharp)
2022
2023
2024
2025 (defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
2026 "Minimal highlighting for C# mode.")
2027
2028 (defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
2029 "Fast normal highlighting for C# mode.")
2030
2031 (defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
2032 "Accurate normal highlighting for C# mode.")
2033
2034 (defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
2035 "Default expressions to highlight in C# mode.")
2036
2037
2038 (defvar csharp-mode-syntax-table nil
2039 "Syntax table used in csharp-mode buffers.")
2040 (or csharp-mode-syntax-table
2041 (setq csharp-mode-syntax-table
2042 (funcall (c-lang-const c-make-mode-syntax-table csharp))))
2043
2044 (defvar csharp-mode-abbrev-table nil
2045 "Abbreviation table used in csharp-mode buffers.")
2046 (c-define-abbrev-table 'csharp-mode-abbrev-table
2047 ;; Keywords that if they occur first on a line might alter the
2048 ;; syntactic context, and which therefore should trig reindentation
2049 ;; when they are completed.
2050 '(("else" "else" c-electric-continued-statement 0)
2051 ("while" "while" c-electric-continued-statement 0)
2052 ("catch" "catch" c-electric-continued-statement 0)
2053 ("finally" "finally" c-electric-continued-statement 0)))
2054
2055 (defvar csharp-mode-map (let ((map (c-make-inherited-keymap)))
2056 ;; Add bindings which are only useful for C#
2057 map)
2058 "Keymap used in csharp-mode buffers.")
2059
2060
2061 (defvar csharp--yasnippet-has-been-fixed nil
2062 "indicates whether yasnippet has been patched for use with csharp.
2063 Intended for internal use only.")
2064
2065 (defvar csharp--flymake-has-been-installed nil
2066 "one-time use boolean, to check whether csharp tweaks for flymake (advice
2067 etc) have been installed.")
2068
2069 (defvar csharp-flymake-csc-arguments
2070 (list "/t:module" "/nologo")
2071 "A list of arguments to use with the csc.exe
2072 compiler, when using flymake with a
2073 direct csc.exe build for syntax checking purposes.")
2074
2075
2076 (defvar csharp-flymake-aux-error-info nil
2077 "a list of auxiliary flymake error info items. Each item in the
2078 list is a pair, consisting of a line number and a column number.
2079 This info is set by advice to flymake-parse-line, and used by
2080 advice attached to flymake-goto-line, to navigate to the proper
2081 error column when possible. ")
2082
2083
2084 (defvar csharp-flymake-csc-error-pattern
2085 "^[ \t]*\\([_A-Za-z0-9][^(]+\\.cs\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) ?: \\(\\(error\\|warning\\) +:? *C[SA][0-9]+ *:[ \t\n]*\\(.+\\)\\)"
2086 "The regex pattern for C# compiler error messages. Follows
2087 the same form as an entry in `flymake-err-line-patterns'. The
2088 value is a STRING, a regex.")
2089
2090 ;; TODO
2091 ;; Defines our constant for finding attributes.
2092 ;;(defconst csharp-attribute-regex "\\[\\([XmlType]+\\)(")
2093 ;;(defconst csharp-attribute-regex "\\[\\(.\\)")
2094 ;; This doesn't work because the string regex happens before this point
2095 ;; and getting the font-locking to work before and after is fairly difficult
2096 ;;(defconst csharp-attribute-regex
2097 ;; (concat
2098 ;; "\\[[a-zA-Z][ \ta-zA-Z0-9.]+"
2099 ;; "\\((.*\\)?"
2100 ;;))
2101
2102
2103 ;; ==================================================================
2104 ;; end of c# values for "language constants" defined in cc-langs.el
2105 ;; ==================================================================
2106
2107
2108
2109
2110
2111 ;; ========================================================================
2112 ;; Flymake integration
2113
2114 (defun csharp-flymake-init ()
2115 (csharp-flymake-init-impl
2116 'flymake-create-temp-inplace t t 'csharp-flymake-get-cmdline))
2117
2118 (defun csharp-flymake-init-impl (create-temp-f use-relative-base-dir use-relative-source get-cmdline-f)
2119 "Create syntax check command line for a directly checked source file.
2120 Use CREATE-TEMP-F for creating temp copy."
2121 (let* ((args nil)
2122 (temp-source-file-name (flymake-init-create-temp-buffer-copy create-temp-f)))
2123 (setq args (flymake-get-syntax-check-program-args
2124 temp-source-file-name "."
2125 use-relative-base-dir use-relative-source
2126 get-cmdline-f))
2127 args))
2128
2129
2130 (defun csharp-flymake-cleanup ()
2131 "Delete the temporary .netmodule file created in syntax checking
2132 a C# buffer, then call through to flymake-simple-cleanup."
2133
2134 (if flymake-temp-source-file-name
2135 (progn
2136 (let* ((netmodule-name
2137 (concat (file-name-sans-extension flymake-temp-source-file-name)
2138 ".netmodule"))
2139 (expanded-netmodule-name (expand-file-name netmodule-name ".")))
2140 (if (file-exists-p expanded-netmodule-name)
2141 (flymake-safe-delete-file expanded-netmodule-name)))
2142 ))
2143 (flymake-simple-cleanup))
2144
2145
2146 (defun csharp-split-string-respecting-quotes (s)
2147 "splits a string into tokens, respecting double quotes
2148 For example, the string 'This is \"a string\"' will be split into 3 tokens.
2149
2150 More pertinently, the string
2151 'csc /t:module /R:\"c:\abba dabba\dooo\Foo.dll\"'
2152
2153 ...will be split into 3 tokens.
2154
2155 This fn also removes quotes from the tokens that have them. This is for
2156 compatibility with flymake and the process-start fn.
2157
2158 "
2159 (let ((local-s s)
2160 (my-re-1 "[^ \"]*\"[^\"]+\"\\|[^ \"]+")
2161 (my-re-2 "\\([^ \"]*\\)\"\\([^\"]+\\)\"")
2162 (tokens))
2163 (while (string-match my-re-1 local-s)
2164 (let ((token (match-string 0 local-s))
2165 (remainder (substring local-s (match-end 0))))
2166 (if (string-match my-re-2 token)
2167 (setq token (concat (match-string 1 token) (match-string 2 token))))
2168 ;;(message "token: %s" token)
2169 (setq tokens (append tokens (list token)))
2170 (setq local-s remainder)))
2171 tokens))
2172
2173
2174 (defun csharp-get-value-from-comments (marker-string line-limit)
2175 "gets a string from the header comments in the current buffer.
2176
2177 This is used to extract the flymake command and the compile
2178 command from the comments.
2179
2180 It looks for \"marker-string:\" and returns the string that
2181 follows it, or returns nil if that string is not found.
2182
2183 eg, when marker-string is \"flymake\", and the following
2184 string is found at the top of the buffer:
2185
2186 flymake: csc.exe /r:Hallo.dll
2187
2188 ...then this command will return the string
2189
2190 \"csc.exe /r:Hallo.dll\"
2191
2192 It's ok to have whitespace between the marker and the following
2193 colon.
2194
2195 "
2196
2197 (let (start search-limit found)
2198 ;; determine what lines to look in
2199 (save-excursion
2200 (save-restriction
2201 (widen)
2202 (cond ((> line-limit 0)
2203 (goto-char (setq start (point-min)))
2204 (forward-line line-limit)
2205 (setq search-limit (point)))
2206 ((< line-limit 0)
2207 (goto-char (setq search-limit (point-max)))
2208 (forward-line line-limit)
2209 (setq start (point)))
2210 (t ;0 => no limit (use with care!)
2211 (setq start (point-min))
2212 (setq search-limit (point-max))))))
2213
2214 ;; look in those lines
2215 (save-excursion
2216 (save-restriction
2217 (widen)
2218 (let ((re-string
2219 (concat "\\b" marker-string "[ \t]*:[ \t]*\\(.+\\)$")))
2220 (if (and start
2221 (< (goto-char start) search-limit)
2222 (re-search-forward re-string search-limit 'move))
2223
2224 (buffer-substring-no-properties
2225 (match-beginning 1)
2226 (match-end 1))))))))
2227
2228
2229
2230
2231 (defun csharp-replace-command-tokens (explicitly-specified-command)
2232 "Replace tokens in the flymake or compile command extracted from the
2233 buffer, to allow specification of the original and modified
2234 filenames.
2235
2236 @@ORIG@@ - gets replaced with the original filename
2237 @@FILE@@ - gets replaced with the name of the temporary file
2238 created by flymake
2239
2240 "
2241 (let ((massaged-command explicitly-specified-command))
2242 (if (string-match "@@SRC@@" massaged-command)
2243 (setq massaged-command
2244 (replace-match
2245 (file-relative-name flymake-temp-source-file-name) t t massaged-command)))
2246 (if (string-match "@@ORIG@@" massaged-command)
2247 (setq massaged-command
2248 (replace-match
2249 (file-relative-name buffer-file-name) t t massaged-command)))
2250 massaged-command))
2251
2252
2253 ;;(setq flymake-log-level 3)
2254
2255 (defun csharp-flymake-get-final-csc-arguments (initial-arglist)
2256 "Gets the command used by csc.exe for flymake runs.
2257 This may inject a /t:module into an arglist, where it is not
2258 present.
2259
2260 This fn burps if a different /t: argument is found.
2261
2262 "
2263 (interactive)
2264 (let ((args initial-arglist)
2265 arg
2266 (found nil))
2267 (while args
2268 (setq arg (car args))
2269 (cond
2270 ((string-equal arg "/t:module") (setq found t))
2271 ((string-match "^/t:" arg)
2272 (setq found t)
2273 (message "csharp-mode: WARNING /t: option present in arglist, and not /t:module; fix this.")))
2274
2275 (setq args (cdr args)))
2276
2277 (setq args
2278 (if found
2279 initial-arglist
2280 (append (list "/t:module") initial-arglist)))
2281
2282 (if (called-interactively-p 'any)
2283 (message "result: %s" (prin1-to-string args)))
2284
2285 args))
2286
2287
2288 (defun csharp-flymake-get-cmdline (source base-dir)
2289 "Gets the cmd line for running a flymake session in a C# buffer.
2290 This gets called by flymake itself.
2291
2292 The fn looks in the buffer for a line that looks like:
2293
2294 flymake: <command goes here>
2295
2296 (It should be embedded into a comment)
2297
2298 Typically the command will be a line that runs nmake.exe,
2299 msbuild.exe, or csc.exe, with various options. It should
2300 eventually run the CSC.exe compiler, or something else that emits
2301 error messages in the same form as the C# compiler, like FxCopCmd.exe
2302
2303 Some notes on implementation:
2304
2305 1. csharp-mode copies the buffer to a temporary file and
2306 compiles *that*. This temporary file has a different name
2307 than the actual file name for the buffer - _flymake gets
2308 appended to the basename. Therefore, you should specify
2309 Foo_flymake.cs for the filename, if you want to explicitly
2310 refer to it.
2311
2312 If you want to refer to it implicitly, you can use the special
2313 token \"@@SRC@@\" in the command. It will get replaced with the
2314 name of the temporary file at runtime. If you want to refer to
2315 the original name of the buffer, use @@ORIG@@.
2316
2317 2. In general, when running the compiler, you should use a
2318 target type of \"module\" (eg, /t:module) to allow
2319 csharp-mode to clean up the products of the build.
2320
2321 3. See `csharp-cmd-line-limit' for a way to restrict where
2322 csharp-mode will search for the command.
2323
2324 4. If this string is not found, then this fn will fallback to
2325 a generic, generated csc.exe command.
2326
2327 "
2328 (let ((explicitly-specified-command
2329 (csharp-get-value-from-comments "flymake" csharp-cmd-line-limit)))
2330
2331 (cond
2332 (explicitly-specified-command
2333
2334 ;; the marker string was found in the buffer
2335 (let ((tokens (csharp-split-string-respecting-quotes
2336 (csharp-replace-command-tokens explicitly-specified-command))))
2337
2338 (list (car tokens) (cdr tokens))))
2339
2340 ;; ;; implicitly append? the name of the temporary source file
2341 ;; (list (car tokens) (append (cdr tokens) (list flymake-temp-source-file-name)))))
2342
2343 (t
2344 ;; fallback
2345 (list "csc.exe"
2346 (append (csharp-flymake-get-final-csc-arguments
2347 csharp-flymake-csc-arguments)
2348 (list source)))))))
2349
2350
2351 ;; (defun csharp-flymake-get-cmdline (source base-dir)
2352 ;; "Gets the cmd line for running a flymake session in a C# buffer.
2353 ;; This gets called by flymake itself.
2354 ;;
2355 ;; The fn looks in the buffer for a line that looks like:
2356 ;;
2357 ;; flymake: <command goes here>
2358 ;;
2359 ;; (It should be embedded into a comment)
2360 ;;
2361 ;; Typically the command will be a line that runs nmake.exe,
2362 ;; msbuild.exe, or cscc.exe, with various options. It should
2363 ;; eventually run the CSC.exe compiler, or something else that emits
2364 ;; error messages in the same form as the C# compiler.
2365 ;;
2366 ;; In general, when running the compiler, you should use a target
2367 ;; type of \"module\" (eg, /t:module) to allow csharp-mode to
2368 ;; clean up the products of the build.
2369 ;;
2370 ;; See `csharp-cmd-line-limit' for a way to restrict where
2371 ;; csharp-mode will search for the command.
2372 ;;
2373 ;; If this string is not found, then this fn will fallback to a
2374 ;; generic, generated csc.exe command.
2375 ;;
2376 ;; "
2377 ;; (let ((explicitly-specified-command
2378 ;; (let ((line-limit csharp-cmd-line-limit)
2379 ;; start search-limit found)
2380 ;; ;; determine what lines to look in
2381 ;; (save-excursion
2382 ;; (save-restriction
2383 ;; (widen)
2384 ;; (cond ((> line-limit 0)
2385 ;; (goto-char (setq start (point-min)))
2386 ;; (forward-line line-limit)
2387 ;; (setq search-limit (point)))
2388 ;; ((< line-limit 0)
2389 ;; (goto-char (setq search-limit (point-max)))
2390 ;; (forward-line line-limit)
2391 ;; (setq start (point)))
2392 ;; (t ;0 => no limit (use with care!)
2393 ;; (setq start (point-min))
2394 ;; (setq search-limit (point-max))))))
2395 ;;
2396 ;; ;; look in those lines
2397 ;; (save-excursion
2398 ;; (save-restriction
2399 ;; (widen)
2400 ;; (if (and start
2401 ;; (< (goto-char start) search-limit)
2402 ;; (re-search-forward "\\bflymake-command[ \t]*:[ \t]*\\(.+\\)$" search-limit 'move))
2403 ;;
2404 ;; (buffer-substring-no-properties
2405 ;; (match-beginning 1)
2406 ;; (match-end 1))))))))
2407 ;;
2408 ;; (cond
2409 ;; (explicitly-specified-command
2410 ;; ;; the marker string was found in the buffer
2411 ;; (let ((tokens (csharp-split-string-respecting-quotes
2412 ;; explicitly-specified-command)))
2413 ;; ;; implicitly append the name of the temporary source file
2414 ;; (list (car tokens) (append (cdr tokens) (list flymake-temp-source-file-name)))))
2415 ;;
2416 ;; (t
2417 ;; ;; fallback
2418 ;; (list "csc.exe"
2419 ;; (append (csharp-flymake-get-final-csc-arguments
2420 ;; csharp-flymake-csc-arguments)
2421 ;; (list source)))))))
2422
2423
2424
2425
2426
2427 (defun csharp-flymake-install ()
2428 "Change flymake variables and fns to work with C#.
2429
2430 This fn does these things:
2431
2432 1. add a C# entry to the flymake-allowed-file-name-masks,
2433 or replace it if it already exists.
2434
2435 2. add a C# entry to flymake-err-line-patterns.
2436 This isn't strictly necessary because of item #4.
2437
2438 3. override the definition for flymake-process-sentinel
2439 to NOT check the process status on exit. MSBuild.exe
2440 sets a non-zero status code when compile errors occur,
2441 which causes flymake to disable itself with the regular
2442 flymake-process-sentinel.
2443
2444 4. redefine flymake-start-syntax-check-process to unset the
2445 query-on-exit flag for flymake processes. This allows emacs to
2446 exit even if flymake is currently running.
2447
2448 5. provide advice to flymake-parse-line and
2449 flymake-parse-err-lines, specifically set up for C#
2450 buffers. This allows optimized searching for errors in csc.exe
2451 output, and storing column numbers, for use in #6.
2452
2453 6. define advice to flymake-goto-line , to allow it to goto the
2454 appropriate column for the error on a given line. This advice
2455 looks in flymake-er-info, a list, and uses the heuristic that
2456 the first error that matches the given line number, is the error
2457 we want. This will break if there is more than one error on a
2458 single line.
2459
2460 "
2461
2462 (flymake-log 2 "csharp-flymake-install")
2463
2464 (or csharp--flymake-has-been-installed
2465 (progn
2466
2467 ;; 1. add a C# entry to the flymake-allowed-file-name-masks
2468 (let* ((key "\\.cs\\'")
2469 (csharpentry (assoc key flymake-allowed-file-name-masks)))
2470 (if csharpentry
2471 (setcdr csharpentry '(csharp-flymake-init csharp-flymake-cleanup))
2472 (add-to-list
2473 'flymake-allowed-file-name-masks
2474 (list key 'csharp-flymake-init 'csharp-flymake-cleanup))))
2475
2476
2477 ;; 2. add a C# entry to flymake-err-line-patterns
2478 ;;
2479 ;; The value of each entry is a list, (STRING IX1 IX2 IX3 IX4), where
2480 ;; STRING is the regex, and the other 4 values are indexes into the
2481 ;; regex captures for the filename, line, column, and error text,
2482 ;; respectively.
2483 (add-to-list
2484 'flymake-err-line-patterns
2485 (list csharp-flymake-csc-error-pattern 1 2 3 4))
2486
2487
2488
2489 ;; 3. override the definition for flymake-process-sentinel
2490 ;;
2491 ;; DPC - 2011 Feb 26
2492 ;; Redefining a function is a bit unusual, but I think it is necessary
2493 ;; to remove the check on process exit status. For VBC.exe, it gives
2494 ;; a 1 status when compile errors result. Likewise msbuild.exe. This
2495 ;; means flymake turns itself off, which we don't want. This really
2496 ;; ought to be tunable in flymake, but I guess no one asked for that
2497 ;; feature yet.
2498 (defun flymake-process-sentinel (process event)
2499 "Sentinel for syntax check buffers."
2500 (when (memq (process-status process) '(signal exit))
2501 (let* ((exit-status (process-exit-status process))
2502 (command (process-command process))
2503 (source-buffer (process-buffer process))
2504 (cleanup-f (flymake-get-cleanup-function (buffer-file-name source-buffer))))
2505
2506 (flymake-log 2 "process %d exited with code %d"
2507 (process-id process) exit-status)
2508 (condition-case err
2509 (progn
2510 (flymake-log 3 "cleaning up using %s" cleanup-f)
2511 (when (buffer-live-p source-buffer)
2512 (with-current-buffer source-buffer
2513 (funcall cleanup-f)))
2514
2515 (delete-process process)
2516 (setq flymake-processes (delq process flymake-processes))
2517
2518 (when (buffer-live-p source-buffer)
2519 (with-current-buffer source-buffer
2520
2521 (flymake-parse-residual)
2522 ;;(flymake-post-syntax-check exit-status command)
2523 (flymake-post-syntax-check 0 command)
2524 (setq flymake-is-running nil))))
2525 (error
2526 (let ((err-str (format "Error in process sentinel for buffer %s: %s"
2527 source-buffer (error-message-string err))))
2528 (flymake-log 0 err-str)
2529 (with-current-buffer source-buffer
2530 (setq flymake-is-running nil))))))))
2531
2532
2533 ;; 4. redefine this fn - the reason is to allow exit without query on
2534 ;; flymake processes. Not sure why this is not the default.
2535 (defun flymake-start-syntax-check-process (cmd args dir)
2536 "Start syntax check process."
2537 (let* ((process nil))
2538 (condition-case err
2539 (progn
2540 (when dir
2541 (let ((default-directory dir))
2542 (flymake-log 3 "starting process on dir %s" default-directory)))
2543 (setq process (apply 'start-process "flymake-proc" (current-buffer) cmd args))
2544
2545 ;; dino - exit without query on active flymake processes
2546 (set-process-query-on-exit-flag process nil)
2547
2548 (set-process-sentinel process 'flymake-process-sentinel)
2549 (set-process-filter process 'flymake-process-filter)
2550 (push process flymake-processes)
2551
2552 (setq flymake-is-running t)
2553 (setq flymake-last-change-time nil)
2554 (setq flymake-check-start-time (flymake-float-time))
2555
2556 (flymake-report-status nil "*")
2557 (flymake-log 2 "started process %d, command=%s, dir=%s"
2558 (process-id process) (process-command process)
2559 default-directory)
2560 process)
2561 (error
2562 (let* ((err-str (format "Failed to launch syntax check process '%s' with args %s: %s"
2563 cmd args (error-message-string err)))
2564 (source-file-name buffer-file-name)
2565 (cleanup-f (flymake-get-cleanup-function source-file-name)))
2566 (flymake-log 0 err-str)
2567 (funcall cleanup-f)
2568 (flymake-report-fatal-status "PROCERR" err-str))))))
2569
2570
2571 ;; 5. define some advice for the error parsing
2572 (defadvice flymake-parse-err-lines (before
2573 csharp-flymake-parse-line-patch-1
2574 activate compile)
2575 (if (string-match "\\.[Cc][Ss]$" (file-relative-name buffer-file-name))
2576 ;; clear the auxiliary line information list, when a new parse
2577 ;; starts.
2578 (setq csharp-flymake-aux-error-info nil)))
2579
2580 (defadvice flymake-parse-line (around
2581 csharp-flymake-parse-line-patch-2
2582 activate compile)
2583 ;; This advice will run in all buffers. Let's may sure we
2584 ;; actually execute the important stiff only when a C# buffer is active.
2585 (if (string-match "\\.[Cc][Ss]$" (file-relative-name buffer-file-name))
2586
2587 (let (raw-file-name
2588 e-text
2589 result
2590 (pattern (list csharp-flymake-csc-error-pattern 1 2 3 4))
2591 (line-no 0)
2592 (col-no 0)
2593 (err-type "e"))
2594 (if (string-match (car pattern) line)
2595 (let* ((file-idx (nth 1 pattern))
2596 (line-idx (nth 2 pattern))
2597 (col-idx (nth 3 pattern))
2598 (e-idx (nth 4 pattern)))
2599 (flymake-log 3 "parse line: fx=%s lx=%s ex=%s"
2600 file-idx line-idx e-idx)
2601 (setq raw-file-name (if file-idx (match-string file-idx line) nil))
2602 (setq line-no (if line-idx (string-to-number (match-string line-idx line)) 0))
2603 (setq col-no (if col-idx (string-to-number (match-string col-idx line)) 0))
2604 (setq e-text (if e-idx
2605 (match-string e-idx line)
2606 (flymake-patch-e-text (substring line (match-end 0)))))
2607 (or e-text (setq e-text "<no error text>"))
2608 (if (and e-text (string-match "^[wW]arning" e-text))
2609 (setq err-type "w"))
2610 (flymake-log 3 "parse line: fx=%s/%s lin=%s/%s col=%s/%s text=%s"
2611 file-idx raw-file-name
2612 line-idx line-no
2613 col-idx (prin1-to-string col-no)
2614 e-text)
2615
2616 ;; add one entry to the list of auxiliary error information.
2617 (add-to-list 'csharp-flymake-aux-error-info
2618 (list line-no col-no))
2619
2620 (setq ad-return-value
2621 (flymake-ler-make-ler raw-file-name line-no err-type e-text nil))
2622 )))
2623
2624 ;; else - not in a C# buffer
2625 ad-do-it))
2626
2627
2628 ;; 6. finally, define some advice for the line navigation. It moves
2629 ;; to the proper column, given the line number containing the
2630 ;; error. It first calls the normal `flymake-goto-line', and assumes
2631 ;; that the result is that the cursor is on the line that contains the
2632 ;; error. At exit from that fn, the column is not important. This advice
2633 ;; sets the column.
2634 (defadvice flymake-goto-line (around
2635 csharp-flymake-goto-line-patch
2636 activate compile)
2637 ;; This advice will run in all buffers. Let's may sure we
2638 ;; actually execute the important stuff only when a C# buffer is active.
2639 ad-do-it
2640 (if (string-match "\\.[Cc][Ss]$" (file-relative-name buffer-file-name))
2641 (let* ((lno (ad-get-arg 0))
2642 (epair (assoc lno csharp-flymake-aux-error-info)))
2643 (if epair
2644 (forward-char (- (cadr epair) (current-column) 1))))))
2645
2646
2647 ;; 7. finally, set the flag
2648 (setq csharp--flymake-has-been-installed t))))
2649
2650
2651
2652 ;; Need to temporarily turn off flymake while reverting.
2653 ;; There' some kind of race-condition where flymake is trying
2654 ;; to compile while the buffer is being changed, and that
2655 ;; causes flymake to choke.
2656 (defadvice revert-buffer (around
2657 csharp-advise-revert-buffer
2658 activate compile)
2659 (let ((is-flymake-enabled
2660 (and (fboundp 'flymake-mode)
2661 flymake-mode)))
2662 ;; disable
2663 (if is-flymake-enabled
2664 (flymake-mode-off))
2665
2666 ;; revert
2667 ad-do-it
2668
2669 ;; enable
2670 (if is-flymake-enabled
2671 (flymake-mode-on))))
2672
2673 ;; ++++++++++++++++++++++
2674
2675
2676
2677
2678 ;; ========================================================================
2679 ;; moving
2680
2681 ;; alist of regexps for various structures in a csharp source file.
2682 (eval-and-compile
2683 (defconst csharp--regexp-alist
2684 (list
2685
2686 `(func-start
2687 ,(concat
2688 "^[ \t\n\r\f\v]*" ;; leading whitespace
2689 "\\("
2690 "public\\(?: static\\)?\\|" ;; 1. access modifier
2691 "private\\(?: static\\)?\\|"
2692 "protected\\(?: internal\\)?\\(?: static\\)?\\|"
2693 "static\\|"
2694 "\\)"
2695 "[ \t\n\r\f\v]+"
2696 "\\(?:override[ \t\n\r\f\v]+\\)?" ;; optional
2697 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; 2. return type - possibly generic
2698 "[ \t\n\r\f\v]+"
2699 "\\([[:alpha:]_][[:alnum:]_]*\\)" ;; 3. name of func
2700 "[ \t\n\r\f\v]*"
2701 "\\(\([^\)]*\)\\)" ;; 4. params w/parens
2702 "[ \t\n\r\f\v]*"
2703 ))
2704
2705 `(ctor-start
2706 ,(concat
2707 "^[ \t\n\r\f\v]*" ;; leading whitespace
2708 "\\("
2709 "public\\|" ;; 1. access modifier
2710 "private\\|"
2711 "protected\\(?: internal\\)?\\|"
2712 "static\\|"
2713 "\\)"
2714 "[ \t\n\r\f\v]+"
2715 "\\([[:alpha:]_][[:alnum:]_]*\\)" ;; 2. name of ctor
2716 "[ \t\n\r\f\v]*"
2717 "\\(\([^\)]*\)\\)" ;; 3. parameter list (with parens)
2718 "\\(" ;; 4. ctor dependency
2719 "[ \t\n]*:[ \t\n]*" ;; colon
2720 "\\(?:this\\|base\\)" ;; this or base
2721 "[ \t\n\r\f\v]*"
2722 "\\(?:\([^\)]*\)\\)" ;; parameter list (with parens)
2723 "\\)?" ;; possibly
2724 "[ \t\n\r\f\v]*"
2725 ))
2726
2727
2728 `(using-stmt
2729 ,(concat
2730 ;;"^[ \t\n\r\f\v]*"
2731 "\\(\\<using\\)"
2732 "[ \t\n\r\f\v]+"
2733 "\\(?:"
2734 "\\([[:alpha:]_][[:alnum:]_]*\\)" ;; alias
2735 "[ \t\n\r\f\v]*"
2736 "="
2737 "[ \t\n\r\f\v]*"
2738 "\\)?"
2739 "\\("
2740 "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
2741 "[A-Za-z_][[:alnum:]]*"
2742 "\\)" ;; imported namespace
2743 "[ \t\n\r\f\v]*"
2744 ";"
2745 ))
2746
2747 `(class-start
2748 ,(concat
2749 "^[ \t]*" ;; leading whitespace
2750 "\\("
2751 "public\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|" ;; access modifiers
2752 "internal\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|"
2753 "static\\(?: internal\\)?[ \t]+\\|"
2754 "sealed\\(?: internal\\)?[ \t]+\\|"
2755 "static[ \t]+\\|"
2756 "sealed[ \t]+\\|"
2757 "\\)"
2758 "\\(\\(?:partial[ \t]+\\)?class\\|struct\\)" ;; class/struct keyword
2759 "[ \t]+"
2760 "\\([[:alpha:]_][[:alnum:]]*\\)" ;; type name
2761 "\\("
2762 "[ \t\n]*:[ \t\n]*" ;; colon
2763 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; base / intf - poss generic
2764 "\\("
2765 "[ \t\n]*,[ \t\n]*"
2766 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; addl interface - poss generic
2767 "\\)*"
2768 "\\)?" ;; possibly
2769 "[ \t\n\r\f\v]*"
2770 ))
2771
2772 `(genclass-start
2773 ,(concat
2774 "^[ \t]*" ;; leading whitespace
2775 "\\("
2776 "public\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|" ;; access modifiers
2777 "internal\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|"
2778 "static\\(?: internal\\)?[ \t]+\\|"
2779 "sealed\\(?: internal\\)?[ \t]+\\|"
2780 "static[ \t]+\\|"
2781 "sealed[ \t]+\\|"
2782 "\\)"
2783 "\\(\\(?:partial[ \t]+\\)?class\\|struct\\)" ;; class/struct keyword
2784 "[ \t]+"
2785 "\\([[:alpha:]_][[:alnum:]_<>, ]*\\)" ;; type name (generic)
2786 "\\("
2787 "[ \t\n]*:[ \t\n]*" ;; colon
2788 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; base / intf - poss generic
2789 "\\("
2790 "[ \t\n]*,[ \t\n]*"
2791 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; addl interface - poss generic
2792 "\\)*"
2793 "\\)?" ;; possibly
2794 "[ \t\n\r\f\v]*"
2795 ))
2796
2797 `(enum-start
2798 ,(concat
2799 "^[ \t\f\v]*" ;; leading whitespace
2800 "\\("
2801 "public[ \t]+enum\\|" ;; enum keyword
2802 "enum"
2803 "\\)"
2804 "[ \t\n\r\f\v]+"
2805 "\\([[:alpha:]_][[:alnum:]_]*\\)" ;; name of enum
2806 "[ \t\n\r\f\v]*"
2807 "\\(:[ \t\n\r\f\v]*"
2808 "\\("
2809 "sbyte\\|byte\\|short\\|ushort\\|int\\|uint\\|long\\|ulong"
2810 "\\)"
2811 "[ \t\n\r\f\v]*"
2812 "\\)?" ;; possibly
2813 "[ \t\n\r\f\v]*"
2814 ))
2815
2816
2817 `(intf-start
2818 ,(concat
2819 "^[ \t\f\v]*" ;; leading whitespace
2820 "\\(?:"
2821 "public\\|internal\\|" ;; access modifier
2822 "\\)"
2823 "[ \t\n\r\f\v]+"
2824 "\\(interface\\)"
2825 "[ \t\n\r\f\v]+"
2826 "\\([[:alpha:]_][[:alnum:]_]*\\)" ;; name of interface
2827 "[ \t\n\r\f\v]*"
2828 ))
2829
2830 `(prop-start
2831 ,(concat
2832 "^[ \t\f\v]*" ;; leading whitespace
2833 "\\("
2834 "public\\|" ;; 1: access modifier
2835 "private\\|"
2836 "protected internal\\|"
2837 "internal protected\\|"
2838 "internal\\|"
2839 "\\)"
2840 "[ \t\n\r\f\v]+"
2841 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; 2: return type - possibly generic
2842 "[ \t\n\r\f\v]+"
2843 "\\("
2844 "\\(?:[A-Za-z_][[:alnum:]_]*\\.\\)*" ;; possible prefix interface
2845 "[[:alpha:]_][[:alnum:]_]*" ;; 3: name of prop
2846 "\\)"
2847 "[ \t\n\r\f\v]*"
2848 ))
2849
2850 `(indexer-start
2851 ,(concat
2852 "^[ \t\f\v]*" ;; leading whitespace
2853 "\\("
2854 "public\\|" ;; 1: access modifier
2855 "private\\|"
2856 "protected internal\\|"
2857 "internal protected\\|"
2858 "internal\\|"
2859 "\\)"
2860 "[ \t\n\r\f\v]+"
2861 "\\([[:alpha:]_][^\t\(\n]+\\)" ;; 2: return type - possibly generic
2862 "[ \t\n\r\f\v]+"
2863 "\\(this\\)" ;; 3: 'this' keyword
2864 "[ \t\n\r\f\v]*"
2865 "\\[" ;; open square bracket
2866 "[ \t\n\r\f\v]*"
2867 "\\([^\]]+\\)" ;; 4: index type
2868 "[ \t\n\r\f\v]+"
2869 "[[:alpha:]_][[:alnum:]_]*" ;; index name - a simple identifier
2870 "\\]" ;; closing sq bracket
2871 "[ \t\n\r\f\v]*"
2872 ))
2873
2874 `(namespace-start
2875 ,(concat
2876 "^[ \t\f\v]*" ;; leading whitespace
2877 "\\(namespace\\)"
2878 "[ \t\n\r\f\v]+"
2879 "\\("
2880 "\\(?:[A-Za-z_][[:alnum:]_]*\\.\\)*" ;; name of namespace
2881 "[A-Za-z_][[:alnum:]]*"
2882 "\\)"
2883 "[ \t\n\r\f\v]*"
2884 ))
2885
2886 )))
2887
2888
2889 (defun csharp--regexp (symbol)
2890 "Retrieves a regexp from the `csharp--regexp-alist' corresponding
2891 to the given symbol.
2892 "
2893 (let ((elt (assoc symbol csharp--regexp-alist)))
2894 (if elt (cadr elt) nil)))
2895
2896
2897 (defun csharp-move-back-to-beginning-of-block ()
2898 "Moves to the previous open curly.
2899 "
2900 (interactive)
2901 (re-search-backward "{" (point-min) t))
2902
2903
2904 (defun csharp--move-back-to-beginning-of-something (must-match &optional must-not-match)
2905 "Moves back to the open-curly that defines the beginning of *something*,
2906 defined by the given MUST-MATCH, a regexp which must match immediately
2907 preceding the curly. If MUST-NOT-MATCH is non-nil, it is treated
2908 as a regexp that must not match immediately preceding the curly.
2909
2910 This is a helper fn for `csharp-move-back-to-beginning-of-defun' and
2911 `csharp-move-back-to-beginning-of-class'
2912
2913 "
2914 (interactive)
2915 (let (done
2916 (found (point))
2917 (need-to-backup (not (looking-at "{"))))
2918 (while (not done)
2919 (if need-to-backup
2920 (setq found (csharp-move-back-to-beginning-of-block)))
2921 (if found
2922 (setq done (and (looking-back must-match)
2923 (or (not must-not-match)
2924 (not (looking-back must-not-match))))
2925 need-to-backup t)
2926 (setq done t)))
2927 found))
2928
2929
2930
2931 (defun csharp-move-back-to-beginning-of-defun ()
2932 "Moves back to the open-curly that defines the beginning of the
2933 enclosing method. If point is outside a method, then move back to the
2934 beginning of the prior method.
2935
2936 See also, `csharp-move-fwd-to-end-of-defun'.
2937 "
2938 (interactive)
2939 (cond
2940
2941 ((bobp) nil)
2942
2943 (t
2944 (let (found)
2945 (save-excursion
2946 ;; handle the case where we're at the top of a fn now.
2947 ;; if the user is asking to move back, then obviously
2948 ;; he wants to move back to a *prior* defun.
2949 (if (and (looking-at "{")
2950 (looking-back (csharp--regexp 'func-start))
2951 (not (looking-back (csharp--regexp 'namespace-start))))
2952 (forward-char -1))
2953
2954 ;; now do the real work
2955 (setq found (csharp--move-back-to-beginning-of-something
2956 (csharp--regexp 'func-start)
2957 (csharp--regexp 'namespace-start))))
2958 (if found
2959 (goto-char found))))))
2960
2961
2962 (defun csharp--on-defun-close-curly-p ()
2963 "return t when point is on the close-curly of a method."
2964 (and (looking-at "}")
2965 (save-excursion
2966 (and
2967 (progn (forward-char) (forward-sexp -1) t)
2968 (not (looking-back (csharp--regexp 'class-start)))
2969 (not (looking-back (csharp--regexp 'namespace-start)))
2970 (looking-back (csharp--regexp 'func-start))))))
2971
2972 (defun csharp--on-ctor-close-curly-p ()
2973 "return t when point is on the close-curly of a constructor."
2974 (and (looking-at "}")
2975 (save-excursion
2976 (and
2977 (progn (forward-char) (forward-sexp -1) t)
2978 (looking-back (csharp--regexp 'ctor-start))))))
2979
2980 (defun csharp--on-class-close-curly-p ()
2981 "return t when point is on the close-curly of a class or struct."
2982 (and (looking-at "}")
2983 (save-excursion
2984 (and
2985 (progn (forward-char) (forward-sexp -1) t)
2986 (not (looking-back (csharp--regexp 'namespace-start)))
2987 (looking-back (csharp--regexp 'class-start))))))
2988
2989 (defun csharp--on-intf-close-curly-p ()
2990 "return t when point is on the close-curly of an interface."
2991 (and (looking-at "}")
2992 (save-excursion
2993 (and
2994 (progn (forward-char) (forward-sexp -1) t)
2995 (looking-back (csharp--regexp 'intf-start))))))
2996
2997 (defun csharp--on-enum-close-curly-p ()
2998 "return t when point is on the close-curly of an enum."
2999 (and (looking-at "}")
3000 (save-excursion
3001 (and
3002 (progn (forward-char) (forward-sexp -1) t)
3003 (looking-back (csharp--regexp 'enum-start))))))
3004
3005 (defun csharp--on-namespace-close-curly-p ()
3006 "return t when point is on the close-curly of a namespace."
3007 (and (looking-at "}")
3008 (save-excursion
3009 (and
3010 (progn (forward-char) (forward-sexp -1) t)
3011 (looking-back (csharp--regexp 'namespace-start))))))
3012
3013 (defun csharp--on-defun-open-curly-p ()
3014 "return t when point is on the open-curly of a method."
3015 (and (looking-at "{")
3016 (not (looking-back (csharp--regexp 'class-start)))
3017 (not (looking-back (csharp--regexp 'namespace-start)))
3018 (looking-back (csharp--regexp 'func-start))))
3019
3020 (defun csharp--on-class-open-curly-p ()
3021 "return t when point is on the open-curly of a class."
3022 (and (looking-at "{")
3023 (not (looking-back (csharp--regexp 'namespace-start)))
3024 (looking-back (csharp--regexp 'class-start))))
3025
3026 (defun csharp--on-genclass-open-curly-p ()
3027 "return t when point is on the open-curly of a generic class."
3028 (and (looking-at "{")
3029 (looking-back (csharp--regexp 'genclass-start))))
3030
3031 (defun csharp--on-namespace-open-curly-p ()
3032 "return t when point is on the open-curly of a namespace."
3033 (and (looking-at "{")
3034 (looking-back (csharp--regexp 'namespace-start))))
3035
3036 (defun csharp--on-ctor-open-curly-p ()
3037 "return t when point is on the open-curly of a ctor."
3038 (and (looking-at "{")
3039 (looking-back (csharp--regexp 'ctor-start))))
3040
3041 (defun csharp--on-intf-open-curly-p ()
3042 "return t when point is on the open-curly of a interface."
3043 (and (looking-at "{")
3044 (looking-back (csharp--regexp 'intf-start))))
3045
3046 (defun csharp--on-prop-open-curly-p ()
3047 "return t when point is on the open-curly of a property."
3048 (and (looking-at "{")
3049 (not (looking-back (csharp--regexp 'class-start)))
3050 (looking-back (csharp--regexp 'prop-start))))
3051
3052 (defun csharp--on-indexer-open-curly-p ()
3053 "return t when point is on the open-curly of a C# indexer."
3054 (and (looking-at "{")
3055 (looking-back (csharp--regexp 'indexer-start))))
3056
3057 (defun csharp--on-enum-open-curly-p ()
3058 "return t when point is on the open-curly of a interface."
3059 (and (looking-at "{")
3060 (looking-back (csharp--regexp 'enum-start))))
3061
3062
3063
3064 (defun csharp-move-fwd-to-end-of-defun ()
3065 "Moves forward to the close-curly that defines the end of the enclosing
3066 method. If point is outside a method, moves forward to the close-curly that
3067 defines the end of the next method.
3068
3069 See also, `csharp-move-back-to-beginning-of-defun'.
3070 "
3071 (interactive)
3072
3073 (let ((really-move
3074 (lambda ()
3075 (let ((start (point))
3076 dest-char)
3077 (save-excursion
3078 (csharp-move-back-to-beginning-of-defun)
3079 (forward-sexp)
3080 (if (>= (point) start)
3081 (setq dest-char (point))))
3082 (if dest-char
3083 (goto-char dest-char))))))
3084
3085 (cond
3086
3087 ;; case 1: end of buffer. do nothing.
3088 ((eobp) nil)
3089
3090 ;; case 2: we're at the top of a class
3091 ((csharp--on-class-open-curly-p)
3092 (let (found-it)
3093 (save-excursion
3094 (forward-char 1) ;; get off the curly
3095 (setq found-it
3096 (and ;; look for next open curly
3097 (re-search-forward "{" (point-max) t)
3098 (funcall really-move))))
3099 (if found-it
3100 (goto-char found-it))))
3101
3102
3103 ;; case 3: we're at the top of a fn now.
3104 ((csharp--on-defun-open-curly-p)
3105 (forward-sexp))
3106
3107
3108 ;; case 4: we're at the bottom of a fn now (possibly
3109 ;; after just calling csharp-move-fwd-to-end-of-defun.
3110 ((and (looking-back "}")
3111 (save-excursion
3112 (forward-sexp -1)
3113 (csharp--on-defun-open-curly-p)))
3114
3115 (let (found-it)
3116 (save-excursion
3117 (setq found-it
3118 (and (re-search-forward "{" (point-max) t)
3119 (funcall really-move))))
3120 (if found-it
3121 (goto-char found-it))))
3122
3123
3124 ;; case 5: we're at none of those places.
3125 (t
3126 (funcall really-move)))))
3127
3128
3129
3130
3131 (defun csharp-move-back-to-beginning-of-class ()
3132 "Moves back to the open-curly that defines the beginning of the
3133 enclosing class. If point is outside a class, then move back to the
3134 beginning of the prior class.
3135
3136 See also, `csharp-move-fwd-to-end-of-defun'.
3137 "
3138 (interactive)
3139
3140 (cond
3141 ((bobp) nil)
3142
3143 (t
3144 (let (found)
3145 (save-excursion
3146 ;; handle the case where we're at the top of a class now.
3147 ;; if the user is asking to move back, then obviously
3148 ;; he wants to move back to a *prior* defun.
3149 (if (and (looking-at "{")
3150 (looking-back (csharp--regexp 'class-start))
3151 (not (looking-back (csharp--regexp 'namespace-start))))
3152 (forward-char -1))
3153
3154 ;; now do the real work
3155 (setq found (csharp--move-back-to-beginning-of-something
3156 (csharp--regexp 'class-start)
3157 (csharp--regexp 'namespace-start))))
3158 (if found
3159 (goto-char found))))))
3160
3161
3162
3163
3164 (defun csharp-move-fwd-to-end-of-class ()
3165 "Moves forward to the close-curly that defines the end of the
3166 enclosing class.
3167
3168 See also, `csharp-move-back-to-beginning-of-class'.
3169 "
3170 (interactive)
3171 (let ((start (point))
3172 dest-char)
3173 (save-excursion
3174 (csharp-move-back-to-beginning-of-class)
3175 (forward-sexp)
3176 (if (>= (point) start)
3177 (setq dest-char (point))))
3178
3179 (if dest-char
3180 (goto-char dest-char))))
3181
3182
3183
3184 (defun csharp-move-back-to-beginning-of-namespace ()
3185 "Moves back to the open-curly that defines the beginning of the
3186 enclosing namespace. If point is outside a namespace, then move back
3187 to the beginning of the prior namespace.
3188
3189 "
3190 (interactive)
3191 (cond
3192
3193 ((bobp) nil)
3194
3195 (t
3196 (let (found)
3197 (save-excursion
3198 ;; handle the case where we're at the top of a namespace now.
3199 ;; if the user is asking to move back, then obviously
3200 ;; he wants to move back to a *prior* defun.
3201 (if (and (looking-at "{")
3202 (looking-back (csharp--regexp 'namespace-start)))
3203 (forward-char -1))
3204
3205 ;; now do the real work
3206 (setq found (csharp--move-back-to-beginning-of-something
3207 (csharp--regexp 'namespace-start))))
3208 (if found
3209 (goto-char found))))))
3210
3211 ;; moving
3212 ;; ========================================================================
3213
3214
3215
3216
3217 ;; ==================================================================
3218 ;;; imenu stuff
3219
3220 ;; define some advice for menu construction.
3221
3222 ;; The way imenu constructs menus from the index alist, in
3223 ;; `imenu--split-menu', is ... ah ... perplexing. If the csharp
3224 ;; create-index fn returns an ordered menu, and the imenu "sort" fn has
3225 ;; been set to nil, imenu still sorts the menu, according to the rule
3226 ;; that all submenus must appear at the top of any menu. Why? I don't
3227 ;; know. This advice disables that weirdness in C# buffers.
3228
3229 (defadvice imenu--split-menu (around
3230 csharp--imenu-split-menu-patch
3231 activate compile)
3232 ;; This advice will run in all buffers. Let's may sure we
3233 ;; actually execute the important bits only when a C# buffer is active.
3234 (if (and (string-match "\\.[Cc][Ss]$" (file-relative-name buffer-file-name))
3235 (boundp 'csharp-want-imenu)
3236 csharp-want-imenu)
3237 (let ((menulist (copy-sequence menulist))
3238 keep-at-top)
3239 (if (memq imenu--rescan-item menulist)
3240 (setq keep-at-top (list imenu--rescan-item)
3241 menulist (delq imenu--rescan-item menulist)))
3242 ;; This is the part from the original imenu code
3243 ;; that puts submenus at the top. huh? why?
3244 ;; --------------------------------------------
3245 ;; (setq tail menulist)
3246 ;; (dolist (item tail)
3247 ;; (when (imenu--subalist-p item)
3248 ;; (push item keep-at-top)
3249 ;; (setq menulist (delq item menulist))))
3250 (if imenu-sort-function
3251 (setq menulist (sort menulist imenu-sort-function)))
3252 (if (> (length menulist) imenu-max-items)
3253 (setq menulist
3254 (mapcar
3255 (lambda (menu)
3256 (cons (format "From: %s" (caar menu)) menu))
3257 (imenu--split menulist imenu-max-items))))
3258 (setq ad-return-value
3259 (cons title
3260 (nconc (nreverse keep-at-top) menulist))))
3261 ;; else
3262 ad-do-it))
3263
3264
3265 ;;
3266 ;; I used this to examine the performance of the imenu scanning.
3267 ;; It's not necessary during normal operation.
3268 ;;
3269 ;; (defun csharp-imenu-begin-profile ()
3270 ;; "turn on profiling"
3271 ;; (interactive)
3272 ;; (let ((fns '(csharp--on-class-open-curly-p
3273 ;; csharp--on-namespace-open-curly-p
3274 ;; csharp--on-ctor-open-curly-p
3275 ;; csharp--on-enum-open-curly-p
3276 ;; csharp--on-intf-open-curly-p
3277 ;; csharp--on-prop-open-curly-p
3278 ;; csharp--on-indexer-open-curly-p
3279 ;; csharp--on-defun-open-curly-p
3280 ;; csharp--imenu-create-index-helper
3281 ;; looking-back
3282 ;; looking-at)))
3283 ;; (if (fboundp 'elp-reset-all)
3284 ;; (elp-reset-all))
3285 ;; (mapc 'elp-instrument-function fns)))
3286
3287
3288
3289 (defun csharp--imenu-remove-param-names-from-paramlist (s)
3290 "The input string S is a parameter list, of the form seen in a
3291 C# method. TYPE1 NAME1 [, TYPE2 NAME2 ...]
3292
3293 This fn returns a string of the form TYPE1 [, TYPE2...]
3294
3295 Upon entry, it's assumed that the parens included in S.
3296
3297 "
3298 (if (string= s "()")
3299 s
3300 (save-match-data
3301 (let* (new
3302 (state 0) ;; 0 => ws, 1=>slurping param...
3303 c
3304 cs
3305 nesting
3306 need-type
3307 ix2
3308 (s2 (substring s 1 -1))
3309 (len (length s2))
3310 (i (1- len)))
3311
3312 (while (> i 0)
3313 (setq c (aref s2 i) ;; current character
3314 cs (char-to-string c)) ;; s.t. as a string
3315
3316 (cond
3317
3318 ;; backing over whitespace "after" the param
3319 ((= state 0)
3320 (cond
3321 ;; more ws
3322 ((string-match "[ \t\f\v\n\r]" cs)
3323 t)
3324 ;; a legal char for an identifier
3325 ((string-match "[A-Za-z_0-9]" cs)
3326 (setq state 1))
3327 (t
3328 (error "unexpected char (A)"))))
3329
3330
3331 ;; slurping param name
3332 ((= state 1)
3333 (cond
3334 ;; ws signifies the end of the param
3335 ((string-match "[ \t\f\v\n\r]" cs)
3336 (setq state 2))
3337 ;; a legal char for an identifier
3338 ((string-match "[A-Za-z_0-9]" cs)
3339 t)
3340 (t
3341 (error "unexpected char (B)"))))
3342
3343
3344 ;; ws between typespec and param name
3345 ((= state 2)
3346 (cond
3347 ((string-match "[ \t\f\v\n\r]" cs)
3348 t)
3349 ;; non-ws indicates the type spec is beginning
3350 (t
3351 (incf i)
3352 (setq state 3
3353 need-type nil
3354 nesting 0
3355 ix2 i))))
3356
3357
3358 ;; slurping type
3359 ((= state 3)
3360 (cond
3361 ((= ?> c) (incf nesting))
3362 ((= ?< c)
3363 (decf nesting)
3364 (setq need-type t))
3365
3366 ;; ws or comma maybe signifies the end of the typespec
3367 ((string-match "[ \t\f\v\n\r,]" cs)
3368 (if (and (= nesting 0) (not need-type))
3369 (progn
3370 (setq new (cons (substring s2 (1+ i) ix2) new))
3371 (setq state
3372 (if (= c ?,) 0 4)))))
3373
3374 ((string-match "[A-Za-z_0-9]" cs)
3375 (setq need-type nil))))
3376
3377
3378 ;; awaiting comma or b-o-s
3379 ((= state 4)
3380 (cond
3381
3382 ((= ?, c)
3383 (if (= nesting 0)
3384 (setq state 0)))
3385
3386 ((string-match "[ \t\f\v\n\r]" cs)
3387 t)
3388
3389 ((= 93 c) (incf nesting)) ;; sq brack
3390 ((= 91 c) ;; open sq brack
3391 (decf nesting))
3392
3393 ;; handle this (extension methods), out, ref, params
3394 ((and (>= i 5)
3395 (string= (substring s2 (- i 5) (1+ i)) "params"))
3396 (setf (car new) (concat "params " (car new)))
3397 (setq i (- i 5)))
3398
3399 ((and (>= i 3)
3400 (string= (substring s2 (- i 3) (1+ i)) "this"))
3401 (setf (car new) (concat "this " (car new)))
3402 (setq i (- i 3)))
3403
3404 ((and (>= i 2)
3405 (string= (substring s2 (- i 2) (1+ i)) "ref"))
3406 (setf (car new) (concat "ref " (car new)))
3407 (setq i (- i 2)))
3408
3409 ((and (>= i 2)
3410 (string= (substring s2 (- i 2) (1+ i)) "out"))
3411 (setf (car new) (concat "out " (car new)))
3412 (setq i (- i 2)))
3413
3414 (t
3415 (error "unexpected char (C)"))))
3416 )
3417
3418 (decf i))
3419
3420 (if (and (= state 3) (= nesting 0))
3421 (setq new (cons (substring s2 i ix2) new)))
3422
3423 (concat "("
3424 (if new
3425 (mapconcat 'identity new ", ")
3426 "")
3427 ")")))))
3428
3429
3430 (defun csharp--imenu-item-basic-comparer (a b)
3431 "Compares the car of each element, assumed to be a string."
3432 (string-lessp (car a) (car b)))
3433
3434
3435 (defun csharp--imenu-get-method-name-from-sig (sig)
3436 "Extract a method name with its parameter list from a method
3437 signature, SIG. This is used to aid in sorting methods by name,
3438 and secondarily by parameter list.
3439
3440 For this input:
3441
3442 private Dict<String, int> DoSomething(int, string)
3443
3444 ...the output is:
3445
3446 DoSomething(int, string)
3447
3448 "
3449 (let* (c
3450 result
3451 (state 0)
3452 (len (length sig))
3453 (i (1- len)))
3454 (while (> i 0)
3455 (setq c (aref sig i))
3456
3457 (cond
3458 ((and (= state 0) (= c 40))
3459 (setq state 1))
3460
3461 ((and (= state 1) (or (= c 9) (= c 32)))
3462 (setq result (substring sig (1+ i))
3463 i 0)))
3464 (decf i))
3465 result))
3466
3467
3468
3469 (defun csharp--imenu-item-method-name-comparer (a b)
3470 "Compares the method names in the respective cars of each element.
3471
3472 The car of each element is assumed to be a string with multiple
3473 tokens in it, representing a method signature, including access
3474 modifier, return type, and parameter list (surrounded by parens).
3475 If the method takes no params, then it's just an empty pair of
3476 parens.
3477
3478 This fn extracts the method name and param list from that
3479 signature and compares *that*.
3480
3481 "
3482 (let ((methoda (csharp--imenu-get-method-name-from-sig (car a)))
3483 (methodb (csharp--imenu-get-method-name-from-sig (car b))))
3484 ;;(csharp-log -1 "compare '%s' <> '%s'" methoda methodb)
3485 (string-lessp methoda methodb)))
3486
3487
3488
3489 (defun csharp--imenu-create-index-helper (&optional parent-ns indent-level
3490 consider-usings consider-namespaces)
3491 "Helper fn for `csharp-imenu-create-index'.
3492
3493 Scans a possibly narrowed section of a c# buffer. It finds
3494 namespaces, classes, structs, enums, interfaces, and methods
3495 within classes and structs.
3496
3497 The way it works: it looks for an open-curly. If the open-curly
3498 is a namespace or a class, it narrows to whatever is inside the
3499 curlies, then recurses.
3500
3501 Otherwise (the open-curly is neither of those things), this fn
3502 tries to recognize the open-curly as the beginning of an enum,
3503 method, or interface.
3504
3505 If it succeeds, then a menu item is created for the thing. Then
3506 it jumps to the matching close-curly, and continues. Stop when no
3507 more open-curlies are found.
3508
3509 "
3510
3511 ;; A C# module consists of zero of more explicitly denoted (and
3512 ;; possibly nested) namespaces. In the absence of an
3513 ;; explicitly-denoted namespace, the global namespace is implicitly
3514 ;; applied. Within each namespace there can be zero or more
3515 ;; "container" things - like class, struct, or interface; each with
3516 ;; zero or more indexable items - like methods, constructors.
3517 ;; and so on.
3518
3519 ;; This fn parses the module and indexes those items, creating a
3520 ;; hierarchically organized list to describe them. Each container
3521 ;; (ns/class/struct/etc) is represented on a separate submenu.
3522
3523 ;; It works like this:
3524 ;; (start at the top of the module)
3525 ;;
3526 ;; 1. look for a using clause
3527 ;; yes - insert an item in the menu; move past all using clauses.
3528 ;;
3529 ;; 2. go to next open curly
3530 ;;
3531 ;; 2. beginning of a container? (a class or namespace)
3532 ;;
3533 ;; yes - narrow, and recurse
3534 ;;
3535 ;; no - create a menu item for the thing, whatever it is. add to
3536 ;; the submenu. Go to the end of the thing (to the matching
3537 ;; close curly) then goto step 1.
3538 ;;
3539
3540 (let (container-name
3541 (pos-last-curly -1)
3542 this-flavor
3543 this-item
3544 this-menu
3545 found-usings
3546 done)
3547
3548 (while (not done)
3549
3550 ;; move to the next thing
3551 (c-forward-syntactic-ws)
3552 (cond
3553 ((and consider-usings
3554 (re-search-forward (csharp--regexp 'using-stmt) (point-max) t))
3555 (goto-char (match-beginning 1))
3556 (setq found-usings t
3557 done nil))
3558
3559 ((re-search-forward "{" (point-max) t)
3560 (if (= pos-last-curly (point))
3561 (progn
3562 ;;(csharp-log -1 "imenu: No advance? quitting (%d)" (point))
3563 (setq done t)) ;; haven't advanced- likely a loop
3564
3565 (setq pos-last-curly (point))
3566 (let ((literal (csharp-in-literal)))
3567 ;; skip over comments?
3568 (cond
3569
3570 ((memq literal '(c c++))
3571 (while (memq literal '(c c++))
3572 (end-of-line)
3573 (forward-char 1)
3574 (setq literal (csharp-in-literal)))
3575 (if (re-search-forward "{" (point-max) t)
3576 (forward-char -1)
3577 ;;(csharp-log -1 "imenu: No more curlies (A) (%d)" (point))
3578 (setq done t)))
3579
3580 ((eq literal 'string)
3581 (if (re-search-forward "\"" (point-max) t)
3582 (forward-char 1)
3583 ;;(csharp-log -1 "imenu: Never-ending string? posn(%d)" (point))
3584 (setq done t)))
3585
3586 (t
3587 (forward-char -1)))))) ;; backup onto the curly
3588
3589 (t
3590 ;;(csharp-log -1 "imenu: No more curlies (B) posn(%d)" (point))
3591 (setq done t)))
3592
3593
3594 (if (not done)
3595 (cond
3596
3597 ;; case 1: open curly for an array initializer
3598 ((looking-back "\\[\\][ \t\n\r]*")
3599 (forward-sexp 1))
3600
3601 ;; case 2: just jumped over a string
3602 ((looking-back "\"")
3603 (forward-char 1))
3604
3605 ;; case 3: at the head of a block of using statements
3606 (found-usings
3607 (setq found-usings nil
3608 consider-usings nil) ;; only one batch
3609 (let ((first-using (match-beginning 1))
3610 (count 0)
3611 marquis
3612 ;; don't search beyond next open curly
3613 (limit (1-
3614 (save-excursion
3615 (re-search-forward "{" (point-max) t)))))
3616
3617 ;; count the using statements
3618 (while (re-search-forward (csharp--regexp 'using-stmt) limit t)
3619 (incf count))
3620
3621 (setq marquis (if (eq count 1) "using (1)"
3622 (format "usings (%d)" count)))
3623 (push (cons marquis first-using) this-menu)))
3624
3625
3626 ;; case 4: an interface or enum inside the container
3627 ;; (must come before class / namespace )
3628 ((or (csharp--on-intf-open-curly-p)
3629 (csharp--on-enum-open-curly-p))
3630 (setq consider-namespaces nil
3631 consider-usings nil
3632 this-menu
3633 (append this-menu
3634 (list
3635 (cons (concat
3636 (match-string-no-properties 1) ;; thing flavor
3637 " "
3638 (match-string-no-properties 2)) ;; intf name
3639 (match-beginning 1)))))
3640 (forward-sexp 1))
3641
3642
3643 ;; case 5: at the start of a container (class, namespace)
3644 ((or (and consider-namespaces (csharp--on-namespace-open-curly-p))
3645 (csharp--on-class-open-curly-p)
3646 (csharp--on-genclass-open-curly-p))
3647
3648 ;; produce a fully-qualified name for this thing
3649 (if (string= (match-string-no-properties 1) "namespace")
3650 (setq this-flavor (match-string-no-properties 1)
3651 this-item (match-string-no-properties 2))
3652 (setq this-flavor (match-string-no-properties 2)
3653 this-item (match-string-no-properties 3)
3654 consider-usings nil
3655 consider-namespaces nil))
3656
3657 (setq container-name (if parent-ns
3658 (concat parent-ns "." this-item)
3659 this-item))
3660
3661 ;; create a submenu
3662 (let (submenu
3663 (top (match-beginning 1))
3664 (open-curly (point))
3665 (close-curly (save-excursion
3666 (forward-sexp 1)
3667 (point))))
3668 (setq submenu
3669 (list
3670 (concat this-flavor " " container-name)
3671 (cons "(top)" top)))
3672
3673 ;; find all contained items
3674 (save-restriction
3675 (narrow-to-region (1+ open-curly) (1- close-curly))
3676
3677 (let* ((yok (string= this-flavor "namespace"))
3678 (child-menu
3679 (csharp--imenu-create-index-helper container-name
3680 (concat indent-level " ")
3681 yok yok)))
3682 (if child-menu
3683 (setq submenu
3684 (append submenu
3685 (sort child-menu
3686 'csharp--imenu-item-basic-comparer))))))
3687 (setq submenu
3688 (append submenu
3689 (list (cons "(bottom)" close-curly))))
3690
3691 (setq this-menu
3692 (append this-menu (list submenu)))
3693
3694 (goto-char close-curly)))
3695
3696
3697 ;; case 6: a property
3698 ((csharp--on-prop-open-curly-p)
3699 (setq consider-namespaces nil
3700 consider-usings nil
3701 this-menu
3702 (append this-menu
3703 (list
3704 (cons (concat
3705 "prop "
3706 (match-string-no-properties 3)) ;; prop name
3707 (match-beginning 1)))))
3708 (forward-sexp 1))
3709
3710
3711 ;; case 7: an indexer
3712 ((csharp--on-indexer-open-curly-p)
3713 (setq consider-namespaces nil
3714 consider-usings nil
3715 this-menu
3716 (append this-menu
3717 (list
3718 (cons (concat
3719 "indexer "
3720 (match-string-no-properties 4)) ;; index type
3721 (match-beginning 1)))))
3722 (forward-sexp 1))
3723
3724
3725 ;; case 8: a constructor inside the container
3726 ((csharp--on-ctor-open-curly-p)
3727 (setq consider-namespaces nil
3728 consider-usings nil
3729 this-menu
3730 (append this-menu
3731 (list
3732 (cons (concat
3733 "ctor "
3734 (match-string-no-properties 2) ;; ctor name
3735 (csharp--imenu-remove-param-names-from-paramlist
3736 (match-string-no-properties 3))) ;; ctor params
3737 (match-beginning 1)))))
3738 (forward-sexp 1))
3739
3740
3741 ;; case 9: a method inside the container
3742 ((csharp--on-defun-open-curly-p)
3743 (setq consider-namespaces nil
3744 consider-usings nil
3745 this-menu
3746 (append this-menu
3747 (list
3748 (cons (concat
3749 "method "
3750 (match-string-no-properties 2) ;; return type
3751 " "
3752 (match-string-no-properties 3) ;; func name
3753 (csharp--imenu-remove-param-names-from-paramlist
3754 (match-string-no-properties 4))) ;; fn params
3755 (match-beginning 1)))))
3756 (forward-sexp 1))
3757
3758
3759 ;; case 10: unknown open curly - just jump over it.
3760 ((looking-at "{")
3761 (forward-sexp 1))
3762
3763 ;; case 11: none of the above. shouldn't happen?
3764 (t
3765 (forward-char 1)))))
3766
3767 this-menu))
3768
3769
3770 ;; =======================================================
3771 ;; DPC Thu, 19 May 2011 11:25
3772 ;; There are two challenges with the imenu support: generating the
3773 ;; index, and generating a reasonable display for the index. The index
3774 ;; generation is pretty straightforward: use regexi to locate
3775 ;; interesting stuff in the buffer.
3776 ;;
3777 ;; The menu generation is a little trickier. Long lists of methods
3778 ;; mixed with properties and interfaces (etc) will be displayed in the
3779 ;; menu but will look Very Bad. Better to organize the menu into
3780 ;; submenus, organized primarily by category. Also the menus should be
3781 ;; sorted, for ease of human scanning. The next section of logic is
3782 ;; designed to do the stuff for the menu generation.
3783
3784
3785 (defcustom csharp-imenu-max-similar-items-before-extraction 6
3786 "The maximum number of things of a particular
3787 category (constructor, property, method, etc) that will be
3788 separely displayed on an imenu without factoring them into a
3789 separate submenu.
3790
3791 For example, if a module has 3 consructors, 5 methods, and 7
3792 properties, and the value of this variable is 4, then upon
3793 refactoring, the constructors will remain in the toplevel imenu
3794 and the methods and properties will each get their own
3795 category-specific submenu.
3796
3797 See also `csharp-imenu-min-size-for-sub-submenu'.
3798
3799 For more information on how csharp-mode uses imenu,
3800 see `csharp-want-imenu', and `csharp-mode'.
3801 "
3802 :type 'integer
3803 :group 'csharp)
3804
3805
3806 (defcustom csharp-imenu-min-size-for-sub-submenu 18
3807 "The minimum number of imenu items of a particular
3808 category (constructor, property, method, etc) that will be
3809 broken out into sub-submenus.
3810
3811 For example, if a module has 28 properties, then the properties will
3812 be placed in a submenu, and then that submenu with be further divided
3813 into smaller submenus.
3814
3815 See also `csharp-imenu-max-similar-items-before-extraction'
3816
3817 For more information on how csharp-mode uses imenu,
3818 see `csharp-want-imenu', and `csharp-mode'.
3819 "
3820 :type 'integer
3821 :group 'csharp)
3822
3823
3824 (defun csharp--first-word (s)
3825 "gets the first word from the given string.
3826 It had better be a string!"
3827 (car (split-string s nil t)))
3828
3829
3830 (defun csharp--make-plural (s)
3831 "make a word plural. For use within the generated imenu."
3832 (cond
3833 ((string= s "prop") "properties")
3834 ((string= s "class") "classes")
3835 ((string= s "ctor") "constructors")
3836 (t (concat s "s"))))
3837
3838
3839 (defun csharp--imenu-counts (list)
3840 "Returns an alist, each item is a cons cell where the car is a
3841 unique first substring of an element of LIST, and the cdr is the
3842 number of occurrences of that substring in elements in the
3843 list.
3844
3845 For a complicated imenu generated for a large C# module, the result of
3846 this fn will be something like this:
3847
3848 ((\"(top)\" . 1)
3849 (\"properties\" . 38)
3850 (\"methods\" . 12)
3851 (\"constructors\" . 7)
3852 (\"(bottom)\" . 1))
3853
3854 "
3855 (flet ((helper (list new)
3856 (if (null list) new
3857 (let* ((elt (car list))
3858 (topic (csharp--make-plural (csharp--first-word (car elt))))
3859 (xelt (assoc topic new)))
3860 (helper (cdr list)
3861 (if xelt
3862 (progn (incf (cdr xelt)) new)
3863 (cons (cons topic 1) new)))))))
3864 (nreverse (helper list nil))))
3865
3866
3867
3868 (defun csharp--imenu-get-submenu-size (n)
3869 "Gets the preferred size of submenus given N, the size of the
3870 flat, unparceled menu.
3871
3872 Suppose there are 50 properties in a given C# module. This fn maps
3873 from that number, to the maximum size of the submenus into which the
3874 large set of properties should be broken.
3875
3876 Currently the submenu size for 50 is 12. To change this, change
3877 the lookup table.
3878
3879 The reason it's a lookup table and not a simple arithmetic
3880 function: I think it would look silly to have 2 submenus each
3881 with 24 items. Sixteen or 18 items on a submenu seems fine when
3882 you're working through 120 items total. But if you have only 28
3883 items, better to have 3 submenus with 10 and 9 items each. So
3884 it's not a linear function. That's what this lookup tries to do.
3885
3886 "
3887 (let ((size-pairs '((100 . 22)
3888 (80 . 20)
3889 (60 . 18)
3890 (40 . 15)
3891 (30 . 14)
3892 (24 . 11)
3893 (0 . 9)))
3894 elt
3895 (r 0))
3896
3897 (while (and size-pairs (eq r 0))
3898 (setq elt (car size-pairs))
3899 (if (> n (car elt))
3900 (setq r (cdr elt)))
3901 (setq size-pairs (cdr size-pairs)))
3902 r))
3903
3904
3905
3906 (defun csharp--imenu-remove-category-names (menu-list)
3907 "Input is a list, each element is (LABEL . LOCATION). This fn
3908 returns a modified list, with the first word - the category name
3909 - removed from each label.
3910
3911 "
3912 (mapcar (lambda (elt)
3913 (let ((tokens (split-string (car elt) "[ \t]" t)))
3914 (cons (mapconcat 'identity (cdr tokens) " ")
3915 (cdr elt))))
3916 menu-list))
3917
3918 (defun string-indexof (s c)
3919 "Returns the index of the first occurrence of character C in string S.
3920 Returns nil if not found.
3921
3922 See also, `string-lastindexof'
3923
3924 "
3925 (let ((len (length s))
3926 (i 0) ix c2)
3927 (while (and (< i len) (not ix))
3928 (setq c2 (aref s i))
3929 (if (= c c2)
3930 (setq ix i))
3931 (incf i))
3932 ix))
3933
3934 (defun string-lastindexof (s c)
3935 "Returns the index of the last occurrence of character C in string S.
3936 Returns nil if not found.
3937
3938 See also, `string-indexof'
3939
3940 "
3941 (let ((i (1- (length s)))
3942 ix c2)
3943 (while (and (>= i 0) (not ix))
3944 (setq c2 (aref s i))
3945 (if (= c c2)
3946 (setq ix i))
3947 (decf i))
3948 ix))
3949
3950
3951 (defun csharp--imenu-submenu-label (sig flavor)
3952 "generate a submenu label from the given signature, SIG.
3953 The sig is a method signature, property type-and-name,
3954 constructor, and so on, indicated by FLAVOR.
3955
3956 This fn returns a simple name that can be used in the label for a
3957 break out submenu.
3958
3959 "
3960 (if (string= flavor "method")
3961 (let ((method-name (csharp--imenu-get-method-name-from-sig sig)))
3962 (substring method-name 0 (string-indexof method-name 40)))
3963 (substring sig (1+ (string-lastindexof sig 32)))))
3964
3965
3966
3967
3968 (defun csharp--imenu-break-one-menu-into-submenus (menu-list)
3969 "Parcels a flat list MENU-LIST up into smaller sublists. It tries
3970 to balance the number of sublists and the size of each sublist.
3971
3972 The max size of any sublist will be about 20 (arbitrary) and the
3973 min size will be 7 or so. See `csharp--imenu-get-submenu-size'
3974 for how this is done.
3975
3976 It does this destructively, using `nbutlast'.
3977
3978 Returns a new list, containing sublists.
3979 "
3980
3981 (let ((len (length menu-list))
3982 (counts (csharp--imenu-counts menu-list)))
3983
3984 (cond
3985 ;; a small number, and all the same flavor
3986 ((and (< len csharp-imenu-min-size-for-sub-submenu) (= (length counts) 1))
3987 (csharp--imenu-remove-category-names
3988 (sort menu-list
3989 (if (string= (caar counts) "methods")
3990 'csharp--imenu-item-method-name-comparer
3991 'csharp--imenu-item-basic-comparer))))
3992
3993 ;; is the length already pretty short?
3994 ((< len csharp-imenu-min-size-for-sub-submenu)
3995 menu-list)
3996
3997 ((/= (length counts) 1)
3998 menu-list)
3999
4000 (t
4001 (let* ((lst (sort menu-list
4002 (if (string= (caar counts) "methods")
4003 'csharp--imenu-item-method-name-comparer
4004 'csharp--imenu-item-basic-comparer)))
4005 new
4006 (sz (csharp--imenu-get-submenu-size len)) ;; goal max size of sublist
4007 (n (ceiling (/ (* 1.0 len) sz))) ;; total number of sublists
4008 (adj-sz (ceiling (/ (* 1.0 len) n))) ;; maybe a little less than sz
4009 (nsmall (mod (- adj-sz (mod len adj-sz)) adj-sz)) ;; num of (n-1) lists
4010 (i 0)
4011 (base-name (csharp--first-word (caar lst)))
4012 label
4013 chunksz
4014 this-chunk)
4015
4016 (while lst
4017 (setq chunksz (if (> nsmall i) (1- adj-sz) adj-sz)
4018 this-chunk (csharp--imenu-remove-category-names
4019 (nthcdr (- len chunksz) lst))
4020 lst (nbutlast lst chunksz)
4021 ;;label (format "%s %d" plural-name (- n i))
4022 label (concat "from " (csharp--imenu-submenu-label (caar this-chunk) base-name))
4023 new (cons (cons label this-chunk) new)
4024 len (- len chunksz))
4025 (incf i))
4026 new)))))
4027
4028
4029
4030 (defun csharp--imenu-break-into-submenus (menu-list)
4031 "For an imenu menu-list with category-based submenus,
4032 possibly break a submenu into smaller sublists, based on size.
4033
4034 "
4035 (mapcar (lambda (elt)
4036 (if (imenu--subalist-p elt)
4037 (cons (car elt)
4038 (csharp--imenu-break-one-menu-into-submenus (cdr elt)))
4039 elt))
4040 menu-list))
4041
4042
4043
4044
4045
4046 (defun csharp--imenu-reorg-alist-intelligently (menu-alist)
4047 "Accepts an imenu alist. Returns an alist, reorganized.
4048 Things get sorted, factored out into category submenus,
4049 and split into multiple submenus, where conditions warrant.
4050
4051 For example, suppose this imenu alist is generated from a scan:
4052
4053 ((\"usings (4)\" . 1538)
4054 (\"namespace Ionic.Zip\"
4055 (\"(top)\" . 1651)
4056 (\"partial class Ionic.Zip.ZipFile\"
4057 (\"(top)\" . 5473)
4058 (\"prop FullScan\" . 8036)
4059 ...
4060 (\"prop Comment\" . 21118)
4061 (\"prop Verbose\" . 32278)
4062 (\"method override String ToString\" . 96577)
4063 (\"method internal void NotifyEntryChanged\" . 97608)
4064 ....
4065 (\"method internal void Reset\" . 98231)
4066 (\"ctor ZipFile\" . 103598)
4067 ...
4068 (\"ctor ZipFile\" . 109723)
4069 (\"ctor ZipFile\" . 116487)
4070 (\"indexer int\" . 121232)
4071 (\"indexer String\" . 124933)
4072 (\"(bottom)\" . 149777))
4073 (\"public enum Zip64Option\" . 153839)
4074 (\"enum AddOrUpdateAction\" . 154815)
4075 (\"(bottom)\" . 154893)))
4076
4077
4078 This is displayed as a toplevel menu with 2 items; the namespace
4079 menu has 5 items (top, bottom, the 2 enums, and the class). The
4080 class menu has 93 items. It needs to be reorganized to be more usable.
4081
4082 After transformation of the alist through this fn, the result is:
4083
4084 ((\"usings (4)\" . 1538)
4085 (\"namespace Ionic.Zip\"
4086 (\"(top)\" . 1651)
4087 (\"partial class Ionic.Zip.ZipFile\"
4088 (\"(top)\" . 5473)
4089 (\"properties\"
4090 (\"WriteStream\" . 146489)
4091 (\"Count\" . 133827)
4092 ....
4093 (\"BufferSize\" . 12837)
4094 (\"FullScan\" . 8036))
4095 (\"methods\"
4096 (\"virtual void Dispose\" . 144389)
4097 (\"void RemoveEntry\" . 141027)
4098 ....
4099 (\"method override String ToString\" . 96577)
4100 (\"method bool ContainsEntry\" . 32517))
4101 (\"constructors\"
4102 (\"ZipFile\" . 116487)
4103 ....
4104 (\"ZipFile\" . 105698)
4105 (\"ZipFile\" . 103598))
4106 (\"indexer int\" . 121232)
4107 (\"indexer String\" . 124933)
4108 (\"(bottom)\" . 149777))
4109 (\"public enum Zip64Option\" . 153839)
4110 (\"enum AddOrUpdateAction\" . 154815)
4111 (\"(bottom)\" . 154893)))
4112
4113 All menus are the same except the class menu, which has been
4114 organized into subtopics, each of which gets its own cascaded
4115 submenu. If the submenu itself holds more than
4116 `csharp-imenu-max-similar-items-before-extraction' items that are
4117 all the same flavor (properties, methods, etc), thos get split
4118 out into multiple submenus.
4119
4120 "
4121 (let ((counts (csharp--imenu-counts menu-alist)))
4122 (flet ((helper
4123 (list new)
4124 (if (null list)
4125 new
4126 (let* ((elt (car list))
4127 (topic (csharp--make-plural (csharp--first-word (car elt))))
4128 (xelt (assoc topic new)))
4129 (helper
4130 (cdr list)
4131 (if xelt
4132 (progn
4133 (rplacd xelt (cons elt (cdr xelt)))
4134 new)
4135 (cons
4136
4137 (cond
4138 ((> (cdr (assoc topic counts))
4139 csharp-imenu-max-similar-items-before-extraction)
4140 (cons topic (list elt)))
4141
4142 ((imenu--subalist-p elt)
4143 (cons (car elt)
4144 (csharp--imenu-reorg-alist-intelligently (cdr elt))))
4145 (t
4146 elt))
4147
4148 new)))))))
4149
4150 (csharp--imenu-break-into-submenus
4151 (nreverse (helper menu-alist nil))))))
4152
4153
4154
4155
4156 (defun csharp-imenu-create-index ()
4157 "This function is called by imenu to create an index for the
4158 current C# buffer, conforming to the format specified in
4159 `imenu--index-alist' .
4160
4161 See `imenu-create-index-function' for background information.
4162
4163 To produce the index, which lists the classes, functions,
4164 methods, and properties for the current buffer, this function
4165 scans the entire buffer.
4166
4167 This can take a long time for a large buffer. The scan uses
4168 regular expressions that attempt to match on the general-case C#
4169 syntax, for classes and functions, generic types, base-classes,
4170 implemented interfaces, and so on. This can be time-consuming.
4171 For a large source file, say 160k, it can take 10 seconds or more.
4172 The UI hangs during the scan.
4173
4174 imenu calls this fn when it feels like it, I suppose when it
4175 thinks the buffer has been updated. The user can also kick it off
4176 explicitly by selecting *Rescan* from the imenu menu.
4177
4178 After generating the hierarchical list of props, methods,
4179 interfaces, classes, and namespaces, csharp-mode re-organizes the
4180 list as appropriate:
4181
4182 - it extracts sets of like items into submenus. All properties
4183 will be placed on a submenu. See
4184 `csharp-imenu-max-similar-items-before-extraction' for a way
4185 to tune this.
4186
4187 - it converts those submenus into sub-submenus, if there are more than
4188 `csharp-imenu-min-size-for-sub-submenu' items.
4189
4190 - it sorts each set of items on the outermost menus lexicographically.
4191
4192 The result of these transformations is what is provided to imenu
4193 to generate the visible menus. Just FYI - the reorganization of
4194 the scan results is much much faster than the actual generation
4195 of the scan results. If you're looking to save time, the re-org
4196 logic is not where the cost is.
4197
4198 imenu itself likes to sort the menus. See `imenu--split-menu' and
4199 also `csharp--imenu-split-menu-patch', which is advice that
4200 attempts to disable the weird re-jiggering that imenu performs.
4201
4202 "
4203 ;; I think widen/narrow causes the buffer to be marked as
4204 ;; modified. This is a bit surprising, but I have no other
4205 ;; explanation for the source of the problem.
4206 ;; So I use `c-save-buffer-state' so that the buffer is not
4207 ;; marked modified when the scan completes.
4208
4209 (c-save-buffer-state ()
4210 (save-excursion
4211 (save-restriction
4212 (widen)
4213 (goto-char (point-min))
4214
4215 (let ((index-alist
4216 (csharp--imenu-create-index-helper nil "" t t)))
4217
4218 (csharp--imenu-reorg-alist-intelligently index-alist)
4219
4220 ;;index-alist
4221
4222 ;; What follows is No longer used.
4223 ;; =======================================================
4224
4225 ;; If the index menu contains exactly one element, and it is
4226 ;; a namespace menu, then remove it. This simplifies the
4227 ;; menu, and results in no loss of information: all types
4228 ;; get fully-qualified names anyway. This will probably
4229 ;; cover the majority of cases; often a C# source module
4230 ;; defines either one class, or a set of related classes
4231 ;; inside a single namespace.
4232
4233 ;; To remove that namespace, we need to prune & graft the tree.
4234 ;; Remove the ns hierarchy level, but also remove the 1st and
4235 ;; last elements in the sub-menu, which represent the top and
4236 ;; bottom of the namespace.
4237
4238 ;; (if (and
4239 ;; (= 1 (length index-alist))
4240 ;; (consp (car index-alist))
4241 ;; (let ((tokens (split-string
4242 ;; (car (car index-alist))
4243 ;; "[ \t]" t)))
4244 ;; (and (<= 1 (length tokens))
4245 ;; (string= (downcase
4246 ;; (nth 0 tokens)) "namespace"))))
4247 ;;
4248 ;; (let (elt
4249 ;; (newlist (cdar index-alist)))
4250 ;; (setf (car (car newlist)) (car (car index-alist)))
4251 ;; newlist)
4252 ;;
4253 ;; index-alist)
4254
4255 )))))
4256
4257
4258 ;; ==================================================================
4259
4260
4261
4262
4263 ;; ==================================================================
4264 ;; C# code-doc insertion magic
4265 ;; ==================================================================
4266 ;;
4267 ;; In Visual Studio, if you type three slashes, it immediately expands into
4268 ;; an inline code-documentation fragment. The following method does the
4269 ;; same thing.
4270 ;;
4271 ;; This is the kind of thing that could be handled by YASnippet or
4272 ;; another similarly flexible snippet framework. But I don't want to
4273 ;; introduce a dependency on yasnippet to csharp-mode. So the capability
4274 ;; must live within csharp-mode itself.
4275
4276 (defun csharp-maybe-insert-codedoc (arg)
4277
4278 "Insert an xml code documentation template as appropriate, when
4279 typing slashes. This fn gets bound to / (the slash key), in
4280 csharp-mode. If the slash being inserted is not the third
4281 consecutive slash, the slash is inserted as normal. If it is the
4282 third consecutive slash, then a xml code documentation template
4283 may be inserted in some cases. For example,
4284
4285 a <summary> template is inserted if the prior line is empty,
4286 or contains only an open curly brace;
4287 a <remarks> template is inserted if the prior word
4288 closes the <summary> element;
4289 a <returns> template is inserted if the prior word
4290 closes the <remarks> element;
4291 an <example> template is inserted if the prior word closes
4292 the <returns> element;
4293 a <para> template is inserted if the prior word closes
4294 a <para> element.
4295
4296 In all other cases the slash is inserted as normal.
4297
4298 If you want the default cc-mode behavior, which implies no automatic
4299 insertion of xml code documentation templates, then use this in
4300 your `csharp-mode-hook' function:
4301
4302 (local-set-key (kbd \"/\") 'c-electric-slash)
4303
4304 "
4305 (interactive "*p")
4306 ;;(message "csharp-maybe-insert-codedoc")
4307 (let (
4308 (cur-point (point))
4309 (char last-command-event)
4310 (cb0 (char-before (- (point) 0)))
4311 (cb1 (char-before (- (point) 1)))
4312 is-first-non-whitespace
4313 did-auto-insert
4314 )
4315
4316 ;; check if two prior chars were slash, in other words,
4317 ;; check if this is the third slash in a row.
4318 (if (and (= char ?/) cb0 (= ?/ cb0) cb1 (= ?/ cb1))
4319
4320 (progn
4321 ;;(message "yes - this is the third consecutive slash")
4322 (setq is-first-non-whitespace
4323 (save-excursion
4324 (back-to-indentation)
4325 (= cur-point (+ (point) 2))))
4326
4327 (if is-first-non-whitespace
4328 ;; This is a 3-slash sequence. It is the first non-whitespace text
4329 ;; on the line. Now we need to examine the surrounding context
4330 ;; in order to determine which xml cod doc template to insert.
4331 (let (word-back char0 char1
4332 word-fore char-0 char-1
4333 text-to-insert ;; text to insert in lieu of slash
4334 fn-to-call ;; func to call after inserting text
4335 (preceding-line-is-empty (or
4336 (= (line-number-at-pos) 1)
4337 (save-excursion
4338 (forward-line -1)
4339 (beginning-of-line)
4340 (looking-at "[ \t]*$\\|[ \t]*{[ \t]*$"))))
4341 (flavor 0) ;; used only for diagnostic purposes
4342 )
4343
4344 ;;(message "starting a 3-slash comment")
4345 ;; get the prior word, and the 2 chars preceding it.
4346 (backward-word)
4347
4348 (setq word-back (thing-at-point 'word)
4349 char0 (char-before (- (point) 0))
4350 char1 (char-before (- (point) 1)))
4351
4352 ;; restore prior position
4353 (goto-char cur-point)
4354
4355 ;; get the following word, and the 2 chars preceding it.
4356 (forward-word)
4357 (backward-word)
4358 (setq word-fore (thing-at-point 'word)
4359 char-0 (char-before (- (point) 0))
4360 char-1 (char-before (- (point) 1)))
4361
4362 ;; restore prior position again
4363 (goto-char cur-point)
4364
4365 (cond
4366 ;; The preceding line is empty, or all whitespace, or
4367 ;; contains only an open-curly. In this case, insert a
4368 ;; summary element pair.
4369 (preceding-line-is-empty
4370 (setq text-to-insert "/ <summary>\n/// \n/// </summary>"
4371 flavor 1) )
4372
4373 ;; The preceding word closed a summary element. In this case,
4374 ;; if the forward word does not open a remarks element, then
4375 ;; insert a remarks element.
4376 ((and (string-equal word-back "summary") (eq char0 ?/) (eq char1 ?<))
4377 (if (not (and (string-equal word-fore "remarks") (eq char-0 ?<)))
4378 (setq text-to-insert "/ <remarks>\n/// <para>\n/// \n/// </para>\n/// </remarks>"
4379 flavor 2)))
4380
4381 ;; The preceding word closed the remarks section. In this case,
4382 ;; insert an example element.
4383 ((and (string-equal word-back "remarks") (eq char0 ?/) (eq char1 ?<))
4384 (setq text-to-insert "/ <example>\n/// \n/// </example>"
4385 flavor 3))
4386
4387 ;; The preceding word closed the example section. In this
4388 ;; case, insert an returns element. This isn't always
4389 ;; correct, because sometimes the xml code doc is attached to
4390 ;; a class or a property, neither of which has a return
4391 ;; value. A more intelligent implementation would inspect the
4392 ;; syntax state and only inject a returns element if
4393 ;; appropriate.
4394 ((and (string-equal word-back "example") (eq char0 ?/) (eq char1 ?<))
4395 (setq text-to-insert "/ <returns></returns>"
4396 fn-to-call (lambda ()
4397 (backward-word)
4398 (backward-char)
4399 (backward-char)
4400 (c-indent-line-or-region)
4401 )
4402 flavor 4))
4403
4404 ;; The preceding word opened the remarks section, or it
4405 ;; closed a para section. In this case, insert a para
4406 ;; element, using appropriate indentation with respect to the
4407 ;; prior tag.
4408 ((or
4409 (and (string-equal word-back "remarks") (eq char0 ?<) (or (eq char1 32) (eq char1 9)))
4410 (and (string-equal word-back "para") (eq char0 ?/) (eq char1 ?<)))
4411
4412 (let (prior-point spacer)
4413 (save-excursion
4414 (backward-word)
4415 (backward-char)
4416 (backward-char)
4417 (setq prior-point (point))
4418 (skip-chars-backward "\t ")
4419 (setq spacer (buffer-substring (point) prior-point))
4420 ;;(message (format "pt(%d) prior(%d) spacer(%s)" (point) prior-point spacer))
4421 )
4422
4423 (if (string-equal word-back "remarks")
4424 (setq spacer (concat spacer " ")))
4425
4426 (setq text-to-insert (format "/%s<para>\n///%s \n///%s</para>"
4427 spacer spacer spacer)
4428 flavor 6)))
4429
4430 ;; The preceding word opened a para element. In this case, if
4431 ;; the forward word does not close the para element, then
4432 ;; close the para element.
4433 ;; --
4434 ;; This is a nice idea but flawed. Suppose I have a para element with some
4435 ;; text in it. If I position the cursor at the first line, then type 3 slashes,
4436 ;; I get a close-element, and that would be inappropriate. Not sure I can
4437 ;; easily solve that problem, so the best thing might be to simply punt, and
4438 ;; require people to close their own elements.
4439 ;;
4440 ;; ( (and (string-equal word-back "para") (eq char0 60) (or (eq char1 32) (eq char1 9)))
4441 ;; (if (not (and (string-equal word-fore "para") (eq char-0 47) (eq char-1 60) ))
4442 ;; (setq text-to-insert "/ \n/// </para>\n///"
4443 ;; fn-to-call (lambda ()
4444 ;; (previous-line)
4445 ;; (end-of-line)
4446 ;; )
4447 ;; flavor 7) )
4448 ;; )
4449
4450 ;; the default case - do nothing
4451 (t nil))
4452
4453 (if text-to-insert
4454 (progn
4455 ;;(message (format "inserting special text (f(%d))" flavor))
4456
4457 ;; set the flag, that we actually inserted text
4458 (setq did-auto-insert t)
4459
4460 ;; save point of beginning of insertion
4461 (setq cur-point (point))
4462
4463 ;; actually insert the text
4464 (insert text-to-insert)
4465
4466 ;; indent the inserted string, and re-position point, either through
4467 ;; the case-specific fn, or via the default progn.
4468 (if fn-to-call
4469 (funcall fn-to-call)
4470
4471 (let ((newline-count 0) (pos 0) ix)
4472
4473 ;; count the number of newlines in the inserted string
4474 (while (string-match "\n" text-to-insert pos)
4475 (setq pos (match-end 0)
4476 newline-count (+ newline-count 1) )
4477 )
4478
4479 ;; indent what we just inserted
4480 (c-indent-region cur-point (point) t)
4481
4482 ;; move up n/2 lines. This assumes that the
4483 ;; inserted text is ~symmetric about the halfway point.
4484 ;; The assumption holds if the xml code doc uses a
4485 ;; begin-elt and end-elt on a new line all by themselves,
4486 ;; and a blank line in between them where the point should be.
4487 ;; A more intelligent implementation would use a specific
4488 ;; marker string, like @@DOT, to note the desired point.
4489 (forward-line (- 0 (/ newline-count 2)))
4490 (end-of-line)))))))))
4491
4492 (if (not did-auto-insert)
4493 (self-insert-command (prefix-numeric-value arg)))))
4494
4495 ;; ==================================================================
4496 ;; end of c# code-doc insertion magic
4497 ;; ==================================================================
4498
4499
4500
4501
4502 ;; ==================================================================
4503 ;; c# fontification extensions
4504 ;; ==================================================================
4505 ;; Commentary:
4506 ;;
4507 ;; The purpose of the following code is to fix font-lock for C#,
4508 ;; specifically for the verbatim-literal strings. C# is a cc-mode
4509 ;; language and strings are handled mostly like other c-based
4510 ;; languages. The one exception is the verbatim-literal string, which
4511 ;; uses the syntax @"...".
4512 ;;
4513 ;; `parse-partial-sexp' treats those strings as just regular strings,
4514 ;; with the @ a non-string character. This is fine, except when the
4515 ;; verblit string ends in a slash, in which case, font-lock breaks from
4516 ;; that point onward in the buffer.
4517 ;;
4518 ;; This is an attempt to fix that.
4519 ;;
4520 ;; The idea is to scan the buffer in full for verblit strings, and apply the
4521 ;; appropriate syntax-table text properties for verblit strings. Also setting
4522 ;; `parse-sexp-lookup-properties' to t tells `parse-partial-sexp'
4523 ;; to use the syntax-table text properties set up by the scan as it does
4524 ;; its parse.
4525 ;;
4526 ;; Also need to re-scan after any changes in the buffer, but on a more
4527 ;; limited region.
4528 ;;
4529
4530
4531 ;; ;; I don't remember what this is supposed to do,
4532 ;; ;; or how I figured out the value.
4533 ;; ;;
4534 ;; (defconst csharp-font-lock-syntactic-keywords
4535 ;; '(("\\(@\\)\\(\"\\)[^\"]*\\(\"\\)\\(\"\\)[^\"]*\\(\"\\)[^\"]"
4536 ;; (1 '(6)) (2 '(7)) (3 '(1)) (4 '(1)) (5 '(7))
4537 ;; ))
4538 ;; "Highlighting of verbatim literal strings. See also the variable
4539 ;; `font-lock-keywords'.")
4540
4541
4542
4543 (defun csharp-time ()
4544 "returns the time of day as a string. Used in the `csharp-log' function."
4545 (substring (current-time-string) 11 19)) ;24-hr time
4546
4547
4548 (defun csharp-log (level text &rest args)
4549 "Log a message at level LEVEL.
4550 If LEVEL is higher than `csharp-log-level', the message is
4551 ignored. Otherwise, it is printed using `message'.
4552 TEXT is a format control string, and the remaining arguments ARGS
4553 are the string substitutions (see `format')."
4554 (if (<= level csharp-log-level)
4555 (let* ((msg (apply 'format text args)))
4556 (message "C# %s %s" (csharp-time) msg))))
4557
4558
4559
4560 (defun csharp-max-beginning-of-stmt ()
4561 "Return the greater of `c-beginning-of-statement-1' and
4562 `c-beginning-of-statement' . I don't understand why both of
4563 these methods are necessary or why they differ. But they do."
4564
4565 (let (dash
4566 nodash
4567 (curpos (point)))
4568
4569 ;; I think this may need a save-excursion...
4570 ;; Calling c-beginning-of-statement-1 resets the point!
4571
4572 (setq dash (progn (c-beginning-of-statement-1) (point)))
4573 (csharp-log 3 "max-bostmt dash(%d)" dash)
4574 (goto-char curpos)
4575
4576 (setq nodash (progn (c-beginning-of-statement 1) (point)))
4577 (csharp-log 3 "max-bostmt nodash(%d)" nodash)
4578 (goto-char curpos)
4579
4580 (max dash nodash)))
4581
4582
4583
4584
4585
4586 (defun csharp-set-vliteral-syntax-table-properties (beg end)
4587 "Scan the buffer text between BEG and END, a verbatim literal
4588 string, setting and clearing syntax-table text properties where
4589 necessary.
4590
4591 We need to modify the default syntax-table text property in these cases:
4592 (backslash) - is not an escape inside a verbatim literal string.
4593 (double-quote) - can be a literal quote, when doubled.
4594
4595 BEG is the @ delimiter. END is the 'old' position of the ending quote.
4596
4597 see http://www.sunsite.ualberta.ca/Documentation/Gnu/emacs-lisp-ref-21-2.7/html_node/elisp_592.html
4598 for the list of syntax table numeric codes.
4599
4600 "
4601
4602 (csharp-log 3 "set-vlit-syntax-table: beg(%d) end(%d)" beg end)
4603
4604 (if (and (> beg 0) (> end 0))
4605
4606 (let ((curpos beg)
4607 (state 0))
4608
4609 (c-clear-char-properties beg end 'syntax-table)
4610
4611 (while (<= curpos end)
4612
4613 (cond
4614 ((= state 0)
4615 (if (= (char-after curpos) ?@)
4616 (progn
4617 (c-put-char-property curpos 'syntax-table '(6)) ; (6) = expression prefix, (3) = symbol
4618 ;;(message (format "set-s-t: prefix pos(%d) chr(%c)" beg (char-after beg)))
4619 )
4620 )
4621 (setq state (+ 1 state)))
4622
4623 ((= state 1)
4624 (if (= (char-after curpos) ?\")
4625 (progn
4626 (c-put-char-property curpos 'syntax-table '(7)) ; (7) = string quote
4627 ;;(message (format "set-s-t: open quote pos(%d) chr(%c)"
4628 ;; curpos (char-after curpos)))
4629 ))
4630 (setq state (+ 1 state)))
4631
4632 ((= state 2)
4633 (cond
4634 ;; handle backslash inside the string
4635 ((= (char-after curpos) ?\\)
4636 (c-put-char-property curpos 'syntax-table '(2)) ; (1) = punctuation, (2) = word
4637 ;;(message (format "set-s-t: backslash word pos(%d) chr(%c)" curpos (char-after curpos)))
4638 )
4639
4640 ;; doubled double-quote
4641 ((and
4642 (= (char-after curpos) ?\")
4643 (= (char-after (+ 1 curpos)) ?\"))
4644 (c-put-char-property curpos 'syntax-table '(2)) ; (1) = punctuation, (2) = word
4645 (c-put-char-property (+ 1 curpos) 'syntax-table '(2)) ; (1) = punctuation
4646 ;;(message (format "set-s-t: double doublequote pos(%d) chr(%c)" curpos (char-after curpos)))
4647 (setq curpos (+ curpos 1))
4648 )
4649
4650 ;; a single double-quote, which should be a string terminator
4651 ((= (char-after curpos) ?\")
4652 (c-put-char-property curpos 'syntax-table '(7)) ; (7) = string quote
4653 ;;(message (format "set-s-t: close quote pos(%d) chr(%c)" curpos (char-after curpos)))
4654 ;;go no further
4655 (setq state (+ 1 state)))
4656
4657 ;; everything else
4658 (t
4659 ;;(message (format "set-s-t: none pos(%d) chr(%c)" curpos (char-after curpos)))
4660 nil))))
4661 ;; next char
4662 (setq curpos (+ curpos 1))))))
4663
4664
4665
4666 (defun csharp-end-of-verbatim-literal-string (&optional lim)
4667 "Moves to and returns the position of the end quote of the verbatim literal
4668 string. When calling, point should be on the @ of the verblit string.
4669 If it is not, then no movement is performed and `point' is returned.
4670
4671 This function ignores text properties. In fact it is the
4672 underlying scanner used to set the text properties in a C# buffer.
4673 "
4674
4675 (csharp-log 3 "end-of-vlit-string: point(%d) c(%c)" (point) (char-after))
4676
4677 (let (curpos
4678 (max (or lim (point-max))))
4679
4680 (if (not (looking-at "@\""))
4681 (point)
4682 (forward-char 2) ;; pass up the @ sign and first quote
4683 (setq curpos (point))
4684
4685 ;; Within a verbatim literal string, a doubled double-quote
4686 ;; escapes the double-quote."
4687 (while (and ;; process characters...
4688 (or ;; while...
4689 (not (eq (char-after curpos) ?\")) ;; it's not a quote
4690 (eq (char-after (+ curpos 1)) ?\")) ;; or, its a double (double) quote
4691 (< curpos max)) ;; and we're not done yet
4692
4693 (cond
4694 ((and (eq (char-after curpos) ?\") ;; it's a double-quote.
4695 (eq (char-after (+ curpos 1)) ?\"))
4696 (setq curpos (+ 2 curpos))) ;; Skip 2
4697 (t ;; anything else
4698 (setq curpos (+ 1 curpos))))) ;; skip fwd 1
4699 curpos)))
4700
4701
4702
4703
4704 (defun csharp-scan-for-verbatim-literals-and-set-props (&optional beg end)
4705 "Scans the buffer, between BEG and END, for verbatim literal
4706 strings, and sets override text properties on each string to
4707 allow proper syntax highlighting, indenting, and cursor movement.
4708
4709 BEG and END define the limits of the scan. When nil, they
4710 default to `point-min' and `point-max' respectively.
4711
4712 Setting text properties generally causes the buffer to be marked
4713 as modified, but this fn suppresses that via the
4714 `c-buffer-save-state' macro, for any changes in text properties
4715 that it makes. This fn also ignores the read-only setting on a
4716 buffer, using the same macro.
4717
4718 This fn is called when a csharp-mode buffer is loaded, with BEG
4719 and END set to nil, to do a full scan. It is also called on
4720 every buffer change, with the BEG and END set to the values for
4721 the change.
4722
4723 The return value is nil if the buffer was not a csharp-mode
4724 buffer. Otherwise it is the last cursor position examined by the
4725 scan.
4726 "
4727
4728 (if (not (c-major-mode-is 'csharp-mode)) ;; don't scan if not csharp mode
4729 nil
4730 (save-excursion
4731 (c-save-buffer-state
4732 ((curpos (or beg (point-min)))
4733 (lastpos (or end (point-max)))
4734 (state 0) (start 0) (cycle 0)
4735 literal eos limits)
4736
4737 (csharp-log 3 "verblit scan")
4738 (goto-char curpos)
4739
4740 (while (and (< curpos lastpos) (< cycle 10000))
4741 (cond
4742
4743 ;; Case 1: current char is a @ sign
4744 ;; --------------------------------------------
4745 ;; Check to see if it demarks the beginning of a verblit
4746 ;; string.
4747 ((= ?@ (char-after curpos))
4748
4749 ;; are we in a comment? a string? Maybe the @ is a prefix
4750 ;; to allow the use of a reserved word as a symbol. Let's find out.
4751
4752 ;; not sure why I need both of the following.
4753 (syntax-ppss-flush-cache 1)
4754 (parse-partial-sexp 1 curpos)
4755 (goto-char curpos)
4756 (setq literal (csharp-in-literal))
4757 (cond
4758
4759 ;; Case 1.A: it's a @ within a string.
4760 ;; --------------------------------------------
4761 ;; This should never happen, because this scanner hops over strings.
4762 ;; But it might happen if the scan starts at an odd place.
4763 ((eq literal 'string) nil)
4764
4765 ;; Case 1.B: The @ is within a comment. Hop over it.
4766 ((and (memq literal '(c c++))
4767 ;; This is a kludge for XEmacs where we use
4768 ;; `buffer-syntactic-context', which doesn't correctly
4769 ;; recognize "\*/" to end a block comment.
4770 ;; `parse-partial-sexp' which is used by
4771 ;; `c-literal-limits' will however do that in most
4772 ;; versions, which results in that we get nil from
4773 ;; `c-literal-limits' even when `c-in-literal' claims
4774 ;; we're inside a comment.
4775 ;;(setq limits (c-literal-limits start)))
4776 (setq limits (c-literal-limits)))
4777
4778 ;; advance to the end of the comment
4779 (if limits
4780 (progn
4781 (csharp-log 4 "scan: jump end comment A (%d)" (cdr limits))
4782 (setq curpos (cdr limits)))))
4783
4784
4785 ;; Case 1.B: curpos is at least 2 chars before the last
4786 ;; position to examine, and, the following char is a
4787 ;; double-quote (ASCII 34).
4788 ;; --------------------------------------------
4789 ;; This looks like the beginning of a verbatim string
4790 ;; literal.
4791 ((and (< (+ 2 curpos) lastpos)
4792 (= ?\" (char-after (+ 1 curpos))))
4793
4794 (setq eos (csharp-end-of-verbatim-literal-string))
4795 ;; set override syntax properties on the verblit string
4796 (csharp-set-vliteral-syntax-table-properties curpos eos)
4797
4798 (csharp-log 4 "scan: jump end verblit string (%d)" eos)
4799 (setq curpos eos))))
4800
4801
4802 ;; Case 2: current char is a double-quote.
4803 ;; --------------------------------------------
4804 ;; If this is a string, we hop over it, on the assumption that
4805 ;; this scanner need not bother with regular literal strings, which
4806 ;; get the proper syntax with the generic approach.
4807 ;; If in a comment, hop over the comment.
4808 ((= ?\" (char-after curpos))
4809 (goto-char curpos)
4810 (setq literal (c-in-literal))
4811 (cond
4812
4813 ;; Case 2.A: a quote within a string
4814 ;; --------------------------------------------
4815 ;; This shouldn't happen, because we hop over strings.
4816 ;; But it might.
4817 ((eq literal 'string) nil)
4818
4819 ;; Case 2.B: a quote within a comment
4820 ;; --------------------------------------------
4821 ((and (memq literal '(c c++))
4822 ;; This is a kludge for XEmacs where we use
4823 ;; `buffer-syntactic-context', which doesn't correctly
4824 ;; recognize "\*/" to end a block comment.
4825 ;; `parse-partial-sexp' which is used by
4826 ;; `c-literal-limits' will however do that in most
4827 ;; versions, which results in that we get nil from
4828 ;; `c-literal-limits' even when `c-in-literal' claims
4829 ;; we're inside a comment.
4830 ;;(setq limits (c-literal-limits start)))
4831 (setq limits (c-literal-limits)))
4832
4833 ;; advance to the end of the comment
4834 (if limits
4835 (progn
4836 (setq curpos (cdr limits))
4837 (csharp-log 3 "scan: jump end comment B (%s)" curpos))))
4838
4839
4840 ;; Case 2.C: Not in a comment, and not in a string.
4841 ;; --------------------------------------------
4842 ;; This is the beginning of a literal (but not verbatim) string.
4843 (t
4844 (forward-char 1) ;; pass up the quote
4845 (if (consp (setq limits (c-literal-limits)))
4846 (progn
4847 (csharp-log 4 "scan: jump end literal (%d)" (cdr limits))
4848 (setq curpos (cdr limits))))))))
4849
4850 (setq cycle (+ 1 cycle))
4851 (setq curpos (+ 1 curpos))
4852 (c-safe (goto-char curpos)))))))
4853
4854
4855
4856 (defun csharp--before-font-lock (beg end old-len)
4857 "Adjust`syntax-table' properties on the region affected by the change
4858 in a csharp-mode buffer.
4859
4860 This function is the C# value for `c-before-font-lock-function'.
4861 It intended to be called only by the cc-mode runtime.
4862
4863 It prepares the buffer for font locking, hence must get called
4864 before `font-lock-after-change-function'.
4865
4866 It does hidden buffer changes.
4867
4868 BEG, END and OLD-LEN have the same meaning here as for any
4869 after-change function.
4870
4871 Point is undefined both before and after this function call.
4872 The return value is meaningless, and is ignored by cc-mode.
4873 "
4874 (csharp-log 2 "before font lock %d %d %d %d" beg end old-len (point))
4875 (let ((start-scan (progn
4876 ;; is this right? I think
4877 (c-beginning-of-statement 1)
4878 (point))))
4879 (csharp-scan-for-verbatim-literals-and-set-props start-scan end)))
4880
4881
4882
4883 (c-lang-defconst c-before-font-lock-function
4884 csharp 'csharp--before-font-lock)
4885
4886 ;; ==================================================================
4887 ;; end of c# fontification extensions
4888 ;; ==================================================================
4889
4890
4891
4892
4893
4894 ;; ==================================================================
4895 ;; C#-specific optimizations of cc-mode funcs
4896 ;; ==================================================================
4897
4898 ;; There's never a need to move over an Obj-C directive in csharp-mode.
4899 (defadvice c-forward-objc-directive (around
4900 csharp-mode-advice-2
4901 compile activate)
4902 (if (c-major-mode-is 'csharp-mode)
4903 nil
4904 ad-do-it)
4905 )
4906
4907 ;; ==================================================================
4908 ;; end of C#-specific optimizations of cc-mode funcs
4909 ;; ==================================================================
4910
4911
4912
4913
4914
4915
4916
4917
4918 ;; ==================================================================
4919 ;; c# - monkey-patching of basic parsing logic
4920 ;; ==================================================================
4921 ;;
4922 ;; The following 2 defuns redefine functions from cc-mode, to add
4923 ;; special cases for C#. These primarily deal with indentation of
4924 ;; instance initializers, which are somewhat unique to C#. I couldn't
4925 ;; figure out how to get cc-mode to do what C# needs, without modifying
4926 ;; these defuns.
4927 ;;
4928
4929 (defun c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
4930 ;; Return non-nil if we're looking at the beginning of a block
4931 ;; inside an expression. The value returned is actually a cons of
4932 ;; either 'inlambda, 'inexpr-statement or 'inexpr-class and the
4933 ;; position of the beginning of the construct.
4934 ;;
4935 ;; LIM limits the backward search. CONTAINING-SEXP is the start
4936 ;; position of the closest containing list. If it's nil, the
4937 ;; containing paren isn't used to decide whether we're inside an
4938 ;; expression or not. If both LIM and CONTAINING-SEXP are used, LIM
4939 ;; needs to be farther back.
4940 ;;
4941 ;; If CHECK-AT-END is non-nil then extra checks at the end of the
4942 ;; brace block might be done. It should only be used when the
4943 ;; construct can be assumed to be complete, i.e. when the original
4944 ;; starting position was further down than that.
4945 ;;
4946 ;; This function might do hidden buffer changes.
4947
4948 (save-excursion
4949 (let ((res 'maybe) passed-paren
4950 (closest-lim (or containing-sexp lim (point-min)))
4951 ;; Look at the character after point only as a last resort
4952 ;; when we can't disambiguate.
4953 (block-follows (and (eq (char-after) ?{) (point))))
4954
4955 (while (and (eq res 'maybe)
4956 (progn (c-backward-syntactic-ws)
4957 (> (point) closest-lim))
4958 (not (bobp))
4959 (progn (backward-char)
4960 (looking-at "[\]\).]\\|\w\\|\\s_"))
4961 (c-safe (forward-char)
4962 (goto-char (scan-sexps (point) -1))))
4963
4964 (setq res
4965 (if (looking-at c-keywords-regexp)
4966 (let ((kw-sym (c-keyword-sym (match-string 1))))
4967 (cond
4968 ((and block-follows
4969 (c-keyword-member kw-sym 'c-inexpr-class-kwds))
4970 (and (not (eq passed-paren ?\[))
4971
4972 ;; dinoch Thu, 22 Apr 2010 18:20
4973 ;; ============================================
4974 ;; looking at new MyType() { ... }
4975 ;; means this is a brace list, so, return nil,
4976 ;; implying NOT looking-at-inexpr-block
4977 (not
4978 (and (c-major-mode-is 'csharp-mode)
4979 (looking-at "new[ \t\n\f\v\r]+\\([[:alnum:]_]+\\)\\b")))
4980
4981 (or (not (looking-at c-class-key))
4982 ;; If the class instantiation is at the start of
4983 ;; a statement, we don't consider it an
4984 ;; in-expression class.
4985 (let ((prev (point)))
4986 (while (and
4987 (= (c-backward-token-2 1 nil closest-lim) 0)
4988 (eq (char-syntax (char-after)) ?w))
4989 (setq prev (point)))
4990 (goto-char prev)
4991 (not (c-at-statement-start-p)))
4992 ;; Also, in Pike we treat it as an
4993 ;; in-expression class if it's used in an
4994 ;; object clone expression.
4995 (save-excursion
4996 (and check-at-end
4997 (c-major-mode-is 'pike-mode)
4998 (progn (goto-char block-follows)
4999 (zerop (c-forward-token-2 1 t)))
5000 (eq (char-after) ?\())))
5001 (cons 'inexpr-class (point))))
5002 ((c-keyword-member kw-sym 'c-inexpr-block-kwds)
5003 (when (not passed-paren)
5004 (cons 'inexpr-statement (point))))
5005 ((c-keyword-member kw-sym 'c-lambda-kwds)
5006 (when (or (not passed-paren)
5007 (eq passed-paren ?\())
5008 (cons 'inlambda (point))))
5009 ((c-keyword-member kw-sym 'c-block-stmt-kwds)
5010 nil)
5011 (t
5012 'maybe)))
5013
5014 (if (looking-at "\\s(")
5015 (if passed-paren
5016 (if (and (eq passed-paren ?\[)
5017 (eq (char-after) ?\[))
5018 ;; Accept several square bracket sexps for
5019 ;; Java array initializations.
5020 'maybe)
5021 (setq passed-paren (char-after))
5022 'maybe)
5023 'maybe))))
5024
5025 (if (eq res 'maybe)
5026 (when (and c-recognize-paren-inexpr-blocks
5027 block-follows
5028 containing-sexp
5029 (eq (char-after containing-sexp) ?\())
5030 (goto-char containing-sexp)
5031 (if (or (save-excursion
5032 (c-backward-syntactic-ws lim)
5033 (and (> (point) (or lim (point-min)))
5034 (c-on-identifier)))
5035 (and c-special-brace-lists
5036 (c-looking-at-special-brace-list)))
5037 nil
5038 (cons 'inexpr-statement (point))))
5039
5040 res))))
5041
5042
5043
5044
5045
5046 (defun c-inside-bracelist-p (containing-sexp paren-state)
5047 ;; return the buffer position of the beginning of the brace list
5048 ;; statement if we're inside a brace list, otherwise return nil.
5049 ;; CONTAINING-SEXP is the buffer pos of the innermost containing
5050 ;; paren. PAREN-STATE is the remainder of the state of enclosing
5051 ;; braces
5052 ;;
5053 ;; N.B.: This algorithm can potentially get confused by cpp macros
5054 ;; placed in inconvenient locations. It's a trade-off we make for
5055 ;; speed.
5056 ;;
5057 ;; This function might do hidden buffer changes.
5058 (or
5059 ;; This will pick up brace list declarations.
5060 (c-safe
5061 (save-excursion
5062 (goto-char containing-sexp)
5063 (c-safe (c-forward-sexp -1))
5064 (let (bracepos)
5065 (if (and (or (looking-at c-brace-list-key)
5066
5067 (progn
5068 (c-safe (c-forward-sexp -1))
5069 (looking-at c-brace-list-key))
5070
5071 ;; dinoch Thu, 22 Apr 2010 18:20
5072 ;; ============================================
5073 ;; looking enum Foo : int
5074 ;; means this is a brace list, so, return nil,
5075 ;; implying NOT looking-at-inexpr-block
5076
5077 (and (c-major-mode-is 'csharp-mode)
5078 (progn
5079 (c-safe (c-forward-sexp -1))
5080 (looking-at csharp-enum-decl-re))))
5081
5082 (setq bracepos (c-down-list-forward (point)))
5083 (not (c-crosses-statement-barrier-p (point)
5084 (- bracepos 2))))
5085 (point)))))
5086
5087 ;; this will pick up array/aggregate init lists, even if they are nested.
5088 (save-excursion
5089 (let ((class-key
5090 ;; Pike can have class definitions anywhere, so we must
5091 ;; check for the class key here.
5092 (and (c-major-mode-is 'pike-mode)
5093 c-decl-block-key))
5094 bufpos braceassignp lim next-containing)
5095 (while (and (not bufpos)
5096 containing-sexp)
5097 (when paren-state
5098 (if (consp (car paren-state))
5099 (setq lim (cdr (car paren-state))
5100 paren-state (cdr paren-state))
5101 (setq lim (car paren-state)))
5102 (when paren-state
5103 (setq next-containing (car paren-state)
5104 paren-state (cdr paren-state))))
5105 (goto-char containing-sexp)
5106 (if (c-looking-at-inexpr-block next-containing next-containing)
5107 ;; We're in an in-expression block of some kind. Do not
5108 ;; check nesting. We deliberately set the limit to the
5109 ;; containing sexp, so that c-looking-at-inexpr-block
5110 ;; doesn't check for an identifier before it.
5111 (setq containing-sexp nil)
5112 ;; see if the open brace is preceded by = or [...] in
5113 ;; this statement, but watch out for operator=
5114 (setq braceassignp 'dontknow)
5115 (c-backward-token-2 1 t lim)
5116 ;; Checks to do only on the first sexp before the brace.
5117 (when (and c-opt-inexpr-brace-list-key
5118 (eq (char-after) ?\[))
5119 ;; In Java, an initialization brace list may follow
5120 ;; directly after "new Foo[]", so check for a "new"
5121 ;; earlier.
5122 (while (eq braceassignp 'dontknow)
5123 (setq braceassignp
5124 (cond ((/= (c-backward-token-2 1 t lim) 0) nil)
5125 ((looking-at c-opt-inexpr-brace-list-key) t)
5126 ((looking-at "\\sw\\|\\s_\\|[.[]")
5127 ;; Carry on looking if this is an
5128 ;; identifier (may contain "." in Java)
5129 ;; or another "[]" sexp.
5130 'dontknow)
5131 (t nil)))))
5132 ;; Checks to do on all sexps before the brace, up to the
5133 ;; beginning of the statement.
5134 (while (eq braceassignp 'dontknow)
5135 (cond ((eq (char-after) ?\;)
5136 (setq braceassignp nil))
5137 ((and class-key
5138 (looking-at class-key))
5139 (setq braceassignp nil))
5140 ((eq (char-after) ?=)
5141 ;; We've seen a =, but must check earlier tokens so
5142 ;; that it isn't something that should be ignored.
5143 (setq braceassignp 'maybe)
5144 (while (and (eq braceassignp 'maybe)
5145 (zerop (c-backward-token-2 1 t lim)))
5146 (setq braceassignp
5147 (cond
5148 ;; Check for operator =
5149 ((and c-opt-op-identifier-prefix
5150 (looking-at c-opt-op-identifier-prefix))
5151 nil)
5152 ;; Check for `<opchar>= in Pike.
5153 ((and (c-major-mode-is 'pike-mode)
5154 (or (eq (char-after) ?`)
5155 ;; Special case for Pikes
5156 ;; `[]=, since '[' is not in
5157 ;; the punctuation class.
5158 (and (eq (char-after) ?\[)
5159 (eq (char-before) ?`))))
5160 nil)
5161 ((looking-at "\\s.") 'maybe)
5162 ;; make sure we're not in a C++ template
5163 ;; argument assignment
5164 ((and
5165 (c-major-mode-is 'c++-mode)
5166 (save-excursion
5167 (let ((here (point))
5168 (pos< (progn
5169 (skip-chars-backward "^<>")
5170 (point))))
5171 (and (eq (char-before) ?<)
5172 (not (c-crosses-statement-barrier-p
5173 pos< here))
5174 (not (c-in-literal))
5175 ))))
5176 nil)
5177 (t t))))))
5178 (if (and (eq braceassignp 'dontknow)
5179 (/= (c-backward-token-2 1 t lim) 0))
5180 (setq braceassignp nil)))
5181 (if (not braceassignp)
5182 (if (eq (char-after) ?\;)
5183 ;; Brace lists can't contain a semicolon, so we're done.
5184 (setq containing-sexp nil)
5185 ;; Go up one level.
5186 (setq containing-sexp next-containing
5187 lim nil
5188 next-containing nil))
5189 ;; we've hit the beginning of the aggregate list
5190 (c-beginning-of-statement-1
5191 (c-most-enclosing-brace paren-state))
5192 (setq bufpos (point))))
5193 )
5194 bufpos))
5195 ))
5196
5197 ;; ==================================================================
5198 ;; end of monkey-patching of basic parsing logic
5199 ;; ==================================================================
5200
5201
5202
5203
5204 ;;(easy-menu-define csharp-menu csharp-mode-map "C# Mode Commands"
5205 ;; ;; Can use `csharp' as the language for `c-mode-menu'
5206 ;; ;; since its definition covers any language. In
5207 ;; ;; this case the language is used to adapt to the
5208 ;; ;; nonexistence of a cpp pass and thus removing some
5209 ;; ;; irrelevant menu alternatives.
5210 ;; (cons "C#" (c-lang-const c-mode-menu csharp)))
5211
5212 ;;; Autoload mode trigger
5213 ;;;###autoload
5214 (add-to-list 'auto-mode-alist '("\\.cs$" . csharp-mode))
5215
5216
5217 (c-add-style "C#"
5218 '("Java"
5219 (c-basic-offset . 4)
5220 (c-comment-only-line-offset . (0 . 0))
5221 (c-offsets-alist . (
5222 (access-label . -)
5223 (arglist-close . c-lineup-arglist)
5224 (arglist-cont . 0)
5225 (arglist-cont-nonempty . c-lineup-arglist)
5226 (arglist-intro . c-lineup-arglist-intro-after-paren)
5227 (block-close . 0)
5228 (block-open . 0)
5229 (brace-entry-open . 0)
5230 (brace-list-close . 0)
5231 (brace-list-entry . 0)
5232 (brace-list-intro . +)
5233 (brace-list-open . +)
5234 (c . c-lineup-C-comments)
5235 (case-label . +)
5236 (catch-clause . 0)
5237 (class-close . 0)
5238 (class-open . 0)
5239 (comment-intro . c-lineup-comment)
5240 (cpp-macro . 0)
5241 (cpp-macro-cont . c-lineup-dont-change)
5242 (defun-block-intro . +)
5243 (defun-close . 0)
5244 (defun-open . 0)
5245 (do-while-closure . 0)
5246 (else-clause . 0)
5247 (extern-lang-close . 0)
5248 (extern-lang-open . 0)
5249 (friend . 0)
5250 (func-decl-cont . +)
5251 (inclass . +)
5252 (inexpr-class . +)
5253 (inexpr-statement . 0)
5254 (inextern-lang . +)
5255 (inher-cont . c-lineup-multi-inher)
5256 (inher-intro . +)
5257 (inlambda . c-lineup-inexpr-block)
5258 (inline-close . 0)
5259 (inline-open . 0)
5260 (innamespace . +)
5261 (knr-argdecl . 0)
5262 (knr-argdecl-intro . 5)
5263 (label . 0)
5264 (lambda-intro-cont . +)
5265 (member-init-cont . c-lineup-multi-inher)
5266 (member-init-intro . +)
5267 (namespace-close . 0)
5268 (namespace-open . 0)
5269 (statement . 0)
5270 (statement-block-intro . +)
5271 (statement-case-intro . +)
5272 (statement-case-open . +)
5273 (statement-cont . +)
5274 (stream-op . c-lineup-streamop)
5275 (string . c-lineup-dont-change)
5276 (substatement . +)
5277 (substatement-open . 0)
5278 (template-args-cont c-lineup-template-args +)
5279 (topmost-intro . 0)
5280 (topmost-intro-cont . +)
5281 ))
5282 ))
5283
5284
5285
5286 (defun csharp-guess-compile-command ()
5287 "set `compile-command' intelligently depending on the
5288 current buffer, or the contents of the current directory.
5289 "
5290 (interactive)
5291 (set (make-local-variable 'compile-command)
5292
5293 (cond
5294 ((or (file-expand-wildcards "*.csproj" t)
5295 (file-expand-wildcards "*.vcproj" t)
5296 (file-expand-wildcards "*.vbproj" t)
5297 (file-expand-wildcards "*.shfbproj" t)
5298 (file-expand-wildcards "*.sln" t))
5299 (concat csharp-msbuild-tool " "))
5300
5301 ;; sometimes, not sure why, the buffer-file-name is
5302 ;; not set. Can use it only if set.
5303 (buffer-file-name
5304 (let ((filename (file-name-nondirectory buffer-file-name)))
5305 (cond
5306
5307 ;; editing a c# file - check for an explicitly-specified command
5308 ((string-equal (substring buffer-file-name -3) ".cs")
5309 (let ((explicitly-specified-command
5310 (csharp-get-value-from-comments "compile" csharp-cmd-line-limit)))
5311
5312
5313 (if explicitly-specified-command
5314 (csharp-replace-command-tokens explicitly-specified-command)
5315 (concat csharp-make-tool " " ;; assume a makefile exists
5316 (file-name-sans-extension filename)
5317 ".exe"))))
5318
5319 ;; something else - do a typical .exe build
5320 (t
5321 (concat csharp-make-tool " "
5322 (file-name-sans-extension filename)
5323 ".exe")))))
5324 (t
5325 ;; punt
5326 (concat csharp-make-tool " ")))))
5327
5328
5329
5330 (defun csharp-invoke-compile-interactively ()
5331 "fn to wrap the `compile' function. This simply
5332 checks to see if `compile-command' has been previously set, and
5333 if not, invokes `csharp-guess-compile-command' to set the value.
5334 Then it invokes the `compile' function, interactively.
5335
5336 The effect is to guess the compile command only once, per buffer.
5337
5338 I tried doing this with advice attached to the `compile'
5339 function, but because of the interactive nature of the fn, it
5340 didn't work the way I wanted it to. So this fn should be bound to
5341 the key sequence the user likes for invoking compile, like ctrl-c
5342 ctrl-e.
5343
5344 "
5345 (interactive)
5346 (cond
5347 ((not (boundp 'csharp-local-compile-command-has-been-set))
5348 (csharp-guess-compile-command)
5349 (set (make-local-variable 'csharp-local-compile-command-has-been-set) t)))
5350 ;; local compile command has now been set
5351 (call-interactively 'compile))
5352
5353
5354
5355
5356 ;;; The entry point into the mode
5357 ;;;###autoload
5358 (defun csharp-mode ()
5359 "Major mode for editing C# code. This mode is derived from CC Mode to
5360 support C#.
5361
5362 Normally, you'd want to autoload this mode by setting `auto-mode-alist' with
5363 an entry for csharp, in your .emacs file:
5364
5365 (autoload 'csharp-mode \"csharp-mode\" \"Major mode for editing C# code.\" t)
5366 (setq auto-mode-alist
5367 (append '((\"\\.cs$\" . csharp-mode)) auto-mode-alist))
5368
5369 The mode provides fontification and indent for C# syntax, as well
5370 as some other handy features.
5371
5372 At mode startup, there are two interesting hooks that run:
5373 `c-mode-common-hook' is run with no args, then `csharp-mode-hook' is run after
5374 that, also with no args.
5375
5376 To run your own logic after csharp-mode starts, do this:
5377
5378 (defun my-csharp-mode-fn ()
5379 \"my function that runs when csharp-mode is initialized for a buffer.\"
5380 (turn-on-font-lock)
5381 (turn-on-auto-revert-mode) ;; helpful when also using Visual Studio
5382 (setq indent-tabs-mode nil) ;; tabs are evil
5383 (flymake-mode 1)
5384 (yas/minor-mode-on)
5385 (require 'rfringe) ;; handy for flymake
5386 (require 'flymake-cursor) ;; also handy for flymake
5387 ....your own code here...
5388 )
5389 (add-hook 'csharp-mode-hook 'my-csharp-mode-fn t)
5390
5391
5392 The function above is just a suggestion.
5393
5394
5395 Compile integration:
5396 ========================
5397
5398 csharp-mode binds the function `csharp-invoke-compile-interactively' to
5399 \"\C-x\C-e\" . This function attempts to intellgently guess the format of the
5400 compile command to use for a buffer. It looks in the comments at the head of
5401 the buffer for a line that begins with compile: . For exammple:
5402
5403 // compile: csc.exe /t:library /r:Mylib.dll Foo.cs
5404
5405 If csharp-mode finds a line like this, it will suggest the text that follows
5406 as the compilation command when running `compile' for the first time. If such
5407 a line is not found, csharp-mode falls back to a msbuild or nmake command.
5408 See the documentation on `csharp-cmd-line-limit' for further information. If
5409 you don't want this magic, then you can just run `compile' directly, rather
5410 than `csharp-invoke-compile-interactively' .
5411
5412 This mode will also automatically add a symbol and regexp to the
5413 `compilation-error-regexp-alist' and`compilation-error-regexp-alist-alist'
5414 respectively, for Csc.exe error and warning messages. If you invoke `compile',
5415 then `next-error' should work properly for error messages produced by csc.exe.
5416
5417
5418 Flymake Integraiton
5419 ========================
5420
5421 You can use flymake with csharp mode to automatically check the syntax of your
5422 csharp code, and highlight errors. To do so, add a comment line like this to
5423 each .cs file that you use flymake with:
5424
5425 // flymake: csc.exe /t:module /R:Foo.dll @@FILE@@
5426
5427 csharp-mode replaces special tokens in the command with different values:
5428
5429 @@ORIG@@ - gets replaced with the original filename
5430 @@FILE@@ - gets replaced with the name of the temporary file
5431 created by flymake. This is usually what you want in place of the
5432 name of the file to be compiled.
5433
5434 See the documentation on `csharp-cmd-line-limit' for further information.
5435
5436 You may also want to run a syntax checker, like fxcop:
5437
5438 // flymake: fxcopcmd.exe /c /F:MyLibrary.dll
5439
5440 In this case you don't need either of the tokens described above.
5441
5442 If the module has no external dependencies, then you need not specify any
5443 flymake command at all. csharp-mode will implicitly act as if you had
5444 specified the command:
5445
5446 // flymake: csc.exe /t:module /nologo @@FILE@@
5447
5448 It looks for the EXE on the path. You can specify a full path if you like.
5449
5450
5451 YASnippet and IMenu Integraiton
5452 ===============================
5453
5454 Check the menubar for menu entries for YASnippet and Imenu; the latter
5455 is labelled \"Index\".
5456
5457 The Imenu index gets computed when the file is .cs first opened and loaded.
5458 This may take a moment or two. If you don't like this delay and don't
5459 use imenu, you can turn this off with the variable `csharp-want-imenu'.
5460
5461
5462
5463 Key bindings:
5464 \\{csharp-mode-map}"
5465 (interactive)
5466 (kill-all-local-variables)
5467 (make-local-variable 'beginning-of-defun-function)
5468 (make-local-variable 'end-of-defun-function)
5469 (c-initialize-cc-mode t)
5470 (set-syntax-table csharp-mode-syntax-table)
5471
5472 ;; define underscore as part of a word in the Csharp syntax table
5473 (modify-syntax-entry ?_ "w" csharp-mode-syntax-table)
5474
5475 ;; define @ as an expression prefix in Csharp syntax table
5476 (modify-syntax-entry ?@ "'" csharp-mode-syntax-table)
5477
5478 (setq major-mode 'csharp-mode
5479 mode-name "C#"
5480 local-abbrev-table csharp-mode-abbrev-table
5481 abbrev-mode t)
5482 (use-local-map csharp-mode-map)
5483
5484 ;; `c-init-language-vars' is a macro that is expanded at compile
5485 ;; time to a large `setq' with all the language variables and their
5486 ;; customized values for our language.
5487 (c-init-language-vars csharp-mode)
5488
5489 ;; `c-common-init' initializes most of the components of a CC Mode
5490 ;; buffer, including setup of the mode menu, font-lock, etc.
5491 ;; There's also a lower level routine `c-basic-common-init' that
5492 ;; only makes the necessary initialization to get the syntactic
5493 ;; analysis and similar things working.
5494 (c-common-init 'csharp-mode)
5495
5496 ;; compile
5497 (local-set-key "\C-x\C-e" 'csharp-invoke-compile-interactively)
5498
5499 ;; to allow next-error to work with csc.exe:
5500 (setq compilation-scroll-output t)
5501
5502 ;; csc.exe, the C# Compiler, produces errors like this:
5503 ;; file.cs(6,18): error CS1006: Name of constructor must match name of class
5504 (if (boundp 'compilation-error-regexp-alist-alist)
5505 (progn
5506 (add-to-list
5507 'compilation-error-regexp-alist-alist
5508 '(ms-csharp "^[ \t]*\\([-_:A-Za-z0-9][^\n(]*\\.\\(?:cs\\|xaml\\)\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) ?: \\(error\\|warning\\) CS[0-9]+:" 1 2 3))
5509 (add-to-list
5510 'compilation-error-regexp-alist
5511 'ms-csharp)))
5512
5513 ;; flymake
5514 (eval-after-load "flymake"
5515 '(progn
5516 (if csharp-want-flymake-fixup
5517 (csharp-flymake-install))))
5518
5519 (eval-after-load "yasnippet"
5520 '(progn
5521 (if csharp-want-yasnippet-fixup
5522 (csharp-yasnippet-fixup))))
5523
5524
5525 (local-set-key (kbd "/") 'csharp-maybe-insert-codedoc)
5526 (local-set-key (kbd "{") 'csharp-insert-open-brace)
5527
5528 ;; Need the following for parse-partial-sexp to work properly with
5529 ;; verbatim literal strings Setting this var to non-nil tells
5530 ;; `parse-partial-sexp' to pay attention to the syntax text
5531 ;; properties on the text in the buffer. If csharp-mode attaches
5532 ;; text syntax to @"..." then, `parse-partial-sexp' will treat those
5533 ;; strings accordingly.
5534 (set (make-local-variable 'parse-sexp-lookup-properties) t)
5535
5536 ;; scan the entire buffer for verblit strings
5537 ;; This will happen on font; it's necessary only
5538 ;; if font-lock is disabled. But it won't hurt.
5539 (csharp-scan-for-verbatim-literals-and-set-props nil nil)
5540
5541 ;; Allow fill-paragraph to work on xml code doc
5542 ;; This setting gets overwritten quietly by c-run-mode-hooks,
5543 ;; so I put it afterwards to make it stick.
5544 (make-local-variable 'paragraph-separate)
5545
5546 ;;(message "C#: set paragraph-separate")
5547
5548 ;; Speedbar handling
5549 (if (fboundp 'speedbar-add-supported-extension)
5550 (speedbar-add-supported-extension '(".cs"))) ;; idempotent
5551
5552 (c-update-modeline)
5553 (c-run-mode-hooks 'c-mode-common-hook 'csharp-mode-hook)
5554
5555 ;; maybe do imenu scan after hook returns
5556 (if csharp-want-imenu
5557 (progn
5558 ;; There are two ways to do imenu indexing. One is to provide a
5559 ;; function, via `imenu-create-index-function'. The other is to
5560 ;; provide imenu with a list of regexps via
5561 ;; `imenu-generic-expression'; imenu will do a "generic scan" for you.
5562 ;; csharp-mode uses the former method.
5563 ;;
5564 (setq imenu-create-index-function 'csharp-imenu-create-index)
5565 (imenu-add-menubar-index)))
5566
5567 ;; The paragraph-separate variable was getting stomped by
5568 ;; other hooks, so it must reside here.
5569 (setq paragraph-separate
5570 "[ \t]*\\(//+\\|\\**\\)\\([ \t]+\\|[ \t]+<.+?>\\)$\\|^\f")
5571
5572 (setq beginning-of-defun-function 'csharp-move-back-to-beginning-of-defun)
5573 ;; end-of-defun-function can remain forward-sexp !!
5574
5575 (set (make-local-variable 'comment-auto-fill-only-comments) t)
5576 )
5577
5578
5579
5580
5581 ;; =======================================================
5582 ;;
5583 ;; This section attempts to workaround an anomalous display behavior
5584 ;; for tooltips. It's not strictly necessary, only for aesthetics. The
5585 ;; issue is that tooltips can get clipped. This is the topic of Emacs
5586 ;; bug #5908, unfixed in v23 and present in v22.
5587
5588
5589 (defadvice tooltip-show (before
5590 flymake-for-csharp-fixup-tooltip
5591 (arg &optional use-echo-area)
5592 activate compile)
5593 (progn
5594 (if ;;(and (not use-echo-area) (eq major-mode 'csharp-mode))
5595 (not use-echo-area)
5596 (let ((orig (ad-get-arg 0)))
5597 (ad-set-arg 0 (concat " " (cheeso-string-trim (cheeso-reform-string 74 orig) ?\ )))
5598 ))))
5599
5600
5601
5602
5603 ;; ========================================================================
5604 ;; YA-snippet integration
5605
5606 (defun csharp-yasnippet-fixup ()
5607 "Sets snippets into ya-snippet for C#, if yasnippet is loaded,
5608 and if the snippets do not already exist.
5609 "
5610 (if (not csharp--yasnippet-has-been-fixed)
5611 (if (fboundp 'yas/snippet-table-fetch)
5612 ;; yasnippet is present
5613 (let ((snippet-table (yas/snippet-table 'csharp-mode))
5614 (keymap (if yas/use-menu
5615 (yas/menu-keymap-for-mode 'csharp-mode)
5616 nil))
5617 (yas/require-template-condition nil)
5618 (builtin-snips
5619 '(
5620 ("xmls" "{
5621 XmlSerializer s1 = new XmlSerializer(typeof(${1:type}));
5622
5623 // use this to \"suppress\" the default xsd and xsd-instance namespaces
5624 XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
5625 ns.Add(\"\", \"\");
5626
5627 s1.Serialize(new XTWFND(System.Console.Out), object, ns);
5628 System.Console.WriteLine(\"\\n\");
5629 }
5630
5631 $0
5632 /// XmlTextWriterFormattedNoDeclaration
5633 /// helper class : eliminates the XML Documentation at the
5634 /// start of a XML doc.
5635 /// XTWFND = XmlTextWriterFormattedNoDeclaration
5636 /// usage: s1.Serialize(new XTWFND(System.Console.Out), thing, ns);
5637
5638 public class XTWFND : System.Xml.XmlTextWriter
5639 {
5640 public XTWFND(System.IO.StringWriter w) : base(w) { Formatting=System.Xml.Formatting.Indented; }
5641 public XTWFND(System.IO.TextWriter w) : base(w) { Formatting = System.Xml.Formatting.Indented; }
5642 public XTWFND(System.IO.Stream s) : base(s, null) { Formatting = System.Xml.Formatting.Indented; }
5643 public XTWFND(string filename) : base(filename, null) { Formatting = System.Xml.Formatting.Indented; }
5644 public override void WriteStartDocument() { }
5645 }
5646
5647 " "xmlserializer { ... }" nil)
5648 ("wl" "System.Console.WriteLine(${0://thing to do});
5649 " "WriteLine( ... );" nil)
5650 ("while" "while (${1:condition})
5651 {
5652 ${0://thing to do}
5653 }" "while (...) { ... }" nil)
5654 ("using" "using (${1:type} ${2:var} = new ${1:type}(${4:ctor args}))
5655 {
5656 ${5:// body...}
5657 }" "using ... { ... }" nil)
5658 ("try" "try
5659 {
5660 $0
5661 }
5662 catch (System.Exception exc1)
5663 {
5664 throw new Exception(\"uncaught exception\", exc1);
5665 }" "try { ... } catch { ... }" nil)
5666 ("sum" "/// <summary>
5667 /// ${1:description}
5668 /// </summary>
5669 ///
5670 /// <remarks>
5671 /// ${2:comments}
5672 /// </remarks>
5673 " "/// <summary>..." nil)
5674 ("sing" "#region Singleton ${1:className}
5675 public sealed class $1
5676 {
5677 private readonly static $1 _instance = new $1();
5678 public static $1 Instance { get { return _instance; } }
5679 static $1() { /* required for lazy init */ }
5680 private $1()
5681 {
5682 // implementation here
5683 }
5684 }
5685
5686 #endregion
5687 " "public sealed class Singleton {...}" nil)
5688 ("setting" " #region Property${1:PropName}
5689
5690 private string default$1 = ${2:defaultValue};
5691 private string _$1;
5692 public string $1
5693 {
5694 get
5695 {
5696 if (_$1 == null)
5697 {
5698 _$1 = System.Configuration.ConfigurationManager.AppSettings[\"$1\"];
5699
5700 if (string.IsNullOrEmpty(_$1))
5701 {
5702 _$1 = default$1;
5703 }
5704 }
5705 return this._$1;
5706 }
5707 set
5708 {
5709 string new$1 = value;
5710 // optional validation:
5711 //Validation.EnforceXxxx(new$1, \"$1\");
5712 _$1 = new$1;
5713 }
5714 }
5715
5716 #endregion
5717 " "config setting" nil)
5718 ("prop" "private ${1:Type} _${2:Name};
5719 public ${1:Type} ${2:Name}
5720 {
5721 get
5722 {
5723 ${3://get impl}
5724 }
5725
5726 set
5727 {
5728 ${4://get impl}
5729 }
5730
5731 }" "property ... { ... }" nil)
5732 ("pa" " for (int i=0; i < args.Length; i++)
5733 {
5734 switch (args[i])
5735 {
5736 case \"-?\":
5737 case \"-help\":
5738 throw new ArgumentException(args[i]);
5739
5740 default: // positional args
5741 if ($2 != 0)
5742 // we have all the args we need
5743 throw new ArgumentException(args[i]);
5744
5745 if ($1 == null)
5746 $1 = args[i];
5747
5748 else
5749 $2 = System.Int32.Parse(args[i]);
5750
5751 break;
5752 }
5753 }
5754
5755 // check values
5756 if ($1 == null)
5757 throw new ArgumentException();
5758
5759 if ($2 == 0)
5760 throw new ArgumentException();
5761
5762
5763 " "switch(args[0]) {...}" nil)
5764 ("openread" " using(Stream src = File.OpenRead($1))
5765 {
5766 using(Stream dest= File.Create($2))
5767 {
5768 if (compress)
5769 Compress(src, dest);
5770 else
5771 Decompress(src, dest);
5772 }
5773 }
5774 " "File.OpenRead(...)" nil)
5775 ("ofd" "var dlg = new System.Windows.Forms.OpenFileDialog();
5776 dlg.Filter = \"${1:filter string}\"; // ex: \"C# (*.cs)|*.cs|Text (*.txt)|*.txt\";
5777 if (dlg.ShowDialog() == DialogResult.OK)
5778 {
5779 string fileName = dlg.FileName;
5780 $0
5781 }
5782 " "new OpenFileDialog; if (DialogResult.OK) { ... }" nil)
5783 ("ife" "if (${1:predicate})
5784 {
5785 ${2:// then clause}
5786 }
5787 else
5788 {
5789 ${3:// else clause}
5790 }" "if (...) { ... } else { ... }" nil)
5791 ("fore" "foreach (${1:type} ${2:var} in ${3:IEnumerable})
5792 {
5793 ${4:// body...}
5794 }" "foreach ... { ... }" nil)
5795 ("for" "for (int ${1:index}=0; $1 < ${2:Limit}; $1++)
5796 {
5797 ${3:// body...}
5798 }" "for (...) { ... }" nil)
5799 ("fbd" "using (FolderBrowserDialog dialog = new FolderBrowserDialog())
5800 {
5801 dialog.Description = \"Open a folder which contains the xml output\";
5802 dialog.ShowNewFolderButton = false;
5803 dialog.RootFolder = Environment.SpecialFolder.MyComputer;
5804 if(dialog.ShowDialog() == DialogResult.OK)
5805 {
5806 string folder = dialog.SelectedPath;
5807 foreach (string fileName in Directory.GetFiles(folder, \"*.xml\", SearchOption.TopDirectoryOnly))
5808 {
5809 SQLGenerator.GenerateSQLTransactions(Path.GetFullPath(fileName));
5810 }
5811 }
5812 }
5813
5814 " "new FolderBrowserDialog; if (DialogResult.OK) { ... }" nil)
5815 ("doc" "/// <summary>
5816 /// ${1:summary}
5817 /// </summary>
5818 ///
5819 /// <remarks>
5820 /// ${2:remarks}
5821 /// </remarks>
5822 $0" "XML Documentation" nil)
5823 ("conv" " List<String> Values = new List<String>() { \"7\", \"13\", \"41\", \"3\" };
5824
5825 // ConvertAll maps the given delegate across all the List elements
5826 var foo = Values.ConvertAll((s) => { return System.Convert.ToInt32(s); }) ;
5827
5828 System.Console.WriteLine(\"typeof(foo) = {0}\", foo.GetType().ToString());
5829
5830 Array.ForEach(foo.ToArray(),Console.WriteLine);
5831 " "ConvertAll((s) => { ... });" nil)
5832 ("cla" "public class ${1:Classname}
5833 {
5834 // default ctor
5835 public ${1:Classname}()
5836 {
5837 }
5838
5839 ${2:// methods here}
5840 }" "class ... { ... }" nil)
5841 ("ca" " List<String> Values = new List<String>() { \"7\", \"13\", \"41\", \"3\" };
5842
5843 // ConvertAll maps the given delegate across all the List elements
5844 var foo = Values.ConvertAll((s) => { return System.Convert.ToInt32(s); }) ;
5845
5846 System.Console.WriteLine(\"typeof(foo) = {0}\", foo.GetType().ToString());
5847
5848 Array.ForEach(foo.ToArray(),Console.WriteLine);
5849 " "ConvertAll((s) => { ... });" nil)
5850 ("ass" "
5851
5852 [assembly: AssemblyTitle(\"$1\")]
5853 [assembly: AssemblyCompany(\"${2:YourCoName}\")]
5854 [assembly: AssemblyProduct(\"${3}\")]
5855 [assembly: AssemblyCopyright(\"Copyright © ${4:Someone} 2011\")]
5856 [assembly: AssemblyTrademark(\"\")]
5857 [assembly: AssemblyCulture(\"\")]
5858 [assembly: AssemblyConfiguration(\"\")]
5859 [assembly: AssemblyDescription(\"${5}\")]
5860 [assembly: AssemblyVersion(\"${6:1.0.1.0}\")]
5861 [assembly: AssemblyFileVersion(\"${7:1.0.1.0}\")]
5862
5863 " "assembly info" nil)
5864 )))
5865
5866 (setq csharp--yasnippet-has-been-fixed t)
5867
5868 (add-to-list 'yas/known-modes 'csharp-mode)
5869
5870 ;; It's possible that Csharp-mode is not on the yasnippet menu
5871 ;; Install it here.
5872 (when yas/use-menu
5873 (define-key
5874 yas/menu-keymap
5875 (vector 'csharp-mode)
5876 `(menu-item "C#" ,keymap)))
5877
5878 ;; Insert the snippets from above into the table if they
5879 ;; are not already defined.
5880 (mapcar
5881 '(lambda (item)
5882 (let* ((full-key (car item))
5883 (existing-snip
5884 (yas/snippet-table-fetch snippet-table full-key)))
5885 (if (not existing-snip)
5886 (let* ((key (file-name-sans-extension full-key))
5887 (name (caddr item))
5888 (condition (nth 3 item))
5889 (template (yas/make-template (cadr item)
5890 (or name key)
5891 condition)))
5892 (yas/snippet-table-store snippet-table
5893 full-key
5894 key
5895 template)
5896 (when yas/use-menu
5897 (define-key keymap (vector (make-symbol full-key))
5898 `(menu-item ,(yas/template-name template)
5899 ,(yas/make-menu-binding (yas/template-content template))
5900 :keys ,(concat key yas/trigger-symbol))))))))
5901 builtin-snips)))))
5902
5903
5904
5905
5906 (message (concat "Done loading " load-file-name))
5907
5908
5909 (provide 'csharp-mode)
5910
5911 ;;; csharp-mode.el ends here
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.