diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..da26b1e
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,47 @@
+name: Build & Test
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ continue-on-error: ${{ matrix.experimental }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ emacs-version: [26.1, 26.2, 26.3, 27.1, 27.2, 28.1]
+ experimental: [false]
+ include:
+ - os: ubuntu-latest
+ emacs-version: snapshot
+ experimental: true
+ - os: macos-latest
+ emacs-version: snapshot
+ experimental: true
+ - os: windows-latest
+ emacs-version: snapshot
+ experimental: true
+
+ steps:
+ # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
+ - uses: actions/checkout@v2
+
+ - uses: jcs090218/setup-emacs@master
+ with:
+ version: ${{ matrix.emacs-version }}
+
+ - uses: emacs-eask/setup-eask@master
+ with:
+ version: 'snapshot'
+
+ - name: Run a multi-line script
+ run: |
+ emacs --version
+ make ci
diff --git a/.gitignore b/.gitignore
index f3d8a8a..a7748ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,10 @@ tramp
/eshell/history
/eshell/lastdir
+# GNU ELPA generated files
+/csharp-mode-autoloads.el
+/csharp-mode-pkg.el
+
# elpa packages
/elpa/
@@ -35,6 +39,8 @@ tramp
.cask/
dist/
+# eask packages
+.eask/
### Elisp ###
# Compiled
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index a3388fd..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: generic
-sudo: false
-before_install:
- - curl -fsSkL https://gist.github.com/rejeep/ebcd57c3af83b049833b/raw > x.sh && source ./x.sh
- - evm install $EVM_EMACS --use --skip
- - cask
-
-env:
- - EVM_EMACS=emacs-24.4-travis
- - EVM_EMACS=emacs-24.5-travis
- - EVM_EMACS=emacs-25.1-travis
- - EVM_EMACS=emacs-25.2-travis
- - EVM_EMACS=emacs-25.3-travis
- - EVM_EMACS=emacs-26.1-travis
- - EVM_EMACS=emacs-26.2-travis
- - EVM_EMACS=emacs-git-snapshot-travis
-
-matrix:
- allow_failures:
- - env: EVM_EMACS=emacs-git-snapshot-travis
-
-script:
- - emacs --version
- - make test
diff --git a/Cask b/Cask
deleted file mode 100644
index f69b06f..0000000
--- a/Cask
+++ /dev/null
@@ -1,5 +0,0 @@
-(source gnu)
-(source melpa)
-
-(files "*.el")
-(package-file "csharp-mode.el")
diff --git a/Eask b/Eask
new file mode 100644
index 0000000..6a08eb7
--- /dev/null
+++ b/Eask
@@ -0,0 +1,19 @@
+(package "csharp-mode"
+ "1.1.1"
+ "C# mode derived mode")
+
+(website-url "https://github.com/emacs-csharp/csharp-mode")
+(keywords "c#" "languages" "oop" "mode")
+
+(package-file "csharp-mode.el")
+
+(source "gnu")
+(source "melpa")
+
+(depends-on "emacs" "26.1")
+
+(development
+ (depends-on "assess")
+ (depends-on "m-buffer"))
+
+(setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3724298
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ A major mode for editing C# in emacs.
+ Copyright (C) 2020 Theodor Thornhill and contributors
+
+ 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 3 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. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ csharp-mode.el Copyright (C) 2020 Theodor Thornhill and contributors
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.org b/README.org
index 963b22c..60ef54c 100644
--- a/README.org
+++ b/README.org
@@ -1,10 +1,24 @@
+[[https://github.com/emacs-csharp/csharp-mode/actions][file:https://github.com/emacs-csharp/csharp-mode/workflows/Build%20&%20Test/badge.svg?branch=master]]
+[[https://melpa.org/#/csharp-mode][file:https://melpa.org/packages/csharp-mode-badge.svg]]
+[[https://stable.melpa.org/#/csharp-mode][file:https://stable.melpa.org/packages/csharp-mode-badge.svg]]
+[[https://elpa.gnu.org/packages/csharp-mode.html][file:https://elpa.gnu.org/packages/csharp-mode.svg]]
+* Obsoletion warning
-* csharp-mode
+This mode is effectively no longer maintained. /No/ new features
+will be added, and the mode itself has been moved into Emacs core.
+
+Thus development of support for C# will continue in core Emacs. However, this repo will continue
+being available from (M)ELPA for some time for backwards compatibility.
-This is a mode for editing C# in emacs. It's based on cc-mode, v5.30.3 and above.
+If you are running Emacs 29 or later you are advised to remove this package and rely
+on what's in core. Bug reports should be directed to the Emacs bug tracker
+after Emacs 29 is released.
+
+* csharp-mode
-[[https://travis-ci.org/josteink/csharp-mode/][Travis-CI build-page]]. Build-status: [[https://api.travis-ci.org/josteink/csharp-mode.png]]
+This is a mode for editing C# in emacs. It's using CC mode or [[https://github.com/ubolonton/emacs-tree-sitter][tree-sitter]] for
+highlighting and indentation.
** Main features
@@ -16,16 +30,50 @@ This is a mode for editing C# in emacs. It's based on cc-mode, v5.30.3 and above
- instance initializers
- anonymous functions and methods
- verbatim literal strings (those that begin with @)
- - generics
-- automagic code-doc generation when you type three slashes.
+ - generics
- intelligent insertion of matched pairs of curly braces.
-- imenu indexing of C# source, for easy menu-based navigation.
- compilation-mode support for msbuild, devenv and xbuild.
+** tree-sitter support
+You can enable experimental tree sitter support for indentation and highlighting using
+#+begin_src elisp
+ (use-package tree-sitter :ensure t)
+ (use-package tree-sitter-langs :ensure t)
+ (use-package tree-sitter-indent :ensure t)
+
+ (use-package csharp-mode
+ :ensure t
+ :config
+ (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-tree-sitter-mode)))
+#+end_src
+If you are using this, clearly state so if you find any issues.
+
+Note that we don't depend on tree-sitter yet, so you have to manually install
+the packages involved. The simplest way is to use the provided snippet above.
+
+*** Using and evolving the tree-sitter functionality.
+=tree-sitter= introduces a minor mode called =tree-sitter-debug-mode= where you can
+look at the actual syntax tree it produces. If and when you spot missing or
+wrong syntax highlighting, look at how the patterns are written in
+=csharp-tree-sitter-mode.el=, then submit a pr with a couple new ones added. When
+testing and debugging this, it is actually as simple as =M-x eval-buffer= on
+=csharp-tree-sitter-mode.el=, then =M-x revert-buffer= in the file you are testing.
+It should update and show the correct syntax highlighting.
+
+
+So the development cycle is:
+- Spot missing syntax highlighting
+- View AST with =tree-sitter-debug-mode=
+- Locate offending part
+- Add new pattern
+- =M-x eval-buffer= in =csharp-tree-sitter-mode.el=
+- =M-x revert-buffer= inside your =some-test-file.cs=
+
+
** Usage
-This package is currently available on MELPA & Marmalade. Install using
-~M-x package-installcsharp-mode~.
+This package is currently available on both ELPA and MELPA. Install using ~M-x
+package-installcsharp-mode~.
Once installed the package should be automatically used for files with a '.cs'-extension.
@@ -44,14 +92,14 @@ To do so, add the following to your .emacs-file:
(add-hook 'csharp-mode-hook 'my-csharp-mode-hook)
#+END_SRC
-For further mode-specific customization, ~M-x customize-group RET csharp RET~ will show available settings with documentation. For configuring ~cc-mode~ settings (which csharp-mode derives from) see the [[https://www.gnu.org/software/emacs/manual/html_mono/ccmode.html][cc-mode manual]].
+For further mode-specific customization, ~M-x customize-group RET csharp RET~ will show available settings with documentation.
For more advanced and IDE-like functionality we recommend using csharp-mode together
-with [[https://github.com/OmniSharp/omnisharp-emacs][Omnisharp-Emacs]].
+with [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]] or [[https://github.com/joaotavora/eglot][eglot]]
* Attribution
-This repo is a fork of the code originally developed by Dylan R. E. Moonfire and
+This repo was a fork of the code originally developed by Dylan R. E. Moonfire and
further maintained by Dino Chiesa as hosted on [[https://code.google.com/p/csharpmode/][Google code]].
** New focus
@@ -66,6 +114,12 @@ be maintaining.
The goal: That what we package in csharp-mode actually works and works well.
+* ELPA
+This package aims to stay as close to mainline emacs as it can. As such,
+paperwork with the FSF is needed for contributions of significant size.
+
+
* License
-The original project was licensed under [[https://www.gnu.org/licenses/gpl-2.0.html][GPL v2]], so this one is too.
+The original project was licensed under [[https://www.gnu.org/licenses/gpl-2.0.html][GPL v2+]], but after a rewrite in September
+2020, it was relicensed to GPLv3+
diff --git a/csharp-compilation.el b/csharp-compilation.el
new file mode 100644
index 0000000..342ac5e
--- /dev/null
+++ b/csharp-compilation.el
@@ -0,0 +1,139 @@
+;;; csharp-compilation.el --- compilation support for C# -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author : Theodor Thornhill
+;; Maintainer : Jostein Kjønigsen
+;; Theodor Thornhill
+;; Created : September 2020
+;; Modified : 2020
+;; Version : 2.0.0
+;; Keywords : c# languages oop mode
+;; X-URL : https://github.com/emacs-csharp/csharp-mode
+
+;; 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 3 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. If not, see .
+
+
+(require 'compile)
+
+;; 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.")
+
+(defconst csharp-compilation-re-dotnet-error
+ "\\([^\r\n]+\\) : error [A-Z]+[0-9]+:")
+
+(defconst csharp-compilation-re-dotnet-warning
+ "\\([^\r\n]+\\) : warning [A-Z]+[0-9]+:")
+
+(defconst csharp-compilation-re-dotnet-testfail
+ (concat
+ "[[:blank:]]+Stack Trace:\n"
+ "[[:blank:]]+at [^\n]+ in \\([^\n]+\\):line \\([0-9]+\\)"))
+
+
+(eval-after-load 'compile
+ (lambda ()
+ (dolist
+ (regexp
+ `((dotnet-testfail
+ ,csharp-compilation-re-dotnet-testfail
+ 1 2)
+ (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))
+ (dotnet-error
+ ,csharp-compilation-re-dotnet-error
+ 1)
+ (dotnet-warning
+ ,csharp-compilation-re-dotnet-warning
+ 1 nil nil 1)))
+ (add-to-list 'compilation-error-regexp-alist-alist regexp)
+ (add-to-list 'compilation-error-regexp-alist (car regexp)))))
+
+(provide 'csharp-compilation)
+
+;;; csharp-compilation.el ends here
diff --git a/csharp-mode-tests.el b/csharp-mode-tests.el
index 502aded..0b2cdf7 100644
--- a/csharp-mode-tests.el
+++ b/csharp-mode-tests.el
@@ -1,19 +1,43 @@
+;;; csharp-mode-tests.el --- Tests for csharp-mode.el -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author: Josten Kjønigsen
+;; Keywords: tests
+
+;; 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 3 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. If not, see .
+
(require 'ert)
(require 'cl-lib)
(require 'csharp-mode)
-(require 'cl)
(require 'package)
-;; development only packages, not declared as a package-dependency
-(package-initialize)
-(add-to-list 'package-archives '("melpa" . "https://stable.melpa.org/packages/"))
+(defun csharp-mode-tests-setup ()
+ "Prepare `csharp-mode' for running tests via make."
-;; assess depends on dash 2.12.1, which is no longer available
-;; installing dash, resolves 2.13.0, and fixes this broken dependency.
-(dolist (p '(dash assess))
- (when (not (package-installed-p p))
- (package-refresh-contents)
- (package-install p)))
+ ;; development only packages, not declared as a package-dependency
+ (package-initialize)
+ (add-to-list 'package-archives '("melpa" . "https://stable.melpa.org/packages/"))
+
+ ;; required to resolve SEQ (or anything on elpa) on Emacs25.
+ (setq package-check-signature nil)
+ (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432
+
+ (dolist (p '(assess))
+ (when (not (package-installed-p p))
+ (package-refresh-contents)
+ (package-install p))))
;;; test-helper functions
@@ -54,6 +78,19 @@
"true" 'font-lock-constant-face
))
+(when (and (>= emacs-major-version 29)
+ (string-lessp "5.35.0" c-version))
+ (ert-deftest fontification-of-multiline-strings ()
+ (assess-face-in-file= "./test-files/multiline-strings.cs"
+ "Literal0" 'font-lock-variable-name-face
+ "Literal1" 'font-lock-variable-name-face
+ "Literal2" 'font-lock-variable-name-face
+ "Literal3" 'font-lock-variable-name-face
+ "Literal4" 'font-lock-variable-name-face
+ "Literal5" 'font-lock-variable-name-face
+ "Literal6" 'font-lock-variable-name-face
+ "Literal7" 'font-lock-variable-name-face)))
+
(ert-deftest fontification-of-constants ()
(require 'assess)
(assess-face-in-text=
@@ -64,6 +101,26 @@
"value" 'font-lock-constant-face
))
+(ert-deftest fontification-of-package ()
+ (require 'assess)
+ (assess-face-in-text=
+ "var package = true;"
+ "package" 'font-lock-variable-name-face))
+
+(ert-deftest fontification-of-functions ()
+ (require 'assess)
+ (assess-face-in-text= "var foo = bar.Baz()"
+ "Baz" 'font-lock-function-name-face)
+ (assess-face-in-text= "var foo = bar.Baz()"
+ "Baz" 'font-lock-function-name-face
+ "Quux" 'font-lock-type-face))
+
+(ert-deftest fontification-of-import ()
+ (require 'assess)
+ (assess-face-in-text=
+ "var import = true;"
+ "import" 'font-lock-variable-name-face))
+
(ert-deftest fontification-of-literals-allows-multi-line-strings ()
(require 'assess)
(should (assess-face-at=
@@ -79,35 +136,35 @@
19 'font-lock-string-face
)))
-(ert-deftest fontification-of-compiler-directives ()
- ;; this replaces the manual test of
- ;; test-files/fontification-test-compiler-directives.cs, but file
- ;; has been kept around to assist manual testing/verification.
- (assess-face-in-file= "test-files/fontification-test-compiler-directives.cs"
- "strReference" 'font-lock-string-face
- "strVerification" 'font-lock-string-face
- "singleQuote" 'font-lock-string-face
- "doubleQuote" 'font-lock-string-face)
-
- (assess-face-in-text=
- "#region test\nbool bar = true;"
- ;; should not be interpreted as string because of trailing \!
- "bool" 'font-lock-type-face
- "bar" 'font-lock-variable-name-face
- "true" 'font-lock-constant-face
- )
- (should (assess-face-at=
- "#region test'\nx = true;"
- 'csharp-mode
- ;; should not be interpreted as string because of trailing \!
- "true" 'font-lock-constant-face
- ))
- (should (assess-face-at=
- "#region test\"\nx = true;"
- 'csharp-mode
- ;; should not be interpreted as string because of trailing \!
- "true" 'font-lock-constant-face
- )))
+;; (ert-deftest fontification-of-compiler-directives ()
+;; ;; this replaces the manual test of
+;; ;; test-files/fontification-test-compiler-directives.cs, but file
+;; ;; has been kept around to assist manual testing/verification.
+;; (assess-face-in-file= "test-files/fontification-test-compiler-directives.cs"
+;; "strReference" 'font-lock-string-face
+;; "strVerification" 'font-lock-string-face
+;; "singleQuote" 'font-lock-string-face
+;; "doubleQuote" 'font-lock-string-face)
+
+;; (assess-face-in-text=
+;; "#region test\nbool bar = true;"
+;; ;; should not be interpreted as string because of trailing \!
+;; "bool" 'font-lock-type-face
+;; "bar" 'font-lock-variable-name-face
+;; "true" 'font-lock-constant-face
+;; )
+;; (should (assess-face-at=
+;; "#region test'\nx = true;"
+;; 'csharp-mode
+;; ;; should not be interpreted as string because of trailing \!
+;; "true" 'font-lock-constant-face
+;; ))
+;; (should (assess-face-at=
+;; "#region test\"\nx = true;"
+;; 'csharp-mode
+;; ;; should not be interpreted as string because of trailing \!
+;; "true" 'font-lock-constant-face
+;; )))
(ert-deftest fontification-of-compiler-directives-after-comments ()
(assess-face-in-file= "./test-files/fontification-test-compiler-directives-with-comments.cs"
@@ -128,19 +185,23 @@
(ert-deftest fontification-of-using-statements ()
(assess-face-in-file= "./test-files/using-fontification.cs"
"using" 'font-lock-keyword-face
- "Reference" 'font-lock-constant-face
- "Under_scored" 'font-lock-constant-face
- "WithNumbers09.Ok" 'font-lock-constant-face
- "WithNumbers09.OkV2" 'font-lock-constant-face
+ "Reference" 'font-lock-variable-name-face
+ "Under_scored" 'font-lock-variable-name-face
+ "WithNumbers09" 'font-lock-variable-name-face
+ "Ok" 'font-lock-variable-name-face
+ "WithNumbers09" 'font-lock-variable-name-face
+ "OkV2" 'font-lock-variable-name-face
))
(ert-deftest fontification-of-namespace-statements ()
(assess-face-in-file= "./test-files/namespace-fontification.cs"
"namespace" 'font-lock-keyword-face
- "Reference" 'font-lock-constant-face
- "Under_scored" 'font-lock-constant-face
- "WithNumbers09.Ok" 'font-lock-constant-face
- "WithNumbers09.OkV2" 'font-lock-constant-face
+ "Reference" 'font-lock-variable-name-face
+ "Under_scored" 'font-lock-variable-name-face
+ "WithNumbers09" 'font-lock-variable-name-face
+ "Ok" 'font-lock-variable-name-face
+ "WithNumbers09" 'font-lock-variable-name-face
+ "Ok" 'font-lock-variable-name-face
))
(defun list-repeat-once (mylist)
@@ -193,7 +254,14 @@
"C:\\inservice\\SystemTesting\\OperateDeviceProxy\\OperateDevice_Proxy\\Program.cs"
"c:\\inservice\\systemtesting\\operationsproxy\\operationsproxy.cpp"
"c:\\inservice\\systemtesting\\operationsproxy\\operationsproxy.cpp"
- "c:\\inservice\\systemtesting\\operationsproxy\\operationsproxy.cpp"))))
+ "c:\\inservice\\systemtesting\\operationsproxy\\operationsproxy.cpp"))
+
+ ("./test-files/dotnet-nuget-error.txt" ,csharp-compilation-re-dotnet-error
+ ("/home/jostein/build/sample-app/sample-app.csproj"))
+ ("./test-files/dotnet-nuget-warning.txt" ,csharp-compilation-re-dotnet-warning
+ ("/home/jostein/build/sample-app/sample-app.csproj"))
+ ("./test-files/dotnet-test-fail-xunit.txt" ,csharp-compilation-re-dotnet-testfail
+ ("/home/jostein/build/sample-app/Module/Testee.cs"))))
(let* ((file-name (car test-case))
(regexp (cadr test-case))
@@ -210,152 +278,6 @@
(equal expected (match-string 1)))))
(kill-buffer buffer))))
-(defun imenu-get-item (index haystack)
- (let ((result))
- (dolist (item index)
- (when (not result)
- (let ((name (car item))
- (value (cdr item)))
- (if (string-prefix-p haystack name)
- (setq result item)
- (when (listp value)
- (setq result (imenu-get-item value haystack)))))))
- result))
-
-(defmacro def-imenutest (testname filename &rest items)
- `(ert-deftest ,testname ()
- (let* ((find-file-hook nil) ;; avoid vc-mode file-hooks when opening!
- (buffer (find-file-read-only ,filename))
- (index (csharp--imenu-create-index-function)))
- (dolist (item ',items)
- (should (imenu-get-item index item)))
- (kill-buffer buffer))))
-
-(def-imenutest imenu-parsing-supports-generic-parameters
- "./test-files/imenu-generics-test.cs"
- "(method) NoGeneric(" "(method) OneGeneric(" "(method) TwoGeneric(")
-
-(def-imenutest imenu-parsing-supports-comments
- "./test-files/imenu-comment-test.cs"
- "(method) HasNoComment(" "(method) HasComment(" "(method) CommentedToo(")
-
-(def-imenutest imenu-parsing-supports-explicit-interface-properties
- "./test-files/imenu-interface-property-test.cs"
- "(prop) IImenuTest.InterfaceString")
-
-(def-imenutest imenu-parsing-supports-explicit-interface-methods
- "./test-files/imenu-interface-property-test.cs"
- "(method) IImenuTest.MethodName")
-
-(def-imenutest imenu-parsing-provides-types-with-namespace-names
- "./test-files/imenu-namespace-test.cs"
- "class ImenuTest.ImenuTestClass"
- "interface ImenuTest.ImenuTestInterface"
- "enum ImenuTest.ImenuTestEnum")
-
-(def-imenutest imenu-parsing-supports-fields-keywords
- "./test-files/imenu-field-keyword-test.cs"
- "(field) TestBool"
- "(field) CommentedField"
- "(field) _MultiLineComment"
- "(field) VolatileTest"
- "(field) m_Member")
-
-(def-imenutest imenu-parsing-supports-method-keywords
- "./test-files/imenu-method-test.cs"
- "(method) GetTickCount64("
- "(method) OpenWebServiceAsync("
- "(method) ToString("
- "(method) AbstractMethod("
- "(method) UnsafeCopy("
- "(method) GenericMethod1"
- "(method) GenericMethod2"
- "(method) NestedGeneric")
-
-(def-imenutest imenu-parsing-supports-delegates
- "./test-files/imenu-delegate-test.cs"
- "delegate PromptCallback"
- "delegate PromptStateCallback"
- "delegate PromptStateCallback"
- "delegate Foobar.TargetCallback"
- "delegate Foobar.TargetStateCallback"
- "delegate Foobar.TargetStateCallback")
-
-(ert-deftest imenu-indexing-resolves-correct-container ()
- (let* ((testcase-no-namespace '( ("class Global" . 10)
- (("namespace_a" . 20) ("namespace_b" . 30))
- nil))
- (testcase-namespace-a '( ("class A" . 10)
- (("namespace_a" . 0) ("namespace_b" . 30))
- "namespace_a"))
- (testcase-namespace-b '( ("class B" . 40)
- (("namespace_a" . 0) ("namespace_b" . 30))
- "namespace_b"))
- (testcases (list testcase-no-namespace
- testcase-namespace-a
- testcase-namespace-b)))
- (dolist (testcase testcases)
- (let ((class (car testcase))
- (namespaces (cadr testcase))
- (expected (caddr testcase)))
- (should (equal expected
- (csharp--imenu-get-container-name class namespaces)))))))
-
-(ert-deftest imenu-indexing-resolves-correct-name ()
- (let* ((testcase-no-namespace '( ("class Global" . 10)
- (("namespace_a" . 20) ("namespace_b" . 30))
- "class Global"))
- (testcase-namespace-a '( ("class A" . 10)
- (("namespace_a" . 0) ("namespace_b" . 30))
- "class namespace_a.A"))
- (testcase-namespace-b '( ("class B" . 40)
- (("namespace_a" . 0) ("namespace_b" . 30))
- "class namespace_b.B"))
- (testcases (list testcase-no-namespace
- testcase-namespace-a
- testcase-namespace-b)))
- (dolist (testcase testcases)
- (let ((class (car testcase))
- (namespaces (cadr testcase))
- (expected (caddr testcase)))
- (should (equal expected
- (csharp--imenu-get-class-name class namespaces)))))))
-
-(ert-deftest imenu-transforms-index-correctly ()
- ;; this test-case checks for the following aspects of the transformation:
- ;; 1. hierarchial nesting
- ;; 2. sorting of members
- (should (equalp
- '(("class A" . (("( top )" . 20)
- ("(method) method_a1" . 30)
- ("(method) method_a2" . 25)))
- ("class B" . (("( top )" . 0)
- ("(method) method_b1" . 15)
- ("(method) method_b2" . 10))))
-
- (csharp--imenu-transform-index
- '(("class" . (("class B" . 0) ("class A" . 20)))
- ("method" . (("method_b2" . 10) ("method_b1" . 15)
- ("method_a2" . 25) ("method_a1" . 30))))))))
-
-(ert-deftest imenu-transforms-index-correctly-with-namespaces ()
- ;; this test-case checks for the following aspects of the transformation:
- ;; 1. hierarchial nesting
- ;; 2. sorting of members
- (should (equalp
- '(("class ns.A" . (("( top )" . 20)
- ("(method) method_a1" . 30)
- ("(method) method_a2" . 25)))
- ("class ns.B" . (("( top )" . 0)
- ("(method) method_b1" . 15)
- ("(method) method_b2" . 10))))
-
- (csharp--imenu-transform-index
- '(("namespace" . (("ns" . 0)))
- ("class" . (("class B" . 0) ("class A" . 20)))
- ("method" . (("method_b2" . 10) ("method_b1" . 15)
- ("method_a2" . 25) ("method_a1" . 30))))))))
-
(defvar csharp-hook1 nil)
(defvar csharp-hook2 nil)
@@ -398,16 +320,16 @@
(forward-word -1)
(should (looking-at "fontifies")))
-(ert-deftest fontification-of-regions ()
- (require 'assess)
- (require 'm-buffer)
- (find-file "test-files/region-fontification.cs")
- (csharp-mode)
- (let ((buf (current-buffer)))
- ;; look for 'a region comment' - should always be a comment
- (should (assess-face-at= buf 'csharp-mode (lambda (buf) (m-buffer-match buf "a region comment")) 'font-lock-comment-face))
- ;; look for 'string' - should always be a type
- (should (assess-face-at= buf 'csharp-mode (lambda (buf) (m-buffer-match buf "string")) 'font-lock-type-face))))
+;; (ert-deftest fontification-of-regions ()
+;; (require 'assess)
+;; (require 'm-buffer)
+;; (find-file "test-files/region-fontification.cs")
+;; (csharp-mode)
+;; (let ((buf (current-buffer)))
+;; ;; look for 'a region comment' - should always be a comment
+;; (should (assess-face-at= buf 'csharp-mode (lambda (buf) (m-buffer-match buf "a region comment")) 'font-lock-comment-face))
+;; ;; look for 'string' - should always be a type
+;; (should (assess-face-at= buf 'csharp-mode (lambda (buf) (m-buffer-match buf "string")) 'font-lock-type-face))))
(ert-deftest activating-mode-doesnt-clobber-global-adaptive-fill-regexp ()
(let ((before adaptive-fill-regexp))
@@ -417,29 +339,29 @@
(equal before adaptive-fill-regexp))))
(ert-deftest activating-mode-style-defaults-to-csharp ()
- (let ((c-default-style "defaultc#"))
- (with-temp-buffer
- (csharp-mode)
- (should
- (equal "defaultc#" c-indentation-style))))
- (let ((c-default-style '((csharp-mode . "defaultc#fromlist")
- (java-mode . "defaultjava"))))
+ (with-temp-buffer
+ (csharp-mode)
+ (should
+ (equal "csharp" c-indentation-style)))
+
+ (let ((c-default-style "csharp"))
(with-temp-buffer
(csharp-mode)
(should
- (equal "defaultc#fromlist" c-indentation-style))))
- (let (c-default-style)
+ (equal "csharp" c-indentation-style))))
+
+ (let ((c-default-style '((csharp-mode . "csharp")
+ (java-mode . "java"))))
(with-temp-buffer
(csharp-mode)
(should
- (equal "C#" c-indentation-style)))))
+ (equal "csharp" c-indentation-style)))))
(ert-deftest inside-bracelist-test ()
- (let ((c-default-style "defaultc#"))
- (with-temp-buffer
- (csharp-mode)
- (insert "public class A { public void F() {")
- (call-interactively #'newline))))
+ (with-temp-buffer
+ (csharp-mode)
+ (insert "public class A { public void F() {")
+ (call-interactively #'newline)))
;;(ert-run-tests-interactively t)
;; (local-set-key (kbd "") '(lambda ()
diff --git a/csharp-mode.el b/csharp-mode.el
index 4e0c088..9c6f26b 100644
--- a/csharp-mode.el
+++ b/csharp-mode.el
@@ -1,3109 +1,602 @@
-;;; csharp-mode.el --- C# mode derived mode
+;;; csharp-mode.el --- C# mode derived mode -*- lexical-binding: t; -*-
-;; Author : Dylan R. E. Moonfire (original)
+;; Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+;; Author : Theodor Thornhill
;; Maintainer : Jostein Kjønigsen
-;; Created : Feburary 2005
-;; Modified : 2018
-;; Version : 0.9.2
+;; Theodor Thornhill
+;; Created : September 2020
+;; Modified : 2020
+;; Version : 2.0.0
;; Keywords : c# languages oop mode
-;; X-URL : https://github.com/josteink/csharp-mode
-;; Last-saved : 2018-Jul-08
+;; X-URL : https://github.com/emacs-csharp/csharp-mode
+;; Package-Requires: ((emacs "26.1"))
-;;
;; 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
+;; the Free Software Foundation, either version 3 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
-;;
-;; 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.
-;;
+;; along with this program. If not, see .
+
+
;;; 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)
+(require 'cc-mode)
+(require 'cc-langs)
-;; Work around emacs bug#23053
(eval-when-compile
- (require 'cc-langs))
+ (require 'cc-fonts))
-;; 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)))
+(require 'csharp-compilation)
+(eval-and-compile
+ (when (version< "29" emacs-version)
+ (warn "csharp-mode is part of Emacs as of Emacs 29 - please delete this package.")))
(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--regex-identifier
+ "[A-Za-z][A-Za-z0-9_]*"
+ "Regex describing an dentifier in C#.")
- (defconst csharp-type-initializer-statement-re
- (concat
- "\\\\.]*\\)")
- "Regexp that captures a type-initializer statement in C#")
+ (defconst csharp--regex-identifier-matcher
+ (concat "\\(" csharp--regex-identifier "\\)")
+ "Regex matching an identifier in C#.")
- (defconst csharp-enum-decl-re
- (concat
- "\\>" "=="
+ "!=" ">" "<" ">=" "<="))
+(c-lang-defconst c-multiline-string-start-char
+ csharp ?@)
+(c-lang-defconst c-ml-string-opener-re
+ ;; "\\(\\(?:@\\$?\\)\\(\"\\)\\)"
+ csharp
+ (rx
+ (group
+ (or "@" "@$")
+ (group "\""))))
+
+(c-lang-defconst c-ml-string-max-opener-len
+ csharp 3)
+
+(c-lang-defconst c-ml-string-max-closer-len
+ csharp 2)
+
+(c-lang-defconst c-ml-string-any-closer-re
+ ;; "\\(?:\"\"\\)*\\(\\(\"\\)\\)\\(?:[^\"]\\|\\'\\)"
+ csharp
+ (rx
+ (seq
+ (zero-or-more "\"\"")
+ (group
+ (group "\""))
+ (or (not (any "\"")) eos))))
+
+(c-lang-defconst c-ml-string-back-closer-re
+ ;; "\\(?:\\`\\|[^\"]\\)\"*"
+ csharp
+ (rx
+ (seq
+ (or bos
+ (not (any "\"")))
+ (zero-or-more "\""))))
+(c-lang-defconst c-type-prefix-kwds
+ csharp '("class" "interface" "struct"))
-;; ==================================================================
-;; csharp-mode utility and feature defuns
-;; ==================================================================
+(c-lang-defconst c-class-decl-kwds
+ csharp '("class" "interface" "struct"))
-(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.
+;;; Keyword lists
-This is the C# version of the function. It gets set into
-the variable `c-at-vsemi-p-fn'.
+(c-lang-defconst c-primitive-type-kwds
+ csharp '("bool" "byte" "sbyte" "char" "decimal" "double" "float" "int" "uint"
+ "long" "ulong" "short" "ushort" "void" "object" "string" "var"))
-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.
+(c-lang-defconst c-other-decl-kwds
+ csharp nil)
-A vsemi appears in 2 cases in C#:
+(c-lang-defconst c-type-list-kwds
+ csharp nil)
- - after an attribute that decorates a class, method, field, or
- property.
+(c-lang-defconst c-other-block-decl-kwds
+ csharp nil)
- - in an object initializer, before the open-curly?
+(c-lang-defconst c-return-kwds
+ csharp '("return"))
-An example of the former is [WebMethod] or [XmlElement].
+(c-lang-defconst c-typedef-kwds
+ csharp nil)
-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 "\\]+>[ \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-lang-defconst c-typeof-kwds
+ csharp '("typeof" "is" "as"))
- (c-safe (backward-sexp))
- (c-backward-syntactic-ws)
- (cond
+(c-lang-defconst c-type-modifier-prefix-kwds
+ csharp '("volatile"))
- ((eq (char-before) 93) ;; close sq brace (a previous attribute)
- (csharp--at-vsemi-p (point))) ;; recurse
+(c-lang-defconst c-type-modifier-kwds
+ csharp '("readonly" "new"))
- ((or
- (eq (char-before) 59) ;; semicolon
- (eq (char-before) 123) ;; open curly
- (eq (char-before) 125)) ;; close curly
- t)
+(c-lang-defconst c-brace-list-decl-kwds
+ csharp '("enum" "new"))
- ;; attr is used within a line of code
- (t nil)))
+(c-lang-defconst c-recognize-post-brace-list-type-p
+ csharp t)
- (t nil)))
+(c-lang-defconst c-ref-list-kwds
+ csharp nil)
- (t nil))
- )))
+(c-lang-defconst c-using-kwds
+ csharp '("using"))
+(c-lang-defconst c-equals-type-clause-kwds
+ csharp '("using"))
-;; ==================================================================
-;; end of csharp-mode utility and feature defuns
-;; ==================================================================
+(defun csharp-at-vsemi-p (&optional pos)
+ (if pos (goto-char pos))
+ (save-excursion
+ (beginning-of-line)
+ (c-forward-syntactic-ws)
+ (looking-at "using\\s *(")))
+(c-lang-defconst c-at-vsemi-p-fn
+ csharp 'csharp-at-vsemi-p)
+(defun csharp-vsemi-status-unknown () t)
-;; ==================================================================
-;; c# values for "language constants" defined in cc-langs.el
-;; ==================================================================
+(c-lang-defconst c-vsemi-status-unknown-p-fn
+ csharp 'csharp-vsemi-status-unknown-p)
-(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-modifier-kwds
+ csharp '("abstract" "default" "final" "native" "private" "protected"
+ "public" "partial" "internal" "readonly" "static" "event" "transient"
+ "volatile" "sealed" "ref" "out" "virtual" "implicit" "explicit"
+ "fixed" "override" "params" "async" "await" "extern" "unsafe"
+ "get" "set" "this" "const" "delegate"))
-(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)))))
+(c-lang-defconst c-other-kwds
+ csharp '("select" "from" "where" "join" "in" "on" "equals" "into"
+ "orderby" "ascending" "descending" "group" "when"
+ "let" "by" "namespace"))
+(c-lang-defconst c-colon-type-list-kwds
+ csharp '("class" "struct" "interface"))
- ;; 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))
+(c-lang-defconst c-block-stmt-1-kwds
+ csharp '("do" "else" "finally" "try"))
+(c-lang-defconst c-block-stmt-1-2-kwds
+ csharp '("try"))
- ;; Fontify all keywords except the primitive types.
- ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
- 1 font-lock-keyword-face)
- ))
+(c-lang-defconst c-block-stmt-2-kwds
+ csharp '("for" "if" "switch" "while" "catch" "foreach" "fixed" "checked"
+ "unchecked" "using" "lock"))
+(c-lang-defconst c-simple-stmt-kwds
+ csharp '("break" "continue" "goto" "throw" "return" "yield"))
+(c-lang-defconst c-constant-kwds
+ csharp '("true" "false" "null" "value"))
-(c-lang-defconst c-basic-matchers-after
- csharp `(
+(c-lang-defconst c-primary-expr-kwds
+ csharp '("this" "base" "operator"))
- ;; 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 "\\ 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)
+(c-lang-defconst c-inexpr-class-kwds
+ csharp nil)
+(c-lang-defconst c-class-decl-kwds
+ csharp '("class" "struct" "interface"))
- ))
+(c-lang-defconst c-std-abbrev-keywords
+ csharp (append (c-lang-const c-std-abbrev-keywords) '("catch" "finally")))
-;; verbatim string literals can be multiline
-(c-lang-defconst c-multiline-string-start-char
- csharp ?@)
+(c-lang-defconst c-decl-prefix-re
+ csharp "\\([{}(;,<]+\\)")
-(defun csharp-mode-syntax-propertize-function (beg end)
- "Apply syntax table properties to special constructs in region BEG to END.
-Currently handled:
+(c-lang-defconst c-recognize-typeless-decls
+ csharp t)
-- 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 *")
+(c-lang-defconst c-opt-cpp-macro-define
+ csharp (if (c-lang-const c-opt-cpp-prefix)
+ "define"))
-;; there are no message directives in C#
(c-lang-defconst c-cpp-message-directives
- csharp nil)
+ csharp '("error" "warning" "region"))
(c-lang-defconst c-cpp-expr-directives
- csharp '("if"))
-
-(c-lang-defconst c-opt-cpp-macro-define
- csharp "define")
+ csharp '("if" "elif"))
-;; $ 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-other-op-syntax-tokens
+ csharp (append '("#")
+ (c-lang-const c-other-op-syntax-tokens)))
+(c-lang-defconst c-line-comment-starter
+ csharp "//")
+(c-lang-defconst c-doc-comment-start-regexp
+ csharp "///")
-(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-add-style "csharp"
+ '("java"
+ (c-basic-offset . 4)
+ (c-comment-only-line-offset . (0 . 0))
+ (c-offsets-alist . ((inline-open . 0)
+ (arglist-intro . +)
+ (arglist-close . 0)
+ (inexpr-class . 0)
+ (case-label . +)
+ (cpp-macro . c-lineup-dont-change)
+ (substatement-open . 0)))))
-(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\".
+(eval-and-compile
+ (unless (or (stringp c-default-style)
+ (assoc 'csharp-mode c-default-style))
+ (setq c-default-style
+ (cons '(csharp-mode . "csharp")
+ c-default-style))))
+
+(defun csharp--color-forwards (font-lock-face)
+ (let (id-beginning)
+ (goto-char (match-beginning 0))
+ (forward-word)
+ (while (and (not (or (eq (char-after) ?\;)
+ (eq (char-after) ?\{)))
+ (progn
+ (forward-char)
+ (c-forward-syntactic-ws)
+ (setq id-beginning (point))
+ (> (skip-chars-forward
+ (c-lang-const c-symbol-chars))
+ 0))
+ (not (get-text-property (point) 'face)))
+ (c-put-font-lock-face id-beginning (point) font-lock-face)
+ (c-forward-syntactic-ws))))
-This regexp is assumed to not match any non-operator identifier."
- csharp (c-make-keywords-re t '("operator")))
+(c-lang-defconst c-basic-matchers-before
+ csharp `(
+ ;; Warning face on unclosed strings
+ ,@(if (version< emacs-version "27.0")
+ ;; Taken from 26.1 branch
+ `(,(c-make-font-lock-search-function
+ (concat ".\\(" c-string-limit-regexp "\\)")
+ '((c-font-lock-invalid-string))))
+ `(("\\s|" 0 font-lock-warning-face t nil)))
+
+ ;; Invalid single quotes
+ c-font-lock-invalid-single-quotes
+
+ ;; 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)))))
-;; 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"))
+ ;; Keywords except the primitive types.
+ ,`(,(concat "\\<" (c-lang-const c-regular-keywords-regexp))
+ 1 font-lock-keyword-face)
-;; Handle typeless variable declaration
-(c-lang-defconst c-typeless-decl-kwds
- csharp '("var"))
+ ;; Chained identifiers in using/namespace statements
+ ,`(,(c-make-font-lock-search-function
+ csharp--regex-using-or-namespace
+ `((csharp--color-forwards font-lock-variable-name-face)
+ nil
+ (goto-char (match-end 0)))))
-;; 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"))
+ ;; Negation character
+ (eval . (list "\\(!\\)[^=]" 1 c-negation-char-face-name))
-;; 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"))
+ ;; Types after 'new'
+ (eval . (list (concat "\\ *" csharp--regex-type-name-matcher)
+ 1 font-lock-type-face))
+ ;; Single identifier in attribute
+ (eval . (list (concat "\\[" csharp--regex-type-name-matcher "\\][^;]")
+ 1 font-lock-variable-name-face t))
-;; 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"))
+ ;; Function names
+ (eval . (list "\\([A-Za-z0-9_]+\\)\\(<[a-zA-Z0-9, ]+>\\)?("
+ 1 font-lock-function-name-face))
+ ;; Nameof
+ (eval . (list (concat "\\(\\\\) *(")
+ 1 font-lock-function-name-face))
-;; Statements that break out of braces
-(c-lang-defconst c-simple-stmt-kwds
- csharp '("return" "continue" "break" "throw" "goto" ))
+ (eval . (list (concat "\\ *( *"
+ csharp--regex-identifier-matcher
+ " *) *")
+ 1 font-lock-variable-name-face))
-;; Statements that allow a label
-;; TODO?
-(c-lang-defconst c-before-label-kwds
- csharp nil)
+ ;; Catch statements with type only
+ (eval . (list (concat "\\ *( *"
+ csharp--regex-type-name-matcher
+ " *) *")
+ 1 font-lock-type-face))
+ ))
-;; Constant keywords
-(c-lang-defconst c-constant-kwds
- csharp '("true" "false" "null" "value"))
+(c-lang-defconst c-basic-matchers-after
+ csharp (append
+ ;; Merge with cc-mode defaults - enables us to add more later
+ (c-lang-const c-basic-matchers-after)))
-;; Keywords that start "primary expressions."
-(c-lang-defconst c-primary-expr-kwds
- csharp '("this" "base" "operator"))
+(defcustom csharp-codedoc-tag-face 'c-doc-markup-face-name
+ "Face to be used on the codedoc docstring tags.
-;; Treat namespace as an outer block so class indenting
-;; works properly.
-(c-lang-defconst c-other-block-decl-kwds
- csharp '("namespace"))
+Should be one of the font lock faces, such as
+`font-lock-variable-name-face' and friends.
-(c-lang-defconst c-other-kwds
- csharp '("sizeof" "typeof" "is" "as" "yield"
- "where" "select" "in" "from" "let" "orderby" "ascending" "descending"
- "await" "async"))
+Needs to be set before `csharp-mode' is loaded, because of
+compilation and evaluation time conflicts."
+ :type 'symbol)
-(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)))
+(defcustom csharp-font-lock-extra-types
+ (list csharp--regex-type-name)
+ (c-make-font-lock-extra-types-blurb "C#" "csharp-mode" (concat))
+ :type 'c-extra-types-widget
+ :group 'c)
(defconst csharp-font-lock-keywords-1 (c-lang-const c-matchers-1 csharp)
- "Minimal highlighting for C# mode.")
+ "Minimal font locking for C# mode.")
(defconst csharp-font-lock-keywords-2 (c-lang-const c-matchers-2 csharp)
- "Fast normal highlighting for C# mode.")
+ "Fast normal font locking for C# mode.")
(defconst csharp-font-lock-keywords-3 (c-lang-const c-matchers-3 csharp)
- "Accurate normal highlighting for C# mode.")
+ "Accurate normal font locking for C# mode.")
(defvar csharp-font-lock-keywords csharp-font-lock-keywords-3
"Default expressions to highlight in C# mode.")
+(defun csharp-font-lock-keywords-2 ()
+ (c-compose-keywords-list csharp-font-lock-keywords-2))
+(defun csharp-font-lock-keywords-3 ()
+ (c-compose-keywords-list csharp-font-lock-keywords-3))
+(defun csharp-font-lock-keywords ()
+ (c-compose-keywords-list csharp-font-lock-keywords))
+
+;;; Doc comments
+
+(defconst codedoc-font-lock-doc-comments
+ ;; Most of this is taken from the javadoc example, however, we don't use the
+ ;; '@foo' syntax, so I removed that. Supports the XML tags only
+ `((,(concat "?\\sw" ; XML tags.
+ "\\("
+ (concat "\\sw\\|\\s \\|[=\n\r*.:]\\|"
+ "\"[^\"]*\"\\|'[^']*'")
+ "\\)*/?>")
+ 0 ,csharp-codedoc-tag-face prepend nil)
+ ;; ("\\([a-zA-Z0-9_]+\\)=" 0 font-lock-variable-name-face prepend nil)
+ ;; ("\".*\"" 0 font-lock-string-face prepend nil)
+ ("&\\(\\sw\\|[.:]\\)+;" ; XML entities.
+ 0 ,csharp-codedoc-tag-face prepend nil)))
+
+(defconst codedoc-font-lock-keywords
+ `((,(lambda (limit)
+ (c-font-lock-doc-comments "///" limit
+ codedoc-font-lock-doc-comments)))))
+
+;;; End of doc comments
+
+;;; Adding syntax constructs
-(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)
+(advice-add 'c-looking-at-inexpr-block
+ :around #'csharp-looking-at-inexpr-block)
- (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)
+(defun csharp-looking-at-inexpr-block (orig-fun &rest args)
+ (let ((res (csharp-at-lambda-header)))
+ (if res
+ res
+ (apply orig-fun args))))
+(defun csharp-at-lambda-header ()
+ (save-excursion
+ (c-backward-syntactic-ws)
+ (unless (bobp)
+ (backward-char)
+ (c-safe (goto-char (scan-sexps (point) -1)))
+ (when (or (looking-at "([[:alnum:][:space:]_,]*)[ \t\n]*=>[ \t\n]*{")
+ (looking-at "[[:alnum:]_]+[ \t\n]*=>[ \t\n]*{"))
+ ;; If we are at a C# lambda header
+ (cons 'inexpr (point))))))
+
+(advice-add 'c-guess-basic-syntax
+ :around #'csharp-guess-basic-syntax)
+
+(defun csharp-guess-basic-syntax (orig-fun &rest args)
(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)
+ (;; Attributes
(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)
-
+ (goto-char (c-point 'iopl))
+ (and
+ (eq (char-after) ?\[)
+ (save-excursion
+ (c-go-list-forward)
+ (and (eq (char-before) ?\])
+ (not (eq (char-after) ?\;))))))
+ `((annotation-top-cont ,(c-point 'iopl))))
+
+ ((and
+ ;; Heuristics to find object initializers
+ (save-excursion
+ ;; Next non-whitespace character should be '{'
+ (goto-char (c-point 'boi))
+ (eq (char-after) ?{))
+ (save-excursion
+ ;; 'new' should be part of the line
+ (goto-char (c-point 'iopl))
+ (looking-at ".*new.*"))
+ ;; Line should not already be terminated
+ (save-excursion
+ (goto-char (c-point 'eopl))
+ (or (not (eq (char-before) ?\;))
+ (not (eq (char-before) ?\{)))))
+ (if (save-excursion
+ ;; if we have a hanging brace on line before
+ (goto-char (c-point 'eopl))
+ (eq (char-before) ?\{))
+ `((brace-list-intro ,(c-point 'iopl)))
+ `((block-open) (statement ,(c-point 'iopl)))))
(t
- (let (found)
+ (apply orig-fun args))))
+
+;;; End of new syntax constructs
+
+
+
+;;; Fix for strings on version 27.1
+
+(when (version= emacs-version "27.1")
+ ;; See:
+ ;; https://github.com/emacs-csharp/csharp-mode/issues/175
+ ;; https://github.com/emacs-csharp/csharp-mode/issues/151
+ ;; for the full story.
+ (defun c-pps-to-string-delim (end)
+ (let* ((start (point))
+ (no-st-s `(0 nil nil ?\" nil nil 0 nil ,start nil nil))
+ (st-s `(0 nil nil t nil nil 0 nil ,start nil nil))
+ no-st-pos st-pos
+ )
+ (parse-partial-sexp start end nil nil no-st-s 'syntax-table)
+ (setq no-st-pos (point))
+ (goto-char start)
+ (while (progn
+ (parse-partial-sexp (point) end nil nil st-s 'syntax-table)
+ (unless (bobp)
+ (c-clear-syn-tab (1- (point))))
+ (setq st-pos (point))
+ (and (< (point) end)
+ (not (eq (char-before) ?\")))))
+ (goto-char (min no-st-pos st-pos))
+ nil))
+
+ (defun c-multiline-string-check-final-quote ()
+ (let (pos-ll pos-lt)
(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 " " 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 template is inserted if the prior line is empty,
- or contains only an open curly brace;
- a template is inserted if the prior word
- closes the element;
- a template is inserted if the prior word
- closes the element;
- an template is inserted if the prior word closes
- the element;
- a template is inserted if the prior word closes
- a 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 "/ \n /// \n /// "
- 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 "/ \n /// \n /// \n /// \n /// "
- 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 "/ \n /// \n /// "
- 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 "/ "
- 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\n ///%s \n ///%s"
- 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/// \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)))))
+ (goto-char (point-max))
+ (skip-chars-backward "^\"")
+ (while
+ (and
+ (not (bobp))
+ (cond
+ ((progn
+ (setq pos-ll (c-literal-limits)
+ pos-lt (c-literal-type pos-ll))
+ (memq pos-lt '(c c++)))
+ ;; In a comment.
+ (goto-char (car pos-ll)))
+ ((save-excursion
+ (backward-char) ; over "
+ (c-is-escaped (point)))
+ ;; At an escaped string.
+ (backward-char)
+ t)
+ (t
+ ;; At a significant "
+ (c-clear-syn-tab (1- (point)))
+ (setq pos-ll (c-literal-limits)
+ pos-lt (c-literal-type pos-ll))
+ nil)))
+ (skip-chars-backward "^\""))
+ (cond
+ ((bobp))
+ ((eq pos-lt 'string)
+ (c-put-syn-tab (1- (point)) '(15)))
+ (t nil))))))
-(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.
+;;; End of fix for strings on version 27.1
- (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 `= 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))
+(defvar csharp-mode-syntax-table
+ (funcall (c-lang-const c-make-mode-syntax-table csharp))
+ "Syntax table used in `csharp-mode' buffers.")
+(defvar csharp-mode-map
+ (let ((map (c-make-inherited-keymap)))
+ map)
+ "Keymap used in `csharp-mode' buffers.")
-(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 . +)
- ))
- ))
+(easy-menu-define csharp-mode-menu csharp-mode-map "C# Mode Commands."
+ (cons "C#" (c-lang-const c-mode-menu csharp)))
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-mode))
+;; Custom variables
;;;###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'.
-
-
+ "Major mode for editing Csharp code.
Key bindings:
\\{csharp-mode-map}"
- (make-local-variable 'beginning-of-defun-function)
- (make-local-variable 'end-of-defun-function)
+ :after-hook (c-update-modeline)
+ (when (version< "29" emacs-version)
+ (warn "csharp-mode is part of Emacs as of Emacs 29 - please delete this package."))
(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))))
+ (c-common-init 'csharp-mode)
+ (setq-local c-doc-comment-style '((csharp-mode . codedoc)))
+ (run-mode-hooks 'c-mode-common-hook))
(provide 'csharp-mode)
diff --git a/csharp-tree-sitter.el b/csharp-tree-sitter.el
new file mode 100644
index 0000000..c6b2146
--- /dev/null
+++ b/csharp-tree-sitter.el
@@ -0,0 +1,425 @@
+;;; csharp-tree-sitter.el --- tree sitter support for C# -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+;; Author : Theodor Thornhill
+;; Maintainer : Jostein Kjønigsen
+;; Theodor Thornhill
+;; Created : September 2020
+;; Modified : 2020
+;; Version : 2.0.0
+;; Keywords : c# languages oop mode
+;; X-URL : https://github.com/emacs-csharp/csharp-mode
+;; Package-Requires: ((emacs "26.1") (tree-sitter "0.12.1") (tree-sitter-indent "0.1") (tree-sitter-langs "0.9.1"))
+
+;; 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 3 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. If not, see .
+
+
+;;; Code:
+(require 'cl-lib)
+(require 'cl-extra)
+(require 'seq)
+
+(when t
+ ;; In order for the package to be usable and installable (and hence
+ ;; compilable) without tree-sitter, wrap the `require's within a dummy `when'
+ ;; so they're only executed when loading this file but not when compiling it.
+ (require 'tree-sitter)
+ (require 'tree-sitter-hl)
+ (require 'tree-sitter-indent)
+ (require 'tree-sitter-langs))
+;; Vars and functions defined by the above packages:
+(defvar tree-sitter-major-mode-language-alist) ;From `tree-sitter-langs'.
+(declare-function tree-sitter-indent-mode "ext:tree-sitter-indent")
+(declare-function tree-sitter-indent-line "ext:tree-sitter-indent")
+(declare-function tree-sitter-hl-mode "ext:tree-sitter-hl")
+(declare-function tsc-node-end-position "ext:tree-sitter")
+(declare-function tsc-node-start-position "ext:tree-sitter")
+(declare-function tree-sitter-node-at-point "ext:tree-sitter")
+
+(require 'csharp-compilation)
+
+(defvar csharp-mode-syntax-table)
+(defvar csharp-mode-map)
+
+;;; Tree-sitter
+
+(defconst csharp-mode-tree-sitter-patterns
+ [ ;; Various constructs
+ (comment) @comment
+ (modifier) @keyword
+ (this_expression) @keyword
+ (escape_sequence) @keyword
+
+ ;; Literals
+ [(real_literal) (integer_literal)] @number
+ (null_literal) @constant
+ (boolean_literal) @constant
+ [(string_literal)
+ (verbatim_string_literal)
+ (interpolated_string_text)
+ (interpolated_verbatim_string_text)
+ (character_literal)
+ "\""
+ "$\""
+ "@$\""
+ "$@\""] @string
+
+ ;; Keywords
+ ["using" "namespace" "class" "if" "else" "throw" "new" "for"
+ "return" "await" "struct" "enum" "switch" "case"
+ "default" "typeof" "try" "catch" "finally" "break"
+ "foreach" "in" "yield" "get" "set" "when" "as" "out"
+ "is" "while" "continue" "this" "ref" "goto" "interface"
+ "from" "where" "select" "lock" "base" "record" "init"
+ "with" "let" "static"
+ ] @keyword
+
+ ;; Linq
+ (from_clause (identifier) @variable)
+ (group_clause)
+ (order_by_clause)
+ (select_clause (identifier) @variable)
+ (query_continuation (identifier) @variable) @keyword
+
+ ;; Enum
+ (enum_member_declaration (identifier) @variable)
+ (enum_declaration (identifier) @type)
+
+ ;; Interface
+ (interface_declaration
+ name: (identifier) @type)
+
+ ;; Struct
+ (struct_declaration (identifier) @type)
+
+ ;; Record
+ (record_declaration (identifier) @type)
+
+ (with_expression
+ (with_initializer_expression
+ (simple_assignment_expression
+ (identifier) @variable)))
+
+ ;; Namespace
+ (namespace_declaration
+ name: (identifier) @type)
+
+ ;; Class
+ (base_list (identifier) @type)
+ (property_declaration
+ (generic_name))
+ (property_declaration
+ type: (nullable_type) @type
+ name: (identifier) @variable)
+ (property_declaration
+ type: (predefined_type) @type
+ name: (identifier) @variable)
+ (property_declaration
+ type: (identifier) @type
+ name: (identifier) @variable)
+ (class_declaration
+ name: (identifier) @type)
+ (constructor_declaration (identifier) @type)
+
+ ;; Method
+ (method_declaration (identifier) @type (identifier) @function)
+ (method_declaration (predefined_type) @type (identifier) @function)
+ (method_declaration (nullable_type) @type (identifier) @function)
+ (method_declaration (void_keyword) @type (identifier) @function)
+ (method_declaration (generic_name) (identifier) @function)
+ (method_declaration (qualified_name (identifier) @type) (identifier) @function)
+
+ ;; Function
+ (local_function_statement (identifier) @type (identifier) @function)
+ (local_function_statement (predefined_type) @type (identifier) @function)
+ (local_function_statement (nullable_type) @type (identifier) @function)
+ (local_function_statement (void_keyword) @type (identifier) @function)
+ (local_function_statement (generic_name) (identifier) @function)
+
+ ;; Lambda
+ (lambda_expression
+ (identifier) @variable)
+
+ ;; Parameter
+ (parameter
+ type: (qualified_name) @type)
+ (parameter
+ type: (identifier) @type
+ name: (identifier) @variable.parameter)
+ (parameter (identifier) @variable.parameter)
+
+ ;; Array
+ (array_rank_specifier (identifier) @variable)
+ (array_type (identifier) @type)
+ (array_creation_expression)
+
+ ;; Attribute
+ (attribute (identifier) @variable (attribute_argument_list))
+ (attribute (identifier) @variable)
+
+ ;; Object init
+ (anonymous_object_creation_expression)
+ (object_creation_expression (identifier) @type)
+ (initializer_expression (identifier) @variable)
+
+ ;; Typeof
+ (type_of_expression (identifier) @variable)
+
+ ;; Member access
+ (invocation_expression (member_access_expression (generic_name (identifier) @method.call)))
+ (invocation_expression (member_access_expression (identifier)\? @method.call .))
+ (member_access_expression (identifier) @variable)
+
+ ;; Variable
+ (variable_declaration (identifier) @type)
+ (variable_declarator (identifier) @variable)
+
+ ;; Equals value
+ (equals_value_clause (identifier) @variable)
+
+ ;; Return
+ (return_statement (identifier) @variable)
+ (yield_statement (identifier) @variable)
+
+ ;; Type
+ (type_parameter
+ (identifier) @type)
+ (type_argument_list
+ (identifier) @type.argument)
+ (generic_name
+ (identifier) @type)
+ (implicit_type) @type
+ (predefined_type) @type
+ (nullable_type) @type
+ ["operator"] @type
+
+ ;; Type constraints
+ (type_parameter_constraints_clause
+ (identifier) @type)
+ ;; (type_parameter_constraint
+ ;; (identifier) @type) ;; causes parsing error in tree-sitter
+ (type_constraint
+ (identifier) @type)
+
+ ;; Exprs
+ (binary_expression (identifier) @variable (identifier) @variable)
+ (binary_expression (identifier)* @variable)
+ (conditional_expression (identifier) @variable)
+ ;; (prefix_unary_expression (identifier)* @variable) ;; crashes tree-sitter c-code with SIGABRT
+ (postfix_unary_expression (identifier)* @variable)
+ (assignment_expression (identifier) @variable)
+ (cast_expression (identifier) @type)
+
+ ;; Preprocessor
+ (preprocessor_directive) @constant
+ (preprocessor_call (identifier) @string)
+
+ ;; Loop
+ (for_each_statement (implicit_type) @type (identifier) @variable)
+ (for_each_statement (predefined_type) @type (identifier) @variable)
+ (for_each_statement (identifier) @type (identifier) @variable)
+
+ ;; Exception
+ (catch_declaration (identifier) @type (identifier) @variable)
+ (catch_declaration (identifier) @type)
+
+ ;; Switch
+ (switch_statement (identifier) @variable)
+ (switch_expression (identifier) @variable)
+
+ ;; If
+ (if_statement (identifier) @variable)
+
+ ;; Declaration expression
+ (declaration_expression (implicit_type) (identifier) @variable)
+
+ ;; Arrow expression
+ (arrow_expression_clause (identifier) @variable)
+
+ ;; Lock statement
+ (lock_statement (identifier) @variable)
+
+ ;; Other
+ ;; (argument_list
+ ;; (identifier) @variable) ;; causes parsing error in tree-sitter
+ (label_name) @variable
+ (using_directive (identifier) @type.parameter)
+ (using_directive (qualified_name) @type.parameter)
+ (using_directive (name_equals (identifier) @type.parameter))
+ ;; (await_expression (identifier)* @function) ;; crashes tree-sitter c-code with sigabrt!
+ (invocation_expression (identifier) @function)
+ (element_access_expression (identifier) @variable)
+ (conditional_access_expression (identifier) @variable)
+ (member_binding_expression (identifier) @variable)
+ (name_colon (identifier)* @variable.special)
+ (field_declaration)
+ (argument (identifier) @variable)
+
+ ;; Catch-alls
+ (identifier) @variable
+
+ ;; Interpolation
+ ;; (interpolated_string_expression) @string
+ ]
+ )
+
+;;; Tree-sitter indentation
+
+(defgroup csharp-mode-indent nil "Indent lines using Tree-sitter as backend"
+ :group 'tree-sitter)
+
+(defcustom csharp-tree-sitter-indent-offset 4
+ "Indent offset for csharp-tree-sitter-mode."
+ :type 'integer
+ :group 'csharp)
+
+(defvar tree-sitter-indent-csharp-tree-sitter-scopes
+ '((indent-all
+ ;; these nodes are always indented
+ . (accessor_declaration
+ break_statement
+ arrow_expression_clause
+ parameter_list
+ conditional_expression
+ constructor_initializer
+ argument_list
+ "."))
+ (indent-rest
+ ;; if parent node is one of these and node is not first → indent
+ . (binary_expression
+ switch_section))
+ (indent-body
+ ;; if parent node is one of these and current node is in middle → indent
+ . (enum_member_declaration_list
+ base_list
+ block
+ anonymous_object_creation_expression
+ initializer_expression
+ expression_statement
+ declaration_list
+ attribute_argument_list
+ switch_body
+ switch_expression))
+ (paren-indent
+ ;; if parent node is one of these → indent to paren opener
+ . (parenthesized_expression))
+ (align-char-to
+ ;; chaining char → node types we move parentwise to find the first chaining char
+ . ())
+ (aligned-siblings
+ ;; siblings (nodes with same parent) should be aligned to the first child
+ . (parameter
+ argument))
+ (multi-line-text
+ ;; if node is one of these, then don't modify the indent
+ ;; this is basically a peaceful way out by saying "this looks like something
+ ;; that cannot be indented using AST, so best I leave it as-is"
+ . (preprocessor_call
+ labeled_statement))
+ (outdent
+ ;; these nodes always outdent (1 shift in opposite direction)
+ . (case_switch_label))
+ (align-to-node-line
+ ;; this group has lists of alist (node type . (node types... ))
+ ;; we move parentwise, searching for one of the node
+ ;; types associated with the key node type. if found,
+ ;; align key node with line where the ancestor node
+ ;; was found.
+ . ((block . (lambda_expression)))))
+ "Scopes for indenting in C#.")
+
+;;; tree-sitter helper-functions. navigation, editing, etc.
+;;; may be subject to future upstreaming-effort
+
+(defun csharp-beginning-of-defun ()
+ "Replacement-function for `beginning-of-defun' for `csharp-tree-sitter-mode'."
+ (interactive)
+ (when-let ((declaration
+ (cl-some (lambda (decl)
+ (tree-sitter-node-at-point decl))
+ '(method_declaration
+ constructor_declaration
+ class_declaration
+ namespace_declaration))))
+ (goto-char (tsc-node-start-position declaration))))
+
+(defun csharp-end-of-defun ()
+ "Replacement-function for `end-of-defun' for `csharp-tree-sitter-mode'."
+ (interactive)
+ (when-let ((declaration
+ (cl-some (lambda (decl)
+ (tree-sitter-node-at-point decl))
+ '(method_declaration
+ constructor_declaration
+ class_declaration
+ namespace_declaration))))
+ (goto-char (tsc-node-end-position declaration))))
+
+(defun csharp-delete-method-at-point ()
+ "Deletes the method at point."
+ (interactive)
+ (when-let ((method (tree-sitter-node-at-point 'method_declaration)))
+ (delete-region (tsc-node-start-position method)
+ (tsc-node-end-position method))))
+
+(defun csharp-change-string-at-point ()
+ "Change string at point."
+ (interactive)
+ (when-let ((method (tree-sitter-node-at-point 'string_literal)))
+ (delete-region (1+ (tsc-node-start-position method))
+ (1- (tsc-node-end-position method)))))
+
+;;; end tree-sitter helper-functions.
+
+(defvar csharp-tree-sitter-mode-map
+ (let ((map (make-sparse-keymap)))
+ map)
+ "Keymap used in csharp-mode buffers.")
+
+(defvar csharp-tree-sitter-mode-syntax-table
+ (let ((table (make-syntax-table)))
+ (modify-syntax-entry ?@ "_" table)
+ table))
+
+;;;###autoload
+(define-derived-mode csharp-tree-sitter-mode prog-mode "C#"
+ "Major mode for editing Csharp code.
+
+Key bindings:
+\\{csharp-tree-sitter-mode-map}"
+ :group 'csharp
+ :syntax-table csharp-tree-sitter-mode-syntax-table
+
+ (setq-local indent-line-function #'tree-sitter-indent-line)
+ (setq-local beginning-of-defun-function #'csharp-beginning-of-defun)
+ (setq-local end-of-defun-function #'csharp-end-of-defun)
+
+ ;; https://github.com/ubolonton/emacs-tree-sitter/issues/84
+ (unless font-lock-defaults
+ (setq font-lock-defaults '(nil)))
+ (setq-local tree-sitter-hl-default-patterns csharp-mode-tree-sitter-patterns)
+ ;; Comments
+ (setq-local comment-start "// ")
+ (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+ (setq-local comment-end "")
+
+ (tree-sitter-hl-mode)
+ (tree-sitter-indent-mode))
+
+(add-to-list 'tree-sitter-major-mode-language-alist '(csharp-tree-sitter-mode . c-sharp))
+
+(provide 'csharp-tree-sitter)
+
+;;; csharp-tree-sitter.el ends here
diff --git a/gpl-2.0.txt b/gpl-2.0.txt
deleted file mode 100644
index d159169..0000000
--- a/gpl-2.0.txt
+++ /dev/null
@@ -1,339 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- 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; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- , 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
diff --git a/makefile b/makefile
index efaf0ca..8496823 100644
--- a/makefile
+++ b/makefile
@@ -1,22 +1,27 @@
-EMACS="$(shell which emacs)"
-EMACS_CLI=$(EMACS) -Q -batch -L .
-CASK=~/.cask/bin/cask
+EMACS ?= emacs
+EASK ?= eask
-package: build
- $(CASK) package
+TESTHOME=/tmp/emacs
-build: test
- $(CASK) build
+ci: build test
-test: *.el
- + $(EMACS_CLI) -l csharp-mode-tests.el -f ert-run-tests-batch-and-exit
+package:
+ $(EASK) package
+
+build: package
+ $(EASK) install
+ $(EASK) compile
+
+test:
+ @echo "Testing..."
+ $(EMACS) -Q -batch -L . -l csharp-mode-tests.el -f csharp-mode-tests-setup -f ert-run-tests-batch-and-exit
clean:
- $(CASK) clean-elc
+ $(EASK) clean-elc
rm -rf dist
+ rm -rf $(TESTHOME)
check-defuns:
grep "^(defun " csharp-mode.el | sed -r "s/\(defun ([a-z0-9-]+) .*$$/\1/" | sort >/tmp/defuns.txt
for line in `cat /tmp/defuns.txt` ; do echo -n "$$line: " ; grep "$$line" csharp-mode.el | grep -v defun | wc -l ; done >/tmp/use-count.txt
grep " 0" /tmp/use-count.txt
-
diff --git a/test-files/dotnet-nuget-error.txt b/test-files/dotnet-nuget-error.txt
new file mode 100644
index 0000000..d746737
--- /dev/null
+++ b/test-files/dotnet-nuget-error.txt
@@ -0,0 +1 @@
+/home/jostein/build/sample-app/sample-app.csproj : error NU1102: - Found 24 version(s) in Net5 [ Nearest version: 5.0.0-rc.2.20513.86 ] [/home/jostein/build/sample-app/sample-app.sln]
diff --git a/test-files/dotnet-nuget-warning.txt b/test-files/dotnet-nuget-warning.txt
new file mode 100644
index 0000000..0cd5656
--- /dev/null
+++ b/test-files/dotnet-nuget-warning.txt
@@ -0,0 +1 @@
+/home/jostein/build/sample-app/sample-app.csproj : warning NU1604: Project dependency JetBrains.Annotations does not contain an inclusive lower bound. Include a lower bound in the dependency version to ensure consistent restore results.
diff --git a/test-files/dotnet-test-fail-xunit.txt b/test-files/dotnet-test-fail-xunit.txt
new file mode 100644
index 0000000..930308d
--- /dev/null
+++ b/test-files/dotnet-test-fail-xunit.txt
@@ -0,0 +1,8 @@
+[xUnit.net 00:00:00.60] SampleApp.Module.Tests.TestClass.Some_Testcase_Gone_Wrong [FAIL]
+ X SampleApp.Module.Tests.TestClass.Some_Testcase_Gone_Wrong [2ms]
+ Error Message:
+ System.NotImplementedException : The method or operation is not implemented.
+ Stack Trace:
+ at SampleApp.Module.Testee..ctor(IHttpClient httpClient) in /home/jostein/build/sample-app/Module/Testee.cs:line 24
+ at SampleApp.Module.Tests.TestClass.Some_Testcase_Gone_Wrong() in /home/jostein/build/sample-app/tests/TestClass.cs:line 11
+
diff --git a/test-files/fontification-test.cs b/test-files/fontification-test.cs
index 10b4b0a..177badf 100644
--- a/test-files/fontification-test.cs
+++ b/test-files/fontification-test.cs
@@ -1,6 +1,6 @@
+
public const string Literal1 = @"literal without trailing slash";
public const bool1 Reference = true;
-public const string Literal2 = @"literal with trailing slash\";
public const bool2 Reference = true;
-public const string Literal3 = @"multi-line
+public static const string Literal3 = @"multi-line
literal";
diff --git a/test-files/indentation-tests.cs b/test-files/indentation-tests.cs
index 4c32a5a..650ac1e 100644
--- a/test-files/indentation-tests.cs
+++ b/test-files/indentation-tests.cs
@@ -24,13 +24,15 @@ public bool Property
}
///
- /// Codedoc on method-test
+ /// Codedoc on method-test
///
public void Foo(string a = "hkfdhkd", string b = "bbbbbb")
{
// OK!
}
+ [Fact]
+ [Test]
public void Test()
{
if (test)
@@ -117,10 +119,10 @@ public void Test()
PropB = 2
};
- yield return new InnerA.InnerB {
- PropA = 1,
- PropB = 2
- };
+ // yield return new InnerA.InnerB {
+ // PropA = 1,
+ // PropB = 2
+ // };
yield return new InnerA
{
@@ -128,11 +130,11 @@ public void Test()
May = "Yay"
};
- yield return new InnerA.InnerB
- {
- Boo = "Foo",
- May = "Yay"
- };
+ // yield return new InnerA.InnerB
+ // {
+ // Boo = "Foo",
+ // May = "Yay"
+ // };
// extra test-cases
@@ -162,21 +164,29 @@ public void Test()
/* Callback indentation test. */
SomeFunction(() => {
- // empty.
- });
+ System
+ .Console
+ .WriteLine("boo");
+ });
SomeFunction(() =>
{
- // empty.
+ System
+ .Console
+ .WriteLine("boo");
});
SomeFunction((withParam) => {
- // empty.
- });
+ System
+ .Console
+ .WriteLine("boo");
+ });
SomeFunction((withParam) =>
{
- // empty.
+ System
+ .Console
+ .WriteLine("boo");
});
}
@@ -228,3 +238,20 @@ enum AnotherEnum
Second = 2,
Third = 3
}
+
+public async Task WriteAsync()
+{
+ using (var memoryStream = new MemoryStream())
+ using (var writer = new BinaryWriter())
+ {
+ // double using should indent like this
+ }
+
+ using (var memoryStream = new MemoryStream())
+ {
+ using (var writer = new BinaryWriter())
+ {
+ // or this
+ }
+ }
+}
diff --git a/test-files/multiline-strings.cs b/test-files/multiline-strings.cs
new file mode 100644
index 0000000..cd7ee5c
--- /dev/null
+++ b/test-files/multiline-strings.cs
@@ -0,0 +1,10 @@
+public const string Literal0 = @"\
+";
+public const string Literal1 = @"\";
+public const string Literal2 = @"\\";
+public const string Literal3 = @"\\\";
+public const string Literal4 = @$"\\\\";
+public const string Literal5 = $@"\\\\
+";
+public const string Literal6 = "\t\t/* We need to ensure that \"{0}\"comes first in this list. */";
+public const string Literal7 = "";