|
|
- ;;; csharp-mode.el --- C# mode derived mode
-
- ;; Author : Dylan R. E. Moonfire (original)
- ;; Maintainer : Jostein Kjønigsen <jostein@gmail.com>
- ;; Created : Feburary 2005
- ;; Modified : 2018
- ;; Version : 0.9.2
- ;; Package-Version: 20190717.1024
- ;; Keywords : c# languages oop mode
- ;; X-URL : https://github.com/josteink/csharp-mode
- ;; Last-saved : 2018-Jul-08
-
- ;;
- ;; This program is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation; either version 2 of the License, or
- ;; (at your option) any later version.
- ;;
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;;
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program; see the file COPYING. If not, write to
- ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- ;; Boston, MA 02111-1307, USA.
-
- ;;; Commentary:
- ;;
- ;; This is a major mode for editing C# code. It performs automatic
- ;; indentation of C# syntax; font locking; and integration with
- ;; 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.
- ;;
- ;; Features:
- ;;
- ;; - font-lock and indent of C# syntax including:
- ;; all c# keywords and major syntax
- ;; attributes that decorate methods, classes, fields, properties
- ;; enum types
- ;; #if/#endif #region/#endregion
- ;; instance initializers
- ;; anonymous functions and methods
- ;; verbatim literal strings (those that begin with @)
- ;; generics
- ;;
- ;; - automagic code-doc generation when you type three slashes.
- ;;
- ;; - compatible with electric-pair-mode for intelligent insertion
- ;; of matched braces, quotes, etc.
- ;;
- ;; - 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)
- ;; (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."
- ;; (turn-on-auto-revert-mode)
- ;; (setq indent-tabs-mode nil)
- ;; ...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.
- ;;
- ;;
- ;; 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. (Not sure this is still a problem - 19 may
- ;; 2011 DPC)
- ;;
- ;; Method names with a preceding attribute are not fontified.
- ;;
- ;; The symbol followng #if is not fontified. It should be treated like
- ;; define and get font-lock-variable-name-face .
- ;;
- ;; This code doesn't seem to work when you compile it, then
- ;; load/require in the emacs file. You will get an error (error
- ;; "`c-lang-defconst' must be used in a file") which happens because
- ;; cc-mode doesn't think it is in a buffer while loading directly
- ;; from the init. However, if you call it based on a file extension,
- ;; it works properly. Interestingly enough, this doesn't happen if
- ;; you don't byte-compile cc-mode.
- ;;
- ;;
- ;;
- ;; Todo:
- ;;
- ;; 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:
- ;;
- ;; Thanks to Alan Mackenzie and Stefan Monnier for answering questions
- ;; and making suggestions. And to Trey Jackson for sharing his
- ;; knowledge of emacs lisp.
- ;;
- ;;
-
- ;;; Versions:
- ;;
- ;; 0.1.0 - Initial release.
- ;; 0.2.0 - Fixed the identification on the "enum" keyword.
- ;; - Fixed the font-lock on the "base" keyword
- ;; 0.3.0 - Added a regex to fontify attributes. It isn't the
- ;; the best method, but it handles single-like attributes
- ;; well.
- ;; - Got "super" not to fontify as a keyword.
- ;; - Got extending classes and interfaces to fontify as something.
- ;; 0.4.0 - Removed the attribute matching because it broke more than
- ;; it fixed.
- ;; - Corrected a bug with namespace not being properly identified
- ;; and treating the class level as an inner object, which screwed
- ;; up formatting.
- ;; - Added "partial" to the keywords.
- ;; 0.5.0 - Found bugs with compiled cc-mode and loading from init files.
- ;; - Updated the eval-when-compile to code to let the mode be
- ;; compiled.
- ;; 0.6.0 - Added the c-filter-ops patch for 5.31.1 which made that
- ;; function in cc-langs.el unavailable.
- ;; - Added a csharp-lineup-region for indention #region and
- ;; #endregion block differently.
- ;; 0.7.0 - Added autoload so update-directory-autoloads works
- ;; (Thank you, Nikolaj Schumacher)
- ;; - Fontified the entire #region and #endregion lines.
- ;; - Initial work to get get, set, add, remove font-locked.
- ;; 0.7.1 - Added option to indent #if/endif with code
- ;; - Fixed c-opt-cpp-prefix defn (it must not include the BOL
- ;; char (^).
- ;; - proper fontification and indent of classes that inherit
- ;; (previously the colon was confusing the parser)
- ;; - reclassified namespace as a block beginner
- ;; - removed $ as a legal symbol char - not legal in C#.
- ;; - added struct to c-class-decl-kwds so indent is correct
- ;; within a struct.
- ;; 0.7.2 - Added automatic codedoc insertion.
- ;; 0.7.3 - Instance initializers (new Type { ... } ) and
- ;; (new Type() { ...} ) are now indented properly.
- ;; - proper fontification and indent of enums as brace-list-*,
- ;; including special treatment for enums that explicitly
- ;; inherit from an int type. Previously the colon was
- ;; confusing the parser.
- ;; - 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 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.
- ;; 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.
- ;; This is done in the csharp value for c-basic-matchers-after .
- ;; - turn on recognition of generic types. They are now
- ;; fontified correctly.
- ;; - <> are now treated as syntactic parens and can be jumped
- ;; over with c-forward-sexp.
- ;; - 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
- ;;
- ;; 0.8.7 2014 November 29
- ;; - Fix broken cl-dependency in emacs24.4 and defadvice for tooltips.
- ;;
- ;; 0.8.8 2014 December 3
- ;; - Fix broken byte-compile.
- ;; - Add extra C# keywords.
- ;; - Call prog-mode hooks.
- ;;
- ;; 0.8.9 2015 March 15
- ;; - (Re)add compilation-mode support for msbuild and xbuild.
- ;;
- ;; 0.8.10 2015 May 31th
- ;; - Imenu: Correctly handle support for default-values in paramlist.
- ;;
- ;; 0.8.11 2015 November 21st
- ;; - Make mode a derived mode. Improve evil-support.
- ;; - Add support for devenv compilation-output.
- ;; - Fix all runtime warnings
- ;; - Fix error with string-values in #region directives.
- ;;
- ;; 0.8.12 2016 January 6th
- ;; - Various fixes and improvements for imenu indexing.
- ;;
- ;; 0.9.0 2016 September 9th
- ;; - Fix issues with compilation-mode and lines with arrays.
- ;; - Fontification of compiler directives.
- ;; - Much faster, completely rewritten imenu-implementation.
- ;; - Fix indentation issues.
- ;; - Fix Emacs-25 related bugs.
- ;; - Cleaned up dead code.
- ;;
- ;; 0.9.1 2017
- ;; - Fix indentation for generic type-initializers.
- ;; - Fix fontification of using and namespace-statements with
- ;; underscores in them.
- ;; - Fixes for indentation for many kinds of type-initializers.
- ;;
- ;; 0.9.2 2018 July
- ;; - Try to fix some breakage introduced by changes in Emacs 27.
- ;;
- ;;; Code:
-
- (require 'cc-mode)
- (require 'cc-fonts)
- (require 'cl-lib)
-
- ;; prevent warnings like
- ;; csharp-mode.el:4134:21:Warning: reference to free variable
- ;; `compilation-error-regexp-alist-alist'
- (require 'compile)
-
- ;; Work around emacs bug#23053
- (eval-when-compile
- (require 'cc-langs))
-
- ;; Work around emacs bug#18845
- (eval-when-compile
- (when (and (= emacs-major-version 24) (>= emacs-minor-version 4))
- (require 'cl)))
-
- (require 'imenu)
-
- ;; ==================================================================
- ;; 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.
- ;;
- ;; 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)))
-
-
- (defgroup csharp nil
- "Major mode for editing C# code."
- :group 'prog-mode)
-
-
- ;; Custom variables
- ;; ensure all are defined before using ...;
-
- (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)
-
-
- (defcustom csharp-want-imenu t
- "*Whether to generate a buffer index via imenu for C# buffers."
- :type 'boolean :group 'csharp)
-
-
-
-
-
- ;; These are only required at compile time to get the sources for the
- ;; 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)
- (stringp byte-compile-dest-file))
- (cons (file-name-directory byte-compile-dest-file) load-path)
- load-path)))
- (load "cc-mode" nil t)
- (load "cc-fonts" nil t)
- (load "cc-langs" nil t)))
-
- (eval-and-compile
- ;; ==================================================================
- ;; constants used in this module
- ;; ==================================================================
-
- (defconst csharp-type-initializer-statement-re
- (concat
- "\\<new[ \t\n\r\f\v]+"
- "\\([[:alpha:]_][[:alnum:]_<>\\.]*\\)")
- "Regexp that captures a type-initializer statement in C#")
-
- (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#")
-
- ;; ==================================================================
-
- ;; Make our mode known to the language constant system. Use Java
- ;; mode as the fallback for the constants we don't change here.
- ;; This needs to be done also at compile time since the language
- ;; constants are evaluated then.
- (c-add-language 'csharp-mode 'java-mode))
-
- ;; ==================================================================
- ;; end of c# upfront stuff
- ;; ==================================================================
-
-
-
-
-
-
-
-
-
-
-
- ;; ==================================================================
- ;; csharp-mode utility and feature defuns
- ;; ==================================================================
-
- (defun csharp--at-vsemi-p (&optional pos)
- "Determines if there is a virtual semicolon at POS or point.
- 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 2 cases in C#:
-
- - after an attribute that decorates a class, method, field, or
- property.
-
- - in an object initializer, before the open-curly?
-
- An example of the former is [WebMethod] or [XmlElement].
-
- Providing this function allows the indenting in `csharp-mode'
- to work properly with code that includes attributes."
- (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]+"
- ;; typename
- "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
- "[A-Za-z_][[:alnum:]]*"
- ;; simplified generic constraint.
- ;; handles generic sub-types.
- ;; { is optional because otherwise initializers with
- ;; bracket on same line will indent wrongly.
- "\\(?:<[[:alnum:], <>]+>[ \t\n\f\v\r]*{?\\)?"
- ;; optional array-specifier
- "\\(?:\\[\\]\\)?"
- ;; spacing
- "[\ t\n\f\v\r]*") nil)
- (looking-at "[ \t\n\f\v\r]*{"))
- 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
- (concat
- "\\(\\["
- "[ \t\n\r\f\v]*"
- "\\("
- "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
- "[A-Za-z_][[:alnum:]]*"
- "\\)"
- "[^]]*\\]\\)"
- )
- (1+ pos-or-point) t)
-
- (c-safe (backward-sexp))
- (c-backward-syntactic-ws)
- (cond
-
- ((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) 123) ;; open curly
- (eq (char-before) 125)) ;; close curly
- t)
-
- ;; attr is used within a line of code
- (t nil)))
-
- (t nil)))
-
- (t nil))
- )))
-
-
- ;; ==================================================================
- ;; end of csharp-mode utility and feature defuns
- ;; ==================================================================
-
-
-
- ;; ==================================================================
- ;; c# values for "language constants" defined in cc-langs.el
- ;; ==================================================================
-
- (c-lang-defconst c-at-vsemi-p-fn
- csharp 'csharp--at-vsemi-p)
-
-
- ;; This c-opt-after-id-concat-key is a regexp that matches
- ;; dot. In other words: "\\(\\.\\)"
- ;; Not sure why this needs to be so complicated.
- ;; 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 "[[:alpha:]_]" )
-
-
-
-
- ;; The matchers elements can be of many forms. It gets pretty
- ;; complicated. Do a describe-variable on font-lock-keywords to get a
- ;; description. (Why on font-lock-keywords? I don't know, but that's
- ;; where you get the help.)
- ;;
- ;; Aside from the provided documentation, the other option of course, is
- ;; to look in the source code as an example for what to do. The source
- ;; in cc-fonts uses a defun c-make-font-lock-search-function to produce
- ;; most of the matchers. Called this way:
- ;;
- ;; (c-make-font-lock-search-function regexp '(A B c))
- ;;
- ;; 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
- ;; allow nesting.
- ;;
- ;; In c#, the object initializer block is used directly after a
- ;; constructor, like this:
- ;;
- ;; new MyType
- ;; {
- ;; Prop1 = "foo"
- ;; }
- ;;
- ;; csharp-mode needs to fontify the properties in the
- ;; initializer block in font-lock-variable-name-face. The key thing is
- ;; to set the text property on the open curly, using type c-type and
- ;; value c-decl-id-start. This apparently allows `parse-partial-sexp' to
- ;; do the right thing, later.
- ;;
- ;; This simple case is easy to handle in a regex, using the basic
- ;; `c-make-font-lock-search-function' form. But the general syntax for a
- ;; constructor + object initializer in C# is more complex:
- ;;
- ;; new MyType(..arglist..) {
- ;; Prop1 = "foo"
- ;; }
- ;;
- ;; A simple regex match won't satisfy here, because the ..arglist.. can
- ;; be anything, including calls to other constructors, potentially with
- ;; object initializer blocks. This may nest arbitrarily deeply, and the
- ;; regex in emacs doesn't support balanced matching. Therefore there's
- ;; no way to match on the "outside" pair of parens, to find the relevant
- ;; open curly. What's necessary is to do the match on "new MyType" then
- ;; skip over the sexp defined by the parens, then set the text property on
- ;; the appropriate open-curly.
- ;;
- ;; To make that happen, it's good to have insight into what the matcher
- ;; really does. The output of `c-make-font-lock-search-function' before
- ;; byte-compiling, is:
- ;;
- ;; (lambda (limit)
- ;; (let ((parse-sexp-lookup-properties
- ;; (cc-eval-when-compile
- ;; (boundp 'parse-sexp-lookup-properties))))
- ;; (while (re-search-forward REGEX limit t)
- ;; (unless
- ;; (progn
- ;; (goto-char (match-beginning 0))
- ;; (c-skip-comments-and-strings limit))
- ;; (goto-char (match-end 0))
- ;; (progn
- ;; B
- ;; (save-match-data A)
- ;; C ))))
- ;; nil)
- ;;
- ;; csharp-mode uses this hand-crafted form of a matcher to handle the
- ;; general case for constructor + object initializer, within
- ;; `c-basic-matchers-after' .
- ;;
-
-
-
-
- ;; (defun c-make-font-lock-search-function (regexp &rest highlights)
- ;; ;; This function makes a byte compiled function that works much like
- ;; ;; a matcher element in `font-lock-keywords'. It cuts out a little
- ;; ;; bit of the overhead compared to a real matcher. The main reason
- ;; ;; is however to pass the real search limit to the anchored
- ;; ;; matcher(s), since most (if not all) font-lock implementations
- ;; ;; arbitrarily limits anchored matchers to the same line, and also
- ;; ;; to insulate against various other irritating differences between
- ;; ;; the different (X)Emacs font-lock packages.
- ;; ;;
- ;; ;; REGEXP is the matcher, which must be a regexp. Only matches
- ;; ;; where the beginning is outside any comment or string literal are
- ;; ;; significant.
- ;; ;;
- ;; ;; HIGHLIGHTS is a list of highlight specs, just like in
- ;; ;; `font-lock-keywords', with these limitations: The face is always
- ;; ;; overridden (no big disadvantage, since hits in comments etc are
- ;; ;; filtered anyway), there is no "laxmatch", and an anchored matcher
- ;; ;; is always a form which must do all the fontification directly.
- ;; ;; `limit' is a variable bound to the real limit in the context of
- ;; ;; the anchored matcher forms.
- ;; ;;
- ;; ;; This function does not do any hidden buffer changes, but the
- ;; ;; generated functions will. (They are however used in places
- ;; ;; covered by the font-lock context.)
- ;;
- ;; ;; Note: Replace `byte-compile' with `eval' to debug the generated
- ;; ;; lambda easier.
- ;; (byte-compile
- ;; `(lambda (limit)
- ;; (let (;; The font-lock package in Emacs is known to clobber
- ;; ;; `parse-sexp-lookup-properties' (when it exists).
- ;; (parse-sexp-lookup-properties
- ;; (cc-eval-when-compile
- ;; (boundp 'parse-sexp-lookup-properties))))
- ;; (while (re-search-forward ,regexp limit t)
- ;; (unless (progn
- ;; (goto-char (match-beginning 0))
- ;; (c-skip-comments-and-strings limit))
- ;; (goto-char (match-end 0))
- ;; ,@(mapcar
- ;; (lambda (highlight)
- ;; (if (integerp (car highlight))
- ;; (progn
- ;; (unless (eq (nth 2 highlight) t)
- ;; (error
- ;; "The override flag must currently be t in %s"
- ;; highlight))
- ;; (when (nth 3 highlight)
- ;; (error
- ;; "The laxmatch flag may currently not be set in %s"
- ;; highlight))
- ;; `(save-match-data
- ;; (c-put-font-lock-face
- ;; (match-beginning ,(car highlight))
- ;; (match-end ,(car highlight))
- ;; ,(elt highlight 1))))
- ;; (when (nth 3 highlight)
- ;; (error "Match highlights currently not supported in %s"
- ;; highlight))
- ;; `(progn
- ;; ,(nth 1 highlight)
- ;; (save-match-data ,(car highlight))
- ;; ,(nth 2 highlight))))
- ;; highlights))))
- ;; nil))
- ;; )
-
-
- (c-lang-defconst c-basic-matchers-before
- csharp `(
- ;;;; Font-lock the attributes by searching for the
- ;;;; appropriate regex and marking it as TODO.
- ;;,`(,(concat "\\(" csharp-attribute-regex "\\)")
- ;; 0 font-lock-function-name-face)
-
- ;; Put a warning face on the opener of unclosed strings that
- ;; can't span lines. Later font
- ;; lock packages have a `font-lock-syntactic-face-function' for
- ;; this, but it doesn't give the control we want since any
- ;; fontification done inside the function will be
- ;; unconditionally overridden.
- ,(c-make-font-lock-search-function
- ;; Match a char before the string starter to make
- ;; `c-skip-comments-and-strings' work correctly.
- (concat ".\\(" c-string-limit-regexp "\\)")
- '((if (fboundp 'c-font-lock-invalid-string)
- (c-font-lock-invalid-string)
- (csharp-mode-font-lock-invalid-string))))
-
-
- ;; Fontify keyword constants.
- ,@(when (c-lang-const c-constant-kwds)
- (let ((re (c-make-keywords-re nil
- (c-lang-const c-constant-kwds))))
- `((eval . (list ,(concat "\\<\\(" re "\\)\\>")
- 1 c-constant-face-name)))))
-
-
- ;; Fontify the namespaces that follow using statements.
- ;; This regex handles the optional alias, as well.
- ,`(,(concat
- "\\<\\(using\\)[ \t\n\f\v\r]+"
- "\\(?:"
- "\\([A-Za-z0-9_]+\\)"
- "[ \t\n\f\v\r]*="
- "[ \t\n\f\v\r]*"
- "\\)?"
- "\\(\\(?:[A-Za-z0-9_]+\\.\\)*[A-Za-z0-9_]+\\)"
- "[ \t\n\f\v\r]*;")
- (2 font-lock-constant-face t t)
- (3 font-lock-constant-face))
-
-
- ;; Fontify all keywords except the primitive types.
- ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
- 1 font-lock-keyword-face)
- ))
-
-
-
- (c-lang-defconst c-basic-matchers-after
- csharp `(
-
- ;; option 1:
- ;; ,@(when condition
- ;; `((,(byte-compile
- ;; `(lambda (limit) ...
- ;;
- ;; option 2:
- ;; ,`((lambda (limit) ...
- ;;
- ;; I don't know how to avoid the (when condition ...) in the
- ;; byte-compiled version.
- ;;
- ;; 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+
-
- ;; Case 1: invocation of constructor + maybe an object
- ;; initializer. Some possible examples that satisfy:
- ;;
- ;; new Foo ();
- ;;
- ;; new Foo () { };
- ;;
- ;; new Foo { };
- ;;
- ;; new Foo { Prop1= 7 };
- ;;
- ;; new Foo {
- ;; Prop1= 7
- ;; };
- ;;
- ;; new Foo {
- ;; Prop1= 7,
- ;; Prop2= "Fred"
- ;; };
- ;;
- ;; new Foo {
- ;; Prop1= new Bar()
- ;; };
- ;;
- ;; new Foo {
- ;; Prop1= new Bar { PropA = 5.6F }
- ;; };
- ;;
- ,@(when t
- `((,(byte-compile
- `(lambda (limit)
- (let ((parse-sexp-lookup-properties
- (cc-eval-when-compile
- (boundp 'parse-sexp-lookup-properties))))
-
- (while (re-search-forward
- ,(concat "\\<new"
- "[ \t\n\r\f\v]+"
- "\\(\\(?:"
- (c-lang-const c-symbol-key)
- "\\.\\)*"
- (c-lang-const c-symbol-key)
- "\\)"
- )
- limit t)
- (unless
- (progn
- (goto-char (match-beginning 0))
- (c-skip-comments-and-strings limit))
-
- (csharp-log 3 "ctor invoke? at %d" (match-beginning 1))
-
- (save-match-data
- ;; next thing could be: [] () <> or {} or nothing (semicolon, comma).
-
- ;; fontify the typename
- (c-put-font-lock-face (match-beginning 1)
- (match-end 1)
- 'font-lock-type-face)
-
- (goto-char (match-end 0))
- (c-forward-syntactic-ws limit)
- (if (eq (char-after) ?<) ;; ctor for generic type
- (progn
- (csharp-log 3 " - this is a generic type")
- ;; skip over <> safely
- (c-safe (c-forward-sexp 1) t)
- (c-forward-syntactic-ws)))
-
- ;; now, could be [] or (..) or {..} or semicolon.
-
- (csharp-log 3 " - looking for sexp")
-
- (if (or
- (eq (char-after) ?{) ;; open curly
- ;; is square parenthesis block? - start
- (let* ((start (point)) ;; used to hold our position, so that we know that
- (end)) ;; our code isn't stuck trying to look for a non-existant sexp.
- (and (eq (char-after) 91) ;; open square
- (while (and (eq (char-after) 91)
- (not (eq start end)))
- (c-safe (c-forward-sexp 1))
- (setq end (point)))
- (eq (char-before) 93)))
- ;; is square parenthesis block? - end
- (and (eq (char-after) 40) ;; open paren
- (c-safe (c-forward-sexp 1) t)))
-
- (progn
- ;; 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))
- (if (eq (char-after) ?{)
- (let ((start (point))
- (end (if (c-safe (c-forward-sexp 1) t)
- (point) 0)))
- (csharp-log 3 " - open curly gets c-decl-id-start %d" start)
- (c-put-char-property start
- 'c-type
- 'c-decl-id-start)
- (goto-char start)
- (if (> end start)
- (progn
- (forward-char 1) ;; step over open curly
- (c-forward-syntactic-ws)
- (while (> end (point))
- ;; now, try to fontify/assign variables to any properties inside the curlies
- (csharp-log 3 " - inside open curly point(%d)" (point))
- (csharp-log 3 " - next char: %c" (char-after))
- ;; fontify each property assignment
- (if (re-search-forward
- (concat "\\(" (c-lang-const c-symbol-key) "\\)\\s*=")
- end t)
- (progn
- (csharp-log 3 " - found variable %d-%d"
- (match-beginning 1)
- (match-end 1))
- (c-put-font-lock-face (match-beginning 1)
- (match-end 1)
- 'font-lock-variable-name-face)
- (goto-char (match-end 0))
- (c-forward-syntactic-ws)
- ;; advance to the next assignment, if possible
- (if (eq (char-after) ?@)
- (forward-char 1))
-
- (if (c-safe (c-forward-sexp 1) t)
- (progn
- (forward-char 1)
- (c-forward-syntactic-ws))))
-
- ;; else
- (csharp-log 3 " - no more assgnmts found")
- (goto-char end)))))
- )))))
-
- (goto-char (match-end 0))
- )))
- nil))
- )))
-
-
- ;; Case 2: declaration of enum with or without an explicit
- ;; base type.
- ;;
- ;; Examples:
- ;;
- ;; public enum Foo { ... }
- ;;
- ;; public enum Foo : uint { ... }
- ;;
- ,@(when t
- `((,(byte-compile
- `(lambda (limit)
- (let ((parse-sexp-lookup-properties
- (cc-eval-when-compile
- (boundp 'parse-sexp-lookup-properties))))
- (while (re-search-forward
- ,(concat csharp-enum-decl-re
- "[ \t\n\r\f\v]*"
- "{")
- limit t)
-
- (csharp-log 3 "enum? at %d" (match-beginning 0))
-
- (unless
- (progn
- (goto-char (match-beginning 0))
- (c-skip-comments-and-strings limit))
- (progn
- (save-match-data
- (goto-char (match-end 0))
- (c-put-char-property (1- (point))
- 'c-type
- 'c-decl-id-start)
- (c-forward-syntactic-ws))
- (save-match-data
- (with-no-warnings
- (condition-case nil
- (c-font-lock-declarators limit t nil)
- (wrong-number-of-arguments
- (c-font-lock-declarators limit t nil nil)))))
- (goto-char (match-end 0))
- )
- )))
- nil))
- )))
-
- ;; Case 4: using clause. Without this, using (..) gets fontified as a fn.
- ,@(when t
- `((,(byte-compile
- `(lambda (limit)
- (let ((parse-sexp-lookup-properties
- (cc-eval-when-compile
- (boundp 'parse-sexp-lookup-properties))))
- (while (re-search-forward
- ,(concat "\\<\\(using\\)"
- "[ \t\n\r\f\v]*"
- "(")
- limit t)
-
- (csharp-log 3 "using clause p(%d)" (match-beginning 0))
-
- (unless
- (progn
- (goto-char (match-beginning 0))
- (c-skip-comments-and-strings limit))
-
- (save-match-data
- (c-put-font-lock-face (match-beginning 1)
- (match-end 1)
- 'font-lock-keyword-face)
- (goto-char (match-end 0))))))
- nil))
- )))
-
- ;; Case 5: attributes
- ,`((lambda (limit)
- (let ((parse-sexp-lookup-properties
- (cc-eval-when-compile
- (boundp 'parse-sexp-lookup-properties))))
-
- (while (re-search-forward
- ,(concat "[ \t\n\r\f\v]+"
- "\\(\\["
- "[ \t\n\r\f\v]*"
- "\\(?:\\(?:return\\|assembly\\)[ \t]*:[ \t]*\\)?"
- "\\("
- "\\(?:[A-Za-z_][[:alnum:]]*\\.\\)*"
- "[A-Za-z_][[:alnum:]]*"
- "\\)"
- "[^]]*\\]\\)"
- )
- limit t)
-
- (csharp-log 3 "attribute? - %d limit(%d)" (match-beginning 1)
- limit)
-
- (unless
- (progn
- (goto-char (match-beginning 1))
- (c-skip-comments-and-strings limit))
-
- (let ((b2 (match-beginning 2))
- (e2 (match-end 2))
- (is-attr nil))
- (csharp-log 3 " - type match: %d - %d"
- b2 e2)
- (save-match-data
- (c-backward-syntactic-ws)
- (setq is-attr (or
- (eq (char-before) 59) ;; semicolon
- (eq (char-before) 93) ;; close square brace
- (eq (char-before) 123) ;; open curly
- (eq (char-before) 125) ;; close curly
- (save-excursion
- (c-beginning-of-statement-1)
- (looking-at
- "#\\s *\\(pragma\\|endregion\\|region\\|if\\|else\\|endif\\)"))
- )))
-
- (if is-attr
- (progn
- (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))
- ))
- nil))
-
- ;; Fontify labels after goto etc.
- ,@(when (c-lang-const c-before-label-kwds)
- `( ;; (Got three different interpretation levels here,
- ;; which makes it a bit complicated: 1) The backquote
- ;; stuff is expanded when compiled or loaded, 2) the
- ;; eval form is evaluated at font-lock setup (to
- ;; substitute c-label-face-name correctly), and 3) the
- ;; resulting structure is interpreted during
- ;; fontification.)
- (eval
- . ,(let* ((c-before-label-re
- (c-make-keywords-re nil
- (c-lang-const c-before-label-kwds))))
- `(list
- ,(concat "\\<\\(" c-before-label-re "\\)\\>"
- "\\s *"
- "\\(" ; identifier-offset
- (c-lang-const c-symbol-key)
- "\\)")
- (list ,(+ (regexp-opt-depth c-before-label-re) 2)
- c-label-face-name nil t))))))
-
-
-
- ;; Fontify the clauses after various keywords.
- ,@(when (or (c-lang-const c-type-list-kwds)
- (c-lang-const c-ref-list-kwds)
- (c-lang-const c-colon-type-list-kwds)
- (c-lang-const c-paren-type-kwds))
- `((,(c-make-font-lock-search-function
- (concat "\\<\\("
- (c-make-keywords-re nil
- (append (c-lang-const c-type-list-kwds)
- (c-lang-const c-ref-list-kwds)
- (c-lang-const c-colon-type-list-kwds)
- (c-lang-const c-paren-type-kwds)))
- "\\)\\>")
- '((c-fontify-types-and-refs ((c-promote-possible-types t))
- (c-forward-keyword-clause 1)
- (if (> (point) limit) (goto-char limit))))))))
-
-
- ;; Fontify the name that follows each namespace declaration
- ;; this needs to be done in the matchers-after because
- ;; otherwise the namespace names get the font-lock-type-face,
- ;; due to the energetic efforts of c-forward-type.
- ,`("\\<\\(namespace\\)[ \t\n\r\f\v]+\\(\\(?:[A-Za-z0-9_]+\\.\\)*[A-Za-z0-9_]+\\)"
- 2 font-lock-constant-face t)
-
-
- ;; Highlight function-invocation.
- ;; (this may in the future use font-lock-function-call-face, if standardized)
- ,`(,"\\.\\([A-Za-z0-9_]+\\)("
- 1 font-lock-function-name-face t)
-
-
- ))
-
- ;; verbatim string literals can be multiline
- (c-lang-defconst c-multiline-string-start-char
- csharp ?@)
-
- (defun csharp-mode-syntax-propertize-function (beg end)
- "Apply syntax table properties to special constructs in region BEG to END.
- Currently handled:
-
- - Fontify verbatim literal strings correctly
- - Highlight text after #region or #pragma as comment"
- (save-excursion
- (goto-char beg)
- (while (search-forward "@\"" end t)
- (let ((in-comment-or-string-p (save-excursion
- (goto-char (match-beginning 0))
- (or (nth 3 (syntax-ppss))
- (nth 4 (syntax-ppss))))))
- (when (not in-comment-or-string-p)
- (let (done)
- (while (and (not done) (< (point) end))
- (skip-chars-forward "^\"\\\\" end)
- (cond
- ((= (following-char) ?\\)
- (put-text-property (point) (1+ (point))
- 'syntax-table (string-to-syntax "."))
- (forward-char 1))
- ((= (following-char) ?\")
- (forward-char 1)
- (if (= (following-char) ?\")
- (progn
- (put-text-property (1- (point)) (1+ (point))
- 'syntax-table (string-to-syntax "/"))
- (forward-char 1))
- (setq done t)))))))))
-
- (goto-char beg)
- (while (re-search-forward "^\\s *#\\s *\\(region\\|pragma\\)\\s " end t)
- (when (looking-at "\\s *\\S ")
- ;; mark the whitespace separating the directive from the comment
- ;; text as comment starter to allow correct word movement
- (put-text-property (1- (point)) (point)
- 'syntax-table (string-to-syntax "< b"))))))
-
- ;; C# does generics. Setting this to t tells the parser to put
- ;; parenthesis syntax on angle braces that surround a comma-separated
- ;; list.
- (c-lang-defconst c-recognize-<>-arglists
- csharp t)
-
-
- (c-lang-defconst c-identifier-key
- csharp (concat "\\([[:alpha:]_][[:alnum:]_]*\\)" ; 1
- "\\("
- "[ \t\n\r\f\v]*"
- "\\(\\.\\)" ;;(c-lang-const c-opt-identifier-concat-key)
- "[ \t\n\r\f\v]*"
- "\\(\\([[:alpha:]_][[:alnum:]_]*\\)\\)"
- "\\)*"))
-
- ;; C# has a few rules that are slightly different than Java for
- ;; operators. This also removed the Java's "super" and replaces it
- ;; with the C#'s "base".
- (c-lang-defconst c-operators
- csharp `((prefix "base")))
-
-
- ;; 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, 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 *")
-
-
- ;; there are no message directives in C#
- (c-lang-defconst c-cpp-message-directives
- csharp nil)
-
- (c-lang-defconst c-cpp-expr-directives
- csharp '("if"))
-
- (c-lang-defconst c-opt-cpp-macro-define
- csharp "define")
-
- ;; $ is not a legal char in an identifier in C#. So we need to
- ;; create a csharp-specific definition of this constant.
- (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" "struct" "interface"))
-
- (c-lang-defconst c-block-prefix-disallowed-chars
-
- ;; Allow ':' for inherit list starters.
- csharp (cl-set-difference (c-lang-const c-block-prefix-disallowed-chars)
- '(?: ?,)))
-
-
- (c-lang-defconst c-assignment-operators
- csharp '("=" "*=" "/=" "%=" "+=" "-=" ">>=" "<<=" "&=" "^=" "|="))
-
- (c-lang-defconst c-primitive-type-kwds
- ;; ECMA-344, S8
- csharp '("object" "string" "sbyte" "short" "int" "long" "byte"
- "ushort" "uint" "ulong" "float" "double" "bool" "char"
- "decimal" "void"))
-
- ;; The keywords that define that the following is a type, such as a
- ;; class definition.
- (c-lang-defconst c-type-prefix-kwds
- ;; ECMA-344, S?
- csharp '("class" "interface" "struct")) ;; no enum here.
- ;; we want enum to be a brace list.
-
-
- ;; Type modifier keywords. They appear anywhere in types, but modify
- ;; instead of create one.
- (c-lang-defconst c-type-modifier-kwds
- ;; EMCA-344, S?
- csharp '("readonly" "const" "volatile" "new"))
-
-
- ;; Tue, 20 Apr 2010 16:02
- ;; need to verify that this works for lambdas...
- (c-lang-defconst c-special-brace-lists
- csharp '((?{ . ?}) ))
-
-
-
- ;; dinoch
- ;; Thu, 22 Apr 2010 18:54
- ;;
- ;; No idea why this isn't getting set properly in the first place.
- ;; In cc-langs.el, it is set to the union of a bunch of things, none
- ;; of which include "new", or "enum".
- ;;
- ;; But somehow both of those show up in the resulting derived regexp.
- ;; This breaks indentation of instance initializers, such as
- ;;
- ;; var x = new Foo { ... };
- ;;
- ;; Based on my inspection, the existing c-lang-defconst should work!
- ;; I don't know how to fix this c-lang-defconst, so I am re-setting this
- ;; variable here, to provide the regex explicitly.
- ;;
- (c-lang-defconst c-decl-block-key
- 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.
- (c-lang-defconst c-inexpr-brace-list-kwds
- csharp '("new"))
-
-
- ;; ;;(c-lang-defconst c-inexpr-class-kwds
- ;; ;; csharp '("new"))
-
-
-
- (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" "sealed"
- "protected" "ref" "out" "static" "virtual"
- "implicit" "explicit" "fixed"
- "override" "params" "internal" "async" "extern" "unsafe"))
-
-
- ;; Thu, 22 Apr 2010 23:02
- ;; Based on inspection of the cc-mode code, the c-protection-kwds
- ;; c-lang-const is used only for objective-c. So the value is
- ;; irrelevant for csharp.
- (c-lang-defconst c-protection-kwds
- csharp nil
- ;; csharp '("private" "protected" "public" "internal")
- )
-
- (c-lang-defconst c-opt-op-identifier-prefix
- "Regexp matching the token before the ones in
- `c-overloadable-operators' when operators are specified in their \"identifier form\".
-
- This regexp is assumed to not match any non-operator identifier."
- csharp (c-make-keywords-re t '("operator")))
-
- ;; Define the keywords that can have something following after them.
- (c-lang-defconst c-type-list-kwds
- csharp '("struct" "class" "interface" "is" "as" "operator"
- "delegate" "event" "set" "get" "add" "remove"))
-
- ;; Handle typeless variable declaration
- (c-lang-defconst c-typeless-decl-kwds
- csharp '("var"))
-
- ;; Sets up the enum to handle the list properly, and also the new
- ;; keyword to handle object initializers. This requires a modified
- ;; c-basic-matchers-after (see above) in order to correctly fontify C#
- ;; 3.0 object initializers.
- (c-lang-defconst c-brace-list-decl-kwds
- csharp '("enum" "new"))
-
-
- ;; Statement keywords followed directly by a substatement.
- ;; catch is not one of them, because catch has a paren (typically).
- (c-lang-defconst c-block-stmt-1-kwds
- csharp '("do" "else" "try" "finally"))
-
-
- ;; Statement keywords followed by a paren sexp and then by a substatement.
- (c-lang-defconst c-block-stmt-2-kwds
- csharp '("for" "if" "switch" "while" "catch" "foreach" "using"
- "fixed"
- "checked" "unchecked" "lock"))
-
-
- ;; Statements that break out of braces
- (c-lang-defconst c-simple-stmt-kwds
- csharp '("return" "continue" "break" "throw" "goto" ))
-
- ;; Statements that allow a label
- ;; TODO?
- (c-lang-defconst c-before-label-kwds
- csharp nil)
-
- ;; Constant keywords
- (c-lang-defconst c-constant-kwds
- csharp '("true" "false" "null" "value"))
-
- ;; Keywords that start "primary expressions."
- (c-lang-defconst c-primary-expr-kwds
- csharp '("this" "base" "operator"))
-
- ;; Treat namespace as an outer block so class indenting
- ;; works properly.
- (c-lang-defconst c-other-block-decl-kwds
- csharp '("namespace"))
-
- (c-lang-defconst c-other-kwds
- csharp '("sizeof" "typeof" "is" "as" "yield"
- "where" "select" "in" "from" "let" "orderby" "ascending" "descending"
- "await" "async"))
-
- (c-lang-defconst c-overloadable-operators
- ;; EMCA-344, S14.2.1
- csharp '("+" "-" "*" "/" "%" "&" "|" "^"
- "<<" ">>" "==" "!=" ">" "<" ">=" "<="))
-
-
- ;; This c-cpp-matchers stuff is used for fontification.
- ;; see cc-font.el
- ;;
-
- ;; There's no preprocessor in C#, but there are still compiler
- ;; directives to fontify: "#pragma", #region/endregion, #define, #undef,
- ;; #if/else/endif. (The definitions for the extra keywords above are
- ;; enough to incorporate them into the fontification regexps for types
- ;; and keywords, so no additional font-lock patterns are required for
- ;; keywords.)
-
- (c-lang-defconst c-cpp-matchers
- csharp (cons
- ;; Use the eval form for `font-lock-keywords' to be able to use
- ;; the `c-preprocessor-face-name' variable that maps to a
- ;; suitable face depending on the (X)Emacs version.
- '(eval . (list "^\\s *#\\s *\\(pragma\\|undef\\|define\\)\\>\\(.*\\)"
- (list 1 c-preprocessor-face-name)
- '(2 font-lock-string-face)))
- ;; There are some other things in `c-cpp-matchers' besides the
- ;; preprocessor support, so include it.
- (c-lang-const c-cpp-matchers)))
-
-
- ;; allow strings as switch-case values by leaving out string
- ;; delimiters in this definition
- (c-lang-defconst c-nonlabel-token-key
- csharp (c-make-keywords-re t
- (cl-set-difference (c-lang-const c-keywords)
- (append (c-lang-const c-label-kwds)
- (c-lang-const c-protection-kwds))
- :test 'string-equal)))
-
- (defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
- "Minimal highlighting for C# mode.")
-
- (defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
- "Fast normal highlighting for C# mode.")
-
- (defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
- "Accurate normal 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
- (setq csharp-mode-syntax-table
- (funcall (c-lang-const c-make-mode-syntax-table csharp))))
-
- (defvar csharp-mode-abbrev-table nil
- "Abbreviation table used in ‘csharp-mode’ buffers.")
- (c-define-abbrev-table 'csharp-mode-abbrev-table
- ;; Keywords that if they occur first on a line might alter the
- ;; syntactic context, and which therefore should trig reindentation
- ;; when they are completed.
- '(("else" "else" c-electric-continued-statement 0)
- ("while" "while" c-electric-continued-statement 0)
- ("catch" "catch" c-electric-continued-statement 0)
- ("finally" "finally" c-electric-continued-statement 0)))
-
- (defvar csharp-mode-map (let ((map (c-make-inherited-keymap)))
- ;; Add bindings which are only useful for C#
- map)
- "Keymap used in ‘csharp-mode’ buffers.")
-
-
- ;; TODO
- ;; Defines our constant for finding attributes.
- ;;(defconst csharp-attribute-regex "\\[\\([XmlType]+\\)(")
- ;;(defconst csharp-attribute-regex "\\[\\(.\\)")
- ;; This doesn't work because the string regex happens before this point
- ;; and getting the font-locking to work before and after is fairly difficult
- ;;(defconst csharp-attribute-regex
- ;; (concat
- ;; "\\[[a-zA-Z][ \ta-zA-Z0-9.]+"
- ;; "\\((.*\\)?"
- ;;))
-
-
- ;; ==================================================================
- ;; end of c# values for "language constants" defined in cc-langs.el
- ;; ==================================================================
-
-
- ;; ========================================================================
- ;; moving
-
- ;; alist of regexps for various structures in a csharp source file.
- (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]+"
- "\\(" ;; 3. begin name of func
- "\\(?:[A-Za-z_][[:alnum:]_]*\\.\\)*" ;; possible prefix interface
- "[[:alpha:]_][[:alnum:]_]*" ;; actual func name
- "\\(?:<\\(?:[[:alpha:]][[:alnum:]]*\\)\\(?:[, ]+[[:alpha:]][[:alnum:]]*\\)*>\\)?" ;; (with optional generic type parameter(s)
- "\\)" ;; 3. end of name of func
- "[ \t\n\r\f\v]*"
- "\\(\([^\)]*\)\\)" ;; 4. params w/parens
- "\\(?:[ \t]*/[/*].*\\)?" ;; optional comment at end of line
- "[ \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]*"
- ))
-
- `(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)
- "Retrieve a regexp from `csharp--regexp-alist' corresponding to SYMBOL."
- (let ((elt (assoc symbol csharp--regexp-alist)))
- (if elt (cadr elt) nil)))
-
-
- (defun csharp-move-back-to-beginning-of-block ()
- "Move 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)
- "Move back to the open-curly that begin *something*.
- *something* is defined by 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 nil)
- (or (not must-not-match)
- (not (looking-back must-not-match nil))))
- need-to-backup t)
- (setq done t)))
- found))
-
-
-
- (defun csharp-move-back-to-beginning-of-defun ()
- "Move back to the open-curly that start 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) nil)
- (not (looking-back (csharp--regexp 'namespace-start) nil)))
- (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-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) nil))
- (not (looking-back (csharp--regexp 'namespace-start) nil))
- (looking-back (csharp--regexp 'func-start) nil)))
-
-
- (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) nil))
- (looking-back (csharp--regexp 'class-start) nil)))
-
-
- (defun csharp-move-fwd-to-end-of-defun ()
- "Move forward to the close-curly that ends 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 "}" nil)
- (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 ()
- "Move back to the open-curly that begin 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) nil)
- (not (looking-back (csharp--regexp 'namespace-start) nil)))
- (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 ()
- "Move forward to the close-curly that ends 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 ()
- "Move back to the open-curly that begins 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) nil))
- (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
-
- (defconst csharp--imenu-expression
- (let* ((single-space "[ \t\n\r\f\v]")
- (optional-space (concat single-space "*"))
- (bol "^[ \t]*") ;; BOL shouldnt accept lineshift.
- (space (concat single-space "+"))
- (access-modifier (regexp-opt '( "public" "private" "protected" "internal"
- "static" "sealed" "partial" "override" "virtual"
- "abstract" "async" "new" "unsafe")))
- ;; this will allow syntactically invalid combinations of modifiers
- ;; but that's a compiler problem, not a imenu-problem
- (access-modifier-list (concat "\\(?:" access-modifier space "\\)"))
- (access-modifiers (concat access-modifier-list "*"))
- (basic-type (concat
- ;; typename
- "\\(?:[A-Za-z_][[:alnum:]_]*\\.\\)*"
- "[A-Za-z_][[:alnum:]_]*"
- ))
- (type (concat
- basic-type
- ;; simplified, optional generic constraint.
- ;; handles generic sub-types.
- "\\(?:<[[:alnum:],<> \t\n\f\v\r]+>\\)?"))
- (return-type (concat
- type
- ;; optional array-specifier
- "\\(?:\\[\\]\\)?"))
- (interface-prefix (concat "\\(?:" type "\\.\\)"))
- ;; param-list with parens
- (parameter-list "\\(?:\([^!\)]*\)\\)")
- (inheritance-clause (concat "\\(?:"
- optional-space
- ":"
- optional-space type
- "\\(?:" optional-space "," optional-space type "\\)*"
- "\\)?")))
-
- (list (list "namespace"
- (concat bol "namespace" space
- "\\(" basic-type "\\)") 1)
- ;; not all these are classes, but they can hold other
- ;; members, so they are treated uniformly.
- (list "class"
- (concat bol
- access-modifiers
- "\\("
- (regexp-opt '("class" "struct" "interface")) space
- type inheritance-clause "\\)") 1)
- (list "enum"
- (concat bol
- access-modifiers
- "\\(" "enum" space
- basic-type "\\)") 1)
- (list "ctor"
- (concat bol
- ;; ctor MUST have access modifiers, or else we pick
- ;; every if statement in the file...
- access-modifier-list "+"
- "\\("
- basic-type
- optional-space
- parameter-list
- "\\)"
- "\\(?:"
- optional-space
- ":"
- optional-space
- "\\(?:this\\|base\\)"
- optional-space
- parameter-list
- "\\)?"
- optional-space "{") 1)
- (list "method"
- (concat bol
- ;; we MUST require modifiers, or else we cannot reliably
- ;; identify declarations, without also dragging in lots of
- ;; if statements and what not.
- access-modifier-list "+"
- return-type space
- "\\("
- type
- optional-space
- parameter-list
- "\\)"
- ;; optional // or /* comment at end
- "\\(?:[ \t]*/[/*].*\\)?"
- optional-space
- "{") 1)
- (list "method-inf"
- (concat bol
- return-type space
- "\\("
- interface-prefix
- type
- optional-space
- parameter-list
- "\\)"
- ;; optional // or /* comment at end
- "\\(?:[ \t]*/[/*].*\\)?"
- optional-space
- "{") 1)
- (list "method-abs-ext"
- (concat bol
- access-modifier-list "+"
- (regexp-opt '("extern" "abstract")) space
- return-type space
- "\\("
- type
- optional-space
- parameter-list
- "\\)"
- optional-space
- ;; abstract/extern methods are terminated with ;
- ";") 1)
- ;; delegates are almost like abstract methods, so pick them up here
- (list "delegate"
- (concat bol
- access-modifiers
- "delegate" space
- return-type space
- "\\("
- type
- "\\)"
- optional-space
- parameter-list
- ;; optional // or /* comment at end
- optional-space
- ";") 1)
- (list "prop"
- (concat bol
- ;; must require access modifiers, or else we
- ;; pick up pretty much anything.
- access-modifiers
- return-type space
- "\\("
- type
- "\\)"
- optional-space "{" optional-space
- ;; unless we are super-specific and expect the accesors,
- ;; lots of weird things gets slurped into the name.
- ;; including the accessors themselves.
- (regexp-opt '("get" "set"))
- ) 1)
- (list "prop-inf"
- (concat bol
- return-type space
- "\\("
- interface-prefix
- type
- "\\)"
- optional-space "{" optional-space
- ;; unless we are super-specific and expect the accesors,
- ;; lots of weird things gets slurped into the name.
- ;; including the accessors themselves.
- (regexp-opt '("get" "set"))
- ) 1)
- ;; adding fields... too much?
- (list "field"
- (concat bol
- access-modifier-list "+"
- ;; fields can be readonly/const/volatile
- "\\(?:" (regexp-opt '("readonly" "const" "volatile")) space "\\)?"
- return-type space
- "\\("
- type
- "\\)"
- optional-space
- ;; optional assignment
- "\\(?:=[^;]+\\)?"
- ";") 1)
- (list "indexer"
- (concat bol
- access-modifiers
- return-type space
- "this" optional-space
- "\\("
- ;; opening bracket
- "\\[" optional-space
- ;; type
- "\\([^\]]+\\)" optional-space
- type
- ;; closing brackets
- "\\]"
- "\\)"
- optional-space "{" optional-space
- ;; unless we are super-specific and expect the accesors,
- ;; lots of weird things gets slurped into the name.
- ;; including the accessors themselves.
- (regexp-opt '("get" "set"))) 1)
- (list "event"
- (concat bol
- access-modifier-list "+"
- optional-space "event" optional-space
- "\\("
- return-type space
- type
- "\\)"
- optional-space
- ";") 1))))
-
- (defun csharp--imenu-get-pos (pair)
- "Return `position' from a (title . position) cons-pair `PAIR'.
-
- The position may be a integer, or a marker (as returned by
- imenu-indexing). This function ensures what is returned is an
- integer which can be used for easy comparison."
- (let ((pos (cdr pair)))
- (if (markerp pos)
- (marker-position pos)
- pos)))
-
- (defun csharp--imenu-get-container (item containers previous)
- "Return the container which `ITEM' belongs to.
-
- `ITEM' is a (title . position) cons-pair. `CONTAINERS' is a
- list of such. `PREVIOUS' is the name of the previous
- container found when recursing through `CONTAINERS'.
-
- The final result is based on item's position relative to those
- found in `CONTAINERS', or nil if none is found."
- (if (not containers)
- previous
- (let* ((item-pos (csharp--imenu-get-pos item))
- (container (car containers))
- (container-pos (csharp--imenu-get-pos container))
- (rest (cdr containers)))
- (if (and container-pos
- (< item-pos container-pos))
- previous
- (csharp--imenu-get-container item rest container)))))
-
- (defun csharp--imenu-get-container-name (item containers)
- "Return the name of the container which `ITEM' belongs to.
-
- `ITEM' is a (title . position) cons-pair.
- `CONTAINERS' is a list of such.
-
- The name is based on the results from
- `csharp--imenu-get-container'."
- (let ((container (csharp--imenu-get-container item containers nil)))
- (if (not container)
- nil
- (let ((container-p1 (car (split-string (car container)))) ;; namespace
- (container-p2 (cadr (split-string (car container))))) ;; class/interface
- ;; use p1 (namespace) when there is no p2
- (if container-p2
- container-p2
- container-p1)))))
-
- (defun csharp--imenu-sort (items)
- "Sort an imenu-index list `ITEMS' by the string-portion."
- (sort items (lambda (item1 item2)
- (string< (car item1) (car item2)))))
-
- (defun csharp--imenu-get-class-name (class namespaces)
- "Gets a name for a imenu-index `CLASS'.
-
- Result is based on its own name and `NAMESPACES' found in the same file."
- (let ((namespace (csharp--imenu-get-container-name class namespaces))
- (class-name (car class)))
- (if (not namespace)
- class-name
- ;; reformat to include namespace
- (let* ((words (split-string class-name))
- (type (car words))
- (name (cadr words)))
- (concat type " " namespace "." name)))))
-
- (defun csharp--imenu-get-class-nodes (classes namespaces)
- "Create a new alist with CLASSES as root nodes with NAMESPACES added.
-
- Each class will have one imenu index-entry \"( top)\" added by
- default."
-
- (mapcar (lambda (class)
- (let ((class-name (csharp--imenu-get-class-name class namespaces))
- (class-pos (cdr class)))
- ;; construct a new alist-entry where value is itself
- ;; a list of alist-entries with -1- entry which the top
- ;; of the class itself.
- (cons class-name
- (list
- (cons "( top )" class-pos)))))
- classes))
-
- (defun csharp--imenu-get-class-node (result item classes namespaces)
- "Get the class-node in `RESULT' which an `ITEM' should be inserted into.
-
- For this calculation, the original index items `CLASSES' and `NAMESPACES'
- is needed."
- (let* ((class-item (csharp--imenu-get-container item classes nil))
- (class-name (csharp--imenu-get-class-name class-item namespaces)))
- (assoc class-name result)))
-
- (defun csharp--imenu-format-item-node (item type)
- "Format an ITEM with a specified TYPE as an imenu item to be inserted into the index."
- (cons
- (concat "(" type ") " (car item))
- (cdr item)))
-
- (defun csharp--imenu-append-items-to-menu (result key name index classes namespaces)
- "Formats the imenu-index using the provided values.
-
- This is done by modifying the contents of `RESULT' in place."
- ;; items = all methods, all events, etc based on "type"
- (let* ((items (cdr (assoc key index))))
- (dolist (item items)
- (let ((class-node (csharp--imenu-get-class-node result item classes namespaces))
- (item-node (csharp--imenu-format-item-node item name)))
- (nconc class-node (list item-node))))))
-
- (defun csharp--imenu-transform-index (index)
- "Transform an imenu INDEX based on `IMENU-GENERIC-EXPRESSION'.
-
- The resulting structure should be based on full type-names, with
- type-members nested hierarchially below its parent.
-
- See `csharp-mode-tests.el' for examples of expected behaviour
- of such transformations."
- (let* ((result nil)
- (namespaces (cdr (assoc "namespace" index)))
- (classes (cdr (assoc "class" index)))
- (class-nodes (csharp--imenu-get-class-nodes classes namespaces)))
- ;; be explicit about collection variable
- (setq result class-nodes)
- (dolist (type '(("ctor")
- ("method")
- ("method-inf" "method")
- ("method-abs-ext" "method")
- ("prop")
- ("prop-inf" "prop")
- ("field")
- ("event")
- ("indexer")))
- (let* ((key (car type))
- (name (car (last type))))
- (csharp--imenu-append-items-to-menu result key name index classes namespaces)))
-
- ;; add enums and delegates to main result list, as own items.
- ;; We don't support nested types. EOS.
- ;;
- ;; This has the issue that they get reported as "function" in
- ;; `helm-imenu', but there's nothing we can do about that.
- ;; The alternative is making it a menu with -1- submenu which
- ;; says "( top )" but that will be very clicky...
-
- ;; before adding delegates, we need to pad the entry so that it
- ;; matches the "<type> <name>" signature used by all the other
- ;; imenu entries
- (let ((delegates (cdr (assoc "delegate" index))))
- (dolist (delegate delegates)
- (setf (car delegate) (concat "delegate " (car delegate)))))
-
- (dolist (type '("enum" "delegate"))
- (dolist (item (cdr (assoc type index)))
- (let ((item-name (csharp--imenu-get-class-name item namespaces)))
- (setq result (cons (cons item-name (cdr item))
- result)))))
-
- ;; sort individual sub-lists
- (dolist (item result)
- (when (listp (cdr item))
- (setf (cdr item) (csharp--imenu-sort (cdr item)))))
-
- ;; sort main list
- ;; (Enums always sort last though, because they dont have
- ;; sub-menus)
- (csharp--imenu-sort result)))
-
- (defun csharp--imenu-create-index-function ()
- "Create an imenu index."
- (csharp--imenu-transform-index
- (imenu--generic-function csharp--imenu-expression)))
-
- (defun csharp--setup-imenu ()
- "Set up `imenu' for `csharp-mode'."
-
- ;; 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.
- ;;
- ;; We use both.
- ;;
- ;; First we use the `imenu-generic-expression' to build a index for
- ;; us, but we do so inside a `imenu-create-index-function'
- ;; implementation which allows us to tweak the results slightly
- ;; before returning it to Emacs.
- (setq imenu-create-index-function #'csharp--imenu-create-index-function)
- (imenu-add-menubar-index))
-
-
-
-
-
- ;; ==================================================================
- ;; C# code-doc insertion magic
- ;; ==================================================================
- ;;
- ;; In Visual Studio, if you type three slashes, it immediately expands into
- ;; an inline code-documentation fragment. The following method does the
- ;; same thing.
- ;;
- ;; This is the kind of thing that could be handled by YASnippet or
- ;; another similarly flexible snippet framework. But I don't want to
- ;; introduce a dependency on yasnippet to csharp-mode. So the capability
- ;; must live within csharp-mode itself.
-
- (defun csharp-maybe-insert-codedoc (arg)
- "Insert an xml code documentation template on third consecutive slash.
- This fn gets bound to / (the slash key), in
- ‘csharp-mode’. If the slash being inserted is not the third
- consecutive slash, the slash is inserted as normal. If it is the
- third consecutive slash, then a xml code documentation template
- may be inserted in some cases. For example,
-
- a <summary> template is inserted if the prior line is empty,
- or contains only an open curly brace;
- a <remarks> template is inserted if the prior word
- closes the <summary> element;
- a <returns> template is inserted if the prior word
- closes the <remarks> element;
- an <example> template is inserted if the prior word closes
- the <returns> element;
- a <para> template is inserted if the prior word closes
- a <para> element.
-
- In all other cases the slash is inserted as normal.
-
- The prefix argument ARG is passed on to `self-insert-command'
- when the code documentation template isn't triggered. This makes
- sure that M-10 / still produces 10 consecutive slashes as expected.
-
- If you want the default cc-mode behavior, which implies no automatic
- insertion of xml code documentation templates, then use this in
- your `csharp-mode-hook' function:
-
- (local-set-key (kbd \"/\") 'c-electric-slash)"
- (interactive "*p")
- ;;(message "csharp-maybe-insert-codedoc")
- (let (
- (cur-point (point))
- (char last-command-event)
- (cb0 (char-before (- (point) 0)))
- (cb1 (char-before (- (point) 1)))
- is-first-non-whitespace
- did-auto-insert
- )
-
- ;; check if two prior chars were slash, in other words,
- ;; check if this is the third slash in a row.
- (if (and (= char ?/) cb0 (= ?/ cb0) cb1 (= ?/ cb1))
-
- (progn
- ;;(message "yes - this is the third consecutive slash")
- (setq is-first-non-whitespace
- (save-excursion
- (back-to-indentation)
- (= cur-point (+ (point) 2))))
-
- (if is-first-non-whitespace
- ;; This is a 3-slash sequence. It is the first non-whitespace text
- ;; on the line. Now we need to examine the surrounding context
- ;; in order to determine which xml cod doc template to insert.
- (let (word-back char0 char1
- word-fore char-0 char-1
- text-to-insert ;; text to insert in lieu of slash
- fn-to-call ;; func to call after inserting text
- (preceding-line-is-empty (or
- (= (line-number-at-pos) 1)
- (save-excursion
- (forward-line -1)
- (beginning-of-line)
- (looking-at "[ \t]*$\\|[ \t]*{[ \t]*$"))))
- (flavor 0) ;; used only for diagnostic purposes
- )
-
- ;;(message "starting a 3-slash comment")
- ;; get the prior word, and the 2 chars preceding it.
- (backward-word)
-
- (setq word-back (thing-at-point 'word)
- char0 (char-before (- (point) 0))
- char1 (char-before (- (point) 1)))
-
- ;; restore prior position
- (goto-char cur-point)
-
- ;; get the following word, and the 2 chars preceding it.
- (forward-word)
- (backward-word)
- (setq word-fore (thing-at-point 'word)
- char-0 (char-before (- (point) 0))
- char-1 (char-before (- (point) 1)))
-
- ;; restore prior position again
- (goto-char cur-point)
-
- (cond
- ;; The preceding line is empty, or all whitespace, or
- ;; contains only an open-curly. In this case, insert a
- ;; summary element pair.
- (preceding-line-is-empty
- (setq text-to-insert "/ <summary>\n /// \n /// </summary>"
- flavor 1) )
-
- ;; The preceding word closed a summary element. In this case,
- ;; if the forward word does not open a remarks element, then
- ;; insert a remarks element.
- ((and (string-equal word-back "summary") (eq char0 ?/) (eq char1 ?<))
- (if (not (and (string-equal word-fore "remarks") (eq char-0 ?<)))
- (setq text-to-insert "/ <remarks>\n /// <para>\n /// \n /// </para>\n /// </remarks>"
- flavor 2)))
-
- ;; The preceding word closed the remarks section. In this case,
- ;; insert an example element.
- ((and (string-equal word-back "remarks") (eq char0 ?/) (eq char1 ?<))
- (setq text-to-insert "/ <example>\n /// \n /// </example>"
- flavor 3))
-
- ;; The preceding word closed the example section. In this
- ;; case, insert an returns element. This isn't always
- ;; correct, because sometimes the xml code doc is attached to
- ;; a class or a property, neither of which has a return
- ;; value. A more intelligent implementation would inspect the
- ;; syntax state and only inject a returns element if
- ;; appropriate.
- ((and (string-equal word-back "example") (eq char0 ?/) (eq char1 ?<))
- (setq text-to-insert "/ <returns></returns>"
- fn-to-call (lambda ()
- (backward-word)
- (backward-char)
- (backward-char)
- (c-indent-line-or-region)
- )
- flavor 4))
-
- ;; The preceding word opened the remarks section, or it
- ;; closed a para section. In this case, insert a para
- ;; element, using appropriate indentation with respect to the
- ;; prior tag.
- ((or
- (and (string-equal word-back "remarks") (eq char0 ?<) (or (eq char1 32) (eq char1 9)))
- (and (string-equal word-back "para") (eq char0 ?/) (eq char1 ?<)))
-
- (let (prior-point spacer)
- (save-excursion
- (backward-word)
- (backward-char)
- (backward-char)
- (setq prior-point (point))
- (skip-chars-backward "\t ")
- (setq spacer (buffer-substring (point) prior-point))
- ;;(message (format "pt(%d) prior(%d) spacer(%s)" (point) prior-point spacer))
- )
-
- (if (string-equal word-back "remarks")
- (setq spacer (concat spacer " ")))
-
- (setq text-to-insert (format "/%s<para>\n ///%s \n ///%s</para>"
- spacer spacer spacer)
- flavor 6)))
-
- ;; The preceding word opened a para element. In this case, if
- ;; the forward word does not close the para element, then
- ;; close the para element.
- ;; --
- ;; This is a nice idea but flawed. Suppose I have a para element with some
- ;; text in it. If I position the cursor at the first line, then type 3 slashes,
- ;; I get a close-element, and that would be inappropriate. Not sure I can
- ;; easily solve that problem, so the best thing might be to simply punt, and
- ;; require people to close their own elements.
- ;;
- ;; ( (and (string-equal word-back "para") (eq char0 60) (or (eq char1 32) (eq char1 9)))
- ;; (if (not (and (string-equal word-fore "para") (eq char-0 47) (eq char-1 60) ))
- ;; (setq text-to-insert "/ \n/// </para>\n///"
- ;; fn-to-call (lambda ()
- ;; (previous-line)
- ;; (end-of-line)
- ;; )
- ;; flavor 7) )
- ;; )
-
- ;; the default case - do nothing
- (t nil))
-
- (if text-to-insert
- (progn
- ;;(message (format "inserting special text (f(%d))" flavor))
-
- ;; set the flag, that we actually inserted text
- (setq did-auto-insert t)
-
- ;; save point of beginning of insertion
- (setq cur-point (point))
-
- ;; actually insert the text
- (insert text-to-insert)
-
- ;; indent the inserted string, and re-position point, either through
- ;; the case-specific fn, or via the default progn.
- (if fn-to-call
- (funcall fn-to-call)
-
- (let ((newline-count 0) (pos 0) ix)
-
- ;; count the number of newlines in the inserted string
- (while (string-match "\n" text-to-insert pos)
- (setq pos (match-end 0)
- newline-count (+ newline-count 1) )
- )
-
- ;; indent what we just inserted
- (c-indent-region cur-point (point) t)
-
- ;; move up n/2 lines. This assumes that the
- ;; inserted text is ~symmetric about the halfway point.
- ;; The assumption holds if the xml code doc uses a
- ;; begin-elt and end-elt on a new line all by themselves,
- ;; and a blank line in between them where the point should be.
- ;; A more intelligent implementation would use a specific
- ;; marker string, like @@DOT, to note the desired point.
- (forward-line (- 0 (/ newline-count 2)))
- (end-of-line)))))))))
-
- (if (not did-auto-insert)
- (self-insert-command (prefix-numeric-value arg)))))
-
- ;; ==================================================================
- ;; end of c# code-doc insertion magic
- ;; ==================================================================
-
- (defun csharp-time ()
- "Return the time of day as a string. Used in the `csharp-log' function."
- (substring (current-time-string) 11 19)) ;24-hr time
-
-
- (defun csharp-log (level text &rest args)
- "Log a message at level LEVEL.
- If LEVEL is higher than `csharp-log-level', the message is
- ignored. Otherwise, it is printed using `message'.
- TEXT is a format control string, and the remaining arguments ARGS
- are the string substitutions (see `format')."
- (if (<= level csharp-log-level)
- (let* ((msg (apply 'format text args)))
- (message "C# %s %s" (csharp-time) msg))))
-
- ;; ==================================================================
- ;; C#-specific optimizations of cc-mode funcs
- ;; ==================================================================
-
- ;; There's never a need to move over an Obj-C directive in csharp-mode.
- (defadvice c-forward-objc-directive (around
- csharp-mode-advice-2
- compile activate)
- "Make `c-forward-objc-directive' a no-op in `csharp-mode'."
- (if (c-major-mode-is 'csharp-mode)
- nil
- ad-do-it)
- )
-
- ;; ==================================================================
- ;; end of C#-specific optimizations of cc-mode funcs
- ;; ==================================================================
-
-
-
-
-
-
-
-
- ;; ==================================================================
- ;; c# - monkey-patching of basic parsing logic
- ;; ==================================================================
- ;;
- ;; The following 2 defuns redefine functions from cc-mode, to add
- ;; special cases for C#. These primarily deal with indentation of
- ;; instance initializers, which are somewhat unique to C#. I couldn't
- ;; figure out how to get cc-mode to do what C# needs, without modifying
- ;; these defuns.
- ;;
-
- ;; verabatim copy of c-font-lock-invalid-string before it was removed
- ;; from emacs/cc-mode in Git commit bb591f139f0602af292c772f974dcc14dabb1deb.
-
- (defun csharp-mode-font-lock-invalid-string ()
- ;; Assuming the point is after the opening character of a string,
- ;; fontify that char with `font-lock-warning-face' if the string
- ;; decidedly isn't terminated properly.
- ;;
- ;; This function does hidden buffer changes.
- (let ((start (1- (point))))
- (save-excursion
- (and (eq (elt (parse-partial-sexp start (c-point 'eol)) 8) start)
- (if (if (eval-when-compile (integerp ?c))
- ;; Emacs
- (integerp c-multiline-string-start-char)
- ;; XEmacs
- (characterp c-multiline-string-start-char))
- ;; There's no multiline string start char before the
- ;; string, so newlines aren't allowed.
- (not (eq (char-before start) c-multiline-string-start-char))
- ;; Multiline strings are allowed anywhere if
- ;; c-multiline-string-start-char is t.
- (not c-multiline-string-start-char))
- (if c-string-escaped-newlines
- ;; There's no \ before the newline.
- (not (eq (char-before (point)) ?\\))
- ;; Escaped newlines aren't supported.
- t)
- (c-put-font-lock-face start (1+ start) 'font-lock-warning-face)))))
-
- (advice-add 'c-looking-at-inexpr-block
- :around 'csharp--c-looking-at-inexpr-block-hack)
-
- (defun csharp--c-looking-at-inexpr-block-hack (orig-fun &rest args)
- (apply
- (if (eq major-mode 'csharp-mode)
- #'csharp--c-looking-at-inexpr-block
- orig-fun)
- args))
-
- (defun csharp--c-looking-at-inexpr-block (lim containing-sexp &optional check-at-end)
- ;; Return non-nil if we're looking at the beginning of a block
- ;; inside an expression. The value returned is actually a cons of
- ;; either 'inlambda, 'inexpr-statement or 'inexpr-class and the
- ;; position of the beginning of the construct.
- ;;
- ;; LIM limits the backward search. CONTAINING-SEXP is the start
- ;; position of the closest containing list. If it's nil, the
- ;; containing paren isn't used to decide whether we're inside an
- ;; expression or not. If both LIM and CONTAINING-SEXP are used, LIM
- ;; needs to be farther back.
- ;;
- ;; If CHECK-AT-END is non-nil then extra checks at the end of the
- ;; brace block might be done. It should only be used when the
- ;; construct can be assumed to be complete, i.e. when the original
- ;; starting position was further down than that.
- ;;
- ;; This function might do hidden buffer changes.
-
- (save-excursion
- (let ((res 'maybe) passed-paren
- (closest-lim (or containing-sexp lim (point-min)))
- ;; Look at the character after point only as a last resort
- ;; when we can't disambiguate.
- (block-follows (and (eq (char-after) ?{) (point))))
-
- (while (and (eq res 'maybe)
- (progn (c-backward-syntactic-ws)
- (> (point) closest-lim))
- (not (bobp))
- (progn (backward-char)
- (looking-at "[\]\).]\\|\w\\|\\s_"))
- (c-safe (forward-char)
- (goto-char (scan-sexps (point) -1))))
-
- (setq res
- (if (looking-at c-keywords-regexp)
- (let ((kw-sym (c-keyword-sym (match-string 1))))
- (cond
- ((and block-follows
- (c-keyword-member kw-sym 'c-inexpr-class-kwds))
- (and (not (eq passed-paren ?\[))
-
- ;; dinoch Thu, 22 Apr 2010 18:20
- ;; ============================================
- ;; looking at new MyType() { ... }
- ;; means this is a brace list, so, return nil,
- ;; implying NOT looking-at-inexpr-block
- (not
- (and (c-major-mode-is 'csharp-mode)
- (looking-at "new[ \t\n\f\v\r]+\\([[:alnum:]_]+\\)\\b")))
-
- (or (not (looking-at c-class-key))
- ;; If the class instantiation is at the start of
- ;; a statement, we don't consider it an
- ;; in-expression class.
- (let ((prev (point)))
- (while (and
- (= (c-backward-token-2 1 nil closest-lim) 0)
- (eq (char-syntax (char-after)) ?w))
- (setq prev (point)))
- (goto-char prev)
- (not (c-at-statement-start-p)))
- ;; Also, in Pike we treat it as an
- ;; in-expression class if it's used in an
- ;; object clone expression.
- (save-excursion
- (and check-at-end
- (c-major-mode-is 'pike-mode)
- (progn (goto-char block-follows)
- (zerop (c-forward-token-2 1 t)))
- (eq (char-after) ?\())))
- (cons 'inexpr-class (point))))
- ((c-keyword-member kw-sym 'c-inexpr-block-kwds)
- (when (not passed-paren)
- (cons 'inexpr-statement (point))))
- ((c-keyword-member kw-sym 'c-lambda-kwds)
- (when (or (not passed-paren)
- (eq passed-paren ?\())
- (cons 'inlambda (point))))
- ((c-keyword-member kw-sym 'c-block-stmt-kwds)
- nil)
- (t
- 'maybe)))
-
- (if (looking-at "\\s(")
- (if passed-paren
- (if (and (eq passed-paren ?\[)
- (eq (char-after) ?\[))
- ;; Accept several square bracket sexps for
- ;; Java array initializations.
- 'maybe)
- (setq passed-paren (char-after))
- 'maybe)
- 'maybe))))
-
- (if (eq res 'maybe)
- (when (and c-recognize-paren-inexpr-blocks
- block-follows
- containing-sexp
- (eq (char-after containing-sexp) ?\())
- (goto-char containing-sexp)
- (if (or (save-excursion
- (c-backward-syntactic-ws lim)
- (and (> (point) (or lim (point-min)))
- (c-on-identifier)))
- (and c-special-brace-lists
- (c-looking-at-special-brace-list)))
- nil
- (cons 'inexpr-statement (point))))
-
- res))))
-
- (advice-add 'c-inside-bracelist-p
- :around 'csharp-inside-bracelist-or-c-inside-bracelist-p)
-
- (defun csharp-inside-bracelist-or-c-inside-bracelist-p (command &rest args)
- "Run `csharp-inside-bracelist-p' if in `csharp-mode'.
-
- Otherwise run `c-inside-bracelist-p'."
- (if (eq major-mode 'csharp-mode)
- (csharp-inside-bracelist-p (nth 0 args) (nth 1 args))
- (apply command args)))
-
- (defun csharp-inside-bracelist-p (containing-sexp paren-state)
- ;; return the buffer position of the beginning of the brace list
- ;; statement if we're inside a brace list, otherwise return nil.
- ;; CONTAINING-SEXP is the buffer pos of the innermost containing
- ;; paren. PAREN-STATE is the remainder of the state of enclosing
- ;; braces
- ;;
- ;; N.B.: This algorithm can potentially get confused by cpp macros
- ;; placed in inconvenient locations. It's a trade-off we make for
- ;; speed.
- ;;
- ;; This function might do hidden buffer changes.
- (or
- ;; This will pick up brace list declarations.
- (c-safe
- (save-excursion
- (goto-char containing-sexp)
- (c-safe (c-forward-sexp -1))
- (let (bracepos)
- (if (and (or (looking-at c-brace-list-key)
-
- (progn
- (c-safe (c-forward-sexp -1))
- (looking-at c-brace-list-key))
-
- (and (c-major-mode-is 'csharp-mode)
- (or
- ;; dinoch Thu, 22 Apr 2010 18:20
- ;; ============================================
- ;; looking enum Foo : int
- ;; means this is a brace list, so, return nil,
- ;; implying NOT looking-at-inexpr-block
- (progn
- (c-safe (c-forward-sexp -1))
- (looking-at csharp-enum-decl-re))
-
- ;; type-initializers are not properly detected and
- ;; indented unless we help out. (no need to forward
- ;; when looking here, because enum-check already did
- ;; it!)
- (looking-at csharp-type-initializer-statement-re))))
-
- (setq bracepos (c-down-list-forward (point)))
- (or
- (not (c-crosses-statement-barrier-p (point)
- (- bracepos 2)))
- ;; this little hack (combined with the regexp-check above)
- ;; fixes indentation for all type-initializers.
- (c-major-mode-is 'csharp-mode)))
- (point)))))
-
- ;; this will pick up array/aggregate init lists, even if they are nested.
- (save-excursion
- (let ((class-key
- ;; Pike can have class definitions anywhere, so we must
- ;; check for the class key here.
- (and (c-major-mode-is 'pike-mode)
- c-decl-block-key))
- bufpos braceassignp lim next-containing)
- (while (and (not bufpos)
- containing-sexp)
- (when paren-state
- (if (consp (car paren-state))
- (setq lim (cdr (car paren-state))
- paren-state (cdr paren-state))
- (setq lim (car paren-state)))
- (when paren-state
- (setq next-containing (car paren-state)
- paren-state (cdr paren-state))))
- (goto-char containing-sexp)
- (if (c-looking-at-inexpr-block next-containing next-containing)
- ;; We're in an in-expression block of some kind. Do not
- ;; check nesting. We deliberately set the limit to the
- ;; containing sexp, so that c-looking-at-inexpr-block
- ;; doesn't check for an identifier before it.
- (setq containing-sexp nil)
- ;; see if the open brace is preceded by = or [...] in
- ;; this statement, but watch out for operator=
- (setq braceassignp 'dontknow)
- (c-backward-token-2 1 t lim)
- ;; Checks to do only on the first sexp before the brace.
- (when (and c-opt-inexpr-brace-list-key
- (eq (char-after) ?\[))
- ;; In Java, an initialization brace list may follow
- ;; directly after "new Foo[]", so check for a "new"
- ;; earlier.
- (while (eq braceassignp 'dontknow)
- (setq braceassignp
- (cond ((/= (c-backward-token-2 1 t lim) 0) nil)
- ((looking-at c-opt-inexpr-brace-list-key) t)
- ((looking-at "\\sw\\|\\s_\\|[.[]")
- ;; Carry on looking if this is an
- ;; identifier (may contain "." in Java)
- ;; or another "[]" sexp.
- 'dontknow)
- (t nil)))))
- ;; Checks to do on all sexps before the brace, up to the
- ;; beginning of the statement.
- (while (eq braceassignp 'dontknow)
- (cond ((eq (char-after) ?\;)
- (setq braceassignp nil))
- ((and class-key
- (looking-at class-key))
- (setq braceassignp nil))
- ((eq (char-after) ?=)
- ;; We've seen a =, but must check earlier tokens so
- ;; that it isn't something that should be ignored.
- (setq braceassignp 'maybe)
- (while (and (eq braceassignp 'maybe)
- (zerop (c-backward-token-2 1 t lim)))
- (setq braceassignp
- (cond
- ;; Check for operator =
- ((and c-opt-op-identifier-prefix
- (looking-at c-opt-op-identifier-prefix))
- nil)
- ;; Check for `<opchar>= in Pike.
- ((and (c-major-mode-is 'pike-mode)
- (or (eq (char-after) ?`)
- ;; Special case for Pikes
- ;; `[]=, since '[' is not in
- ;; the punctuation class.
- (and (eq (char-after) ?\[)
- (eq (char-before) ?`))))
- nil)
- ((looking-at "\\s.") 'maybe)
- ;; make sure we're not in a C++ template
- ;; argument assignment
- ((and
- (c-major-mode-is 'c++-mode)
- (save-excursion
- (let ((here (point))
- (pos< (progn
- (skip-chars-backward "^<>")
- (point))))
- (and (eq (char-before) ?<)
- (not (c-crosses-statement-barrier-p
- pos< here))
- (not (c-in-literal))
- ))))
- nil)
- (t t))))))
- (if (and (eq braceassignp 'dontknow)
- (/= (c-backward-token-2 1 t lim) 0))
- (setq braceassignp nil)))
- (if (not braceassignp)
- (if (eq (char-after) ?\;)
- ;; Brace lists can't contain a semicolon, so we're done.
- (setq containing-sexp nil)
- ;; Go up one level.
- (setq containing-sexp next-containing
- lim nil
- next-containing nil))
- ;; we've hit the beginning of the aggregate list
- (c-beginning-of-statement-1
- (c-most-enclosing-brace paren-state))
- (setq bufpos (point))))
- )
- bufpos))
- ))
-
- ;; ==================================================================
- ;; end of monkey-patching of basic parsing logic
- ;; ==================================================================
-
-
-
-
- ;;(easy-menu-define csharp-menu csharp-mode-map "C# Mode Commands"
- ;; ;; Can use `csharp' as the language for `c-mode-menu'
- ;; ;; since its definition covers any language. In
- ;; ;; this case the language is used to adapt to the
- ;; ;; nonexistence of a cpp pass and thus removing some
- ;; ;; irrelevant menu alternatives.
- ;; (cons "C#" (c-lang-const c-mode-menu csharp)))
-
- ;;; Compilation regexps
- ;; When invoked by MSBuild, csc’s errors look like this:
- ;; subfolder\file.cs(6,18): error CS1006: Name of constructor must
- ;; match name of class [c:\Users\user\project.csproj]
-
- (defun csharp--compilation-error-file-resolve ()
- "Resolve an msbuild error to a (filename . dirname) cons cell."
- ;; http://stackoverflow.com/a/18049590/429091
- (cons (match-string 1) (file-name-directory (match-string 4))))
-
- (defconst csharp-compilation-re-msbuild-error
- (concat
- "^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
- "\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?): "
- "error [[:alnum:]]+: [^\r\n]+\\[\\([^]\r\n]+\\)\\]$")
- "Regexp to match compilation error from msbuild.")
-
- (defconst csharp-compilation-re-msbuild-warning
- (concat
- "^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
- "\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?): "
- "warning [[:alnum:]]+: [^\r\n]+\\[\\([^]\r\n]+\\)\\]$")
- "Regexp to match compilation warning from msbuild.")
-
- ;; Notes on xbuild and devenv commonalities
- ;;
- ;; These regexes were tailored for xbuild, but apart from the concurrent
- ;; build-marker ("1>") they share exactly the same match-markers.
- ;;
- ;; If we don't exclude the match-markers explicitly, these regexes
- ;; will also be used to match for devenv as well, including the build-marker
- ;; in the file-name, causing the lookup to fail.
- ;;
- ;; So if we don't want devenv to fail, we actually need to handle it in our
- ;; xbuild-regexes, but then we automatically get devenv-support for free.
-
- (defconst csharp-compilation-re-xbuild-error
- (concat
- "^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
- "\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?"
- ;; handle weird devenv output format with 4 numbers, not 2 by having optional
- ;; extra capture-groups.
- "\\(?:,\\([0-9]+\\)\\)*): "
- "error [[:alnum:]]+: .+$")
- "Regexp to match compilation error from xbuild.")
-
- (defconst csharp-compilation-re-xbuild-warning
- (concat
- "^[[:blank:]]*\\(?:[[:digit:]]+>\\)?"
- "\\([^(\r\n)]+\\)(\\([0-9]+\\)\\(?:,\\([0-9]+\\)\\)?"
- ;; handle weird devenv output format with 4 numbers, not 2 by having optional
- ;; extra capture-groups.
- "\\(?:,\\([0-9]+\\)\\)?*): "
- "warning [[:alnum:]]+: .+$")
- "Regexp to match compilation warning from xbuild.")
-
- (eval-after-load 'compile
- (lambda ()
- (dolist
- (regexp
- `((xbuild-error
- ,csharp-compilation-re-xbuild-error
- 1 2 3 2)
- (xbuild-warning
- ,csharp-compilation-re-xbuild-warning
- 1 2 3 1)
- (msbuild-error
- ,csharp-compilation-re-msbuild-error
- csharp--compilation-error-file-resolve
- 2
- 3
- 2
- nil
- (1 compilation-error-face)
- (4 compilation-error-face))
- (msbuild-warning
- ,csharp-compilation-re-msbuild-warning
- csharp--compilation-error-file-resolve
- 2
- 3
- 1
- nil
- (1 compilation-warning-face)
- (4 compilation-warning-face))))
- (add-to-list 'compilation-error-regexp-alist-alist regexp)
- (add-to-list 'compilation-error-regexp-alist (car regexp)))))
-
-
- ;;; Autoload mode trigger
- ;;;###autoload
- (add-to-list 'auto-mode-alist '("\\.cs$" . csharp-mode))
-
-
- (c-add-style "C#"
- '("Java"
- (c-basic-offset . 4)
- (c-comment-only-line-offset . (0 . 0))
- (c-offsets-alist . (
- (access-label . -)
- (arglist-close . c-lineup-arglist)
- (arglist-cont . 0)
- (arglist-cont-nonempty . c-lineup-arglist)
- (arglist-intro . c-lineup-arglist-intro-after-paren)
- (block-close . 0)
- (block-open . 0)
- (brace-entry-open . 0)
- (brace-list-close . 0)
- (brace-list-entry . 0)
- (brace-list-intro . +)
- (brace-list-open . 0)
- (c . c-lineup-C-comments)
- (case-label . +)
- (catch-clause . 0)
- (class-close . 0)
- (class-open . 0)
- (comment-intro . c-lineup-comment)
- (cpp-macro . [0])
- (cpp-macro-cont . c-lineup-dont-change)
- (defun-block-intro . +)
- (defun-close . 0)
- (defun-open . 0)
- (do-while-closure . 0)
- (else-clause . 0)
- (extern-lang-close . 0)
- (extern-lang-open . 0)
- (friend . 0)
- (func-decl-cont . +)
- (inclass . +)
- (inexpr-class . 0)
- (inexpr-statement . 0)
- (inextern-lang . +)
- (inher-cont . c-lineup-multi-inher)
- (inher-intro . +)
- (inlambda . c-lineup-inexpr-block)
- (inline-close . 0)
- (inline-open . 0)
- (innamespace . +)
- (knr-argdecl . 0)
- (knr-argdecl-intro . 5)
- (label . 0)
- (lambda-intro-cont . +)
- (member-init-cont . c-lineup-multi-inher)
- (member-init-intro . +)
- (namespace-close . 0)
- (namespace-open . 0)
- (statement . 0)
- (statement-block-intro . +)
- (statement-case-intro . +)
- (statement-case-open . +)
- (statement-cont . +)
- (stream-op . c-lineup-streamop)
- (string . c-lineup-dont-change)
- (substatement . +)
- (substatement-open . 0)
- (template-args-cont c-lineup-template-args +)
- (topmost-intro . 0)
- (topmost-intro-cont . +)
- ))
- ))
-
-
- ;;;###autoload
- (define-derived-mode csharp-mode prog-mode "C#"
- "Major mode for editing C# code.
-
- 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:
- `prog-mode-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
- ....your own code here...
- )
- (add-hook 'csharp-mode-hook 'my-csharp-mode-fn t)
-
-
- The function above is just a suggestion.
-
-
- Imenu Integration
- ===============================
-
- Check the menubar for menu entries for Imenu; it 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'.
-
-
-
- Key bindings:
- \\{csharp-mode-map}"
- (make-local-variable 'beginning-of-defun-function)
- (make-local-variable 'end-of-defun-function)
- (c-initialize-cc-mode t)
-
- ;; define underscore as part of a word in the Csharp syntax table
- (modify-syntax-entry ?_ "w" csharp-mode-syntax-table)
-
- ;; define @ as an expression prefix in Csharp syntax table
- (modify-syntax-entry ?@ "'" csharp-mode-syntax-table)
-
- ;; `c-init-language-vars' is a macro that is expanded at compile
- ;; time to a large `setq' with all the language variables and their
- ;; customized values for our language.
- (c-init-language-vars csharp-mode)
-
- ;; Use our predefined "C#" style unless a file local or default
- ;; style is found. This is done by rebinding `c-default-style'
- ;; during the `c-common-init' call. 'c-common-init' will initialize
- ;; the buffer's style using the value of `c-default-style'.
- (let ((c-default-style (if (or c-file-style
- (stringp c-default-style)
- (assq 'csharp-mode c-default-style))
- c-default-style
- "C#")))
- ;; `c-common-init' initializes most of the components of a CC Mode
- ;; buffer, including setup of the mode menu, font-lock, etc.
- ;; There's also a lower level routine `c-basic-common-init' that
- ;; only makes the necessary initialization to get the syntactic
- ;; analysis and similar things working.
- (c-common-init 'csharp-mode))
-
- (define-key csharp-mode-map (kbd "/") 'csharp-maybe-insert-codedoc)
-
- ;; Need the following for parse-partial-sexp to work properly with
- ;; verbatim literal strings Setting this var to non-nil tells
- ;; `parse-partial-sexp' to pay attention to the syntax text
- ;; properties on the text in the buffer. If csharp-mode attaches
- ;; text syntax to @"..." then, `parse-partial-sexp' will treat those
- ;; strings accordingly.
- (set (make-local-variable 'parse-sexp-lookup-properties) t)
-
- ;; Allow fill-paragraph to work on xml code doc
- ;; This setting gets overwritten quietly by c-run-mode-hooks,
- ;; so I put it afterwards to make it stick.
- (make-local-variable 'paragraph-separate)
-
- ;; Speedbar handling
- (when (fboundp 'speedbar-add-supported-extension)
- (speedbar-add-supported-extension '(".cs"))) ;; idempotent
-
- (c-update-modeline)
-
- ;; maybe do imenu scan after hook returns
- (when csharp-want-imenu
- (csharp--setup-imenu))
-
- ;; The paragraph-separate variable was getting stomped by
- ;; other hooks, so it must reside here.
- (setq-local paragraph-separate
- "[ \t]*\\(//+\\|\\**\\)\\([ \t]+\\|[ \t]+<.+?>\\)$\\|^\f")
-
- (setq-local 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)
-
- (set (make-local-variable 'syntax-propertize-function)
- 'csharp-mode-syntax-propertize-function)
-
- ;; required since Emacs git master
- ;; https://github.com/emacs-mirror/emacs/commit/edcdf64960a2ab4e8d9ce4419874e43b6d3ccee4
- ;;
- ;; Basically syntax-propertize-function is a construct which belongs
- ;; to font-lock. But correct indentation depends on
- ;; syntax-properties of the text, and that should ideally be
- ;; independent of font-lock being activated or not.
- ;;
- ;; For csharp-mode, this means that with font-lock disabled, we wont
- ;; have our syntax-properties set correctly, and indentation will
- ;; suffer.
- ;;
- ;; To patch our way around this, we issue a syntax-propertize call
- ;; manually, font-lock enabled or not.
- (with-silent-modifications
- (csharp-mode-syntax-propertize-function (point-min) (point-max))))
-
- (provide 'csharp-mode)
-
- ;;; csharp-mode.el ends here
|