Peter Szilagyi avatar Peter Szilagyi committed e839109

Upgraded csharp-mode in a (failed) attempt to fix a compilation error.

Comments (0)

Files changed (1)

elisp/contrib/csharp-mode.el

 ;; Author     : Dylan R. E. Moonfire (original)
 ;; Maintainer : Dino Chiesa <dpchiesa@hotmail.com>
 ;; Created    : Feburary 2005
-;; Modified   : April 2010
-;; Version    : 0.7.6
+;; Modified   : May 2011
+;; Version    : 0.8.6
 ;; Keywords   : c# languages oop mode
 ;; X-URL      : http://code.google.com/p/csharpmode/
-;; Last-saved : <2010-May-24 21:53:58>
+;; Last-saved : <2011-May-22 10:48:26>
 
 ;;
 ;; This program is free software; you can redistribute it and/or modify
 
 ;;; Commentary:
 ;;
-;;    This is a separate mode to implement the C# constructs and
-;;    font-locking. It is based on the java-mode example from cc-mode.
+;;    This is a major mode for editing C# code. It performs automatic
+;;    indentation of C# syntax; font locking; and integration with compile.el;
+;;    flymake.el; yasnippet.el; and imenu.el.
 ;;
 ;;    csharp-mode requires CC Mode 5.30 or later.  It works with
 ;;    cc-mode 5.31.3, which is current at this time.
 ;;
 ;;   - automagic code-doc generation when you type three slashes.
 ;;
-;;   - intelligent inserttion of matched pairs of curly braces.
+;;   - intelligent insertion of matched pairs of curly braces.
 ;;
-;;   - sets the compiler regex for next-error, for csc.exe output.
+;;   - compile tweaks. Infers the compile command from special comments
+;;     in the file header.  Also, sets the regex for next-error, so that
+;;     compile.el can handle csc.exe output.
 ;;
