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 "") + 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 = "";