branch: externals/csharp-mode
commit f6965a6600edb5f68749dc3a9296c8ee0dfd0446
Author: Dino Chiesa <dpchi...@hotmail.com>
Commit: Dino Chiesa <dpchi...@hotmail.com>

    v0.8.3 - imenu
---
 csharp-mode.el | 1699 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 1634 insertions(+), 65 deletions(-)

diff --git a/csharp-mode.el b/csharp-mode.el
index 0f804ef..9479fa3 100644
--- a/csharp-mode.el
+++ b/csharp-mode.el
@@ -4,10 +4,10 @@
 ;; Maintainer : Dino Chiesa <dpchi...@hotmail.com>
 ;; Created    : Feburary 2005
 ;; Modified   : May 2011
-;; Version    : 0.8.2
+;; Version    : 0.8.3
 ;; Keywords   : c# languages oop mode
 ;; X-URL      : http://code.google.com/p/csharpmode/
-;; Last-saved : <2011-May-13 13:20:29>
+;; Last-saved : <2011-May-19 01:30:07>
 
 ;;
 ;; This program is free software; you can redistribute it and/or modify
@@ -28,8 +28,8 @@
 ;;; Commentary:
 ;;
 ;;    This is a major mode for editing C# code. It performs automatic
-;;    indentation of C# syntax; font locking; integration with compile.el;
-;;    integration with flymake.el; integration with yasnippet.el.
+;;    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.
@@ -64,7 +64,10 @@
 ;;
 ;;   - yasnippet integration
 ;;       - preloaded snippets
-;;       -?
+;;
+;;   - imenu integration - generates an index of namespaces/classes/methods
+;;     for easy navigation within the buffer.
+;;
 
 
 ;; Installation instructions
@@ -110,7 +113,7 @@
 ;;  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
+;;   //  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
@@ -128,19 +131,22 @@
 ;;       `csharp-cmd-line-limit'.  See the documentation on that
 ;;       variable for more information.
 ;;
-;;    3. the command SHOULD NOT include the name of the source file
-;;       currently being edited. This is because flymake saves a copy of
-;;       the buffer into a temporary file with a unique name, and then
-;;       compiles that temporary file. The name of the temporary file is
-;;       automatically appended to the end of the command.  The command
-;;       should include /R options specifying external libraries that
-;;       the code depends on.
+;;    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 ifyou had
+;;  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
+;;      // flymake: c:\.net3.5\csc.exe /t:module /nologo @@FILE@@
 ;;
 ;;
 ;;  If you use csc.exe as the syntax check tool (as almost everyone
@@ -197,6 +203,19 @@
 ;;  `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:
 ;;
@@ -220,9 +239,10 @@
 ;;
 ;;  Todo:
 ;;
-;;    Get csharp-mode.el accepted as part of the emacs standard distribution.
-;;    Must contact monnier at iro.umontreal.ca to make this happen.
+;;   Get csharp-mode.el accepted as part of the emacs standard distribution.
+;;   Must contact monnier at iro.umontreal.ca to make this happen.
 ;;
+;;   Figure out how to run FxCop nicely.  Maybe it's a flymake thing.
 ;;
 ;;
 ;;  Acknowledgements:
@@ -300,16 +320,26 @@
 ;;            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 is the purpose of its
+;;            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 - small tweaks; now set a one-time bool for flymake installation
+;;    0.8.2 April 2011 DPC
+;;          - small tweaks; now set a one-time bool for flymake installation
 ;;          - some doc updates on flymake
