MoinMoin Logo
  • Comments
  • Immutable Page
  • Menu
    • Navigation
    • RecentChanges
    • FindPage
    • Local Site Map
    • Help
    • HelpContents
    • HelpOnMoinWikiSyntax
    • Display
    • Attachments
    • Info
    • Raw Text
    • Print View
    • Edit
    • Load
    • Save
  • Login

Navigation

  • Start
  • Sitemap
  • attachment:csharp-mode-0.8.5.el of Emacs

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.
  • [get | view] (2023-09-04 11:06:07, 220.4 KB) [[attachment:csharp-mode-0.8.5.el]]
  • [get | view] (2023-09-04 11:06:07, 56.6 KB) [[attachment:zenburn-theme.el]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.

  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01