+;;   - flymake integration
+;;       - select flymake command from code comments
+;;       - infer flymake command otherwise (presence of makefile, etc)
+;;       - Turn off query-on-exit-flag for the flymake process.
+;;       - define advice to flymake-goto-line , to allow it to goto the
+;;         appropriate column for the error on a given line. This works
+;;         with `flymake-goto-next-error' etc.
 ;;
-
-;;; To use:
+;;   - yasnippet integration
+;;       - preloaded snippets
 ;;
-;; put this in your .emacs:
+;;   - imenu integration - generates an index of namespaces, classes,
+;;     interfaces, methods, and properties for easy navigation within
+;;     the buffer.
+;;
+
+
+;; Installation instructions
+;; --------------------------------
+;;
+;; Put csharp-mode.el somewhere in your load path, optionally byte-compile
+;; it, and add the following to your .emacs file:
 ;;
 ;;   (autoload 'csharp-mode "csharp-mode" "Major mode for editing C# code." t)
-;;
-;; or:
-;;
-;;   (require 'csharp-mode)
-;;
-;;
-;; AND:
-;;
 ;;   (setq auto-mode-alist
 ;;      (append '(("\\.cs$" . csharp-mode)) auto-mode-alist))
+;;
+;;
+;; Optionally, define and register a mode-hook function. To do so, use
+;; something like this in your .emacs file:
+;;
 ;;   (defun my-csharp-mode-fn ()
 ;;      "function that runs when csharp-mode is initialized for a buffer."
-;;      ...insert your code here...
-;;      ...most commonly, your custom key bindings ...
+;;      (turn-on-auto-revert-mode)
+;;      (setq indent-tabs-mode nil)
+;;      (require 'flymake)
+;;      (flymake-mode 1)
+;;      (require 'yasnippet)
+;;      (yas/minor-mode-on)
+;;      (require 'rfringe)
+;;      ...insert more code here...
+;;      ...including any custom key bindings you might want ...
 ;;   )
 ;;   (add-hook  'csharp-mode-hook 'my-csharp-mode-fn t)
 ;;
 ;;
+;;  General
+;;  ----------------------------
+;;
+;;  Mostly C# mode will "just work."  Use `describe-mode' to see the
+;;  default keybindings and the highlights of the mode.
+;;
+;;
+;;  Flymake Integration
+;;  ----------------------------
+;;
+;;  You can use flymake with csharp mode to automatically check the
+;;  syntax of your csharp code, and highlight errors.  To do so, add a
+;;  comment line like this to each .cs file that you use flymake with:
+;;
+;;   //  flymake: c:\.net3.5\csc.exe /t:module /nologo /R:Foo.dll @@FILE@@
+;;
+;;  That lines specifies a command "stub".  Flymake appends the name of
+;;  the file to compile, and then runs the command to check
+;;  syntax. Flymake assumes that syntax errors will be noted in the
+;;  output of the command in a form that fits one of the regexs in the
+;;  `compilation-error-regexp-alist-alist'. Check the flymake module for
+;;  more information on that.
+;;
+;;  Some rules for the command:
+;;
+;;    1. it must appear all on a single line.
+;;
+;;    2. csharp-mode generally looks for the marker line in the first N
+;;       lines of the file, where N is set in
+;;       `csharp-cmd-line-limit'.  See the documentation on that
+;;       variable for more information.
+;;
+;;    3. the command SHOULD use @@FILE@@ in place of the name of the
+;;       source file to be compiled, normally the file being edited.
+;;       This is because normally flymake saves a copy of the buffer
+;;       into a temporary file with a unique name, and then compiles
+;;       that temporary file. The token @@FILE@@ is replaced by
+;;       csharp-mode with the name of the temporary file created by
+;;       flymake, before invoking the command.
+;;
+;;    4. The command should include /R options specifying external
+;;       libraries that the code depends on.
+;;
+;;  If you have no external dependencies, then you need not specify any
+;;  flymake command at all. csharp-mode will implicitly act as if you had
+;;  specified the command:
+;;
+;;      // flymake: c:\.net3.5\csc.exe /t:module /nologo @@FILE@@
+;;
+;;
+;;  If you use csc.exe as the syntax check tool (as almost everyone
+;;  will), the /t:module is important. csharp-mode assumes that the
+;;  syntax-check compile command will produce a file named
+;;  NAME.netmodule, which is the default when using /t:module. (Remember
+;;  than NAME is dynamically generated).  csharp-mode will remove the
+;;  generated netmodule file after the syntax check is complete. If you
+;;  don't specify /t:module, then csharp-mode won't know what file to
+;;  delete.
+;;
+;;  csharp-mode also fiddles with some other flymake things.  In
+;;  particular it: adds .cs to the flymake "allowed filename masks";
+;;  adds parsing for csc error messages; and adds advice to the error
+;;  parsing logic. This all should be pretty benign for all other
+;;  flymake buffers.  But it might not be.
+;;
+;;  You can explicitly turn the flymake integration for C# off by
+;;  setting `csharp-want-flymake-fixup' to nil.
+;;
+;;
+;;  Compile Integration
+;;  ----------------------------
+;;
+;;  csharp-mode binds the function `csharp-invoke-compile-interactively'
+;;  to "\C-x\C-e" .  This function attempts to intellgently guess the
+;;  format of the compile command to use for a buffer.  It looks in the
+;;  comments at the head of the buffer for a line that begins with
+;;  compile: .  If found, csharp-mode suggests the text that follows as
+;;  the compilation command when running `compile' .  If such a line is
+;;  not found, csharp-mode falls back to a msbuild or nmake command.
+;;  See the documentation on `csharp-cmd-line-limit' for further
+;;  information.
+;;
+;;  Also, csharp-mode installs an error regexp for csc.exe into
+;;  `compilation-error-regexp-alist-alist', which allows `next-error'
+;;  and `previous-error' (defined in compile.el) to navigate to the next
+;;  and previous compile errors in the cs buffer, after you've run `compile'.
+;;
+;;
+;;  YASnippet integration
+;;  -----------------------------
+;;
+;;  csharp-mode defines some built-in snippets for
+;;  convenience.  For example, if statements, for, foreach, and
+;;  so on.  You can see them on the YASnippet menu that is displayed
+;;  when a csharp-mode buffer is opened.  csharp-mode defines this
+;;  snippets happens only if ya-snippet is available. (It is done in an
+;;  `eval-after-load' clause.)  The builtin snippets will not overwrite
+;;  snippets that use the same name, if they are defined in the normal
+;;  way (in a compiled bundle) with ya-snippet.
+;;
+;;  You can explicitly turn off ya-snippet integration. See the var,
+;;  `csharp-want-yasnippet-fixup'.
+;;
+;;
+;;  imenu integration
+;;  -----------------------------
+;;
+;;  This should just work. For those who don't know what imenu is, it
+;;  allows navigation to different points within the file from an
+;;  "Index" menu, in the window's menubar.  csharp-mode computes the
+;;  menu containing the namespaces, classes, methods, and so on, in the
+;;  buffer.  This happens at the time the file is loaded; for large
+;;  files it takes a bit of time to complete the scan.  If you don't
+;;  want this capability, set `csharp-want-imenu' to nil.
+;;
+;;
+
 
 ;;; Known Bugs:
 ;;
+;;   The imenu scan is text-based and naive. For example, if you
+;;   intersperse comments between the name of a class/method/namespace,
+;;   and the curly brace, the scan will not recognize the thing being
+;;   declared. This is fixable - would need to extract the buffer
+;;   substring then remove comments before doing the regexp checks - but
+;;   it would make the scan much slower.  Also, the scan doesn't deal
+;;   with preproc symbol definitions and #if/#else. Those things are
+;;   invisible to the scanner csharp-mode uses to build the imenu menu.
+;;
 ;;   Leading identifiers are no longer being fontified, for some reason.
-;;   See matchers-before.
+;;   See matchers-before. (Not sure this is still a problem - 19 may
+;;   2011 DPC)
 ;;
 ;;   Method names with a preceding attribute are not fontified.
 ;;
 ;;
 ;;  Todo:
 ;;
-;;    Get csharp-mode.el accepted as part of the emacs standard distribution.
-;;    Must contact monnier at iro.umontreal.ca to make this happen.
+;;   imenu should scan for and find delegates and events, in addition
+;;   to the classes, structs, properties and methods it does currently.
 ;;
+;;   Get csharp-mode.el accepted as part of the emacs standard distribution.
+;;   Must contact monnier at iro.umontreal.ca to make this happen.
+;;
+;;   Add refactoring capabilities?
+;;     - extract as method - extract a block of code into a method
+;;     - extract as Func<> - extract a block of code into an Action<T>
+;;
+;;   More code-gen power:
+;;     - interface implementation - I think would require csharp-shell
 ;;
 ;;
 ;;  Acknowledgements:
 ;;          - proper fontification of verbatim literal strings,
 ;;            including those that end in slash. This edge case was not
 ;;            handled at all before; it is now handled correctly.
-;;          - code cleanup and organization; removed the linefeed.
-;;          - intelligent curly-brace insertion
+;;          - code cleanup and organization; removed the formfeed.
+;;          - intelligent curly-brace insertion with
+;;            `csharp-insert-open-brace'
 ;;    0.7.4 - added a C# style
 ;;          - using is now a keyword and gets fontified correctly
-;;          - fixed a bug that had crept into the codedoc insertion
+;;          - fixed a bug that had crept into the codedoc insertion.
 ;;    0.7.5 - now fontify namespaces in the using statements. This is
 ;;            done in the csharp value for c-basic-matchers-before .
 ;;          - also fontify the name following namespace decl.
 ;;          - Constructors are now fontified.
 ;;          - Field/Prop names inside object initializers are now fontified.
 ;;
-
-
+;;    0.7.7 - relocate running c-run-mode-hooks to the end of
+;;            csharp-mode, to allow user to modify key bindings in a
+;;            hook if he doesn't like the defaults.
 ;;
-
+;;    0.7.8 - redefine csharp-log to insert timestamp.
+;;          - Fix byte-compile errors on emacs 23.2 ?  Why was
+;;            c-filter-ops duplicated here?  What was the purpose of its
+;;            presence here, I am not clear.
+;;
+;;    0.8.0 - include flymake magic into this module.
+;;          - include yasnippet integration
+;;
+;;    0.8.2 2011 April DPC
+;;          - small tweaks; now set a one-time bool for flymake installation
+;;          - some doc updates on flymake
+;;
+;;    0.8.3 2011 May 17  DPC
+;;          - better help on csharp-mode
+;;          - csharp-move-* functions for manual navigation.
+;;          - imenu integration for menu-driven navigation - navigate to
+;;            named methods, classes, etc.
+;;          - adjusted the flymake regexp to handle output from fxcopcmd,
+;;            and extended the help to provide examples how to use this.
+;;
+;;    0.8.4 DPC 2011 May 18
+;;          - fix a basic bug in the `csharp-yasnippet-fixup' fn.
+;;
+;;    0.8.5 DPC 2011 May 21
+;;          - imenu: correctly parse Properties that are part of an
+;;            explicitly specified interface. Probably need to do this
+;;            for methods, too.
+;;          - fontify the optional alias before namespace in a using (import).
+;;          - Tweak open-curly magic insertion for object initializers.
+;;          - better fontification of variables and references
+;;          - "sealed" is now fontified as a keyword
+;;          - imenu: correctly index ctors that call this or base.
+;;          - imenu: correctly index Extension methods (this System.Enum e)
+;;          - imenu: correctly scan  method params tagged with out, ref, params
+;;          - imenu scan: now handle curlies within strings.
+;;          - imenu: split menus now have better labels, are sorted correctly.
+;;
+;;    0.8.6 DPC 2011 May ??
+;;          - extern keyword
+;;
 
 (require 'cc-mode)
 
 ;; c# upfront stuff
 ;; ==================================================================
 
-;; This is a copy of the function in cc-mode which is used to handle
-;; the eval-when-compile which is needed during other times.
-(defun c-filter-ops (ops opgroup-filter op-filter &optional xlate)
-  ;; See cc-langs.el, a direct copy.
-  (unless (listp (car-safe ops))
-    (setq ops (list ops)))
-  (cond ((eq opgroup-filter t)
-         (setq opgroup-filter (lambda (opgroup) t)))
-        ((not (functionp opgroup-filter))
-         (setq opgroup-filter `(lambda (opgroup)
-                                 (memq opgroup ',opgroup-filter)))))
-  (cond ((eq op-filter t)
-         (setq op-filter (lambda (op) t)))
-        ((stringp op-filter)
-         (setq op-filter `(lambda (op)
-                            (string-match ,op-filter op)))))
-  (unless xlate
-    (setq xlate 'identity))
-  (c-with-syntax-table (c-lang-const c-mode-syntax-table)
-    (delete-duplicates
-     (mapcan (lambda (opgroup)
-               (when (if (symbolp (car opgroup))
-                         (when (funcall opgroup-filter (car opgroup))
-                           (setq opgroup (cdr opgroup))
-                           t)
-                       t)
-                 (mapcan (lambda (op)
-                           (when (funcall op-filter op)
-                             (let ((res (funcall xlate op)))
-                               (if (listp res) res (list res)))))
-                         opgroup)))
-             ops)
-     :test 'equal)))
+;; This is a copy of the function in cc-mode which is used to handle the
+;; eval-when-compile which is needed during other times.
+;;
+;; NB: I think this is needed to satisfy requirements when this module
+;; calls `c-lang-defconst'. (DPC)
+
+;; (defun c-filter-ops (ops opgroup-filter op-filter &optional xlate)
+;;   ;; See cc-langs.el, a direct copy.
+;;   (unless (listp (car-safe ops))
+;;     (setq ops (list ops)))
+;;   (cond ((eq opgroup-filter t)
+;;          (setq opgroup-filter (lambda (opgroup) t)))
+;;         ((not (functionp opgroup-filter))
+;;          (setq opgroup-filter `(lambda (opgroup)
+;;                                  (memq opgroup ',opgroup-filter)))))
+;;   (cond ((eq op-filter t)
+;;          (setq op-filter (lambda (op) t)))
+;;         ((stringp op-filter)
+;;          (setq op-filter `(lambda (op)
+;;                             (string-match ,op-filter op)))))
+;;   (unless xlate
+;;     (setq xlate 'identity))
+;;   (c-with-syntax-table (c-lang-const c-mode-syntax-table)
+;;     (delete-duplicates
+;;      (mapcan (lambda (opgroup)
+;;                (when (if (symbolp (car opgroup))
+;;                          (when (funcall opgroup-filter (car opgroup))
+;;                            (setq opgroup (cdr opgroup))
+;;                            t)
+;;                        t)
+;;                  (mapcan (lambda (op)
+;;                            (when (funcall op-filter op)
+;;                              (let ((res (funcall xlate op)))
+;;                                (if (listp res) res (list res)))))
+;;                          opgroup)))
+;;              ops)
+;;      :test 'equal)))
 
 
 
 ;; These are only required at compile time to get the sources for the
-;; language constants.  (The cc-fonts require and the font-lock
+;; language constants.  (The load of cc-fonts and the font-lock
 ;; related constants could additionally be put inside an
 ;; (eval-after-load "font-lock" ...) but then some trickery is
 ;; necessary to get them compiled.)
+
 (eval-when-compile
   (let ((load-path
          (if (and (boundp 'byte-compile-dest-file)
 
 
 ;; ==================================================================
+;; constants used in this module
+;; ==================================================================
+
+;;(error (byte-compile-dest-file))
+;;(error (c-get-current-file))
+
+(defconst csharp-aspnet-directive-re
+  "<%@.+?%>"
+  "Regex for matching directive blocks in ASP.NET files (.aspx, .ashx, .ascx)")
+
+
+(defconst csharp-enum-decl-re
+  (concat
+   "\\<enum[ \t\n\r\f\v]+"
+   "\\([[:alpha:]_][[:alnum:]_]*\\)"
+   "[ \t\n\r\f\v]*"
+   "\\(:[ \t\n\r\f\v]*"
+   "\\("
+   (c-make-keywords-re nil
+     (list "sbyte" "byte" "short" "ushort" "int" "uint" "long" "ulong"))
+   "\\)"
+   "\\)?")
+  "Regex that captures an enum declaration in C#"
+  )
+
+;; ==================================================================
+
+
+
+
+
+
+;; ==================================================================
 ;; csharp-mode utility and feature defuns
 ;; ==================================================================
 
-
-(defun csharp-at-vsemi-p (&optional pos)
+(defun csharp--at-vsemi-p (&optional pos)
   "Determines if there is a virtual semicolon at POS or point.
-This is the C# version of the function.
-
-A vsemi is a cc-mode concept implying end-of-statement, without
-a semicolon or close-brace. This happens in 2 cases in C#:
+It returns t if at a position where a virtual-semicolon is.
+Otherwise nil.
+
+This is the C# version of the function. It gets set into
+the variable `c-at-vsemi-p-fn'.
+
+A vsemi is a cc-mode concept implying the end of a statement,
+where no actual end-of-statement signifier character ( semicolon,
+close-brace) appears.  The concept is used to allow proper
+indenting of blocks of code: Where a vsemi appears, the following
+line will not indent further.
+
+A vsemi appears in 3 cases in C#:
 
  - after an attribute that decorates a class, method, field, or
    property.
 
+ - in an object initializer, before the open-curly?
+
  - after an ASPNET directive, that appears in a aspx/ashx/ascx file
 
 An example of the former is  [WebMethod] or [XmlElement].
 to work properly with code that includes attributes and ASPNET
 directives.
 
-Returns t if at a position where a virtual-semicolon is.
-Otherwise nil.
 "
-
   (save-excursion
     (let ((pos-or-point (progn (if pos (goto-char pos)) (point))))
 
       (cond
 
+       ;; before open curly in object initializer. new Foo* { }
+       ((and (looking-back
+              (concat "\\<new[ \t\n\f\v\r]+"
+              "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
+              "[A-Za-z_][[:alnum:]]*[\ t\n\f\v\r]*"))
+             (looking-at "[ \t\n\f\v\r]*{"))
+        t)
+
        ;; put a vsemi after an ASPNET directive, like
        ;; <%@ WebHandler Language="C#" Class="Handler" %>
        ((looking-back (concat csharp-aspnet-directive-re "$") nil t)
 
        ;; put a vsemi after an attribute, as with
        ;;   [XmlElement]
+       ;; Except when the attribute is used within a line of code, as
+       ;; specifying something for a parameter.
        ((c-safe (backward-sexp) t)
         (cond
            ((re-search-forward
              (c-backward-syntactic-ws)
              (cond
 
-              ((eq (char-before) 93) ;; close sq brace
-               (csharp-at-vsemi-p (point)))
+              ((eq (char-before) 93) ;; close sq brace (a previous attribute)
+               (csharp--at-vsemi-p (point))) ;; recurse
 
               ((or
                 (eq (char-before) 59) ;; semicolon
                 (eq (char-before) 125)) ;; close curly
                t)
 
+              ;; attr is used within a line of code
               (t nil)))
 
            (t nil)))
 
 
 
+
+
 (defun csharp-lineup-if-and-region (langelem)
 
 "Indent all #region/endregion blocks and #if/endif blocks inline
 
 
 
+  (defun csharp-in-literal (&optional lim detect-cpp)
+    "Return the type of literal point is in, if any.
+Basically this works like `c-in-literal' except it doesn't
+use or fill the cache (`c-in-literal-cache').
+
+The return value is a symbol: `c' if in a C-style comment, `c++'
+if in a C++ style comment, `string' if in a string literal,
+`pound' if DETECT-CPP is non-nil and in a preprocessor line, or
+nil if somewhere else.  Optional LIM is used as the backward
+limit of the search.  If omitted, or nil, `c-beginning-of-syntax'
+is used.
+
+Note that this function might do hidden buffer changes.  See the
+comment at the start of cc-engine.el for more info."
+
+    (let ((rtn
+           (save-excursion
+             (let* ((pos (point))
+                    (lim (or lim (progn
+                                   (c-beginning-of-syntax)
+                                   (point))))
+                    (state (parse-partial-sexp lim pos)))
+               (csharp-log 4 "parse lim(%d) state: %s" lim (prin1-to-string state))
+               (cond
+                ((elt state 3)
+                 (csharp-log 4 "in literal string (%d)" pos)
+                 'string)
+                ((elt state 4)
+                 (csharp-log 4 "in literal comment (%d)" pos)
+                 (if (elt state 7) 'c++ 'c))
+                ((and detect-cpp (c-beginning-of-macro lim)) 'pound)
+                (t nil))))))
+      rtn))
+
+
 
 (defun csharp-insert-open-brace ()
-  "Intelligently insert a pair of curly braces. This fn is most
-    often bound to the open-curly brace, with
-
-        (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
-
-    The default binding for an open curly brace in cc-modes is often
-    `c-electric-brace' or `skeleton-pair-insert-maybe'.  The former
-    can be configured to insert newlines around braces in various
-    syntactic positions.  The latter inserts a pair of braces and
-    then does not insert a newline, and does not indent.
-
-    This fn provides another option, with some additional
-    intelligence for csharp-mode.  When you type an open curly, the
-    appropriate pair of braces appears, with spacing and indent set
-    in a context-sensitive manner.
-
-    Within a string literal, you just get a pair of braces, and
-    point is set between them. Following an equals sign, you get
-    a pair of braces, with a semincolon appended. Otherwise, you
-    get the open brace on a new line, followed by an empty line
-    and the closing brace on the line following, with point on
-    the empty line.
-
-    There may be another way to get this to happen appropriately just
-    within emacs, but I could not figure out how to do it.  So I
-    wrote this alternative.
+  "Intelligently insert a pair of curly braces. This fn should be
+bound to the open-curly brace, with
+
+    (local-set-key (kbd \"{\") 'csharp-insert-open-brace)
+
+The default binding for an open curly brace in cc-modes is often
+`c-electric-brace' or `skeleton-pair-insert-maybe'.  The former
+can be configured to insert newlines around braces in various
+syntactic positions.  The latter inserts a pair of braces and
+then does not insert a newline, and does not indent.
+
+This fn provides another option, with some additional
+intelligence for csharp-mode.  When you type an open curly, the
+appropriate pair of braces appears, with spacing and indent set
+in a context-sensitive manner:
+
+ - Within a string literal, you just get a pair of braces, and
+   point is set between them. This works for String.Format()
+   purposes.
+
+ - Following = or [], as in an array assignment, you get a pair
+   of braces, with two intervening spaces, with a semincolon
+   appended. Point is left between the braces.
+
+ - Following \"new Foo\", it's an object initializer. You get:
+   newline, open brace, newline, newline, close, semi.  Point is
+   left on the blank line between the braces. Unless the object
+   initializer is within an array initializer, in which case, no
+   newlines, and the semi is replaced with a comma. (Try it to
+   see what this means).
+
+ - Following => , implying a lambda, you get an open/close pair,
+   with two intervening spaces, no semicolon, and point on the
+   2nd space.
+
+ - Otherwise, you get a newline, the open curly, followed by
+   an empty line and the closing curly on the line following,
+   with point on the empty line.
+
+
+There may be another way to get this to happen appropriately just
+within emacs, but I could not figure out how to do it.  So I
+wrote this alternative.
 
     "
   (interactive)
      ;; --------------------------------------------
      ;; If one-word-back was "new", then it's an object initializer.
      ((string= one-word-back "new")
-      (save-excursion
-        (message "object initializer")
-        (setq tpoint (point)) ;; prepare to indent-region later
-        (newline)
-        (self-insert-command 1)
-        (newline-and-indent)
-        (newline)
-        (insert "};")
-        (c-indent-region tpoint (point))
-        (previous-line)
-        (indent-according-to-mode)
-        (end-of-line)
-        (setq tpoint (point)))
-      (goto-char tpoint))
+      (csharp-log 2 "object initializer")
+      (setq tpoint (point)) ;; prepare to indent-region later
+      (backward-word 2)
+      (c-backward-syntactic-ws)
+      (if (or (eq (char-before) ?,)       ;; comma
+              (and (eq (char-before) 123) ;; open curly
+                   (progn (backward-char)
+                          (c-backward-syntactic-ws)
+                          (looking-back "\\[\\]"))))
+          (progn
+            ;; within an array - emit no newlines
+            (goto-char tpoint)
+            (self-insert-command 1)
+            (insert "  },")
+            (backward-char 3))
+
+        (progn
+          (goto-char tpoint)
+          (newline)
+          (self-insert-command 1)
+          (newline-and-indent)
+          (newline)
+          (insert "};")
+          (c-indent-region tpoint (point))
+          (forward-line -1)
+          (indent-according-to-mode)
+          (end-of-line))))
+
 
      ;; Case 4: a lambda initialier.
      ;; --------------------------------------------
      ;; If the open curly follows =>, then it's a lambda initializer.
      ((string= (substring preceding3 -2) "=>")
-      (message "lambda init")
+      (csharp-log 2 "lambda init")
       (self-insert-command 1)
       (insert "  }")
       (backward-char 2))
      ;; else, it's a new scope. (if, while, class, etc)
      (t
       (save-excursion
-        (message "new scope")
+        (csharp-log 2 "new scope")
         (set-mark (point)) ;; prepare to indent-region later
         ;; check if the prior sexp is on the same line
         (if (save-excursion
         (insert "}")
         ;;(c-indent-command) ;; not sure of the difference here
         (c-indent-line-or-region)
-        (previous-line)
+        (forward-line -1)
         (end-of-line)
         (newline-and-indent)
         ;; point ends up on an empty line, within the braces, properly indented
 
 
 
-
-
-
 ;; ==================================================================
 ;; c# values for "language constants" defined in cc-langs.el
 ;; ==================================================================
 
-
-;; Java uses a series of regexes to change the font-lock for class
-;; references. The problem comes in because Java uses Pascal (leading
-;; space in names, SomeClass) for class and package names, but
-;; Camel-casing (initial lowercase, upper case in words,
-;; i.e. someVariable) for variables. The notation suggested by EMCA for C# is
-;; to use Pascal notation for everything, except inner variables. So,
-;; the Java regex and formatting produces very wrong results in C#.
-;;(error (byte-compile-dest-file))
-;;(error (c-get-current-file))
-
-(defconst csharp-aspnet-directive-re
-  "<%@.+?%>"
-  "Regex for matching directive blocks in ASP.NET files (.aspx, .ashx, .ascx)")
-
-(defconst csharp-enum-decl-re
-  (concat
-   "\\<enum[ \t\n\r\f\v]+"
-   "\\([[:alpha:]_][[:alnum:]_]*\\)"
-   "[ \t\n\r\f\v]*"
-   "\\(:[ \t\n\r\f\v]*"
-   "\\("
-   (c-make-keywords-re nil
-     (list "sbyte" "byte" "short" "ushort" "int" "uint" "long" "ulong"))
-   "\\)"
-   "\\)?")
-  "Regex that captures an enum declaration in C#"
-  )
-
-
-
-;; 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+X+X+X+X+X+X+X+
-
-;; vsemi's allow proper indentation of code that includes inline
-;; attributes and ASPNET directives. These are c#-specific things that
-;; need custom treatment.
 (c-lang-defconst c-at-vsemi-p-fn
-  csharp 'csharp-at-vsemi-p)
+  csharp 'csharp--at-vsemi-p)
 
 
 ;; This c-opt-after-id-concat-key is a regexp that matches
 ;; This const is now internal (obsolete); need to move to
 ;; c-after-id-concat-ops.  I don't yet understand the meaning
 ;; of that variable, so for now. . .  .
+
+;; (c-lang-defconst c-opt-after-id-concat-key
+;;   csharp (if (c-lang-const c-opt-identifier-concat-key)
+;;              (c-lang-const c-symbol-start)))
+
 (c-lang-defconst c-opt-after-id-concat-key
-  csharp (if (c-lang-const c-opt-identifier-concat-key)
-             (c-lang-const c-symbol-start)))
+  csharp "[[:alpha:]_]" )
+
 
 
 
 ;;
 ;;   (c-make-font-lock-search-function  regexp '(A B c))
 ;;
-;; The REGEXP is used in re-search-forward, and if there's a match, the
-;; A B and C are three forms that are called in a weird combination.
+;; The REGEXP is used in re-search-forward, and if there's a match, then
+;; A is called within a save-match-data. If B and C are non-nil, they
+;; are called as pre and post blocks, respecitvely.
 ;;
 ;; Anyway the c-make-font-lock-search-function works for a single regex,
 ;; but more complicated scenarios such as those intended to match and
 ;; fontify object initializers, call for a hand-crafted lambda.
 ;;
-;; The object initializer is special because, matching on it must
+;; The object initializer is special because matching on it must
 ;; allow nesting.
 ;;
 ;; In c#, the object initializer block is used directly after a
 ;; constructor, like this:
 ;;
-;;     new MyType {
+;;     new MyType
+;;     {
 ;;        Prop1 = "foo"
 ;;     }
 ;;
 
 
            ;; Fontify the namespaces that follow using statements.
-           ;; This regex handles the optional alias, but does not fontify it.
-           ,`("\\<\\(using\\)\s+\\(?:[A-Za-z_][[:alnum:]]*\s*=\s*\\)?\\(\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*[A-Za-z_][[:alnum:]]*\\)\s*;"
-               2 font-lock-constant-face)
+           ;; This regex handles the optional alias, as well.
+           ,`(,(concat
+                "\\<\\(using\\)[ \t\n\f\v\r]+"
+                "\\(?:"
+                "\\([A-Za-z_][[:alnum:]]*\\)"
+                "[ \t\n\f\v\r]*="
+                "[ \t\n\f\v\r]*"
+                "\\)?"
+                "\\(\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*[A-Za-z_][[:alnum:]]*\\)"
+                "[ \t\n\f\v\r]*;")
+              (2 font-lock-constant-face t t)
+              (3 font-lock-constant-face))
 
 
            ;; Fontify all keywords except the primitive types.
               1 font-lock-keyword-face)
 
 
-           ;; Fontify leading identifiers in fully qualified names like
-           ;; "Foo.Bar".
+           ;; Fontify leading identifiers as a reference? in fully
+           ;; qualified names like "Foo.Bar".
            ,@(when (c-lang-const c-opt-identifier-concat-key)
                `((,(byte-compile
                     `(lambda (limit)
+                       (csharp-log 3 "bmb reference? p(%d) L(%d)" (point) limit)
                        (while (re-search-forward
-                               ,(concat "\\(\\<" ; 1
-                                        "\\(" (c-lang-const c-symbol-key)
-                                        "\\)" ; 2
+                               ,(concat "\\(\\<" ;; 1
+                                        "\\("  ;; 2
+                                        ;;"[A-Z]";; uppercase - assume upper = classname
+                                        "[A-Za-z_]"  ;; any old
+                                        "[A-Za-z0-9_]*" ;; old: (c-lang-const c-symbol-key)
+                                        "\\)"
                                         "[ \t\n\r\f\v]*"
-                                        (c-lang-const
-                                         c-opt-identifier-concat-key)
-                                        "[ \t\n\r\f\v]+"
-                                        "\\)"
+                                        "\\."   ;;(c-lang-const c-opt-identifier-concat-key)
+                                        "[ \t\n\r\f\v]*"
+                                        "\\)" ;; 1 ends
                                         "\\("
-                                        (c-lang-const
-                                         c-opt-after-id-concat-key)
-                                        "\\)")
+                                        "[[:alpha:]_][A-Za-z0-9_]*" ;; start of another symbolname
+                                        "\\)"  ;; 3 ends
+                                        )
                                limit t)
+                         (csharp-log 3 "bmb ref? B(%d)" (match-beginning 0))
                          (unless (progn
                                    (goto-char (match-beginning 0))
                                    (c-skip-comments-and-strings limit))
-                           (or (get-text-property (match-beginning 2) 'face)
-                               (c-put-font-lock-face (match-beginning 2)
-                                                     (match-end 2)
-                                                     c-reference-face-name))
-                           (goto-char (match-end 1)))))))))
+                           (let* ((prefix  (match-string 2))
+                                  (me1 (match-end 1))
+                                  (first-char (string-to-char prefix))
+                                  (is-upper (and (>= first-char 65)
+                                                 (<= first-char 90))))
+                             (csharp-log 3 "  - class/intf ref (%s)" prefix)
+                             ;; only put face if not there already
+                             (or (get-text-property (match-beginning 2) 'face)
+                                 (c-put-font-lock-face (match-beginning 2)
+                                                       (match-end 2)
+                                                       (if is-upper
+                                                           font-lock-type-face ;; it's a type!
+                                                         font-lock-variable-name-face)))
+
+                             (goto-char (match-end 3))
+                             (c-forward-syntactic-ws limit)
+
+                             ;; now, maybe fontify the thing afterwards, too
+                             (let ((c (char-after)))
+                               (csharp-log 3 "  - now lkg at c(%c)" c)
+
+                               (cond
+
+                                ((= c 40) ;; open paren
+                                 (or (get-text-property (match-beginning 3) 'face)
+                                     (c-put-font-lock-face (match-beginning 3)
+                                                           (match-end 3)
+                                                           font-lock-function-name-face))
+                                 (goto-char (match-end 3)))
+
+                                ;;  these all look like variables or properties
+                                ((or (= c 59)  ;; semicolon
+                                     (= c 91)  ;; open sq brack
+                                     (= c 41)  ;; close paren
+                                     (= c 44)  ;; ,
+                                     (= c 33)  ;; !
+                                     (= c 124) ;; |
+                                     (= c 61)  ;; =
+                                     (= c 43)  ;; +
+                                     (= c 45)  ;; -
+                                     (= c 42)  ;; *
+                                     (= c 47)) ;; /
+                                 (or (get-text-property (match-beginning 3) 'face)
+                                     (c-put-font-lock-face (match-beginning 3)
+                                                           (match-end 3)
+                                                           font-lock-variable-name-face))
+                                 (goto-char (match-end 3)))
+
+                                (t
+                                 (goto-char (match-end 1)))))))))))))
 
            ))
 
            ;;      Prop1= new Bar { PropA = 5.6F }
            ;;   };
            ;;
-
            ,@(when t
                `((,(byte-compile
                     `(lambda (limit)
                                   (goto-char (match-beginning 0))
                                   (c-skip-comments-and-strings limit))
 
-                              (csharp-log 3 "ctor candidate at %d" (match-beginning 1))
+                              (csharp-log 3 "ctor invoke? at %d" (match-beginning 1))
 
                               (save-match-data
                                 ;; next thing could be: [] () <> or {} or nothing (semicolon, comma).
                                                       'font-lock-type-face)
 
                                 (goto-char (match-end 0))
-                                (c-forward-syntactic-ws)
+                                (c-forward-syntactic-ws limit)
                                 (if (eq (char-after) ?<) ;; ctor for generic type
                                     (progn
-                                      (csharp-log 3 " - generic ctor")
+                                      (csharp-log 3 " - this is a generic type")
                                       ;; skip over <> safely
                                       (c-safe (c-forward-sexp 1) t)
                                       (c-forward-syntactic-ws)))
                                           (c-safe (c-forward-sexp 1) t)))
 
                                     (progn
-                                      ;; at this point we've jumped over any intervening s-exp
+                                      ;; at this point we've jumped over any intervening s-exp,
+                                      ;; like sq brackets or parens.
                                       (c-forward-syntactic-ws)
                                       (csharp-log 3 " - after fwd-syn-ws point(%d)" (point))
                                       (csharp-log 3 " - next char:  %c" (char-after))
                                           (let ((start (point))
                                                 (end (if (c-safe (c-forward-sexp 1) t)
                                                          (point) 0)))
-                                            (csharp-log 3 " - put c-decl-id-start on the open-curly at %d" start)
+                                            (csharp-log 3 " -  open curly gets c-decl-id-start %d" start)
                                             (c-put-char-property start
                                                                  'c-type
                                                                  'c-decl-id-start)
                                                     (csharp-log 3 " -   next char:  %c" (char-after))
                                                     ;; fontify each property assignment
                                                     (if (re-search-forward
-                                                         (concat "\\(" (c-lang-const c-symbol-key) "\\)\s*=")
+                                                         (concat "\\(" (c-lang-const c-symbol-key) "\\)\\s*=")
                                                          end t)
                                                         (progn
                                                           (csharp-log 3 " -   found variable  %d-%d"
                                           "{")
                                  limit t)
 
-                           (csharp-log 3 "enum candidate at %d" (match-beginning 0))
+                           (csharp-log 3 "enum? at %d" (match-beginning 0))
 
                            (unless
                                (progn
                                    "("
                                    "\\)")
                                  limit t)
+
                            (unless
                                (progn
                                  (goto-char (match-beginning 0))
 
                              (goto-char (match-end 0))
 
-                             (csharp-log 3 "ctor decl candidate ending at %d" (point))
+                             (csharp-log 3 "ctor decl? L(%d) B(%d) E(%d)"
+                                         limit (match-beginning 0) (point))
 
                              (backward-char 1) ;; just left of the open paren
                              (save-match-data
 
                                       ;; open curly. no depedency on other ctor.
                                       ((eq (char-after) ?{)
-                                       (csharp-log 3 " - ctor with no dependency? at %d" (point))
+                                       (csharp-log 3 " - no dependency, curly at %d" (point))
                                        (setq found-it t)))
 
                                      )))
                                  (c-put-font-lock-face (match-beginning 3)
                                                        (match-end 3)
                                                        'font-lock-function-name-face))
-                             (goto-char (match-end 0))
-                             )
-                           ))
-                       nil))
-                  )))
+                             (goto-char (match-end 0)))))
+                       nil)))))
 
 
            ;; Case 4: using clause. Without this, using (..) gets fontified as a fn.
                                           "(")
                                  limit t)
 
-                           (csharp-log 3 "using clause at %d" (match-beginning 0))
+                           (csharp-log 3 "using clause p(%d)" (match-beginning 0))
 
                            (unless
                                (progn
 
                         (if is-attr
                             (progn
-                              (csharp-log 3 " - attribute seems likely. type: %d - %d"
-                                          b2 e2)
+                              (if (<= 3 csharp-log-level)
+                                  (csharp-log 3 " - attribute: '%s'"
+                                              (buffer-substring-no-properties b2 e2)))
                               (c-put-font-lock-face b2 e2 'font-lock-type-face)))))
                     (goto-char (match-end 0))
                     ))
            ))
 
 
+
 ;; C# does generics.  Setting this to t tells the parser to put
 ;; parenthesis syntax on angle braces that surround a comma-separated
 ;; list.
   csharp t)
 
 
-
 (c-lang-defconst c-identifier-key
   csharp (concat "\\([[:alpha:]_][[:alnum:]_]*\\)" ; 1
                  "\\("
 
 
 ;; C# uses CPP-like prefixes to mark #define, #region/endregion,
-;; #if/else/endif, and #pragma.  This regexp matches the prefix,
-;; not including the beginning-of-line (BOL), and not including
-;; the term after the prefix (define, pragma, etc).  This regexp says
-;; whitespace, followed by the prefix, followed by maybe more whitespace.
+;; #if/else/endif, and #pragma.  This regexp matches the prefix, not
+;; including the beginning-of-line (BOL), and not including the term
+;; after the prefix (define, pragma, region, etc).  This regexp says
+;; whitespace, followed by the prefix, followed by maybe more
+;; whitespace.
 
 (c-lang-defconst c-opt-cpp-prefix
   csharp "\\s *#\\s *")
 (c-lang-defconst c-symbol-chars
   csharp (concat c-alnum "_"))
 
+;; c-identifier-syntax-modifications by default defines $ as a word
+;; syntax, which is not legal in C#.  So, define our own lang-specific
+;; value.
+(c-lang-defconst c-identifier-syntax-modifications
+  csharp '((?_ . "w")))
+
+
 
 (c-lang-defconst c-colon-type-list-kwds
   csharp '("class"))
 ;; variable here, to provide the regex explicitly.
 ;;
 (c-lang-defconst c-decl-block-key
-
-  csharp '"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)"
-  )
-
+  csharp '"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)" )
 
 
 ;; Thu, 22 Apr 2010  14:29
 
 (c-lang-defconst c-class-decl-kwds
   ;; EMCA-344, S?
+  ;; don't include enum here, because we want it to be fontified as a brace
+  ;; list, with commas delimiting the values. see c-brace-list-decl-kwds
+  ;; below.
   csharp '("class" "interface" "struct" ))  ;; no "enum"!!
 
 
 ;; The various modifiers used for class and method descriptions.
 (c-lang-defconst c-modifier-kwds
-  csharp '("public" "partial" "private" "const" "abstract"
+  csharp '("public" "partial" "private" "const" "abstract" "sealed"
            "protected" "ref" "out" "static" "virtual"
            "override" "params" "internal"))
 
 
 
 ;; Statement keywords followed directly by a substatement.
-;; catch is not one of them.
+;; catch is not one of them, because catch has a paren (typically).
 (c-lang-defconst c-block-stmt-1-kwds
-  csharp '("do" "try" "finally"))
+  csharp '("do" "try" "finally" "unsafe"))
 
 
 ;; Statement keywords followed by a paren sexp and then by a substatement.
   csharp '("namespace"))
 
 (c-lang-defconst c-other-kwds
-  csharp '("in" "sizeof" "typeof" "is" "as" "yield"
-           "where" "select" "from"))
+  csharp '("sizeof" "typeof" "is" "as" "yield" "extern"
+           "where" "select" "in" "from"))
 
 (c-lang-defconst c-overloadable-operators
   ;; EMCA-344, S14.2.1
       ;; preprocessor support, so include it.
       (c-lang-const c-cpp-matchers)))
 
-(defcustom csharp-font-lock-extra-types nil
-  "*List of extra types (aside from the type keywords) to recognize in C# mode.
-Each list item should be a regexp matching a single identifier."
-  :type 'list :group 'csharp)
+
+
+;; Custom variables
+;;;###autoload
+(defcustom csharp-mode-hook nil
+    "*Hook called by `csharp-mode'."
+    :type 'hook
+    :group 'csharp)
+
+  ;; The following fn allows this:
+  ;;    (csharp-log 3 "scan result...'%s'" state)
+
+(defcustom csharp-log-level 0
+    "The current log level for CSharp-mode-specific operations.
+This is used in particular by the verbatim-literal
+string scanning.
+
+Most other csharp functions are not instrumented.
+0 = NONE, 1 = Info, 2 = VERBOSE, 3 = DEBUG, 4 = SHUTUP ALREADY. "
+    :type 'integer
+    :group 'csharp)
+
+
+;;;###autoload
+(defcustom csharp-want-flymake-fixup t
+  "*Whether to enable the builtin C# support for flymake. This is meaningful
+only if flymake is loaded."
+  :type 'boolean :group 'csharp)
+
+;;;###autoload
+(defcustom csharp-want-yasnippet-fixup t
+  "*Whether to enable the builtin C# support for yasnippet. This is meaningful
+only if flymake is loaded."
+  :type 'boolean :group 'csharp)
+
+
+;;;###autoload
+(defcustom csharp-want-imenu t
+  "*Whether to generate a buffer index via imenu for C# buffers."
+  :type 'boolean :group 'csharp)
+
+
+;;;###autoload
+(defcustom csharp-make-tool "nmake.exe"
+  "*The make tool to use. Defaults to nmake, found on path. Specify
+a full path or alternative program name, to tell csharp-mode to use
+a different make tool in compile commands.
+
+See also, `csharp-msbuild-tool'.
+
+"
+  :type 'string :group 'csharp)
+
+
+;;;###autoload
+(defcustom csharp-msbuild-tool "msbuild.exe"
+  "*The tool to use to build .csproj files. Defaults to msbuild, found on
+path. Specify a full path or alternative program name, to tell csharp-mode
+to use a different make tool in compile commands.
+
+See also, `csharp-make-tool'.
+
+"
+  :type 'string :group 'csharp)
+
+
+;;;###autoload
+(defcustom csharp-cmd-line-limit 28
+  "The number of lines at the top of the file to look in, to find
+the command that csharp-mode will use to compile the current
+buffer, or the command \"stub\" that csharp-mode will use to
+check the syntax of the current buffer via flymake.
+
+If the value of this variable is zero, then csharp-mode looks
+everywhere in the file.  If the value is positive, then only in
+the first N lines. If negative, then only in the final N lines.
+
+The line should appear in a comment inside the C# buffer.
+
+
+Compile
+--------
+
+In the case of compile, the compile command must be prefixed with
+\"compile:\".  For example,
+
+ // compile: csc.exe /r:Hallo.dll Arfie.cs
+
+
+This command will be suggested as the compile command when the
+user invokes `compile' for the first time.
+
+
+Flymake
+--------
+
+In the case of flymake, the command \"stub\" string must be
+prefixed with \"flymake:\".  For example,
+
+ // flymake: DOTNETDIR\csc.exe /target:netmodule /r:foo.dll @@FILE@@
+
+In the case of flymake, the string should NOT include the name of
+the file for the buffer being checked. Instead, use the token
+@@FILE@@ .  csharp-mode will replace this token with the name of
+the source file to compile, before passing the command to flymake
+to run it.
+
+If for some reason the command is invalid or illegal, flymake
+will report an error and disable itself.
+
+It might be handy to run fxcop, for example, via flymake.
+
+ // flymake: fxcopcmd.exe /c  /f:MyLibrary.dll
+
+
+
+In all cases
+------------
+
+Be sure to specify the proper path for your csc.exe, whatever
+version that might be, or no path if you want to use the system
+PATH search.
+
+If the buffer depends on external libraries, then you will want
+to include /R arguments to that csc.exe command.
+
+To be clear, this variable sets the number of lines to search for
+the command.  This cariable is an integer.
+
+If the marker string (either \"compile:\" or \"flymake:\"
+is present in the given set of lines, csharp-mode will take
+anything after the marker string as the command to run.
+
+"
+  :type 'integer   :group 'csharp)
+
+
 
 (defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
   "Minimal highlighting for C# mode.")
 (defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
   "Default expressions to highlight in C# mode.")
 
+
 (defvar csharp-mode-syntax-table nil
   "Syntax table used in csharp-mode buffers.")
 (or csharp-mode-syntax-table
   "Keymap used in csharp-mode buffers.")
 
 
+(defvar csharp--yasnippet-has-been-fixed nil
+  "indicates whether yasnippet has been patched for use with csharp.
+Intended for internal use only.")
+
+(defvar csharp--flymake-has-been-installed  nil
+  "one-time use boolean, to check whether csharp tweaks for flymake (advice
+etc) have been installed.")
+
+(defvar csharp-flymake-csc-arguments
+  (list "/t:module" "/nologo")
+  "A list of arguments to use with the csc.exe
+compiler, when using flymake with a
+direct csc.exe build for syntax checking purposes.")
+
+
+(defvar csharp-flymake-aux-error-info nil
+  "a list of auxiliary flymake error info items. Each item in the
+list is a pair, consisting of a line number and a column number.
+This info is set by advice to flymake-parse-line, and used by
+advice attached to flymake-goto-line, to navigate to the proper
+error column when possible. ")
+
+
+(defvar csharp-flymake-csc-error-pattern
+  "^[ \t]*\\([_A-Za-z0-9][^(]+\\.cs\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) ?: \\(\\(error\\|warning\\) +:? *C[SA][0-9]+ *:[ \t\n]*\\(.+\\)\\)"
+  "The regex pattern for C# compiler error messages. Follows
+the same form as an entry in `flymake-err-line-patterns'. The
+value is a STRING, a regex.")
+
 ;; TODO
 ;; Defines our constant for finding attributes.
 ;;(defconst csharp-attribute-regex "\\[\\([XmlType]+\\)(")
 
 
 
+
+;; ========================================================================
+;; Flymake integration
+
+(defun csharp-flymake-init ()
+  (csharp-flymake-init-impl
+   'flymake-create-temp-inplace t t 'csharp-flymake-get-cmdline))
+
+(defun csharp-flymake-init-impl (create-temp-f use-relative-base-dir use-relative-source get-cmdline-f)
+  "Create syntax check command line for a directly checked source file.
+Use CREATE-TEMP-F for creating temp copy."
+  (let* ((args nil)
+        (temp-source-file-name  (flymake-init-create-temp-buffer-copy create-temp-f)))
+    (setq args (flymake-get-syntax-check-program-args
+                temp-source-file-name "."
+                use-relative-base-dir use-relative-source
+                get-cmdline-f))
+    args))
+
+
+(defun csharp-flymake-cleanup ()
+  "Delete the temporary .netmodule file created in syntax checking
+a C# buffer, then call through to flymake-simple-cleanup."
+
+  (if flymake-temp-source-file-name
+      (progn
+        (let* ((netmodule-name
+                (concat (file-name-sans-extension flymake-temp-source-file-name)
+                        ".netmodule"))
+               (expanded-netmodule-name (expand-file-name netmodule-name ".")))
+          (if (file-exists-p expanded-netmodule-name)
+              (flymake-safe-delete-file expanded-netmodule-name)))
+        ))
+  (flymake-simple-cleanup))
+
+
+(defun csharp-split-string-respecting-quotes (s)
+  "splits a string into tokens, respecting double quotes
+For example, the string 'This is \"a string\"' will be split into 3 tokens.
+
+More pertinently, the string
+   'csc /t:module /R:\"c:\abba dabba\dooo\Foo.dll\"'
+
+...will be split into 3 tokens.
+
+This fn also removes quotes from the tokens that have them. This is for
+compatibility with flymake and the process-start fn.
+
+"
+  (let ((local-s s)
+        (my-re-1 "[^ \"]*\"[^\"]+\"\\|[^ \"]+")
+        (my-re-2 "\\([^ \"]*\\)\"\\([^\"]+\\)\"")
+        (tokens))
+    (while (string-match my-re-1 local-s)
+      (let ((token (match-string 0 local-s))
+            (remainder (substring local-s (match-end 0))))
+        (if (string-match my-re-2 token)
+            (setq token (concat (match-string 1 token) (match-string 2 token))))
+        ;;(message "token: %s" token)
+        (setq tokens (append tokens (list token)))
+        (setq local-s remainder)))
+    tokens))
+
+
+(defun csharp-get-value-from-comments (marker-string line-limit)
+  "gets a string from the header comments in the current buffer.
+
+This is used to extract the flymake command and the compile
+command from the comments.
+
+It looks for \"marker-string:\" and returns the string that
+follows it, or returns nil if that string is not found.
+
+eg, when marker-string is \"flymake\", and the following
+string is found at the top of the buffer:
+
+     flymake: csc.exe /r:Hallo.dll
+
+...then this command will return the string
+
+     \"csc.exe /r:Hallo.dll\"
+
+It's ok to have whitespace between the marker and the following
+colon.
+
+"
+
+  (let (start search-limit found)
+    ;; determine what lines to look in
+    (save-excursion
+      (save-restriction
+        (widen)
+        (cond ((> line-limit 0)
+               (goto-char (setq start (point-min)))
+               (forward-line line-limit)
+               (setq search-limit (point)))
+              ((< line-limit 0)
+               (goto-char (setq search-limit (point-max)))
+               (forward-line line-limit)
+               (setq start (point)))
+              (t                        ;0 => no limit (use with care!)
+               (setq start (point-min))
+               (setq search-limit (point-max))))))
+
+    ;; look in those lines
+    (save-excursion
+      (save-restriction
+        (widen)
+        (let ((re-string
+               (concat "\\b" marker-string "[ \t]*:[ \t]*\\(.+\\)$")))
+          (if (and start
+                   (< (goto-char start) search-limit)
+                   (re-search-forward re-string search-limit 'move))
+
+              (buffer-substring-no-properties
+               (match-beginning 1)
+               (match-end 1))))))))
+
+
+
+
+(defun csharp-replace-command-tokens (explicitly-specified-command)
+  "Replace tokens in the flymake or compile command extracted from the
+buffer, to allow specification of the original and modified
+filenames.
+
+  @@ORIG@@ - gets replaced with the original filename
+  @@FILE@@ - gets replaced with the name of the temporary file
+      created by flymake
+
+"
+  (let ((massaged-command explicitly-specified-command))
+    (if (string-match "@@SRC@@" massaged-command)
+        (setq massaged-command
+              (replace-match
+               (file-relative-name flymake-temp-source-file-name) t t massaged-command)))
+    (if (string-match "@@ORIG@@" massaged-command)
+        (setq massaged-command
+              (replace-match
+               (file-relative-name buffer-file-name) t t massaged-command)))
+    massaged-command))
+
+
+;;(setq flymake-log-level 3)
+
+(defun csharp-flymake-get-final-csc-arguments (initial-arglist)
+  "Gets the command used by csc.exe for flymake runs.
+This may inject a /t:module into an arglist, where it is not
+present.
+
+This fn burps if a different /t: argument is found.
+
+"
+  (interactive)
+  (let ((args initial-arglist)
+        arg
+        (found nil))
+    (while args
+      (setq arg (car args))
+      (cond
+       ((string-equal arg "/t:module") (setq found t))
+       ((string-match "^/t:" arg)
+        (setq found t)
+        (message "csharp-mode: WARNING /t: option present in arglist, and not /t:module; fix this.")))
+
+      (setq args (cdr args)))
+
+    (setq args
+          (if found
+              initial-arglist
+            (append (list "/t:module") initial-arglist)))
+
+    (if (called-interactively-p 'any)
+        (message "result: %s" (prin1-to-string args)))
+
+    args))
+
+
+(defun csharp-flymake-get-cmdline (source base-dir)
+  "Gets the cmd line for running a flymake session in a C# buffer.
+This gets called by flymake itself.
+
+The fn looks in the buffer for a line that looks like:
+
+  flymake: <command goes here>
+
+  (It should be embedded into a comment)
+
+Typically the command will be a line that runs nmake.exe,
+msbuild.exe, or csc.exe, with various options. It should
+eventually run the CSC.exe compiler, or something else that emits
+error messages in the same form as the C# compiler, like FxCopCmd.exe
+
+Some notes on implementation:
+
+  1. csharp-mode copies the buffer to a temporary file and
+     compiles *that*.  This temporary file has a different name
+     than the actual file name for the buffer - _flymake gets
+     appended to the basename.  Therefore, you should specify
+     Foo_flymake.cs for the filename, if you want to explicitly
+     refer to it.
+
+     If you want to refer to it implicitly, you can use the special
+     token \"@@SRC@@\" in the command. It will get replaced with the
+     name of the temporary file at runtime. If you want to refer to
+     the original name of the buffer, use @@ORIG@@.
+
+  2. In general, when running the compiler, you should use a
+     target type of \"module\" (eg, /t:module) to allow
+     csharp-mode to clean up the products of the build.
+
+  3. See `csharp-cmd-line-limit' for a way to restrict where
+     csharp-mode will search for the command.
+
+  4. If this string is not found, then this fn will fallback to
+     a generic, generated csc.exe command.
+
+"
+  (let ((explicitly-specified-command
+         (csharp-get-value-from-comments "flymake" csharp-cmd-line-limit)))
+
+    (cond
+     (explicitly-specified-command
+
+      ;; the marker string was found in the buffer
+      (let ((tokens (csharp-split-string-respecting-quotes
+                      (csharp-replace-command-tokens explicitly-specified-command))))
+
+        (list (car tokens) (cdr tokens))))
+
+        ;; ;; implicitly append? the name of the temporary source file
+        ;; (list (car tokens) (append (cdr tokens) (list flymake-temp-source-file-name)))))
+
+     (t
+      ;; fallback
+      (list "csc.exe"
+            (append (csharp-flymake-get-final-csc-arguments
+                     csharp-flymake-csc-arguments)
+                    (list source)))))))
+
+
+;; (defun csharp-flymake-get-cmdline (source base-dir)
+;;   "Gets the cmd line for running a flymake session in a C# buffer.
+;; This gets called by flymake itself.
+;;
+;; The fn looks in the buffer for a line that looks like:
+;;
+;;   flymake: <command goes here>
+;;
+;;   (It should be embedded into a comment)
+;;
+;; Typically the command will be a line that runs nmake.exe,
+;; msbuild.exe, or cscc.exe, with various options. It should
+;; eventually run the CSC.exe compiler, or something else that emits
+;; error messages in the same form as the C# compiler.
+;;
+;; In general, when running the compiler, you should use a target
+;; type of \"module\" (eg, /t:module) to allow csharp-mode to
+;; clean up the products of the build.
+;;
+;; See `csharp-cmd-line-limit' for a way to restrict where
+;; csharp-mode will search for the command.
+;;
+;; If this string is not found, then this fn will fallback to a
+;; generic, generated csc.exe command.
+;;
+;; "
+;;   (let ((explicitly-specified-command
+;;          (let ((line-limit csharp-cmd-line-limit)
+;;                start search-limit found)
+;;            ;; determine what lines to look in
+;;            (save-excursion
+;;              (save-restriction
+;;                (widen)
+;;                (cond ((> line-limit 0)
+;;                       (goto-char (setq start (point-min)))
+;;                       (forward-line line-limit)
+;;                       (setq search-limit (point)))
+;;                      ((< line-limit 0)
+;;                       (goto-char (setq search-limit (point-max)))
+;;                       (forward-line line-limit)
+;;                       (setq start (point)))
+;;                      (t                        ;0 => no limit (use with care!)
+;;                       (setq start (point-min))
+;;                       (setq search-limit (point-max))))))
+;;
+;;            ;; look in those lines
+;;            (save-excursion
+;;              (save-restriction
+;;                (widen)
+;;                (if (and start
+;;                         (< (goto-char start) search-limit)
+;;                         (re-search-forward "\\bflymake-command[ \t]*:[ \t]*\\(.+\\)$" search-limit 'move))
+;;
+;;                    (buffer-substring-no-properties
+;;                     (match-beginning 1)
+;;                     (match-end 1))))))))
+;;
+;;     (cond
+;;      (explicitly-specified-command
+;;       ;; the marker string was found in the buffer
+;;       (let ((tokens (csharp-split-string-respecting-quotes
+;;                      explicitly-specified-command)))
+;;         ;; implicitly append the name of the temporary source file
+;;         (list (car tokens) (append (cdr tokens) (list flymake-temp-source-file-name)))))
+;;
+;;      (t
+;;       ;; fallback
+;;       (list "csc.exe"
+;;             (append (csharp-flymake-get-final-csc-arguments
+;;                      csharp-flymake-csc-arguments)
+;;                     (list source)))))))
+
+
+
+
+
+(defun csharp-flymake-install ()
+  "Change flymake variables and fns to work with C#.
+
+This fn does these things:
+
+1. add a C# entry to the flymake-allowed-file-name-masks,
+   or replace it if it already exists.
+
+2. add a C# entry to flymake-err-line-patterns.
+   This isn't strictly necessary because of item #4.
+
+3. override the definition for flymake-process-sentinel
+   to NOT check the process status on exit. MSBuild.exe
+   sets a non-zero status code when compile errors occur,
+   which causes flymake to disable itself with the regular
+   flymake-process-sentinel.
+
+4. redefine flymake-start-syntax-check-process to unset the
+   query-on-exit flag for flymake processes. This allows emacs to
+   exit even if flymake is currently running.
+
+5. provide advice to flymake-parse-line and
+   flymake-parse-err-lines, specifically set up for C#
+   buffers. This allows optimized searching for errors in csc.exe
+   output, and storing column numbers, for use in #6.
+
+6. define advice to flymake-goto-line , to allow it to goto the
+   appropriate column for the error on a given line. This advice
+   looks in flymake-er-info, a list, and uses the heuristic that
+   the first error that matches the given line number, is the error
+   we want. This will break if there is more than one error on a
+   single line.
+
+"
+
+  (flymake-log 2 "csharp-flymake-install")
+
+  (or csharp--flymake-has-been-installed
+      (progn
+
+  ;; 1. add a C# entry to the flymake-allowed-file-name-masks
+  (let* ((key "\\.cs\\'")
+         (csharpentry (assoc key flymake-allowed-file-name-masks)))
+    (if csharpentry
+        (setcdr csharpentry '(csharp-flymake-init csharp-flymake-cleanup))
+      (add-to-list
+       'flymake-allowed-file-name-masks
+       (list key 'csharp-flymake-init 'csharp-flymake-cleanup))))
+
+
+  ;; 2. add a C# entry to flymake-err-line-patterns
+  ;;
+  ;; The value of each entry is a list, (STRING IX1 IX2 IX3 IX4), where
+  ;; STRING is the regex, and the other 4 values are indexes into the
+  ;; regex captures for the filename, line, column, and error text,
+  ;; respectively.
+  (add-to-list
+   'flymake-err-line-patterns
+   (list csharp-flymake-csc-error-pattern 1 2 3 4))
+
+
+
+  ;; 3.  override the definition for flymake-process-sentinel
+  ;;
+  ;; DPC - 2011 Feb 26
+  ;; Redefining a function is a bit unusual, but I think it is necessary
+  ;; to remove the check on process exit status.  For VBC.exe, it gives
+  ;; a 1 status when compile errors result. Likewise msbuild.exe.  This
+  ;; means flymake turns itself off, which we don't want. This really
+  ;; ought to be tunable in flymake, but I guess no one asked for that
+  ;; feature yet.
+  (defun flymake-process-sentinel (process event)
+    "Sentinel for syntax check buffers."
+    (when (memq (process-status process) '(signal exit))
+      (let* ((exit-status       (process-exit-status process))
+             (command           (process-command process))
+             (source-buffer     (process-buffer process))
+             (cleanup-f         (flymake-get-cleanup-function (buffer-file-name source-buffer))))
+
+        (flymake-log 2 "process %d exited with code %d"
+                     (process-id process) exit-status)
+        (condition-case err
+            (progn
+              (flymake-log 3 "cleaning up using %s" cleanup-f)
+              (when (buffer-live-p source-buffer)
+                (with-current-buffer source-buffer
+                  (funcall cleanup-f)))
+
+              (delete-process process)
+              (setq flymake-processes (delq process flymake-processes))
+
+              (when (buffer-live-p source-buffer)
+                (with-current-buffer source-buffer
+
+                  (flymake-parse-residual)
+                  ;;(flymake-post-syntax-check exit-status command)
+                  (flymake-post-syntax-check 0 command)
+                  (setq flymake-is-running nil))))
+          (error
+           (let ((err-str (format "Error in process sentinel for buffer %s: %s"
+                                  source-buffer (error-message-string err))))
+             (flymake-log 0 err-str)
+             (with-current-buffer source-buffer
+               (setq flymake-is-running nil))))))))
+
+
+  ;; 4. redefine this fn - the reason is to allow exit without query on
+  ;; flymake processes.  Not sure why this is not the default.
+  (defun flymake-start-syntax-check-process (cmd args dir)
+    "Start syntax check process."
+    (let* ((process nil))
+      (condition-case err
+          (progn
+            (when dir
+              (let ((default-directory dir))
+                (flymake-log 3 "starting process on dir %s" default-directory)))
+            (setq process (apply 'start-process "flymake-proc" (current-buffer) cmd args))
+
+            ;; dino - exit without query on active flymake processes
+            (set-process-query-on-exit-flag process nil)
+
+            (set-process-sentinel process 'flymake-process-sentinel)
+            (set-process-filter process 'flymake-process-filter)
+            (push process flymake-processes)
+
+            (setq flymake-is-running t)
+            (setq flymake-last-change-time nil)
+            (setq flymake-check-start-time (flymake-float-time))
+
+            (flymake-report-status nil "*")
+            (flymake-log 2 "started process %d, command=%s, dir=%s"
+                         (process-id process) (process-command process)
+                         default-directory)
+            process)
+        (error
+         (let* ((err-str (format "Failed to launch syntax check process '%s' with args %s: %s"
+                                 cmd args (error-message-string err)))
+                (source-file-name buffer-file-name)
+                (cleanup-f        (flymake-get-cleanup-function source-file-name)))
+           (flymake-log 0 err-str)
+           (funcall cleanup-f)
+           (flymake-report-fatal-status "PROCERR" err-str))))))
+
+
+  ;; 5. define some advice for the error parsing
+  (defadvice flymake-parse-err-lines (before
+                                      csharp-flymake-parse-line-patch-1
+                                      activate compile)
+    (if (string-match "\\.[Cc][Ss]$"  (file-relative-name buffer-file-name))
+        ;; clear the auxiliary line information list, when a new parse
+        ;; starts.
+        (setq csharp-flymake-aux-error-info nil)))
+
+  (defadvice flymake-parse-line (around
+                                 csharp-flymake-parse-line-patch-2
+                                 activate compile)
+    ;; This advice will run in all buffers.  Let's may sure we
+    ;; actually execute the important stiff only when a C# buffer is active.
+    (if (string-match "\\.[Cc][Ss]$"  (file-relative-name buffer-file-name))
+
+        (let (raw-file-name
+              e-text
+              result
+              (pattern (list csharp-flymake-csc-error-pattern 1 2 3 4))
+              (line-no 0)
+              (col-no 0)
+              (err-type "e"))
+          (if (string-match (car pattern) line)
+              (let* ((file-idx (nth 1 pattern))
+                     (line-idx (nth 2 pattern))
+                     (col-idx (nth 3 pattern))
+                     (e-idx (nth 4 pattern)))
+                (flymake-log 3 "parse line: fx=%s lx=%s ex=%s"
+                             file-idx line-idx e-idx)
+                (setq raw-file-name (if file-idx (match-string file-idx line) nil))
+                (setq line-no       (if line-idx (string-to-number (match-string line-idx line)) 0))
+                (setq col-no        (if col-idx  (string-to-number (match-string col-idx line)) 0))
+                (setq e-text      (if e-idx
+                                      (match-string e-idx line)
+                                    (flymake-patch-e-text (substring line (match-end 0)))))
+                (or e-text (setq e-text "<no error text>"))
+                (if (and e-text (string-match "^[wW]arning" e-text))
+                    (setq err-type "w"))
+                (flymake-log 3 "parse line: fx=%s/%s lin=%s/%s col=%s/%s text=%s"
+                             file-idx raw-file-name
+                             line-idx line-no
+                             col-idx (prin1-to-string col-no)
+                             e-text)
+
+                ;; add one entry to the list of auxiliary error information.
+                (add-to-list 'csharp-flymake-aux-error-info
+                             (list line-no col-no))
+
+                (setq ad-return-value
+                      (flymake-ler-make-ler raw-file-name line-no err-type e-text nil))
+                )))
+
+      ;; else - not in a C# buffer
+      ad-do-it))
+
+
+  ;; 6. finally, define some advice for the line navigation.  It moves
+  ;; to the proper column, given the line number containing the
+  ;; error. It first calls the normal `flymake-goto-line', and assumes
+  ;; that the result is that the cursor is on the line that contains the
+  ;; error.  At exit from that fn, the column is not important. This advice
+  ;; sets the column.
+  (defadvice flymake-goto-line (around
+                                csharp-flymake-goto-line-patch
+                                activate compile)
+    ;; This advice will run in all buffers.  Let's may sure we
+    ;; actually execute the important stuff only when a C# buffer is active.
+    ad-do-it
+    (if (string-match "\\.[Cc][Ss]$"  (file-relative-name buffer-file-name))
+        (let* ((lno (ad-get-arg 0))
+              (epair (assoc lno csharp-flymake-aux-error-info)))
+          (if epair
+              (forward-char (- (cadr epair) (current-column) 1))))))
+
+
+  ;; 7. finally, set the flag
+  (setq csharp--flymake-has-been-installed t))))
+
+
+
+;; Need to temporarily turn off flymake while reverting.
+;; There' some kind of race-condition where flymake is trying
+;; to compile while the buffer is being changed, and that
+;; causes flymake to choke.
+(defadvice revert-buffer (around
+                          csharp-advise-revert-buffer
+                          activate compile)
+  (let ((is-flymake-enabled
+         (and (fboundp 'flymake-mode)
+              flymake-mode)))
+    ;; disable
+    (if is-flymake-enabled
+        (flymake-mode-off))
+
+    ;; revert
+    ad-do-it
+
+    ;; enable
+    (if is-flymake-enabled
+        (flymake-mode-on))))
+
+;; ++++++++++++++++++++++
+
+
+
+
+;; ========================================================================
+;; moving
+
+;; alist of regexps for various structures in a csharp source file.
+(eval-and-compile
+  (defconst csharp--regexp-alist
+    (list
+
+     `(func-start
+       ,(concat
+         "^[ \t\n\r\f\v]*"                            ;; leading whitespace
+         "\\("
+         "public\\(?: static\\)?\\|"                  ;; 1. access modifier
+         "private\\(?: static\\)?\\|"
+         "protected\\(?: internal\\)?\\(?: static\\)?\\|"
+         "static\\|"
+         "\\)"
+         "[ \t\n\r\f\v]+"
+         "\\(?:override[ \t\n\r\f\v]+\\)?"            ;; optional
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; 2. return type - possibly generic
+         "[ \t\n\r\f\v]+"
+         "\\([[:alpha:]_][[:alnum:]_]*\\)"            ;; 3. name of func
+         "[ \t\n\r\f\v]*"
+         "\\(\([^\)]*\)\\)"                           ;; 4. params w/parens
+         "[ \t\n\r\f\v]*"
+         ))
+
+     `(ctor-start
+       ,(concat
+         "^[ \t\n\r\f\v]*"                            ;; leading whitespace
+         "\\("
+         "public\\|"                                  ;; 1. access modifier
+         "private\\|"
+         "protected\\(?: internal\\)?\\|"
+         "static\\|"
+         "\\)"
+         "[ \t\n\r\f\v]+"
+         "\\([[:alpha:]_][[:alnum:]_]*\\)"            ;; 2. name of ctor
+         "[ \t\n\r\f\v]*"
+         "\\(\([^\)]*\)\\)"                           ;; 3. parameter list (with parens)
+         "\\("                                        ;; 4. ctor dependency
+         "[ \t\n]*:[ \t\n]*"                          ;; colon
+         "\\(?:this\\|base\\)"                        ;; this or base
+         "[ \t\n\r\f\v]*"
+         "\\(?:\([^\)]*\)\\)"                         ;; parameter list (with parens)
+         "\\)?"                                       ;; possibly
+         "[ \t\n\r\f\v]*"
+         ))
+
+
+     `(using-stmt
+       ,(concat
+         ;;"^[ \t\n\r\f\v]*"
+         "\\(\\<using\\)"
+         "[ \t\n\r\f\v]+"
+         "\\(?:"
+         "\\([[:alpha:]_][[:alnum:]_]*\\)"            ;; alias
+         "[ \t\n\r\f\v]*"
+         "="
+         "[ \t\n\r\f\v]*"
+         "\\)?"
+         "\\("
+         "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
+         "[A-Za-z_][[:alnum:]]*"
+         "\\)"                                        ;; imported namespace
+         "[ \t\n\r\f\v]*"
+         ";"
+         ))
+
+     `(class-start
+       ,(concat
+         "^[ \t]*"                                    ;; leading whitespace
+         "\\("
+         "public\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|"  ;; access modifiers
+         "internal\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|"
+         "static\\(?: internal\\)?[ \t]+\\|"
+         "sealed\\(?: internal\\)?[ \t]+\\|"
+         "static[ \t]+\\|"
+         "sealed[ \t]+\\|"
+         "\\)"
+         "\\(\\(?:partial[ \t]+\\)?class\\|struct\\)" ;; class/struct keyword
+         "[ \t]+"
+         "\\([[:alpha:]_][[:alnum:]]*\\)"             ;; type name
+         "\\("
+         "[ \t\n]*:[ \t\n]*"                          ;; colon
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; base / intf - poss generic
+         "\\("
+         "[ \t\n]*,[ \t\n]*"
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; addl interface - poss generic
+         "\\)*"
+         "\\)?"                                       ;; possibly
+         "[ \t\n\r\f\v]*"
+         ))
+
+     `(genclass-start
+       ,(concat
+         "^[ \t]*"                                    ;; leading whitespace
+         "\\("
+         "public\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|"  ;; access modifiers
+         "internal\\(?: \\(?:static\\|sealed\\)\\)?[ \t]+\\|"
+         "static\\(?: internal\\)?[ \t]+\\|"
+         "sealed\\(?: internal\\)?[ \t]+\\|"
+         "static[ \t]+\\|"
+         "sealed[ \t]+\\|"
+         "\\)"
+         "\\(\\(?:partial[ \t]+\\)?class\\|struct\\)" ;; class/struct keyword
+         "[ \t]+"
+         "\\([[:alpha:]_][[:alnum:]_<>, ]*\\)"        ;; type name (generic)
+         "\\("
+         "[ \t\n]*:[ \t\n]*"                          ;; colon
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; base / intf - poss generic
+         "\\("
+         "[ \t\n]*,[ \t\n]*"
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; addl interface - poss generic
+         "\\)*"
+         "\\)?"                                       ;; possibly
+         "[ \t\n\r\f\v]*"
+         ))
+
+     `(enum-start
+       ,(concat
+         "^[ \t\f\v]*"                                ;; leading whitespace
+         "\\("
+         "public[ \t]+enum\\|"                        ;; enum keyword
+         "enum"
+         "\\)"
+         "[ \t\n\r\f\v]+"
+         "\\([[:alpha:]_][[:alnum:]_]*\\)"            ;; name of enum
+         "[ \t\n\r\f\v]*"
+         "\\(:[ \t\n\r\f\v]*"
+         "\\("
+         "sbyte\\|byte\\|short\\|ushort\\|int\\|uint\\|long\\|ulong"
+         "\\)"
+         "[ \t\n\r\f\v]*"
+         "\\)?"                                       ;; possibly
+         "[ \t\n\r\f\v]*"
+         ))
+
+
+     `(intf-start
+       ,(concat
+         "^[ \t\f\v]*"                                ;; leading whitespace
+         "\\(?:"
+         "public\\|internal\\|"                       ;; access modifier
+         "\\)"
+         "[ \t\n\r\f\v]+"
+         "\\(interface\\)"
+         "[ \t\n\r\f\v]+"
+         "\\([[:alpha:]_][[:alnum:]_]*\\)"            ;; name of interface
+         "[ \t\n\r\f\v]*"
+         ))
+
+     `(prop-start
+       ,(concat
+         "^[ \t\f\v]*"                                ;; leading whitespace
+         "\\("
+         "public\\|"                                  ;; 1: access modifier
+         "private\\|"
+         "protected internal\\|"
+         "internal protected\\|"
+         "internal\\|"
+         "\\)"
+         "[ \t\n\r\f\v]+"
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; 2: return type - possibly generic
+         "[ \t\n\r\f\v]+"
+         "\\("
+         "\\(?:[A-Za-z_][[:alnum:]_]*\\.\\)*"          ;; possible prefix interface
+         "[[:alpha:]_][[:alnum:]_]*"                  ;; 3: name of prop
+         "\\)"
+         "[ \t\n\r\f\v]*"
+         ))
+
+     `(indexer-start
+       ,(concat
+         "^[ \t\f\v]*"                                ;; leading whitespace
+         "\\("
+         "public\\|"                                  ;; 1: access modifier
+         "private\\|"
+         "protected internal\\|"
+         "internal protected\\|"
+         "internal\\|"
+         "\\)"
+         "[ \t\n\r\f\v]+"
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; 2: return type - possibly generic
+         "[ \t\n\r\f\v]+"
+         "\\(this\\)"                                 ;; 3: 'this' keyword
+         "[ \t\n\r\f\v]*"
+         "\\["                                        ;; open square bracket
+         "[ \t\n\r\f\v]*"
+         "\\([^\]]+\\)"                               ;; 4: index type
+         "[ \t\n\r\f\v]+"
+         "[[:alpha:]_][[:alnum:]_]*"                  ;; index name - a simple identifier
+         "\\]"                                        ;; closing sq bracket
+         "[ \t\n\r\f\v]*"
+         ))
+
+     `(namespace-start
+       ,(concat
+         "^[ \t\f\v]*"                                ;; leading whitespace
+         "\\(namespace\\)"
+         "[ \t\n\r\f\v]+"
+         "\\("
+         "\\(?:[A-Za-z_][[:alnum:]_]*\\.\\)*"          ;; name of namespace
+         "[A-Za-z_][[:alnum:]]*"
+         "\\)"
+         "[ \t\n\r\f\v]*"
+         ))
+
+     )))
+
+
+(defun csharp--regexp (symbol)
+  "Retrieves a regexp from the `csharp--regexp-alist' corresponding
+to the given symbol.
+"
+  (let ((elt (assoc symbol csharp--regexp-alist)))
+    (if elt (cadr elt) nil)))
+
+
+(defun csharp-move-back-to-beginning-of-block ()
+  "Moves to the previous open curly.
+"
+  (interactive)
+  (re-search-backward "{" (point-min) t))
+
+
+(defun csharp--move-back-to-beginning-of-something (must-match &optional must-not-match)
+  "Moves back to the open-curly that defines the beginning of *something*,
+defined by the given MUST-MATCH, a regexp which must match immediately
+preceding the curly.  If MUST-NOT-MATCH is non-nil, it is treated
+as a regexp that must not match immediately preceding the curly.
+
+This is a helper fn for `csharp-move-back-to-beginning-of-defun' and
+`csharp-move-back-to-beginning-of-class'
+
+"
+  (interactive)
+  (let (done
+        (found (point))
+        (need-to-backup (not (looking-at "{"))))
+    (while (not done)
+      (if need-to-backup
+          (setq found (csharp-move-back-to-beginning-of-block)))
+      (if found
+          (setq done (and (looking-back must-match)
+                          (or (not must-not-match)
+                              (not (looking-back must-not-match))))
+                need-to-backup t)