+;;
+;;    0.8.3 May 17 2001 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.
+;;
+
 
 
 (require 'cc-mode)
@@ -429,7 +459,7 @@
 ;; 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.
 
@@ -484,7 +514,7 @@ Otherwise nil.
              (cond
 
               ((eq (char-before) 93) ;; close sq brace
-               (csharp-at-vsemi-p (point)))
+               (csharp--at-vsemi-p (point)))
 
               ((or
                 (eq (char-before) 59) ;; semicolon
@@ -543,11 +573,12 @@ Another option is to use `csharp-lineup-region'.
 Basically this works like `c-in-literal' except it doesn't
 use or fill the cache (`c-in-literal-cache').
 
-The return value is `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.
+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."
@@ -708,7 +739,7 @@ comment at the start of cc-engine.el for more info."
 ;; ==================================================================
 
 (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
@@ -1562,7 +1593,6 @@ comment at the start of cc-engine.el for more info."
   csharp 
'"\\(namespace\\)\\([^[:alnum:]_]\\|$\\)\\|\\(class\\|interface\\|struct\\)\\([^[:alnum:]_]\\|$\\)"
 )
 
 
-
 ;; Thu, 22 Apr 2010  14:29
 ;; I want this to handle    var x = new Foo[] { ... };
 ;; not sure if necessary.
@@ -1692,11 +1722,10 @@ comment at the start of cc-engine.el for more info."
     :type 'hook
     :group 'csharp)
 
-
   ;; The following fn allows this:
   ;;    (csharp-log 3 "scan result...'%s'" state)
 
-  (defcustom csharp-log-level 0
+(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.
@@ -1721,7 +1750,13 @@ only if flymake is loaded."
 
 
 ;;;###autoload
-(defcustom csharp-make-tool "nmake"
+(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.
@@ -1733,7 +1768,7 @@ See also, `csharp-msbuild-tool'.
 
 
 ;;;###autoload
-(defcustom csharp-msbuild-tool "msbuild"
+(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.
@@ -1777,17 +1812,22 @@ 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
+ // 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.
-csharp-mode appends the name of the source file to compile, to
-this command \"stub\" before passing the command to flymake to
-run it.
+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
 ------------
@@ -1852,9 +1892,8 @@ anything after the marker string as the command to run.
 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.")
-
+  "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")
@@ -1872,11 +1911,10 @@ error column when possible. ")
 
 
 (defvar csharp-flymake-csc-error-pattern
-  "^[ \t]*\\([_A-Za-z0-9][^(]+\\.cs\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) ?: 
\\(\\(error\\|warning\\) CS[0-9]+:[ \t\n]*\\(.+\\)\\)"
+  "^[ \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.")
+value is a STRING, a regex.")
 
 ;; TODO
 ;; Defines our constant for finding attributes.
@@ -1948,8 +1986,8 @@ compatibility with flymake and the process-start fn.
 
 "
   (let ((local-s s)
-        (my-re-1 "[^ \"]+\"[^\"]+\"\\|[^ \"]+")
-        (my-re-2 "\\([^ \"]+\\)\"\\([^\"]+\\)\"")
+        (my-re-1 "[^ \"]*\"[^\"]+\"\\|[^ \"]+")
+        (my-re-2 "\\([^ \"]*\\)\"\\([^\"]+\\)\"")
         (tokens))
     (while (string-match my-re-1 local-s)
       (let ((token (match-string 0 local-s))
@@ -2089,7 +2127,7 @@ The fn looks in the buffer for a line that looks like:
 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.
+error messages in the same form as the C# compiler, like FxCopCmd.exe
 
 Some notes on implementation:
 
@@ -2215,9 +2253,6 @@ Some notes on implementation:
 
 
 
-
-
-
 (defun csharp-flymake-install ()
 
   "Change flymake variables and fns to work with C#.
@@ -2444,11 +2479,6 @@ This fn does these things:
 
 
 
-;; ++++++++++++++++++++++
-
-
-
-
 ;; 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
@@ -2470,6 +2500,1436 @@ This fn does these things:
     (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]+"
+         "\\([[: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)
+         "[ \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\\)?\\|"
+         "internal\\(?: static\\)?\\|"
+         "static\\(?: internal\\)?\\|"
+         "internal\\(?: sealed\\)?\\|"
+         "sealed\\(?: internal\\)?\\|"
+         "static\\|"
+         "sealed\\|"
+         "\\)"
+         "[ \t]+"
+         "\\(\\(?:partial[ \t]+\\)?class\\|struct\\)" ;; class/struct keyword
+         "[ \t]+"
+         "\\([[:alpha:]_][[:alnum:]]*\\)"             ;; type name
+         "\\("
+         "[ \t\n]*:[ \t\n]*"                          ;; colon
+         "\\("
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; parent type or intf - 
poss generic
+         "\\)"
+         "\\([ \t\n]*,[ \t\n]*"
+         "\\([[:alpha:]_][^\t\(\n]+\\)"               ;; addl interface - 
possibly 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]+"
+         "\\([[: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)
+        (setq done t)))
+    found))
+
+
+
+(defun csharp-move-back-to-beginning-of-defun ()
+  "Moves back to the open-curly that defines the beginning of the
+enclosing method.  If point is outside a method, then move back to the
+beginning of the prior method.
+
+See also, `csharp-move-fwd-to-end-of-defun'.
+"
+  (interactive)
+  (cond
+
+   ((bobp) nil)
+
+   (t
+    (let (found)
+      (save-excursion
+        ;; handle the case where we're at the top of a fn now.
+        ;; if the user is asking to move back, then obviously
+        ;; he wants to move back to a *prior* defun.
+        (if (and (looking-at "{")
+                 (looking-back (csharp--regexp 'func-start))
+                 (not (looking-back (csharp--regexp 'namespace-start))))
+            (forward-char -1))
+
+        ;; now do the real work
+        (setq found (csharp--move-back-to-beginning-of-something
+                     (csharp--regexp 'func-start)
+                     (csharp--regexp 'namespace-start))))
+      (if found
+          (goto-char found))))))
+
+
+(defun csharp--on-defun-close-curly-p ()
+  "return t when point is on the close-curly of a method."
+  (and (looking-at "}")
+       (save-excursion
+         (and
+          (progn (forward-char) (forward-sexp -1) t)
+          (not (looking-back (csharp--regexp 'class-start)))
+          (not (looking-back (csharp--regexp 'namespace-start)))
+          (looking-back (csharp--regexp 'func-start))))))
+
+(defun csharp--on-ctor-close-curly-p ()
+  "return t when point is on the close-curly of a constructor."
+  (and (looking-at "}")
+       (save-excursion
+         (and
+          (progn (forward-char) (forward-sexp -1) t)
+          (looking-back (csharp--regexp 'ctor-start))))))
+
+(defun csharp--on-class-close-curly-p ()
+  "return t when point is on the close-curly of a class or struct."
+  (and (looking-at "}")
+       (save-excursion
+         (and
+          (progn (forward-char) (forward-sexp -1) t)
+          (not (looking-back (csharp--regexp 'namespace-start)))
+          (looking-back (csharp--regexp 'class-start))))))
+
+(defun csharp--on-intf-close-curly-p ()
+  "return t when point is on the close-curly of an interface."
+  (and (looking-at "}")
+       (save-excursion
+         (and
+          (progn (forward-char) (forward-sexp -1) t)
+          (looking-back (csharp--regexp 'intf-start))))))
+
+(defun csharp--on-enum-close-curly-p ()
+  "return t when point is on the close-curly of an enum."
+  (and (looking-at "}")
+       (save-excursion
+         (and
+          (progn (forward-char) (forward-sexp -1) t)
+          (looking-back (csharp--regexp 'enum-start))))))
+
+(defun csharp--on-namespace-close-curly-p ()
+  "return t when point is on the close-curly of a namespace."
+  (and (looking-at "}")
+       (save-excursion
+         (and
+          (progn (forward-char) (forward-sexp -1) t)
+          (looking-back (csharp--regexp 'namespace-start))))))
+
+(defun csharp--on-defun-open-curly-p ()
+  "return t when point is on the open-curly of a method."
+  (and (looking-at "{")
+       (not (looking-back (csharp--regexp 'class-start)))
+       (not (looking-back (csharp--regexp 'namespace-start)))
+       (looking-back (csharp--regexp 'func-start))))
+
+(defun csharp--on-class-open-curly-p ()
+  "return t when point is on the open-curly of a class."
+  (and (looking-at "{")
+       (not (looking-back (csharp--regexp 'namespace-start)))
+       (looking-back (csharp--regexp 'class-start))))
+
+(defun csharp--on-namespace-open-curly-p ()
+  "return t when point is on the open-curly of a namespace."
+  (and (looking-at "{")
+       (looking-back (csharp--regexp 'namespace-start))))
+
+(defun csharp--on-ctor-open-curly-p ()
+  "return t when point is on the open-curly of a ctor."
+  (and (looking-at "{")
+       (looking-back (csharp--regexp 'ctor-start))))
+
+(defun csharp--on-intf-open-curly-p ()
+  "return t when point is on the open-curly of a interface."
+  (and (looking-at "{")
+       (looking-back (csharp--regexp 'intf-start))))
+
+(defun csharp--on-prop-open-curly-p ()
+  "return t when point is on the open-curly of a property."
+  (and (looking-at "{")
+       (not (looking-back (csharp--regexp 'class-start)))
+       (looking-back (csharp--regexp 'prop-start))))
+
+(defun csharp--on-indexer-open-curly-p ()
+  "return t when point is on the open-curly of a C# indexer."
+  (and (looking-at "{")
+       (looking-back (csharp--regexp 'indexer-start))))
+
+;;(not (string= (match-string-no-properties 2) "class"))))
+
+(defun csharp--on-enum-open-curly-p ()
+  "return t when point is on the open-curly of a interface."
+  (and (looking-at "{")
+       (looking-back (csharp--regexp 'enum-start))))
+
+
+
+(defun csharp-move-fwd-to-end-of-defun ()
+  "Moves forward to the close-curly that defines the end of the enclosing
+method. If point is outside a method, moves forward to the close-curly that
+defines the end of the next method.
+
+See also, `csharp-move-back-to-beginning-of-defun'.
+"
+  (interactive)
+
+  (let ((really-move
+         (lambda ()
+           (let ((start (point))
+                 dest-char)
+             (save-excursion
+               (csharp-move-back-to-beginning-of-defun)
+               (forward-sexp)
+               (if (>= (point) start)
+                   (setq dest-char (point))))
+             (if dest-char
+                 (goto-char dest-char))))))
+
+    (cond
+
+     ;; case 1: end of buffer.  do nothing.
+     ((eobp) nil)
+
+     ;; case 2: we're at the top of a class
+     ((csharp--on-class-open-curly-p)
+      (let (found-it)
+        (save-excursion
+          (forward-char 1) ;; get off the curly
+          (setq found-it
+                (and ;; look for next open curly
+                 (re-search-forward "{" (point-max) t)
+                 (funcall really-move))))
+        (if found-it
+            (goto-char found-it))))
+
+
+     ;; case 3: we're at the top of a fn now.
+     ((csharp--on-defun-open-curly-p)
+      (forward-sexp))
+
+
+     ;; case 4: we're at the bottom of a fn now (possibly
+     ;; after just calling csharp-move-fwd-to-end-of-defun.
+     ((and (looking-back "}")
+           (save-excursion
+             (forward-sexp -1)
+             (csharp--on-defun-open-curly-p)))
+
+      (let (found-it)
+        (save-excursion
+          (setq found-it
+                (and (re-search-forward "{" (point-max) t)
+                     (funcall really-move))))
+        (if found-it
+            (goto-char found-it))))
+
+
+     ;; case 5: we're at none of those places.
+     (t
+      (funcall really-move)))))
+
+
+
+
+(defun csharp-move-back-to-beginning-of-class ()
+  "Moves back to the open-curly that defines the beginning of the
+enclosing class.  If point is outside a class, then move back to the
+beginning of the prior class.
+
+See also, `csharp-move-fwd-to-end-of-defun'.
+"
+  (interactive)
+
+  (cond
+   ((bobp) nil)
+
+   (t
+    (let (found)
+      (save-excursion
+        ;; handle the case where we're at the top of a class now.
+        ;; if the user is asking to move back, then obviously
+        ;; he wants to move back to a *prior* defun.
+        (if (and (looking-at "{")
+                 (looking-back (csharp--regexp 'class-start))
+                 (not (looking-back (csharp--regexp 'namespace-start))))
+            (forward-char -1))
+
+        ;; now do the real work
+        (setq found (csharp--move-back-to-beginning-of-something
+                     (csharp--regexp 'class-start)
+                     (csharp--regexp 'namespace-start))))
+      (if found
+          (goto-char found))))))
+
+
+
+
+(defun csharp-move-fwd-to-end-of-class ()
+  "Moves forward to the close-curly that defines the end of the
+enclosing class.
+
+See also, `csharp-move-back-to-beginning-of-class'.
+"
+  (interactive)
+  (let ((start (point))
+        dest-char)
+    (save-excursion
+      (csharp-move-back-to-beginning-of-class)
+      (forward-sexp)
+      (if (>= (point) start)
+          (setq dest-char (point))))
+
+    (if dest-char
+        (goto-char dest-char))))
+
+
+
+(defun csharp-move-back-to-beginning-of-namespace ()
+  "Moves back to the open-curly that defines the beginning of the
+enclosing namespace.  If point is outside a namespace, then move back
+to the beginning of the prior namespace.
+
+"
+  (interactive)
+  (cond
+
+   ((bobp) nil)
+
+   (t
+    (let (found)
+      (save-excursion
+        ;; handle the case where we're at the top of a namespace now.
+        ;; if the user is asking to move back, then obviously
+        ;; he wants to move back to a *prior* defun.
+        (if (and (looking-at "{")
+                 (looking-back (csharp--regexp 'namespace-start)))
+            (forward-char -1))
+
+        ;; now do the real work
+        (setq found (csharp--move-back-to-beginning-of-something
+                     (csharp--regexp 'namespace-start))))
+      (if found
+          (goto-char found))))))
+
+
+
+
+
+;; moving
+;; ========================================================================
+
+
+
+
+;; ==================================================================
+;;; imenu stuff
+
+;; (defun csharp-imenu-create-index-function-fake ()
+;;   "producees a fake index for imenu. See the documentation for
+;; `csharp-imenu-create-index-function' for more information.
+;;
+;; "
+;;   ;; example:
+;;   ;;
+;;   ;;  (("New" . #<marker at 589 in Rijndael-vb.vb>)
+;;   ;;   ("New" . #<marker at 678 in Rijndael-vb.vb>)
+;;   ;;   ("GetRijndaelManaged" . #<marker at 765 in Rijndael-vb.vb>)
+;;   ;;   ("Run" . #<marker at 1282 in Rijndael-vb.vb>)
+;;   ;;   ("Encrypt" . #<marker at 2381 in Rijndael-vb.vb>)
+;;   ;;   ("Decrypt" . #<marker at 3384 in Rijndael-vb.vb>))
+;;
+;;
+;;   '(("Somewhere in the header comment"  . 20)
+;;     ("Imports"  . 375)
+;;     ("Namespace Ionic.Tests.Crypto" . 447)
+;;     ("Class RijndaelVb - a submenu"
+;;      ("ctor"  . 597)
+;;      ("A function..."  . 1282)
+;;      ("etc..." . 3222))))
+
+
+
+;; define some advice for menu construction.
+
+;; The way imenu constructs menus from the index alist, in
+;; `imenu--split-menu', is ... ah ... perplexing.  If the csharp
+;; create-index fn returns an ordered menu, and the imenu "sort" fn has
+;; been set to nil, imenu still sorts the menu, according to the rule
+;; that all submenus must appear at the top of any menu. Why?  I don't
+;; know. This advice disables that weirdness in C# buffers.
+
+(defadvice imenu--split-menu (around
+                              csharp--imenu-split-menu-patch
+                              activate compile)
+  ;; This advice will run in all buffers.  Let's may sure we
+  ;; actually execute the important bits only when a C# buffer is active.
+  (if (and (string-match "\\.[Cc][Ss]$"  (file-relative-name buffer-file-name))
+           (boundp 'csharp-want-imenu)
+           csharp-want-imenu)
+      (let ((menulist (copy-sequence menulist))
+            keep-at-top)
+        (if (memq imenu--rescan-item menulist)
+            (setq keep-at-top (list imenu--rescan-item)
+                  menulist (delq imenu--rescan-item menulist)))
+        ;; This is the part from the original imenu code
+        ;; that puts submenus at the top.  huh? why?
+        ;; --------------------------------------------
+        ;; (setq tail menulist)
+        ;; (dolist (item tail)
+        ;;   (when (imenu--subalist-p item)
+        ;;     (push item keep-at-top)
+        ;;     (setq menulist (delq item menulist))))
+        (if imenu-sort-function
+            (setq menulist (sort menulist imenu-sort-function)))
+        (if (> (length menulist) imenu-max-items)
+            (setq menulist
+                  (mapcar
+                   (lambda (menu)
+                     (cons (format "From: %s" (caar menu)) menu))
+                   (imenu--split menulist imenu-max-items))))
+        (setq ad-return-value
+              (cons title
+                    (nconc (nreverse keep-at-top) menulist))))
+    ;; else
+    ad-do-it))
+
+
+;;
+;; I used this to examine the performance of the imenu scanning.
+;; It's not necessary during normal operation.
+;;
+;; (defun csharp-imenu-begin-profile ()
+;;   "turn on profiling"
+;;   (interactive)
+;;   (let ((fns '(csharp--on-class-open-curly-p
+;;              csharp--on-namespace-open-curly-p
+;;              csharp--on-ctor-open-curly-p
+;;              csharp--on-enum-open-curly-p
+;;              csharp--on-intf-open-curly-p
+;;              csharp--on-prop-open-curly-p
+;;              csharp--on-indexer-open-curly-p
+;;              csharp--on-defun-open-curly-p
+;;              csharp--imenu-create-index-helper
+;;              looking-back
+;;              looking-at)))
+;;     (if (fboundp 'elp-reset-all)
+;;         (elp-reset-all))
+;;     (mapc 'elp-instrument-function fns)))
+
+
+
+(defun csharp--imenu-remove-param-names-from-paramlist (s)
+  "The input string S is a parameter list, of the form seen in a
+C# method.  TYPE1 NAME1 [, TYPE2 NAME2 ...]
+
+This fn returns a string of the form TYPE1 [, TYPE2...]
+
+Upon entry, it's assumed that the parens included in S.
+
+"
+
+  (let* (new
+        (state 0)  ;; 0 => ws, 1=>slurping param...
+        i
+        c
+        cs
+        nesting
+        need-type
+        ix2
+        (s2 (substring s 1 -1))
+        (len (length s2))
+        (i (1- len)))
+
+    (while (> i 0)
+      (setq c (aref s2 i) ;; current character
+            cs (char-to-string c)) ;; s.t. as a string
+
+      (cond
+
+       ;; backing over whitespace "after" the param
+       ((= state 0)
+        (cond
+         ;; more ws
+         ((string-match "[ \t\f\v\n\r]" cs)
+          t)
+         ;; a legal char for an identifier
+         ((string-match "[A-Za-z_0-9]" cs)
+          (setq state 1))
+         (t
+          (error "unexpected char (A)"))))
+
+
+       ;; slurping param name
+       ((= state 1)
+        (cond
+         ;; ws signifies the end of the param
+         ((string-match "[ \t\f\v\n\r]" cs)
+          (setq state 2))
+         ;; a legal char for an identifier
+         ((string-match "[A-Za-z_0-9]" cs)
+          t)
+         (t
+          (error "unexpected char (B)"))))
+
+
+       ;; ws between typespec and param name
+       ((= state 2)
+        (cond
+         ((string-match "[ \t\f\v\n\r]" cs)
+          t)
+         ;; non-ws indicates the type spec is beginning
+         (t
+          (incf i)
+          (setq state 3
+                need-type nil
+                nesting 0
+                ix2 i))))
+
+
+       ;; slurping type
+       ((= state 3)
+        (cond
+         ((= ?> c) (incf nesting))
+         ((= ?< c)
+          (decf nesting)
+          (setq need-type t))
+
+         ;; ws or comma maybe signifies the end of the typespec
+         ((string-match "[ \t\f\v\n\r,]" cs)
+          (if (and (= nesting 0) (not need-type))
+              (progn
+                (setq new (cons (substring s2 (1+ i) ix2) new))
+                (setq state
+                      (if (= c ?,) 0 4)))))
+
+         ((string-match "[A-Za-z_0-9]" cs)
+          (setq need-type nil))))
+
+
+       ;; awaiting comma or b-o-s
+       ((= state 4)
+        (cond
+         ((= ?, c)
+          (setq state 0))
+
+         ((string-match "[ \t\f\v\n\r,]" cs)
+          t)
+         (t
+          (error "unexpected char (C)"))))
+       )
+
+      (decf i))
+
+    (if (and (= state 3) (= nesting 0))
+        (setq new (cons (substring s2 i ix2) new)))
+
+    (concat "("
+            (if new
+                (mapconcat 'identity new ", ")
+              "")
+            ")")))
+
+
+
+(defun csharp--imenu-create-index-helper (&optional parent-ns indent-level
+                                                    consider-usings 
consider-namespaces)
+  "Helper fn for `csharp-imenu-create-index'.
+
+Scans for a namespace, then scans within the namespace for methods.
+Returns a list, suitable for use as an imenu index
+alist. Leaves point after close-curly on the namespace.
+
+"
+
+  ;; A C# module consists of zero of more explicitly denoted (and
+  ;; possibly nested) namespaces. In the absence of an
+  ;; explicitly-denoted namespace, the global namespace is implicitly
+  ;; applied.  Within each namespace there can be zero or more
+  ;; "container" things - like class, struct, or interface; each with
+  ;; zero or more indexable items - like methods, constructors.
+  ;; and so on.
+
+  ;; This fn parses the module and indexes those items, creating a
+  ;; hierarchically organized list to describe them.  Each container
+  ;; (ns/class/struct/etc) is represented on a separate submenu.
+
+  ;; It works like this:
+  ;; (start at the top of the module)
+  ;;
+  ;; 1. look for a using clause
+  ;;    yes - insert an item in the menu; move to next open line; goto step 1
+  ;;
+  ;; 2. go to next open curly
+  ;;
+  ;; 2. beginning of a container?
+  ;;
+  ;;    yes - narrow, and recurse
+  ;;
+  ;;    no - create a menu item for the thing, whatever it is.
+  ;;         add to the submenu. Go to the end of the thing.
+  ;;         then goto step 1.
+  ;;
+
+
+  (let (container-name
+        this-flavor
+        this-item
+        this-menu
+        found-usings
+        done)
+
+    (while (not done)
+
+      ;; move to the next thing
+      (c-forward-syntactic-ws)
+      (cond
+       ((and consider-usings
+             (re-search-forward (csharp--regexp 'using-stmt) (point-max) t))
+        (goto-char (match-beginning 1))
+        (setq found-usings t
+              done nil))
+
+       ((re-search-forward "{" (point-max) t)
+        (let ((literal (csharp-in-literal)))
+          ;; skip over comments?
+          (if (memq literal '(c c++))
+              (progn
+                (while (memq literal '(c c++))
+                  (end-of-line)
+                  (forward-char 1)
+                  (setq literal (csharp-in-literal)))
+                (if (re-search-forward "{" (point-max) t)
+                    (forward-char -1)
+                  (setq done t)))
+
+            (forward-char -1)
+            (setq done nil))))
+
+       (t
+        (setq done t)))
+
+
+
+      (if (not done)
+          (cond
+           ;; case 0: in a string or comment
+           ((csharp-in-literal)
+            t)
+
+           ;; case 1: at the head of a block of using statements
+           (found-usings
+            (setq found-usings nil
+                  consider-usings nil) ;; only one batch
+            (let ((first-using (match-beginning 1))
+                  (count 0)
+                  marquis
+                  ;; don't search beyond next open curly
+                  (limit (1-
+                          (save-excursion
+                            (re-search-forward "{" (point-max) t)))))
+
+              ;; count the using statements
+              (while (re-search-forward (csharp--regexp 'using-stmt) limit t)
+                (incf count))
+
+              (setq marquis (if (eq count 1) "using (1)"
+                              (format "usings (%d)" count)))
+              (push (cons marquis first-using) this-menu)))
+
+
+           ;; case 2: an interface or enum inside the container
+           ;; (must come before class / namespace )
+           ((or (csharp--on-intf-open-curly-p)
+                (csharp--on-enum-open-curly-p))
+            (let ((top (match-beginning 1))
+                  (close-curly (save-excursion
+                                 (forward-sexp 1)
+                                 (point))))
+              (setq consider-namespaces nil
+                    consider-usings nil
+                    this-menu
+                    (append this-menu
+                            (list
+                             (cons (concat
+                                    (match-string-no-properties 1) ;; thing 
flavor
+                                    " "
+                                    (match-string-no-properties 2)) ;; intf 
name
+                                   top))))
+
+              (goto-char close-curly)))
+
+           ;; case 3: at the start of a container (class, namespace)
+           ((or (and consider-namespaces (csharp--on-namespace-open-curly-p))
+                (csharp--on-class-open-curly-p))
+
+            ;; produce a fully-qualified name for this thing
+            (if (string= (match-string-no-properties 1) "namespace")
+                (setq this-flavor (match-string-no-properties 1)
+                      this-item (match-string-no-properties 2))
+              (setq this-flavor (match-string-no-properties 2)
+                    this-item (match-string-no-properties 3)
+                    consider-usings nil
+                    consider-namespaces nil))
+
+            (setq container-name (if parent-ns
+                                     (concat parent-ns "." this-item)
+                                   this-item))
+
+            ;; create a submenu
+            (let (submenu
+                  (top (match-beginning 1))
+                  (open-curly (point))
+                  (close-curly (save-excursion
+                                 (forward-sexp 1)
+                                 (point))))
+
+              (setq submenu
+                    (list
+                     (concat this-flavor " " container-name)
+                     (cons "(top)" top)))
+
+              ;; find all contained items
+              (save-restriction
+                (narrow-to-region (1+ open-curly) (1- close-curly))
+
+                (let ((child-menu
+                       (csharp--imenu-create-index-helper container-name
+                                                          (concat indent-level 
"  ")
+                                                          (string= this-flavor 
"namespace")
+                                                          (string= this-flavor 
"namespace"))))
+                  ;; there may be multiple children; add them all
+                  (if child-menu
+                      (setq submenu (append submenu child-menu)))))
+
+              ;; (setq m (make-marker)
+              ;;       submenu
+              ;;       (append submenu
+              ;;               (list
+              ;;                (cons "(bottom)" (set-marker m close-curly)))))
+
+              (setq submenu
+                    (append submenu
+                            (list
+                             (cons "(bottom)" close-curly))))
+
+              (setq this-menu
+                    (append this-menu (list submenu)))
+
+              (goto-char close-curly)))
+
+
+           ;; case 4: a property
+           ((csharp--on-prop-open-curly-p)
+            (let ((top (match-beginning 1))
+                  (close-curly (save-excursion
+                                 (forward-sexp 1)
+                                 (point))))
+              (setq consider-namespaces nil
+                    consider-usings nil
+                    this-menu
+                    (append this-menu
+                            (list
+                             (cons (concat
+                                    "prop "
+                                    (match-string-no-properties 3)) ;; prop 
name
+                                   top))))
+              (goto-char close-curly)))
+
+           ;; case 4: an indexer
+           ((csharp--on-indexer-open-curly-p)
+            (let ((top (match-beginning 1))
+                  (close-curly (save-excursion
+                                 (forward-sexp 1)
+                                 (point))))
+              (setq consider-namespaces nil
+                    consider-usings nil
+                    this-menu
+                    (append this-menu
+                            (list
+                             (cons (concat
+                                    "indexer "
+                                    (match-string-no-properties 4)) ;; index 
type
+                                   top ))))
+
+              (goto-char close-curly)))
+
+
+           ;; case 6: a constructor inside the container
+           ((csharp--on-ctor-open-curly-p)
+            (let ((top (match-beginning 1))
+                  (close-curly (save-excursion
+                                 (forward-sexp 1)
+                                 (point))))
+
+              (setq consider-namespaces nil
+                    consider-usings nil
+                    this-menu
+                    (append this-menu
+                            (list
+                             (cons (concat
+                                    "ctor "
+                                    (match-string-no-properties 2) ;; ctor name
+                                    
(csharp--imenu-remove-param-names-from-paramlist
+                                     (match-string-no-properties 3))) ;; ctor 
params
+                                   top))))
+
+              (goto-char close-curly)))
+
+
+           ;; case 4: a method inside the container
+           ((csharp--on-defun-open-curly-p)
+            (let ((top (match-beginning 1))
+                  (close-curly (save-excursion
+                                 (forward-sexp 1)
+                                 (point))))
+
+              (setq consider-namespaces nil
+                    consider-usings nil
+                    this-menu
+                    (append this-menu
+                            (list
+                             (cons (concat
+                                    "method "
+                                    (match-string-no-properties 2) ;; return 
type
+                                    " "
+                                    (match-string-no-properties 3) ;; func name
+                                    
(csharp--imenu-remove-param-names-from-paramlist
+                                     (match-string-no-properties 4))) ;; fn 
params
+                                   top ))))
+
+              (goto-char close-curly)))
+
+           (t
+            (forward-char 1)))))
+
+    this-menu))
+
+
+
+(defcustom csharp-imenu-max-similar-items-before-extraction 4
+  "The maximum number of things of a particular
+category (constructor, property, method, etc) that will be
+separely displayed on an imenu without factoring them into a
+separate submenu.
+
+For example, if a module has 3 consructors, 5 methods, and 7
+properties, and the value of this variable is 4, then upon
+refactoring, the constructors will remain in the toplevel imenu
+and the methods and properties will each get their own
+category-specific submenu.
+
+See also `csharp-imenu-min-size-for-sub-submenu'.
+
+For more information on how csharp-mode uses imenu,
+see `csharp-want-imenu', and `csharp-mode'.
+"
+  :type 'integer
+  :group 'csharp)
+
+
+(defcustom csharp-imenu-min-size-for-sub-submenu 18
+  "The minimum number of imenu items  of a particular
+category (constructor, property, method, etc) that will be
+broken out into sub-submenus.
+
+For example, if a module has 28 properties, then the properties will
+be placed in a submenu, and then that submenu with be further divided
+into smaller submenus.
+
+See also `csharp-imenu-max-similar-items-before-extraction'
+
+For more information on how csharp-mode uses imenu,
+see `csharp-want-imenu', and `csharp-mode'.
+"
+  :type 'integer
+  :group 'csharp)
+
+
+(defun csharp--first-word (s)
+  "gets the first word from the given string.
+It had better be a string!"
+  (car (split-string s nil t)))
+
+(defun csharp--make-plural (s)
+  "make a work plural. For use within the generated imenu."
+  (cond
+   ((string= s "prop")
+    "properties")
+   ((string= s "ctor")
+    "constructors")
+   (t
+    (concat s "s"))))
+
+
+
+(defun csharp--imenu-counts (list)
+  "Returns an alist, each item is a cons cell where the car is a
+unique first substring of an element of LIST, and the cdr is the
+number of occurrences of that substring in elements in the
+list.
+
+For a complicated imenu generated for a large C# module, the result of
+this fn will be something like this:
+
+    ((\"(top)\"        . 1)
+     (\"properties\"   . 38)
+     (\"methods\"      . 12)
+     (\"constructors\" . 7)
+     (\"(bottom)\"     . 1))
+
+"
+  (flet ((helper (list new)
+                 (if (null list) new
+                   (let* ((elt (car list))
+                          (topic (csharp--make-plural (csharp--first-word (car 
elt))))
+                          (xelt (assoc topic new)))
+                     (helper (cdr list)
+                             (if xelt
+                                 (progn (incf (cdr xelt)) new)
+                               (cons (cons topic 1) new)))))))
+    (nreverse (helper list nil))))
+
+
+
+(defun csharp--imenu-get-submenu-size (n)
+  "Gets the preferred size of submenus given N, the size of the
+flat, unparceled menu.
+
+Suppose there are 50 properties in a given C# module. This fn maps
+from that number, to the maximum size of the submenus into which the
+large set of properties should be broken.
+
+Currently the submenu size for 50 is 12.  To change this, change
+the lookup table.
+
+The reason it's a lookup table and not a simple arithmetic
+function: I think it would look silly to have 2 submenus each
+with 12 items.  Twelve of 14 items on a submenu seems fine when
+you're working through 120 items. But if you have only 20 items,
+better to have 3 items with 6 and 7 items each.  That's what this
+lookup tries to do.
+
+"
+  (let ((size-pairs '((100 . 20)
+                      (80 . 18)
+                      (60 . 16)
+                      (40 . 15)
+                      (30 . 12)
+                      (24 . 9)
+                      (0  . 7)))
+        elt
+        (r 0))
+
+    (while (and size-pairs (eq r 0))
+      (setq elt (car size-pairs))
+      (if (> n (car elt))
+          (setq r (cdr elt)))
+      (setq size-pairs (cdr size-pairs)))
+    r))
+
+
+(defun csharp--imenu-item-basic-comparer (a b)
+  "Compares the car of each element, assumed to be a string."
+  (string-lessp (car a) (car b)))
+
+
+(defun csharp--imenu-item-last-word-comparer (a b)
+  "Compares the last word in the car of each element. The car of
+each element is assumed to be a string with multiple tokens in it. "
+  (let ((alast (car (last (split-string (car a) "[ \t]" t))))
+        (blast (car (last (split-string (car b) "[ \t]" t)))))
+  (string-lessp alast blast)))
+
+
+(defun csharp--imenu-remove-category-names (menu-list)
+  "Input is a list, each element is (LABEL . LOCATION). This fn
+returns a modified list, with the first word - the category name
+- removed from each label.
+
+"
+  (mapcar (lambda (elt)
+            (let ((tokens (split-string (car elt) "[ \t]" t)))
+              (cons (mapconcat 'identity (cdr tokens) " ")
+                    (cdr elt))))
+          menu-list))
+
+
+(defun csharp--imenu-break-one-menu-into-submenus (menu-list)
+  "Parcels a flat list MENU-LIST up into smaller sublists. It tries
+to balance the number of sublists and the size of each sublist.
+
+The max size of any sublist will be about 20 (arbitrary) and the
+min size will be 7 or so. See `csharp--imenu-get-submenu-size'
+for how this is done.
+
+It does this destructively, using `nbutlast'.
+
+Returns a new list, containing sublists.
+"
+
+  (let ((len (length menu-list))
+        (counts (csharp--imenu-counts menu-list)))
+
+    (cond
+     ;; a small number, and all the same flavor
+     ((and (< len csharp-imenu-min-size-for-sub-submenu) (= (length counts) 1))
+      (csharp--imenu-remove-category-names
+       (sort menu-list
+             (if (string= (caar counts) "methods")
+                 'csharp--imenu-item-last-word-comparer
+               'csharp--imenu-item-basic-comparer))))
+
+     ;; is the length already pretty short?
+     ((< len csharp-imenu-min-size-for-sub-submenu)
+      menu-list)
+
+     ((/= (length counts) 1)
+      menu-list)
+
+     (t
+      (let* ((lst    (sort menu-list 'csharp--imenu-item-basic-comparer))
+             new
+             (sz     (csharp--imenu-get-submenu-size len)) ;; goal max size of 
sublist
+             (n      (ceiling (/ (* 1.0 len) sz))) ;; total number of sublists
+             (adj-sz (ceiling (/ (* 1.0 len) n)))  ;; maybe a little less than 
sz
+             (nsmall (mod (- adj-sz (mod len adj-sz)) adj-sz)) ;; num of (n-1) 
lists
+             (i      0)
+             (base-name (csharp--first-word (caar lst)))
+             (plural-name (csharp--make-plural base-name))
+             label
+             chunksz
+             this-chunk)
+
+        (while lst
+          (setq chunksz (if (> nsmall i) (1- adj-sz) adj-sz)
+                this-chunk (csharp--imenu-remove-category-names
+                            (nthcdr (- len chunksz) lst))
+                lst (nbutlast lst chunksz)
+                label (format "%s %d" plural-name (- n i))
+                new (cons (cons label this-chunk) new)
+                len (- len chunksz))
+          (incf i))
+        new)))))
+
+
+
+(defun csharp--imenu-break-into-submenus (menu-list)
+  "For an imenu menu-list with category-based submenus,
+possibly break a submenu into smaller sublists, based on size.
+
+"
+  (mapcar (lambda (elt)
+            (if (imenu--subalist-p elt)
+                (cons (car elt)
+                      (csharp--imenu-break-one-menu-into-submenus (cdr elt)))
+              elt))
+          menu-list))
+
+
+
+(defun csharp--imenu-reorg-flat-alist-intelligently (menu-alist)
+  "Accepts an imenu alist. Returns an alist, each item is a cons
+cell where the car is a unique first word that appears in the car of
+each element of LIST, and the cdr is a list of the cdrs of each
+of the corresponding unique element in the original list.
+
+It's easier to understand than it is to explain.
+
+Before:
+
+    ((\"usings (4)\" . #<marker at 1538 in ZipFile.cs>)
+     (\"namespace Ionic.Zip\"
+      (\"(top)\" . #<marker at 1651 in ZipFile.cs>)
+      (\"partial class Ionic.Zip.ZipFile\"
+       (\"(top)\" . #<marker at 5473 in ZipFile.cs>)
+       (\"prop FullScan\" . #<marker at 8036 in ZipFile.cs>)
+           ...
+       (\"prop Comment\" . #<marker at 21118 in ZipFile.cs>)
+       (\"prop Verbose\" . #<marker at 32278 in ZipFile.cs>)
+       (\"method override String ToString\" . #<marker at 96577 in ZipFile.cs>)
+       (\"method internal void NotifyEntryChanged\" . #<marker at 97608 in 
ZipFile.cs>)
+          ....
+       (\"method internal void Reset\" . #<marker at 98231 in ZipFile.cs>)
+       (\"ctor ZipFile\" . #<marker at 103598 in ZipFile.cs>)
+           ...
+       (\"ctor ZipFile\" . #<marker at 109723 in ZipFile.cs>)
+       (\"ctor ZipFile\" . #<marker at 116487 in ZipFile.cs>)
+       (\"indexer int\" . #<marker at 121232 in ZipFile.cs>)
+       (\"indexer String\" . #<marker at 124933 in ZipFile.cs>)
+       (\"(bottom)\" . #<marker at 149777 in ZipFile.cs>))
+      (\"public enum Zip64Option\" . #<marker at 153839 in ZipFile.cs>)
+      (\"enum AddOrUpdateAction\" . #<marker at 154815 in ZipFile.cs>)
+      (\"(bottom)\" . #<marker at 154893 in ZipFile.cs>)))
+
+
+This is displayed as a toplevel menu with 2 items; the namespace
+menu has 5 items (top, bottom, the 2 enums, and the class).  The
+class menu has 93 items. Unworkable.
+
+After:
+
+    ((\"usings (4)\" . #<marker at 1538 in ZipFile.cs>)
+     (\"namespace Ionic.Zip\"
+      (\"(top)\" . #<marker at 1651 in ZipFile.cs>)
+      (\"partial class Ionic.Zip.ZipFile\"
+       (\"(top)\" . #<marker at 5473 in ZipFile.cs>)
+       (\"prop\"
+        (\"prop WriteStream\" . #<marker at 146489 in ZipFile.cs>)
+        (\"prop Count\" . #<marker at 133827 in ZipFile.cs>)
+            ....
+        (\"prop BufferSize\" . #<marker at 12837 in ZipFile.cs>)
+        (\"prop FullScan\" . #<marker at 8036 in ZipFile.cs>))
+       (\"method\"
+        (\"method virtual void Dispose\" . #<marker at 144389 in ZipFile.cs>)
+        (\"method void RemoveEntry\" . #<marker at 141027 in ZipFile.cs>)
+           ....
+        (\"method override String ToString\" . #<marker at 96577 in 
ZipFile.cs>)
+        (\"method bool ContainsEntry\" . #<marker at 32517 in ZipFile.cs>))
+       (\"ctor\"
+        (\"ctor ZipFile\" . #<marker at 116487 in ZipFile.cs>)
+           ....
+        (\"ctor ZipFile\" . #<marker at 105698 in ZipFile.cs>)
+        (\"ctor ZipFile\" . #<marker at 103598 in ZipFile.cs>))
+       (\"indexer int\" . #<marker at 121232 in ZipFile.cs>)
+       (\"indexer String\" . #<marker at 124933 in ZipFile.cs>)
+       (\"(bottom)\" . #<marker at 149777 in ZipFile.cs>))
+      (\"public enum Zip64Option\" . #<marker at 153839 in ZipFile.cs>)
+      (\"enum AddOrUpdateAction\" . #<marker at 154815 in ZipFile.cs>)
+      (\"(bottom)\" . #<marker at 154893 in ZipFile.cs>)))
+
+All menus are the same except the class menu, which has been
+organized into subtopics, each of which gets its own cascaded submenu.
+
+"
+  (let ((counts (csharp--imenu-counts menu-alist)))
+
+    (flet ((helper (list new)
+                   (if (null list)
+                       new
+                     (let* ((elt (car list))
+                            (topic (csharp--make-plural (csharp--first-word 
(car elt))))
+                            (xelt (assoc topic new)))
+                       (helper (cdr list)
+                               (if xelt
+                                   (progn
+                                     (rplacd xelt (cons elt (cdr xelt)))
+                                     new)
+                                 (cons
+
+                                  (cond
+                                   ((> (cdr (assoc topic counts)) 
csharp-imenu-max-similar-items-before-extraction )
+                                    (cons topic (list elt)))
+
+                                   ((imenu--subalist-p elt)
+                                    (cons (car elt)
+                                          
(csharp--imenu-reorg-flat-alist-intelligently (cdr elt))))
+                                   (t
+                                      elt))
+
+                                    new)))))))
+
+      (csharp--imenu-break-into-submenus
+       (nreverse (helper menu-alist nil))))))
+
+
+
+
+(defun csharp-imenu-create-index ()
+  "This function is called by imenu to create an index for the
+current C# buffer, conforming to the format specified in
+`imenu--index-alist' .
+
+See `imenu-create-index-function' for background information.
+
+To produce the index, which lists the classes, functions,
+methods, and properties for the current buffer, this function
+scans the entire buffer.
+
+This can take a long time for a large buffer. The scan uses
+regular expressions that attempt to match on the general-case C#
+syntax, for classes and functions, generic types, base-classes,
+implemented interfaces, and so on. This can be time-consuming.
+For a large source file, say 160k, it can take 10 seconds or more.
+The UI hangs during the scan.
+
+imenu calls this fn when it feels like it, I suppose when it
+thinks the buffer has been updated. The user can also kick it off
+explicitly by selecting *Rescan* from the imenu menu.
+
+After generating the hierarchical list of props, methods,
+interfaces, classes, and namespaces, csharp-mode re-organizes the
+list as appropriate:
+
+ - it extracts sets of like items into submenus. All properties
+   will be placed on a submenu. See
+   `csharp-imenu-max-similar-items-before-extraction' for a way
+   to tune this.
+
+ - it converts those submenus into sub-submenus, if there are more than
+   `csharp-imenu-min-size-for-sub-submenu' items.
+
+ - it sorts each set of items on the outermost menus lexicographically.
+
+The result of these transformations is what is provided to imenu
+to generate the visible menus.  Just FYI - the reorganization of
+the scan results is much much faster than the actual generation
+of the scan results. If you're looking to save time, the re-org
+logic is not where the cost is.
+
+imenu itself likes to sort the menus. See `imenu--split-menu' and
+also `csharp--imenu-split-menu-patch', which is advice that
+attempts to disable the weird re-jiggering that imenu performs.
+
+"
+  ;; I think widen/narrow causes the buffer to be marked as
+  ;; modified. This is a bit surprising, but I have no other
+  ;; explanation for the source of the problem.
+  ;; So I use `c-save-buffer-state' so that the buffer is not
+  ;; marked modified when the scan completes.
+
+  (c-save-buffer-state ()
+      (save-excursion
+        (save-restriction
+          (widen)
+          (goto-char (point-min))
+
+          (let ((index-alist
+                 (csharp--imenu-create-index-helper nil "" t t)))
+
+            (csharp--imenu-reorg-flat-alist-intelligently index-alist)
+
+            ;;index-alist
+
+            ;; What follows is No longer used.
+            ;; =======================================================
+
+            ;; If the index menu contains exactly one element, and it is
+            ;; a namespace menu, then remove it.  This simplifies the
+            ;; menu, and results in no loss of information: all types
+            ;; get fully-qualified names anyway. This will probably
+            ;; cover the majority of cases; often a C# source module
+            ;; defines either one class, or a set of related classes
+            ;; inside a single namespace.
+
+            ;; To remove that namespace, we need to prune & graft the tree.
+            ;; Remove the ns hierarchy level, but also remove the 1st and
+            ;; last elements in the sub-menu, which represent the top and
+            ;; bottom of the namespace.
+
+            ;; (if (and
+            ;;      (= 1 (length index-alist))
+            ;;      (consp (car index-alist))
+            ;;      (let ((tokens (split-string
+            ;;                     (car (car index-alist))
+            ;;                     "[ \t]" t)))
+            ;;        (and (<= 1 (length tokens))
+            ;;             (string= (downcase
+            ;;                       (nth 0 tokens)) "namespace"))))
+            ;;
+            ;;     (let (elt
+            ;;           (newlist (cdar index-alist)))
+            ;;       (setf (car (car newlist))  (car (car index-alist)))
+            ;;       newlist)
+            ;;
+            ;;   index-alist)
+
+            )))))
+
+
+;; ==================================================================
+
 
 
 
@@ -2915,7 +4375,6 @@ underlying scanner used to set the text properties in a 
C# buffer.
 
 
   (defun csharp-scan-for-verbatim-literals-and-set-props (&optional beg end)
-
     "Scans the buffer, between BEG and END, for verbatim literal
 strings, and sets override text properties on each string to
 allow proper syntax highlighting, indenting, and cursor movement.
@@ -2935,7 +4394,7 @@ every buffer change, with the BEG and END set to the 
values for
 the change.
 
 The return value is nil if the buffer was not a csharp-mode
-buffer.  Otherwise it is the last cursor position examined by the
+buffer. Otherwise it is the last cursor position examined by the
 scan.
 "
 
@@ -3567,15 +5026,110 @@ ctrl-e.
 ;;; The entry point into the mode
 ;;;###autoload
   (defun csharp-mode ()
-    "Major mode for editing C# code. This mode is derived from CC Mode to
+  "Major mode for editing C# code. This mode is derived from CC Mode to
 support C#.
 
-The hook `c-mode-common-hook' is run with no args at mode
-initialization, then `csharp-mode-hook'.
+Normally, you'd want to autoload this mode by setting `auto-mode-alist' with
+an entry for csharp, in your .emacs file:
+
+   (autoload 'csharp-mode \"csharp-mode\" \"Major mode for editing C# code.\" 
t)
+   (setq auto-mode-alist
+      (append '((\"\\.cs$\" . csharp-mode)) auto-mode-alist))
+
+
+The mode provides fontification and indent for C# syntax, as well
+as some other handy features.
+
+At mode startup, there are two interesting hooks that run:
+`c-mode-common-hook' is run with no args, then `csharp-mode-hook' is run after
+that, also with no args.
+
+To run your own logic after csharp-mode starts, do this:
+
+  (defun my-csharp-mode-fn ()
+    \"my function that runs when csharp-mode is initialized for a buffer.\"
+    (turn-on-font-lock)
+    (turn-on-auto-revert-mode) ;; helpful when also using Visual Studio
+    (setq indent-tabs-mode nil) ;; tabs are evil
+    (flymake-mode 1)
+    (yas/minor-mode-on)
+    (require 'rfringe)  ;; handy for flymake
+    (require 'flymake-cursor) ;; also handy for flymake
+    ....your own code here...
+  )
+  (add-hook  'csharp-mode-hook 'my-csharp-mode-fn t)
+
+
+The function above is just a suggestion.
+
+
+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: .  For exammple:
+
+  // compile: csc.exe /t:library /r:Mylib.dll Foo.cs
+
+If csharp-mode finds a line like this, it will suggest the text that follows
+as the compilation command when running `compile' for the first time.  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. If
+you don't want this magic, then you can just run `compile' directly, rather
+than `csharp-invoke-compile-interactively' .
+
+This mode will also automatically add a symbol and regexp to the
+`compilation-error-regexp-alist' and`compilation-error-regexp-alist-alist'
+respectively, for Csc.exe error and warning messages. If you invoke `compile',
+then `next-error' should work properly for error messages produced by csc.exe.
+
+
+Flymake Integraiton
+========================
+
+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: csc.exe /t:module /R:Foo.dll @@FILE@@
+
+csharp-mode replaces special tokens in the command with different values:
+
+  @@ORIG@@ - gets replaced with the original filename
+  @@FILE@@ - gets replaced with the name of the temporary file
+      created by flymake. This is usually what you want in place of the
+      name of the file to be compiled.
+
+See the documentation on `csharp-cmd-line-limit' for further information.
+
+You may also want to run a syntax checker, like fxcop:
+
+   //  flymake: fxcopcmd.exe /c /F:MyLibrary.dll
+
+In this case you don't need either of the tokens described above.
+
+If the module has 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: csc.exe /t:module /nologo @@FILE@@
+
+It looks for the EXE on the path.  You can specify a full path if you like.
+
+
+YASnippet and IMenu Integraiton
+===============================
+
+Check the menubar for menu entries for YASnippet and Imenu; the latter
+is labelled \"Index\".
+
+The Imenu index gets computed when the file is .cs first opened and loaded.
+This may take a moment or two.  If you don't like this delay and don't
+use imenu, you can turn this off with the variable `csharp-want-imenu'.
+
 
-This mode will automatically add a symbol and regexp to the
-`compilation-error-regexp-alist' and `compilation-error-regexp-alist-alist'
-respectively, for Csc.exe error and warning messages.
 
 Key bindings:
 \\{csharp-mode-map}"
@@ -3622,7 +5176,7 @@ Key bindings:
         (progn
           (add-to-list
            'compilation-error-regexp-alist-alist
-           '(ms-csharp "^[ 
\t]*\\([-_:A-Za-z0-9][^(]*\\.\\(?:cs\\|xaml\\)\\)(\\([0-9]+\\)[,]\\([0-9]+\\)) 
?: \\(error\\|warning\\) CS[0-9]+:" 1 2 3))
+           '(ms-csharp "^[ 
\t]*\\([-_:A-Za-z0-9][^\n(]*\\.\\(?:cs\\|xaml\\)\\)(\\([0-9]+\\)[,]\\([0-9]+\\))
 ?: \\(error\\|warning\\) CS[0-9]+:" 1 2 3))
           (add-to-list
            'compilation-error-regexp-alist
            'ms-csharp)))
@@ -3667,11 +5221,26 @@ Key bindings:
     (c-update-modeline)
     (c-run-mode-hooks 'c-mode-common-hook 'csharp-mode-hook)
 
+    ;; maybe do imenu scan after hook returns
+    (if csharp-want-imenu
+      (progn
+        ;; There are two ways to do imenu indexing. One is to provide a
+        ;; function, via `imenu-create-index-function'.  The other is to
+        ;; provide imenu with a list of regexps via
+        ;; `imenu-generic-expression'; imenu will do a "generic scan" for you.
+        ;; vbnet-mode uses the former method.
+        ;;
+        (setq imenu-create-index-function 'csharp-imenu-create-index)
+        (imenu-add-menubar-index)))
+
     ;; The paragraph-separate variable was getting stomped by
     ;; other hooks, so it must reside here.
     (setq paragraph-separate
           "[ \t]*\\(//+\\|\\**\\)\\([ \t]+\\|[ \t]+<.+?>\\)$\\|^\f")
 
+    (setq beginning-of-defun-function 'csharp-move-back-to-beginning-of-defun)
+    ;; end-of-defun-function   can remain forward-sexp !!
+
     (set (make-local-variable 'comment-auto-fill-only-comments) t)
     )
 

Reply via email to