diff options
47 files changed, 4583 insertions, 2561 deletions
diff --git a/.ert-runner b/.ert-runner new file mode 100644 index 0000000..c07ab9f --- /dev/null +++ b/.ert-runner @@ -0,0 +1 @@ +-L .
\ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed55a4c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.elc +.cask +*-pkg.el diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index ea34aa8..0000000 --- a/AUTHORS +++ /dev/null @@ -1,17 +0,0 @@ -In Emacs, this file should be read in -*- Outline -*- mode. - -Below is a list of people who have made changes or contributed to the -scala emacs mode. - -* Main mode developers -** Michel Schinz <Michel.Schinz at epfl.ch> -** Anders Bach Nielsen <andersbach.nielsen at epfl.ch> - - -* Contributions -** Iulian Dragos <dragos at epfl.ch> -** Stephane Micheloud <michelou at epfl.ch> -** Victor Rodriguez <victorr at gmail.com> -** ? <cwitty at newtonlabs.com> -** Hemant Kumar <gethemant at gmail.com> -** Ulrick Müller <ulm@gentoo.org> @@ -0,0 +1,38 @@ +;;-*- Mode: Emacs-Lisp -*- +;;; Cask --- project definition + +;; Copyright (C) 2015 Sam Halliday + +;; Author: Sam Halliday <Sam.Halliday@gmail.com> + +;;; Commentary: +;; +;; Cask is a package manager for emacs lisp projects, this generates +;; the *-pkg.el file and could be our test runner in the future. +;; +;; See http://cask.readthedocs.org/en/latest/guide/dsl.html for more +;; information about Cask. +;; +;; cask pkg-file +;; +;; cask update +;; cask install +;; +;; are particularly useful commands. +;; +;; To run the tests: +;; cask exec ert-runner +;; +;;; Code: + +(source melpa-stable) + +(package-file "scala-mode.el") + +(development + (depends-on "ert-runner") + (depends-on "ecukes") + (depends-on "espuds") + (depends-on "undercover")) + +;;; Cask ends here @@ -1,124 +0,0 @@ --*- Outline -*- - -* Here is a list of future improvements to the scala mode - -** Automatic indentation should work in all cases - - -Automatic indentation is incredibly basic and doesn't work correctly -in many situations, including: - - - multi-line "case" statements, e.g. - - case Pair(x,y) => - Console.println(x); - Console.println(y); // not indented correctly - - - multi-line "case" patterns, e.g. - - case 'a' | 'b' | 'c' - | 'd' | 'e' | 'f' // not indented correctly - - - multi-line comments, e.g. - - /* - * // not indented correctly - */ // not indented correctly - - - other cases of single-line constructs as soon as they span - multiple lines. - -** Implement customize variable to toggle smart indent on/off - -** Create templates for normal and scaladoc comments (with menu and shurtcut) - -** Scaladoc font-lock mode - -** Support for XEmacs - -** Add support for Flymode - -(require 'scala-mode) -(require 'compile) -(require 'flymake) -(require 'font-lock) - -(defvar scala-build-commad nil) -(make-variable-buffer-local 'scala-build-command) - -(add-hook 'scala-mode-hook - (lambda () - (flymake-mode-on) - )) - -(defun flymake-scala-init () - (let* ((text-of-first-line (buffer-substring-no-properties (point-min) (min 20 (point-max))))) - (progn - (remove-hook 'after-save-hook 'flymake-after-save-hook t) - (save-buffer) - (add-hook 'after-save-hook 'flymake-after-save-hook nil t) - (if (string-match "^//script" text-of-first-line) - (list "fsc" (list "-Xscript" "MainScript" "-d" "c:/tmp" buffer-file-name)) - (or scala-build-command (list "fsc" (list "-d" "c:/tmp" buffer-file-name)))) - ))) - -(push '(".+\\.scala$" flymake-scala-init) flymake-allowed-file-name-masks) -(push '("^\\(.*\\):\\([0-9]+\\): error: \\(.*\\)$" 1 2 nil 3) flymake-err-line-patterns) - -(set (make-local-variable 'indent-line-function) 'scala-indent-line) - -(defun scala-indent-line () - "Indent current line of Scala code." - (interactive) - (indent-line-to (max 0 (scala-calculate-indentation)))) - -(defun scala-calculate-indentation () - "Return the column to which the current line should be indented." - (save-excursion - (scala-maybe-skip-leading-close-delim) - (let ((pos (point))) - (beginning-of-line) - (if (not (search-backward-regexp "[^\n\t\r ]" 1 0)) - 0 - (progn - (scala-maybe-skip-leading-close-delim) - (+ (current-indentation) (* 2 (scala-count-scope-depth (point) pos)))))))) - -(defun scala-maybe-skip-leading-close-delim () - (beginning-of-line) - (forward-to-indentation 0) - (if (looking-at "\\s)") - (forward-char) - (beginning-of-line))) - -(defun scala-face-at-point (pos) - "Return face descriptor for char at point." - (plist-get (text-properties-at pos) 'face)) - -(defun scala-count-scope-depth (rstart rend) - "Return difference between open and close scope delimeters." - (save-excursion - (goto-char rstart) - (let ((open-count 0) - (close-count 0) - opoint) - (while (and (< (point) rend) - (progn (setq opoint (point)) - (re-search-forward "\\s)\\|\\s(" rend t))) - (if (= opoint (point)) - (forward-char 1) - (cond - - ;; Use font-lock-mode to ignore strings and comments - ((scala-face-at-point (- (point) 1))) - - ((looking-back "\\s)") - (incf close-count)) - ((looking-back "\\s(") - (incf open-count)) - ))) - (- open-count close-count)))) - - -(provide 'scala-extensions) - @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 <http://www.gnu.org/licenses/>. + +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: + + <program> Copyright (C) <year> <name of author> + 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 +<http://www.gnu.org/licenses/>. + + 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 +<http://www.gnu.org/philosophy/why-not-lgpl.html>. @@ -2,6 +2,9 @@ # Makefile for compiling the major mode of Emacs ############################################################################## +## This Makefile has been copied from the scala project. +## See the LICENSE at the bottom of this file. + ############################################################################## # Configuration @@ -12,41 +15,46 @@ SOURCE_DIR = $(ROOT) ############################################################################## # Variables +MODE_NAME = scala-mode + # Emacs Lisp ELISP_COMMAND ?= emacs -ELISP_OPTIONS += -batch -no-site-file -ELISP_OPTIONS += -L $(ROOT) -ELISP_OPTIONS += -f batch-byte-compile - - -ELISP_FILES += scala-mode -ELISP_FILES += scala-mode-auto -ELISP_FILES += scala-mode-inf -ELISP_FILES += scala-mode-indent -ELISP_FILES += scala-mode-navigation -ELISP_FILES += scala-mode-lib -ELISP_FILES += scala-mode-ui -ELISP_FILES += scala-mode-fontlock -ELISP_FILES += scala-mode-constants -ELISP_FILES += scala-mode-feature -ELISP_FILES += scala-mode-feature-electric -ELISP_FILES += scala-mode-feature-speedbar -ELISP_FILES += scala-mode-feature-tags - +ELISP_OPTIONS += -batch -no-site-file -q +ELISP_OPTIONS += -L $(ROOT) + + +ELISP_FILES += $(MODE_NAME)-lib +ELISP_FILES += $(MODE_NAME) +ELISP_FILES += $(MODE_NAME)-syntax +ELISP_FILES += $(MODE_NAME)-indent +ELISP_FILES += $(MODE_NAME)-paragraph +ELISP_FILES += $(MODE_NAME)-prettify-symbols +ELISP_FILES += $(MODE_NAME)-imenu +ELISP_FILES += $(MODE_NAME)-fontlock +ELISP_FILES += $(MODE_NAME)-map ELISP_SOURCES += $(ELISP_FILES:%=$(SOURCE_DIR)/%.el) +PKG_FILE += $(SOURCE_DIR)/$(MODE_NAME)-pkg.el + ############################################################################## RM ?= rm -f +RMDIR ?= rmdir TOUCH ?= touch +# Strip the version out of the pkg file +VERSION := $(shell ${ELISP_COMMAND} $(ELISP_OPTIONS) --eval '(princ (format "%s\n" (nth 2 (read (find-file "$(PKG_FILE)")))))') +MODE_NAME_VERSION = $(MODE_NAME)-$(VERSION) + ############################################################################## # Commands all: .latest-build clean: - $(RM) *.elc .latest-* autoloads.el + $(RM) *.elc .latest-* autoloads.el $(MODE_NAME_VERSION).tar + [ ! -d $(MODE_NAME_VERSION) ] || $(RM) $(MODE_NAME_VERSION)/* + [ ! -d $(MODE_NAME_VERSION) ] || $(RMDIR) $(MODE_NAME_VERSION) .PHONY: all .PHONY: clean @@ -55,10 +63,51 @@ clean: # Rules .latest-build: $(ELISP_SOURCES) - $(ELISP_COMMAND) $(ELISP_OPTIONS) $(ELISP_SOURCES) + $(ELISP_COMMAND) $(ELISP_OPTIONS) -f batch-byte-compile $(ELISP_SOURCES) @$(TOUCH) $@ ############################################################################## autoloads: $(ELISP_SOURCES) - emacs -batch -q --no-site-file --eval "(setq make-backup-files nil)" --eval "(setq generated-autoload-file (expand-file-name \"autoloads.el\"))" -f batch-update-autoloads `pwd`
\ No newline at end of file + $(ELISP_COMMAND) $(ELISP_OPTIONS) --eval "(setq make-backup-files nil)" --eval "(setq generated-autoload-file (expand-file-name \"autoloads.el\"))" -f batch-update-autoloads `pwd` + +package: + mkdir -p $(MODE_NAME_VERSION) + cp $(ELISP_SOURCES) $(PKG_FILE) $(MODE_NAME_VERSION) + tar cf $(MODE_NAME_VERSION).tar $(MODE_NAME_VERSION) + +## SCALA LICENSE +## +## Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. +## All rights reserved. +## +## This software was developed by the Programming Methods Laboratory of the +## Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. +## +## Permission to use, copy, modify, and distribute this software in source +## or binary form for any purpose with or without fee is hereby granted, +## provided that the following conditions are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimer in the +## documentation and/or other materials provided with the distribution. +## +## 3. Neither the name of the EPFL nor the names of its contributors +## may be used to endorse or promote products derived from this +## software without specific prior written permission. +## +## +## THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +## ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +## CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. @@ -1,73 +0,0 @@ -In Emacs, this file should be read in -*- Outline -*- mode. - -* Introduction - -This directory contains the Emacs mode for Scala programs. This mode -works only in GNU Emacs 21.1 or later. In particular, it doesn't work -on any version of XEmacs, or any 20.x version of GNU Emacs. - -The mode is currently very basic, and offers: - -*** Basic syntax highlighting. -*** Primitive automatic indentation. -*** Support for interaction with the Scala interpreter. -*** Minor mode for inserting ([{" in pairs. - (scala-mode-electric) -*** Tags support in Scala mode for creating, loading and completion using tags files. - The current implementation works with exuberant ctags (http://ctags.sf.net) - Please see the contrib/ directory for more information. -*** Simple interaction with speedbar with support for scala files. - Using the speedbar it is possible to get an overview of the scala - files and using the emacs tags support, to get an overview of the - tags in a scala file. - For this to work please install CEDET (http://cedet.sf.net) -*** The Scala mode has been cleaned up to work better with yasnippet from - (http://code.google.com/p/yasnippet/). This replaces the old template stuff - and the work on scaladoc. - -* Installation - -Put all ".el" files in a location where Emacs can find them, i.e. a -directory appearing in the "load-path" variable. - - (add-to-list 'load-path "/path/to/some/directory/scala-mode") - -It is recommended, but not required to compile all ".el" files to -".elc" files. This will improve load time in emacs of the scala -mode. On Linux/UNIX simply run "make" in the scala mode directory. - -Add the following line to your Emacs startup file, usually "~/.emacs": - - (require 'scala-mode-auto) - -If you want to use yasnippets with the scala mode there are some things you need to do. -First of all install yasnippets from http://code.google.com/p/yasnippet/ (the non-bundle version) - -Now add the following to your .emacs file to pick up the scala snippets - - (setq yas/my-directory "/path/to/some/directory/scala-mode/contrib/yasnippet/snippets") - (yas/load-directory yas/my-directory) - -And then add this to your .emacs file - - (add-hook 'scala-mode-hook - '(lambda () - (yas/minor-mode-on) - )) - -Restart Emacs or evaluate the above line. - -From that point on, loading a file whose name ends in ".scala" -automatically turns Scala mode on. It can also be turned on manually -using the "scala-mode" command. - -The get the best expirience from using the scala mode in emacs, please -visit the costumization options for the scala mode. - - -* Future plans - -See FUTURE file for a list of future enhancements to the scala emacs -mode. If there is something missing, please post comment on -scala-tools@listes.epfl.ch. - diff --git a/README.md b/README.md new file mode 100644 index 0000000..cc76f26 --- /dev/null +++ b/README.md @@ -0,0 +1,331 @@ +# emacs-scala-mode + +The mode intends to provide basic emacs support for the Scala language, including: + +- local indenting of code, comments and multi-line strings +- motion commands +- highlighting + +See also (emacs-sbt-mode)[https://github.com/hvesalai/emacs-sbt-mode]. + +## Installation + +The preferred mechanism is via MELPA and `use-package` as per our +[Learning Emacs](/editors/emacs/learning) guide: + +```elisp +(use-package scala-mode + :interpreter + ("scala" . scala-mode)) + ``` + +## Multi-line comments + +The start of a multi-line comment is indented to the same level with +code. + +By default, if a multi-line comment begins with `/*` it is considered +to be a Scaladoc comment. Scaladoc comments are indented according to +the Scaladoc style guide. + +```scala +/** This is a Scaladoc comment. + * 2nd line. + */ + ``` + +Alternatively, if the configurable variable +*scala-indent:use-javadoc-style* is set to `t`, multi-line comments +beginning with `/**` will be indented according to the Javadoc style, +wherein all following lines are indented under the first asterisk. + +```scala +/** + * This is a Javadoc-style comment. + * 2nd line. + */ + ``` + +All other multi-line comments are indented under the first asterisk. + +``` +/** + * Supercalifragilistic- + * expialidocious! + */ + +/* + A comment + */ + ``` + +Typing an asterisk in multi-line comment region, at the start of a +line, will trigger indent. Furthermore, if the configurable variable +`scala-indent:add-space-for-scaladoc-asterisk` is `t` (default) and +the asterisk was the last character on the line, a space will be +inserted after it. If you type a forward slash after the automatically +inserted space, the space is deleted again so that you can end the +comment without deleting the space manually. + + +## Filling (i.e. word wrap) + +Paragraph `filling` is supported for comments and multi-line strings. +Auto-fill is not supported yet. + +To re-fill a paragraph, use the `fill-paragraph` command ( `M-q` ). As +always, the column at which to wrap is controlled by the `fill-column` +variable, which you set it with the `set-fill-column` command. To set +the default, you use the `customize-variable` command or a mode-hook. + + +## Motion + +Emacs commands `forward-sexp` and `backward-sexp` ( `M-C-f`, `M-C-b` ) +motion commands will move over reserved words, literals, ids and +lists. + +Text paragraph motion (i.e. `forward-paragraph`, `backward-paragraph`) +works inside comments and multi-line strings, and it respect +Scaladoc's wiki-style markup. + +`scala-syntax:beginning-of-definition` and +`scala-syntax:end-of-definition` move the cursor forward and backward +over class, trait, object, def, val, var, and type definitions. These +functions are assigned to the buffer local variables +`beginning-of-defun-function` and `end-of-defun-function` which makes +it so that the `beginning-of-defun` and `end-of-defun` functions +behave in a way that is appropriate to scala. These functions are not +currently able to support some of the more advanced scala definition +types. + + +## Highlighting + +The highlighting of variable definitions, such as + +```var test = "some mutable variable"``` + +now result in the variable name ("test" above) to be highlighted using +the variable scala-font-lock:var-face. Per default, the value of +scala-font-lock:var-face is 'font-lock-warning-face. You can always +change the highlighting of vars by changing scala-font-lock:var-face +through the Emacs face customization (use `M-x` *customize-face*). + +Very complex scala files may need the following in your emacs init +(.emacs, etc): + +```lisp +;; For complex scala files +(setq max-lisp-eval-depth 50000) +(setq max-specpdl-size 5000) +``` + +## imenu + +scala-mode supports imenu, a library for accessing locations in +documents that is included in emacs 24. The custom variable +`scala-imenu:should-flatten-index` controls whether or not the imenu +index will be hierarchical or completely flat. The current iMenu +implementation only goes one level deep i.e. nested classes are not +traversed. scala-mode's imenu support depends heavily on the +`scala-syntax:end-of-definition` and +`scala-syntax:beginning-of-definition` functions, and as such, it +shares their limitations. + +## Joining lines (delete indentation) and removing horizontal whitespace + +Scala-mode defines its own `scala-indent:join-line` and +`scala-indent:fixup-whitespace` functions. + +Unlike the normal `join-line` (aka `delete-indentation`), +`scala-indent:join-line` detects the comment fill-prefix and removes +it. + +The `scala-indent:fixup-whitespace` first removes all horizontal +whitespace, then adds one space the context requires none to be +present (before semicolon, around dot, after `(` or `[`, before `)` or +`]`, etc). + +## Indenting + +**Where four developers meet, there are four opinions on how code should be indented**. + +`scala-mode` supports 2^4 different ways of applying local heuristics to indentation. + +Note that when using `sbt-scalariform`, your local indentation rules will be overwritten. + +### Run-on lines + +Provided by `scala-indent:default-run-on-strategy` + +The indenting engine has three modes for handling run-on lines. The +`reluctant` (default) mode is geared toward a general style of coding +and the `eager` for strictly functional style. A third mode called +`operators` is between the two. + +The difference between the modes is how they treat run-on lines. For +example, the `eager` mode will indent `map` in the following code + +```scala +val x = List(1, 2, 3) + map(x => x + 1) + ``` + +The `operators` and `eager` modes will indent the second row in the +following code, as the first line ends with an operator character. + +```scala +val x = 20 + + 21 + ``` + +The `reluctant` mode (default) will not indent the line in either +case. However, all three modes will indent the second line in these +examples as it is clear that the first line cannot terminate a +statement. + +```scala +val x = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9). + map (x => x + 1) // last token of previous line cannot terminate a statement + +val y = (List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) + map (x => x + 1)) // inside 'newlines disabled' region + ``` + +You can use empty lines in the `eager` mode to stop it from indenting +a line. For example + +```scala +val x = foo("bar") + ("zot", "kala") // indented as curry + +val y = foo("bar") + +("zot", "kala") // a tuple +``` + +However, in all three modes pressing the `tab` key repeatedly on a +line will toggle between the modes. + +### Value expressions + +Provided by `scala-indent:indent-value-expression` + +When this variable is set to *nil* (default), body of a value +expressions will be indented in the traditional way. + +```scala +val x = try { + some() + } catch { + case e => other + } finally { + clean-up() + } + ``` + +However, when the variable is set to `t`, the body will be indented +one extra step to make the `val`, `var` or `def` stand out. For +example: + +```scala +val x = try { + some() + } catch { + case e => other + } finally { + clean-up() + } + ``` + +### Parameter lists + +Provided by `scala-indent:align-parameters` + +When this variable is set to `nil` (default), parameters and run-on +lines in parameter lists will not align under or according to the +first parameter. + +```scala +val y = List( "Alpha", "Bravo", + "Charlie" ) + +val x = equals(List(1,2,3) map (x => + x + 1)) + ``` + +When the variable is set to `t`, the same will be indented as: + +```scala +val y = List( "Alpha", "Bravo", + "Charlie" ) + +val x = equals(List(1,2,3) map (x => + x + 1)) + ``` + +### Expression forms: if, for, try + +Provided by `scala-indent:align-forms` + +When this variable is set to `nil` (default), `if`, `for` and `try` +forms are not aligned specially. + +```scala +val x = if (kala) + foo + else if (koira) + bar + else + zot + +val x = try "1".toInt +catch { case e => 0} +finally { println("hello") } + +val xs = for (i <- 1 to 10) +yield i +``` + +When the variable is set to `t`, the same will be indented as: + +```scala +val x = if (kala) + foo + else if (koira) + bar + else + zot + +val x = try "1".toInt + catch { case e => 0} + finally { println("hello") } + +val xs = for (i <- 1 to 10) + yield i + ``` + +## Prettify-Symbols + +Scala-mode has a preconfigured list of prettify-symbols rules. The +`prettify-symbols-mode` minor-mode (included with emacs from version +24.4 onwards) displays text in your buffer as (usually) unicode +symbols that express the same thing to improve readability. A good +example would be displaying the boolean operators as their unicode +equivalents. + +To enable the feature just add these lines to the `scala-mode-hook`: + +```elisp +(setq prettify-symbols-alist scala-prettify-symbols-alist) +(prettify-symbols-mode) +``` + +Also feel free to customise the prettify rules by adding or removing +from the `scala-prettify-symbols-alist` alist. + +Libre fonts that seems to work well with this feature are: + +- [Source Code Pro](https://github.com/adobe-fonts/source-code-pro) +- [Hack](https://github.com/chrissimpkins/Hack) diff --git a/contrib/README b/contrib/README deleted file mode 100644 index 03818f2..0000000 --- a/contrib/README +++ /dev/null @@ -1,28 +0,0 @@ -In Emacs, this file should be read in -*- Outline -*- mode. - -* Contribution - -** Exuberant Ctags - -To let ctags know how to parse scala files, put the content of -dot-ctags into your $HOME/.ctags file. This will parse scala files and -give the following kinds. - -Scala - c classes - o objects - t traits - m case-classes - a abstract-classes - f functions - V values - v variables - T types - -The default in the scala mode is to parse scala files for all the -above kinds to produce tags. This can give a rather huge amount of -tags in speedbar for a scala file. This can be reduced by adding - ---Scala-kinds=[+|-]kinds - -where kinds are the one letter abbrevs above. diff --git a/contrib/dot-ctags b/contrib/dot-ctags deleted file mode 100644 index 09f68ff..0000000 --- a/contrib/dot-ctags +++ /dev/null @@ -1,11 +0,0 @@ ---langdef=Scala ---langmap=Scala:.scala ---regex-Scala=/^[^\*\/]*class[ \t]*([a-zA-Z0-9_]+)/\1/c,classes/ ---regex-Scala=/^[^\*\/]*object[ \t]*([a-zA-Z0-9_]+)/\1/o,objects/ ---regex-scala=/^[^\*\/]*trait[ \t]*([a-zA-Z0-9_]+)/\1/t,traits/ ---regex-Scala=/^[^\*\/]*case[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/m,case-classes/ ---regex-Scala=/^[^\*\/]*abstract[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/a,abstract-classes/ ---regex-Scala=/^[^\*\/]*def[ \t]*([a-zA-Z0-9_]+)[ \t]*.*[:=]/\1/f,functions/ ---regex-Scala=/^[^\*\/]*val[ \t]*([a-zA-Z0-9_]+)[ \t]*[:=]/\1/V,values/ ---regex-Scala=/^[^\*\/]*var[ \t]*([a-zA-Z0-9_]+)[ \t]*[:=]/\1/v,variables/ ---regex-Scala=/^[^\*\/]*type[ \t]*([a-zA-Z0-9_]+)[ \t]*[\[<>=]/\1/T,types/ diff --git a/debian/NEWS b/debian/NEWS new file mode 100644 index 0000000..3430ca9 --- /dev/null +++ b/debian/NEWS @@ -0,0 +1,13 @@ +scala-mode-el (1:1.1.0-1) unstable; urgency=medium + Replaced old version of scala-mode that isn't available in Melpa anymore with + newer version which replaced the old in Melpa(once was named scala-mode2, + now it's scala-mode) and is actively maintained. + + This new scala-mode is more tested and reliable, however, it lacks the feature + of executing scala expressions in scala REPL process. For that and many other + IDE like features(complex refactor options, type/class hierarchy, code + navigation, etc.) emacs users are encouraged to install lsp-mode(or eglot) + which works in conjunction with this mode and has support for scala Metals + language server. + + -- Sławomir Wójcik <valdaer@gmail.com> Fri, 12 Jun 2020 01:19:59 +0200 diff --git a/debian/changelog b/debian/changelog index 4a453ce..b410a4e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,42 @@ +scala-mode-el (1:1.1.0-1) unstable; urgency=medium + + [ Sławomir Wójcik ] + * Adopt package; this package is now maintained by the Debian Emacsen team. + (Closes: #766441) + * control: updated maintainer/uploaders, long description and dependencies + * Updated rules to use dh_elpa, added necessary elpa and elpa-test files + * Added watch file + * Updated docs file + * Added source format + * Added NEWS file + * Removed emacsen-install, emacsen-remove and emacsen-startup files which + were no longer necessary due to migration to dh_elpa + + [ Sławomir Wójcik and Nicholas D Steeves] + * New upstream version 1.1.0. + * Migrate to debhelper-compat 13. + * Update copyright file to reflect new upstream source, and migrate to a + machine-readable copyright file. + * Add gbp.conf. + * Introduce elpa-scala-mode; scala-mode-el is now a dummy transitional + package. + * Set Vcs URLs to new Debian Emacsen Team location. + * Declare Standards-Version 4.5.1. + + [ Nicholas D Steeves ] + * Add Breaks, Replaces, and Provides to elpa-scala-mode to ensure smooth + upgrades. + * Add epoch to version, so that apt will upgrade existing users from + 20111005-2.1 to 1.1.0-1. + * Declare Rules-Requires-Root: no. + * Declare dummy transitional package scala-mode-el as Section: oldlibs so + that deborphan will suggest its removal. + * elpa-test: Fix autopkgtests by setting autopkgtest_keep, thus retaining + the files that contain scala-mode's tests. + * Add myself to Uploaders. + + -- Nicholas D Steeves <nsteeves@gmail.com> Sun, 06 Dec 2020 15:57:01 -0500 + scala-mode-el (20111005-2.1) unstable; urgency=medium * Non-maintainer upload. diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 7f8f011..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -7 diff --git a/debian/control b/debian/control index dfd93a0..7c0f937 100644 --- a/debian/control +++ b/debian/control @@ -1,19 +1,37 @@ Source: scala-mode-el Section: editors -Priority: extra -Maintainer: Mike O'Connor <stew@debian.org> -Build-Depends: debhelper (>= 7.0.50~) -Build-Depends-Indep: emacs -Standards-Version: 3.9.2 -Vcs-Browser: http://git.vireo.org/scala-mode-el.git -Vcs-Git: git://git.vireo.org/scala-mode-el.git -Homepage: http://www.scala-lang.org +Priority: optional +Maintainer: Debian Emacsen team <debian-emacsen@lists.debian.org> +Uploaders: Sławomir Wójcik <valdaer@gmail.com>, + Nicholas D Steeves <nsteeves@gmail.com> +Build-Depends: debhelper-compat (= 13), + dh-elpa +Rules-Requires-Root: no +Standards-Version: 4.5.1 +Vcs-Browser: https://salsa.debian.org/emacsen-team/scala-mode-el +Vcs-Git: https://salsa.debian.org/emacsen-team/scala-mode-el.git +Homepage: https://github.com/hvesalai/emacs-scala-mode +Testsuite: autopkgtest-pkg-elpa -Package: scala-mode-el +Package: elpa-scala-mode Architecture: all -Depends: ${misc:Depends}, emacs23 | emacsen +Depends: ${elpa:Depends}, + ${misc:Depends} +Recommends: emacs (>= 46.0) +Enhances: emacs +Breaks: scala-mode-el (<< 1:1.1.0-1~) +Replaces: scala-mode-el (<< 1:1.1.0-1~) +Provides: scala-mode-el Description: Emacs major mode for editing scala source code - scala-mode provides syntax highlighting and indentation for scala - source code inside emacs. Included is the ability to interact with a - scala interpreter inside emacs and to send expressions from scala - source files to the running interpreter. + scala-mode provides syntax highlighting, indentation, comments/multi-line + strings, motion commands for scala source code inside emacs. IDE like features + are in lsp-mode which works in conjunction with this mode and has support for + scala Metals language server. + +Package: scala-mode-el +Section: oldlibs +Architecture: all +Depends: ${misc:Depends}, + elpa-scala-mode +Description: transitional dummy package, scala-mode-el to elpa-scala-mode + This dummy package may be safely removed. diff --git a/debian/copyright b/debian/copyright index 52e3758..2e2a9de 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,58 +1,76 @@ -Initially packaged by Mike O'Connor <stew@debian.org> on October 5, 2011 +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: emacs-scala-mode +Source: https://github.com/hvesalai/emacs-scala-mode -The software was obtained via svn checkout from: -http://lampsvn.epfl.ch/svn-repos/scala/scala-tool-support/trunk/src/emacs +Files: * +Copyright: (C) 2012 Heikki Vesalainen +License: GPL-3+ -See -http://www.scala-lang.org/node/354 +Files: Cask +Copyright: 2015 Sam Halliday +License: GPL-3+ -Authors: -Michel Schinz <Michel.Schinz at epfl.ch> -Anders Bach Nielsen <andersbach.nielsen at epfl.ch> -Iulian Dragos <dragos at epfl.ch> -Stephane Micheloud <michelou at epfl.ch> -Victor Rodriguez <victorr at gmail.com> -? <cwitty at newtonlabs.com> -Hemant Kumar <gethemant at gmail.com> -Ulrick Müller <ulm@gentoo.org> +Files: scala-mode-prettify-symbols.el +Copyright: (C) 2016 Merlin Göttlinger +License: GPL-3+ -Copyright: -Copyright (C) 2009-2011 Scala Dev Team at EPFL -Copyright (c) 2002-2011 EPFL, Lausanne +Files: Makefile +Copyright: (C) 2002-2011 EPFL, Lausanne, unless otherwise specified. +License: SCALA-LICENSE -License: +Files: debian/* +Copyright: (C) 2012 Mike O'Connor <stew@debian.org> + (C) 2020 Sławomir Wójcik <valdaer@gmail.com> +License: GPL-3+ -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. +License: GPL-3+ + 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 <http://www.gnu.org/licenses/>. + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in `/usr/share/common-licenses/GPL-3' +License: SCALA-LICENSE + Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. + All rights reserved. + . + This software was developed by the Programming Methods Laboratory of the + Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. + . + Permission to use, copy, modify, and distribute this software in source + or binary form for any purpose with or without fee is hereby granted, + provided that the following conditions are met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + 3. Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + . + . + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/debian/docs b/debian/docs index a4f04e5..b43bf86 100644 --- a/debian/docs +++ b/debian/docs @@ -1,3 +1 @@ -README -FUTURE -contrib
\ No newline at end of file +README.md diff --git a/debian/elpa b/debian/elpa new file mode 100644 index 0000000..abf136d --- /dev/null +++ b/debian/elpa @@ -0,0 +1 @@ +*.el diff --git a/debian/elpa-test b/debian/elpa-test new file mode 100644 index 0000000..b4a1e32 --- /dev/null +++ b/debian/elpa-test @@ -0,0 +1,2 @@ +ert_eval = (load-file "test/test-helper.el") +autopkgtest_keep = test/* diff --git a/debian/emacsen-install b/debian/emacsen-install deleted file mode 100644 index 87e574f..0000000 --- a/debian/emacsen-install +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -e - -FLAVOR=$1 -if [ ${FLAVOR} = emacs ]; then exit 0; fi -if [ ${FLAVOR%${FLAVOR#?}} = 'x' ]; then echo "ignoring unsupported flavor: $FLAVOR" ; exit 0 ; fi - -PACKAGE=scala-mode -echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR} - -# The byte-compiled files goes into the site-lisp directory. -BCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE} -install -m 755 -d ${BCDIR} - -# The elisp source files are in the generic site-list directory. -SRCDIR=/usr/share/emacs/site-lisp/${PACKAGE} -SRC=`find ${SRCDIR} -name '*.el' -exec basename '{}' ';'` - -# Prepare for byte-compiling the source files. -cd ${BCDIR} -ln -sf ${SRCDIR}/*.el . -cat << EOF > path.el -(setq load-path (cons "." load-path) byte-compile-warnings nil) -EOF - -# Byte-compile elisp files. -FLAGS="--no-site-file --no-init-file --batch -l path.el -f batch-byte-compile" -${FLAVOR} ${FLAGS} ${SRC} -rm -f path.el - -exit 0 diff --git a/debian/emacsen-remove b/debian/emacsen-remove deleted file mode 100644 index 401f332..0000000 --- a/debian/emacsen-remove +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -e -FLAVOR=$1 -PACKAGE=scala-mode -if [ ${FLAVOR} != emacs ]; then - echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR} - rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE} -fi -exit 0 diff --git a/debian/emacsen-startup b/debian/emacsen-startup deleted file mode 100644 index c57f2e8..0000000 --- a/debian/emacsen-startup +++ /dev/null @@ -1,21 +0,0 @@ -;; -*-emacs-lisp-*- - -(if (not (file-exists-p "/usr/share/emacs/site-lisp/scala-mode")) - (message "Package scala-mode-el removed but not purged. Skipping setup.") - - (let ((instdir (concat "/usr/share/" - (symbol-name debian-emacs-flavor) - "/site-lisp/scala-mode"))) - - ;; Only load scala-mode if it has been installed. - (if (zerop (length (file-expand-wildcards instdir))) - (message "scala-mode not for this flavor. Skipping.") - - ;; The sml-mode package follows the Debian/GNU Linux 'emacsen' policy and - ;; byte-compiles its elisp files for each 'emacs flavor'. The compiled - ;; code is then installed in a subdirectory of the respective site-lisp - ;; directory. - (debian-pkg-add-load-path-item instdir) - - ;; Autoload sml-mode top-level functions site-wide. - (load "scala-mode-auto")))) diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..d7d8bf2 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,9 @@ +[DEFAULT] +upstream-branch = upstream +debian-branch = master +upstream-tag = v%(version)s +debian-tag = debian/%(version)s + +sign-tags = True +pristine-tar = False +pristine-tar-commit = False diff --git a/debian/rules b/debian/rules index 47ae75c..e8e22ba 100755 --- a/debian/rules +++ b/debian/rules @@ -1,45 +1,4 @@ #!/usr/bin/make -f -PACKAGE=scala-mode-el - -clean: - dh_testdir - dh_testroot - rm -f build-stamp - $(MAKE) clean - dh_clean - -build: build-arch build-indep -build-arch: build-stamp -build-indep: build-stamp - -build-stamp: - dh_testdir - $(MAKE) autoloads - touch $@ - -install: build - dh_testdir - dh_testroot - dh_prep - dh_installdirs usr/share/emacs/site-lisp/scala-mode - cp *el debian/scala-mode-el/usr/share/emacs/site-lisp/scala-mode - -binary-indep: install - dh_testdir - dh_testroot - dh_installchangelogs - dh_installdocs - dh_installemacsen - dh_compress - dh_fixperms - dh_installdeb - dh_gencontrol - dh_md5sums - dh_builddeb - -binary-arch: build install - -binary: binary-indep binary-arch - -.PHONY: build clean binary-indep binary-arch binary install +%: + dh $@ --with elpa diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..6b129f9 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=4 +opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/emacs-scala-mode-$1\.tar\.gz/ \ + https://github.com/hvesalai/emacs-scala-mode/tags .*/v?(\d\S+)\.tar\.gz diff --git a/scala-mode-auto.el b/scala-mode-auto.el deleted file mode 100644 index 3b45785..0000000 --- a/scala-mode-auto.el +++ /dev/null @@ -1,87 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-auto.el - Autoloads file for the scala mode - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; We now depend on font-locking features only in emacs 21.x and newer -(unless (<= 21 emacs-major-version) - (error - (format "The Scala mode require Emacs version 21.x (and not your Emacs version %s.%s)" emacs-major-version emacs-minor-version))) - -;; TODO insert check for correct version of speedbar - - -;; Attach .scala files to the scala-mode -(add-to-list 'auto-mode-alist '("\\.scala\\'" . scala-mode)) -(modify-coding-system-alist 'file "\\.scala$" 'utf-8) - - -;; Autoload from scala-mode.el -(autoload (quote scala-mode) "scala-mode" "\ -Major mode for editing Scala code. - -When started, run `scala-mode-hook'. - -\\{scala-mode-map}" t nil) - - -;; Autoload from scala-mode-inf.el -(autoload (quote scala-interpreter-running-p-1) "scala-mode-inf" nil t nil) - -(autoload (quote scala-run-scala) "scala-mode-inf" "Run a Scala interpreter in an Emacs buffer" t nil) - -(autoload (quote scala-switch-to-interpreter) "scala-mode-inf" "Switch to buffer containing the interpreter" t nil) - -(autoload (quote scala-eval-region) "scala-mode-inf" "Send current region to Scala interpreter." t nil) - -(autoload (quote scala-eval-buffer) "scala-mode-inf" "Send whole buffer to Scala interpreter." t nil) - -(autoload (quote scala-load-file) "scala-mode-inf" "Load a file in the Scala interpreter." t nil) - -(autoload (quote scala-quit-interpreter) "scala-mode-inf" "Quit Scala interpreter." t nil) - - -(provide 'scala-mode-auto) diff --git a/scala-mode-constants.el b/scala-mode-constants.el deleted file mode 100644 index a98380b..0000000 --- a/scala-mode-constants.el +++ /dev/null @@ -1,204 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-constants.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-constants) - -(require 'cl) -(require 'regexp-opt) - -;; Helper functions - -(defun scala-regexp-opt-charset (chars) - ;; - ;; Return a regexp to match a character in CHARS. - ;; - ;; The basic idea is to find character ranges. Also we take care in the - ;; position of character set meta characters in the character set regexp. - ;; - (let* ((charmap (make-char-table 'case-table)) - (start -1) (end -2) - (charset "") - (bracket "") (dash "") (caret "")) - ;; - ;; Make a character map but extract character set meta characters. - (dolist (char chars) - (case char - (?\] - (setq bracket "]")) - (?^ - (setq caret "^")) - (?- - (setq dash "-")) - (otherwise - (aset charmap char t)))) - ;; - ;; Make a character set from the map using ranges where applicable. - (map-char-table - (lambda (c v) - (when v - (if (listp c) (setq start (car c) end (cdr c)) - (if (= (1- c) end) (setq end c) - (if (> end (+ start 2)) - (setq charset (format "%s%c-%c" charset start end)) - (while (>= end start) - (setq charset (format "%s%c" charset start)) - (incf start))) - (setq start c end c))))) - charmap) - (when (>= end start) - (if (> end (+ start 2)) - (setq charset (format "%s%c-%c" charset start end)) - (while (>= end start) - (setq charset (format "%s%c" charset start)) - (incf start)))) - ;; - ;; Make sure a caret is not first and a dash is first or last. - (if (and (string-equal charset "") (string-equal bracket "")) - (concat "[" dash caret "]") - (concat "[" bracket charset caret dash "]")))) - - -;; Constants - - -(defconst scala-number-re - "[[:digit:]]+\\(\\.[[:digit:]]+\\)?\\([eE][+-]?[[:digit:]]+\\)?[fl]?" - "Regular expression matching a Scala number (integer or float).") - -(defconst scala-rawstring-re - "\"\"\"[^\"\"\"]*\"\"\"" - "Regular expression matching a Scala raw string literal.") - -(defconst scala-string-re - "\"\\([^\"\\\\]\\|\\\\\.\\)*\"" - "Regular expression matching a Scala string literal.") - -(defconst scala-char-re - "'\\([^\\\\]\\|\\(\\\\[^']\\)\\)'" - "Regular expression matching a Scala character literal.") - -(defconst scala-literal-re - (concat "\\(" "\\(" scala-number-re "\\)" - "\\|" "\\(" scala-rawstring-re "\\)" - "\\|" "\\(" scala-string-re "\\)" - "\\|" "\\(" scala-char-re "\\)" "\\)") - "Regular expression matching any Scala literal.") - -(defconst scala-most-special-chars (mapcar 'identity "<>+-*/|@#%&!?$^`~") - "List of almost all Scala special characters. -Not included in this list are the special characters which are -reserved keywords when used alone.") - -(defconst scala-all-special-chars (append (mapcar 'identity ":;,=") - scala-most-special-chars) - "List of all Scala special characters.") - -(defconst scala-most-special-char-re - (scala-regexp-opt-charset scala-most-special-chars) - "Regular expression matching a single Scala special character") - -(defconst scala-all-special-char-re - (scala-regexp-opt-charset scala-all-special-chars) - "Regular expression matching a single Scala special character") - -(defconst scala-keywords-re - (regexp-opt '("abstract" "case" "class" "catch" "def" "do" "else" "extends" - "final" "finally" "for" "forSome" "if" "implicit" "import" "lazy" - "new" "match" "mixin" "object" "override" "package" "private" - "protected" "requires" "return" "sealed" "super" "this" "throw" - "trait" "try" "type" "val" "var" "with" "while" "yield") - 'words)) - -(defconst scala-constants-re - (regexp-opt '("true" "false" "null") 'words)) - -(defconst scala-special-ident-re - (concat "\\(" scala-all-special-char-re "\\{2,\\}" - "\\|" scala-most-special-char-re "+" - "\\)")) - -(defconst scala-ident-re - (let* ((varid-re "[[:alnum:]]+") - (id-re (concat "\\(" varid-re "\\|" scala-special-ident-re "\\)"))) - (concat id-re - "\\(" "_+" "\\(" id-re "\\)?" "\\)*")) - "Regular expression matching a Scala identifier.") - -(defconst scala-var-ident-re - (concat "[[:lower:]][[:alnum:]]*" "\\(_" scala-ident-re "\\)*") - "Relgular expression matching a Scala 'variable' identifier.") - -(defconst scala-qual-ident-re - (concat scala-ident-re "\\(" "\\." scala-ident-re "\\)*")) - -(defconst scala-capitalized-ident-re - (concat "\\(\\)\\([[:upper:]]" scala-ident-re "\\)")) - -(defconst scala-expr-start-re - (concat - (regexp-opt '("if" "else" "for" "do" "yield") 'words) "\\|" - (regexp-opt '("=" "=>") t))) - -(defconst scala-expr-starter - (mapcar (lambda (pair) (cons (car pair) (concat "\\<" (cdr pair) "\\>"))) - '(("else" . "if") - ("yield" . "for") - ("do" . "for") - ("extends" . "class") - ("with" . "class") - ("=>" . "case")))) - -(defconst scala-expr-middle-re - (regexp-opt (mapcar #'car scala-expr-starter) 'words)) - -(defconst scala-compound-expr-re - "\\<else\\s +if\\>") - -(defconst scala-comment-begin-or-end-re - (concat "\\(" "^/\\*.*" "\\|" "^//.*" "\\|" ".*\\*/$" "\\)")) - diff --git a/scala-mode-feature-electric.el b/scala-mode-feature-electric.el deleted file mode 100644 index 09182b6..0000000 --- a/scala-mode-feature-electric.el +++ /dev/null @@ -1,180 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-feature-electric.el - electric editing commands for scala files - -;; Copyright (C) 2009 by Hemant Kumar (gethemant at gmail to com) -;; Modified by Anders Bach Nielsen <andersbach.nielsen at epfl dot ch> to fit into the scala mode -;; Based on ruby-electric by Dee Zsombor <dee dot zsombor at gmail dot com>. -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-feature-electric) - -;; Customization - -(defgroup scala-mode-feature:electric nil - "Minor mode providing electric editing commands for scala files" - :group 'scala) - - -(defcustom scala-mode-feature:electric-expand-delimiters-list '(all) - "*List of contexts where matching delimiter should be -inserted. The word 'all' will do all insertions." - :type '(set :extra-offset 8 - (const :tag "Everything" all ) - (const :tag "Curly brace" ?\{ ) - (const :tag "Square brace" ?\[ ) - (const :tag "Round brace" ?\( ) - (const :tag "Quote" ?\' ) - (const :tag "Double quote" ?\" ) - (const :tag "Back quote" ?\` ) - (const :tag "Vertical bar" ?\| )) - :group 'scala-mode-feature:electric) - - -(defcustom scala-mode-feature:electric-newline-before-closing-bracket nil - "*Controls whether a newline should be inserted before the -closing bracket or not." - :type 'boolean - :group 'scala-mode-feature:electric) - - -(defcustom scala-mode-feature:electric-on-per-default nil - "*Controls whether scala electric mode should be on per default or not." - :type 'boolean - :group 'scala-mode-feature:electric) - -;; Variables - -(defvar scala-mode-feature-electric-matching-delimeter-alist - '((?\[ . ?\]) - (?\( . ?\)) - (?\' . ?\') - (?\` . ?\`) - (?\" . ?\"))) - - -(defvar scala-mode-feature-electric-mode scala-mode-feature:electric-on-per-default - "nil disables scala electric mode, non-nil enables.") - -(defvar scala-mode-feature-electric-mode-map (make-sparse-keymap) - "Keymap for scala electric minor mode.") - -;;; Mode setup - -(make-variable-buffer-local 'scala-mode-feature-electric-mode) - -(defun scala-mode-feature-electric-mode (&optional arg) - "" - (interactive "P") - (setq scala-mode-feature-electric-mode - (if (null arg) - ;; Toggle mode - (not scala-mode-feature-electric-mode) - ;; Enable/Disable according to arg - (> (prefix-numeric-value arg) 0))) - ) - -;; Alias for some backwards compat -(defalias 'scala-electric-mode 'scala-mode-feature-electric-mode) - - -;; Functions -(defun scala-mode-feature-electric-active-p () - scala-mode-feature-electric-mode) - -(defun scala-mode-feature-electric-code-at-point-p() - (and scala-mode-feature-electric-mode - (let* ((properties (text-properties-at (point)))) - (and (null (memq 'font-lock-string-face properties)) - (null (memq 'font-lock-comment-face properties)))))) - -(defun scala-mode-feature-electric-string-at-point-p() - (and scala-mode-feature-electric-mode - (consp (memq 'font-lock-string-face (text-properties-at (point)))))) - -(defun scala-mode-feature-electric-is-last-command-char-expandable-punct-p() - (or (memq 'all scala-mode-feature:electric-expand-delimiters-list) - (memq last-command-char scala-mode-feature:electric-expand-delimiters-list))) - -(defun scala-mode-feature-electric-curlies(arg) - (interactive "P") - (self-insert-command (prefix-numeric-value arg)) - (if (scala-mode-feature-electric-is-last-command-char-expandable-punct-p) - (cond ((scala-mode-feature-electric-code-at-point-p) - (insert " ") - (save-excursion - (if scala-mode-feature:electric-newline-before-closing-bracket - (newline)) - (insert "}"))) - ((scala-mode-feature-electric-string-at-point-p) - (save-excursion - (backward-char 1) - (when (char-equal ?\# (preceding-char)) - (forward-char 1) - (insert "}"))))))) - -(defun scala-mode-feature-electric-matching-char(arg) - (interactive "P") - (self-insert-command (prefix-numeric-value arg)) - (and (scala-mode-feature-electric-is-last-command-char-expandable-punct-p) - (scala-mode-feature-electric-code-at-point-p) - (save-excursion - (insert (cdr (assoc last-command-char - scala-mode-feature-electric-matching-delimeter-alist)))))) - -(defun scala-mode-feature-electric-install () - (or (assoc 'scala-mode-feature-electric-mode minor-mode-alist) - (setq minor-mode-alist - (cons '(scala-mode-feature-electric-mode " electric") minor-mode-alist))) - - (or (assoc 'scala-mode-feature-electric-mode minor-mode-map-alist) - (setq minor-mode-map-alist - (cons (cons 'scala-mode-feature-electric-mode scala-mode-feature-electric-mode-map) - minor-mode-map-alist))) - - (define-key scala-mode-feature-electric-mode-map "{" 'scala-mode-feature-electric-curlies) - (define-key scala-mode-feature-electric-mode-map "(" 'scala-mode-feature-electric-matching-char) - (define-key scala-mode-feature-electric-mode-map "[" 'scala-mode-feature-electric-matching-char) - (define-key scala-mode-feature-electric-mode-map "\"" 'scala-mode-feature-electric-matching-char) - - t) diff --git a/scala-mode-feature-speedbar.el b/scala-mode-feature-speedbar.el deleted file mode 100644 index 94d05ac..0000000 --- a/scala-mode-feature-speedbar.el +++ /dev/null @@ -1,89 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-feature-speedbar.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-feature-speedbar) - -(eval-when-compile - (require 'scala-mode-feature-tags)) - -(require 'speedbar) - -;; Customization - -(defgroup scala-mode-feature:speedbar nil - "Options how the speedbar works under Scala mode" - :group 'scala) - - -(defcustom scala-mode-feature:speedbar-open nil - "Normally scala-mode starts with the speedbar closed.\ -Turning this on will open it whenever scala-mode is loaded." - :type 'boolean - :set (lambda (sym val) - (set-default sym val) - (when val - (speedbar 1))) - :group 'scala-mode-feature:speedbar) - - -(defun scala-mode-feature-speedbar-install () - (define-key speedbar-file-key-map "\C-t" '(lambda () (interactive) - (speedbar-flush-expand-line))) - - (add-hook 'speedbar-mode-hook - (lambda() - (speedbar-add-supported-extension "\\.scala"))) - - (setq speedbar-fetch-etags-command scala-mode-feature:tags-command) - - (setq speedbar-fetch-etags-arguments '("-e" "-f -")) - - (add-to-list 'speedbar-fetch-etags-parse-list - '("\\.scala" . speedbar-parse-c-or-c++tag)) - - t) diff --git a/scala-mode-feature-tags.el b/scala-mode-feature-tags.el deleted file mode 100644 index 9ccfbf1..0000000 --- a/scala-mode-feature-tags.el +++ /dev/null @@ -1,174 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-feature-tags.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-feature-tags) - -(require 'etags) - -(defgroup scala-mode-feature:tags nil - "Creating and using TAGS file searches" - :group 'scala) - - -(defcustom scala-mode-feature:tags-command "ctags" - "Tags command for parsing scala code. -Please see the contrib directory for ctags options for parsing scala files." - :type 'string - :group 'scala-mode-feature:tags) - - -(defcustom scala-mode-feature:tags-option "-e -o %s -R %s" - "Options for the ctags command" - :type 'string - :group 'scala-mode-feature:tags) - - -(defcustom scala-mode-feature:tags-ask-when-reload nil - "Indicates whether the user should confirm reload a TAGS table or not." - :type 'boolean - :group 'scala-mode-feature:tags) - -(defvar scala-mode-feature-tags-completion-table nil - "") - -(defvar scala-mode-feature-tags-tag-file nil - "") - -(defun scala-mode-feature-tags-create (dir-name) - "Create TAGS file" - (interactive "DTAGS file directory: ") - (message "Creating TAGS, please wait...") - (let* - ((tags-file-name (concat dir-name "/TAGS")) - (args (format scala-mode-feature:tags-option tags-file-name dir-name))) - (shell-command - (concat scala-mode-feature:tags-command " " args)) - (flet ((yes-or-no-p (p) (if scala-mode-feature:tags-ask-when-reload - (y-or-n-p p) - t))) - (visit-tags-table tags-file-name)) - (setq scala-mode-feature-tags-tag-file tags-file-name))) - - -(defun scala-mode-feature-tags-load (file-name) - "Load TAGS file" - (interactive "fTAGS file: ") - (if (and (file-exists-p file-name) (file-readable-p file-name)) - (progn - (visit-tags-table file-name) - (setq scala-mode-feature-tags-tag-file file-name)) - (message "The TAGS file does not exist!"))) - - -(defun scala-mode-feature-tags-complete () - "Perform completion on the text around point. -Completes to the set of names listed in the current tags table. -The string to complete is chosen in the same way as the default -for \\[find-tag] (which see)." - (interactive) - (let ((pattern (scala-mode-feature-tags-get-pattern)) - beg - completion - (scala-comp scala-mode-feature-tags-completion-table)) - (if (not pattern) (message "Nothing to complete") - (search-backward pattern) - (setq beg (point)) - (forward-char (length pattern)) - (setq completion (try-completion pattern scala-comp nil)) - (cond - ((eq completion t)) - ((null completion) - (message "Can't find completion for \"%s\"" pattern) - (ding)) - ((not (string= pattern completion)) - (delete-region beg (point)) - (insert completion)) - (t - (message "Making completion list...") - (with-output-to-temp-buffer "*Completions*" - (display-completion-list - (all-completions pattern scala-comp))) - (message "Making completion list...%s" "done")))))) - - -(defun scala-mode-feature-tags-completion-table () - (or (and scala-mode-feature-tags-tag-file - scala-mode-feature-tags-completion-table) - (let ((tags-table - (if (and scala-mode-feature-tags-tag-file - (functionp 'etags-tags-completion-table)) - (with-current-buffer (get-file-buffer scala-mode-feature-tags-tag-file) - (etags-tags-completion-table)) - nil))) - (unless tags-table - (error "No TAGS file active!")) - (setq scala-mode-feature-tags-completion-table tags-table)))) - - -(defun scala-mode-feature-tags-get-pattern () - (save-excursion - (while (looking-at "\\sw\\|\\s_") - (forward-char 1)) - (if (or (re-search-backward "\\sw\\|\\s_" - (save-excursion (beginning-of-line) (point)) - t) - (re-search-forward "\\(\\sw\\|\\s_\\)+" - (save-excursion (end-of-line) (point)) - t)) - (progn (goto-char (match-end 0)) - (buffer-substring-no-properties - (point) - (progn (forward-sexp -1) - (while (looking-at "\\s'") - (forward-char 1)) - (point)))) - nil))) - -(defun scala-mode-feature-tags-install () - - t) diff --git a/scala-mode-feature.el b/scala-mode-feature.el deleted file mode 100644 index 825a7dc..0000000 --- a/scala-mode-feature.el +++ /dev/null @@ -1,69 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-feature.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-feature) - -;; Feature loading - -(defvar scala-mode-feature-list - '(scala-mode-feature-tags - scala-mode-feature-speedbar - scala-mode-feature-electric - ) - "List of features") - -(defvar scala-mode-feature-installed-p nil) - -(defun scala-mode-feature-install () - (unless scala-mode-feature-installed-p - (dolist (feature scala-mode-feature-list) - (when (require feature nil t) - (apply - (intern (concat (symbol-name feature) "-install")) - (list)))) - (setq scala-mode-feature-installed-p t))) diff --git a/scala-mode-fontlock.el b/scala-mode-fontlock.el index 508ce57..7617259 100644 --- a/scala-mode-fontlock.el +++ b/scala-mode-fontlock.el @@ -1,209 +1,584 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-fontlock.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; scala-mode-fontlock.el - Major mode for editing scala, font-lock +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file -(provide 'scala-mode-fontlock) +(require 'scala-mode-syntax) + +(defcustom scala-font-lock:constant-list '() + "A list of strigs that should be fontified in constant +face. This customization property takes effect only after the +scala-mode has been reloaded." + :type '(repeat string) + :group 'scala) + +(defun scala-font-lock:create-user-constant-re () + (regexp-opt scala-font-lock:constant-list 'words)) + +(defun scala-font-lock:mark-reserved-symbols (limit) + (when (re-search-forward scala-syntax:reserved-symbols-re limit t) + (goto-char (match-end 2)))) ;; step back to the match (re matches futher) + +(defun scala-font-lock:mark-underscore (limit) + (when (re-search-forward scala-syntax:reserved-symbol-underscore-re limit t) + (goto-char (match-end 2)))) ;; step back to the match (re matches futher) + +;(defun scala-font-lock:extend-region-function () + +(defun scala-font-lock:limit-pattern2 (&optional start) + (save-excursion + (when start (goto-char start)) + (scala-syntax:skip-forward-ignorable) + (ignore-errors + (while (and (not (or (eobp) + (looking-at scala-syntax:other-keywords-unsafe-re) + (scala-syntax:looking-at-reserved-symbol nil))) + (scala-syntax:looking-at-simplePattern-beginning)) +; (message "- now at %d" (point)) + (if (= (char-after) ?\() + (forward-list) + ;; else + (goto-char (match-end 0)) + (scala-syntax:skip-forward-ignorable) +; (message "+ now at %d" (point)) + (cond ((looking-at "(") + (forward-list)) + ((looking-at "@") + (goto-char (match-end 0))) + ((or (scala-syntax:looking-at-reserved-symbol nil) + (looking-at scala-syntax:other-keywords-unsafe-re)) +; (messssage "saw reserved symbol or keyword") + nil) + ((looking-at scala-syntax:id-re) +; (message "saw id-re %d" (match-beginning 0)) + (goto-char (match-end 0))) +; (t +; (message "nothing special here %s" (point))) + )) + (scala-syntax:skip-forward-ignorable))) +; (message "limit at %s" (point)) + (point))) + +(defun scala-font-lock:limit-pattern2-list (&optional start) + (let ((limit (scala-font-lock:limit-pattern2 start))) + (while (= (char-after limit) ?,) + (setq limit (scala-font-lock:limit-pattern2 (1+ limit)))) +; (message "list limit at %s" limit) + limit)) + +(defun scala-font-lock:mark-pattern1-part (&optional limit pattern-p) + "Parses a part of val, var and case pattern (or id). Always +parses a variable or constant name first and then type, leaving +the pointer at the next variablename, constant name, list or +Pattern3, if any, and setting up match data 1 (variable), +2 (constant) and 3 (type) accordingly. If there is no variable +name before the first type, then the match data for the variable +name is nil. Returns t if something was matched or nil if nothing +was found. + +If pattern-p is defined, then only varid is matched as variable +and everything else is constant. + +Does not continue past limit. +" +; (message "will stop at %d" limit) + (cond + ;; quit if we are past limit + ((or (and limit (>= (point) limit)) + (eobp)) +; (message "at limit %s" (point)) + nil) + ;; Type pattern, just skip the whole thing. It will end at ',' or ')'. + ;; Note: forms starting with ':' are handled by a completely separete + ;; font-lock matcher. + ((scala-syntax:looking-at-reserved-symbol ":") +; (message ":") + (while (not (or (eobp) + (scala-syntax:looking-at "[,);]") + (scala-syntax:looking-at-reserved-symbol "|") + (scala-syntax:looking-at-reserved-symbol + scala-syntax:double-arrow-unsafe-re) + (scala-syntax:looking-at-empty-line-p))) + (scala-syntax:forward-sexp) + (scala-syntax:skip-forward-ignorable)) + (set-match-data nil) + t) + ;; Binding part cannot start with reserved symbols. If they + ;; are seen, we must quit. + ((scala-syntax:looking-at-reserved-symbol nil) +; (message "symbol") + nil) + ((scala-syntax:looking-at-stableIdOrPath) +; (message "stableId") + (let ((beg (match-beginning 0)) + (end (match-end 0)) + (varid (scala-syntax:looking-at-varid-p))) + (goto-char end) + (let ((new-match-data + (cond + ((= (char-after end) ?\() + ;; matched type +; (message "it's a type") + `(,beg ,end nil nil nil nil ,beg ,end)) + ((progn (scala-syntax:backward-sexp) + (= (char-before) ?.)) + ;; matched constant + `(,beg ,end nil nil ,(point) ,end nil nil)) + ((or varid (not pattern-p)) + ;; matched variable name or we can't be sure + `(,beg ,end ,beg ,end nil nil nil nil)) + (t + ;; matched constant + `(,beg ,end nil nil ,beg ,end nil nil))))) + (goto-char end) + (scala-syntax:skip-forward-ignorable) + (cond + ((and (not (or (scala-syntax:looking-at-reserved-symbol nil) + (scala-syntax:looking-at-reserved-symbol "|"))) + (scala-syntax:looking-at-stableIdOrPath)) + (setq new-match-data + (append (butlast new-match-data 2) + `(,(match-beginning 0) + ,(match-end 0)))) + (goto-char (match-end 0)) + (scala-syntax:skip-forward-ignorable)) + ((= (char-after) ?@) + (forward-char) + (scala-syntax:skip-forward-ignorable))) + (set-match-data new-match-data))) + t) + ;; Pattern3 can be a literal. Just skip them. + ((looking-at scala-syntax:literal-re) +; (message "literal") + (goto-char (match-end 0)) + (scala-syntax:skip-forward-ignorable) + (set-match-data nil) + t) + ;; Start of a patterns list or alternatives. Skip if alternatives or + ;; else leave point at start of first element. + ((= (char-after) ?\() +; (message "(") + (let ((alternatives-p + (save-excursion + (forward-char) + (ignore-errors + ;; forward-sexp will terminate the loop with error + ;; if '|' is not found before end of list ')' + (while (not (or (eobp) + (= (char-before) ?|) + (scala-syntax:looking-at-empty-line-p))) + (scala-syntax:forward-sexp)) + t)))) + (if alternatives-p + (forward-list) + (forward-char))) + (scala-syntax:skip-forward-ignorable) + (set-match-data nil) + t) + ;; continuation or end of list, just skip and position at the + ;; next element + ((or (= (char-after) ?,) + (= (char-after) ?\))) +; (message ", or )") + (forward-char) + (scala-syntax:skip-forward-ignorable) + (set-match-data nil) + t) + ;; none of the above, just stop + (t +; (message "Cannot continue Pattern1 at %d" (point)) + nil) +)) + +(defun scala-font-lock:limit-pattern (&optional start) + (save-excursion + (goto-char (scala-font-lock:limit-pattern2 start)) +; (message "now at %d" (point)) + (when (scala-syntax:looking-at-reserved-symbol ":") + (while (not (or (eobp) + (scala-syntax:looking-at-reserved-symbol "|") + (scala-syntax:looking-at-reserved-symbol + scala-syntax:double-arrow-unsafe-re) + (scala-syntax:looking-at-empty-line-p))) + (scala-syntax:forward-sexp) + (scala-syntax:skip-forward-ignorable))) + (if (or (/= (char-after) ?|) + (scala-syntax:looking-at-reserved-symbol + scala-syntax:double-arrow-unsafe-re)) + (point) + (forward-char) + (scala-font-lock:limit-pattern)))) + +(defun scala-font-lock:mark-pattern-part (&optional limit) + (when (scala-syntax:looking-at-reserved-symbol "|") +; (message "skipping |") + (forward-char) + (scala-syntax:skip-forward-ignorable)) + (scala-font-lock:mark-pattern1-part limit t)) + +(defun scala-font-lock:limit-type (&optional start) + start) + + +(defun scala-font-lock:limit-simpleType (&optional start) + (when start (goto-char start)) + (scala-syntax:skip-forward-ignorable) + (setq start (point)) -(require 'cl) -(require 'font-lock) -(require 'scala-mode-constants) -(require 'scala-mode-lib) -(require 'scala-mode-navigation) - - -(defun scala-mark-borders (funs) - (loop for (fun . flag) in funs - if flag collect (point-marker) - while (funcall fun) - if flag collect (point-marker))) - -(defun scala-make-match (funs) - (let ((start-mark (point-marker)) - (markers (scala-mark-borders funs)) - (end-mark (point-marker))) - (cons start-mark (cons end-mark markers)))) - -(defconst scala-binding-end-re - (regexp-opt '(":" "=" "=>" ";" "<-"))) - -(defun scala-match-and-skip-binding (limit) - (skip-chars-forward " ()") - (and (not (or (looking-at "\\<\\(extends\\|with\\)\\>\\|{") - (scala-looking-at-special-identifier scala-binding-end-re))) - (ignore-errors - (save-restriction - (narrow-to-region (point-min) limit) - (let ((matches (scala-make-match - '((scala-forward-ident . t) - ((lambda () - (scala-forward-spaces) - (when (scala-looking-at-special-identifier ":") - (forward-char) - (scala-forward-spaces) - t)) . nil) - ((lambda () - (scala-forward-type) - (scala-when-looking-at "\\s *\\*") - t) . t))))) - (scala-when-looking-at "\\s *,") - (set-match-data matches))) - t))) - -(defun scala-match-and-skip-ident (limit) - (scala-forward-spaces) - (when (and (not (looking-at scala-keywords-re)) - (looking-at scala-qual-ident-re)) + (if (= (char-after) ?\() + (ignore-errors (forward-list)) + (scala-font-lock:mark-simpleType)) + (when (and (not (eobp)) (= (char-after) ?#)) + (scala-font-lock:mark-simpleType)) + (when (and (not (eobp)) (= (char-after) ?\[)) + (ignore-errors (forward-list)) + (scala-syntax:skip-forward-ignorable)) + (let ((limit (point))) + (goto-char start) +; (message "simpeType limit at %d" limit) + limit)) + +(defun scala-font-lock:mark-simpleType (&optional limit) +; (message "looking for simpleType at %d" (point)) + (cond + ;; stop at limit + ((and limit (>= (point) limit)) + nil) + ;; just dive into lists + ((> (skip-chars-forward "[(,)]") 0) +; (message "skipping list-marks") + (scala-syntax:skip-forward-ignorable) + (set-match-data nil) + t) + ;; jump over blocks + ((= (char-after) ?\{) + (ignore-errors + (forward-list) + (set-match-data nil) + t)) + ;; ignore arrows and reserved words and symbols + ((or (scala-syntax:looking-at-reserved-symbol + scala-syntax:double-arrow-unsafe-re) + (scala-syntax:looking-at-reserved-symbol + "<[:%]\\|>?:") + (looking-at "\\<forSome\\>")) +; (message "skipping reserved") + (goto-char (match-end 0)) + (scala-syntax:skip-forward-ignorable) + (set-match-data nil) + t) + ;; color id after '#' + ((= (char-after) ?#) +; (message "at #") + (forward-char) + (if (and (not (or (looking-at scala-syntax:keywords-unsafe-re) + (scala-syntax:looking-at-reserved-symbol nil))) + (looking-at scala-syntax:id-re)) + (goto-char (match-end 0)) nil)) + ;; color paths (including stableid) + ((scala-syntax:looking-at-stableIdOrPath t) +; (message "at path") + (let ((end (match-end 0))) + (goto-char end) + (while (scala-syntax:looking-back-token "this\\|type") + (goto-char (match-beginning 0)) + (skip-chars-backward ".")) + (unless (scala-syntax:looking-back-token scala-syntax:id-re) + (set-match-data nil)) + (goto-char end)) + (scala-syntax:skip-forward-ignorable) + t) + (t +; (message "Cannot continue simpleType at %d" (point)) + nil))) + +(defun scala-font-lock:mark-string-escapes (limit) + (when (re-search-forward scala-syntax:string-escape-re limit t) (goto-char (match-end 0)) - t)) - -(defun scala-match-and-skip-type-param (limit) - (scala-when-looking-at "\\s *[[,]\\s *" - (let ((matches (scala-make-match '((scala-forward-type-param . t))))) - (scala-when-looking-at "\\s *\\]") - (set-match-data matches) - t))) - -(defun scala-match-and-skip-result-type (limit) - (scala-when-looking-at "\\s *:\\s *" - (set-match-data (list (point-marker) - (progn (scala-forward-type) (point-marker)))) - t)) - -(defconst scala-pattern-end-re - (regexp-opt '("if" "case" "class") 'words)) - -(defconst scala-pattern-end-special-re - (regexp-opt '( "=>" "=" "<-") t)) - -(defun scala-match-and-skip-pattern (limit) - (while (progn - (skip-chars-forward "()[], ") - (and (not (or (looking-at scala-pattern-end-re) - (scala-looking-at-special-identifier - scala-pattern-end-special-re))) - (looking-at scala-literal-re))) - (goto-char (match-end 0))) - (and (not (or (looking-at scala-pattern-end-re) - (scala-looking-at-special-identifier scala-pattern-end-special-re))) - (let ((case-fold-search nil)) - (cond ((looking-at scala-capitalized-ident-re) - (goto-char (match-end 0))) - ((scala-match-and-skip-binding limit) t))))) - - -(defvar scala-font-lock-keywords + (or (eq (nth 3 (save-excursion (syntax-ppss (match-beginning 0)))) ?\") + (scala-font-lock:mark-string-escapes limit)))) + +(defun scala-font-lock:mark-numberLiteral (re limit) + (when (re-search-forward re limit t) + (if (string-match-p scala-syntax:number-safe-start-re + ;; get char-before match or a magic ',', which is safe + (string (or (char-before (match-beginning 0)) ?,))) + t + (scala-font-lock:mark-numberLiteral re limit)))) + +(defun scala-font-lock:mark-floatingPointLiteral (limit) + (scala-font-lock:mark-numberLiteral + scala-syntax:floatingPointLiteral-re + limit)) + +(defun scala-font-lock:mark-integerLiteral (limit) + (scala-font-lock:mark-numberLiteral + scala-syntax:integerLiteral-re + limit)) + +(defun scala-font-lock:keywords () + ;; chars, string, comments are handled acording to syntax and + ;; syntax propertize + `(;; keywords - (,scala-keywords-re 0 font-lock-keyword-face nil) - - ;; constants - (,scala-constants-re - 0 ,(if (boundp 'font-lock-constant-face) - 'font-lock-constant-face - 'font-lock-keyword-face) - nil) - - ;; modules - (,(concat "\\<\\(module\\|object\\)\\>\\s *\\(" scala-ident-re "\\)") - (2 font-lock-variable-name-face nil)) - - ;; type definitions - (,(concat "\\<type\\>\\s *\\(" scala-ident-re "\\)") - (1 font-lock-type-face nil)) - - ;; variables - ("\\<var\\>" - (scala-match-and-skip-binding (goto-char (match-end 0)) - nil - (1 font-lock-variable-name-face nil) - (2 font-lock-type-face nil t))) - - ;; functions - (,(concat "\\(^\\|[^(,]\\)\\s *\\<def\\>" - "\\s *" - "\\(" - scala-ident-re - "\\)\\s *") - (2 font-lock-function-name-face nil) - (scala-match-and-skip-type-param (goto-char (match-end 0)) nil - (1 font-lock-type-face nil t)) - (scala-match-and-skip-binding nil nil - (1 font-lock-variable-name-face nil) - (2 font-lock-type-face nil t)) - (scala-match-and-skip-result-type nil nil - (0 font-lock-type-face nil))) - - ;; class definitions - ("\\<\\(class\\|trait\\)\\>" - (scala-match-and-skip-ident (goto-char (match-end 0)) nil - (1 font-lock-type-face nil)) - (scala-match-and-skip-type-param nil nil - (1 font-lock-type-face nil t)) - (scala-match-and-skip-binding nil nil - (1 font-lock-variable-name-face nil) - (2 font-lock-type-face nil t))) - - ;; "extends" and "with" clauses - ("\\<\\(extends\\|with\\)\\>\\s *[^{]" - (scala-match-and-skip-ident (goto-char (1- (match-end 0))) nil - (0 font-lock-type-face nil)) - (scala-match-and-skip-type-param nil nil - (1 font-lock-type-face nil t))) - - ;; patterns - ("\\<\\(case\\|val\\)\\>\\s *" - (scala-match-and-skip-pattern (goto-char (match-end 0)) nil - (1 font-lock-variable-name-face nil) - (2 font-lock-type-face nil t))) + (,scala-syntax:override-re 2 scala-font-lock:override-face) + (,scala-syntax:abstract-re 2 scala-font-lock:abstract-face) + (,scala-syntax:final-re 2 scala-font-lock:final-face) + (,scala-syntax:sealed-re 2 scala-font-lock:sealed-face) + (,scala-syntax:implicit-re 2 scala-font-lock:implicit-face) + (,scala-syntax:lazy-re 2 scala-font-lock:lazy-face) + (,scala-syntax:private-re 2 scala-font-lock:private-face) + (,scala-syntax:protected-re 2 scala-font-lock:protected-face) + (,scala-syntax:other-keywords-re 2 font-lock-keyword-face) + (,scala-syntax:value-keywords-re 2 font-lock-constant-face) + (,scala-syntax:path-keywords-re 2 font-lock-keyword-face) + + ;; User defined constants + (,(scala-font-lock:create-user-constant-re) 0 font-lock-constant-face) + + ;; Annotations + (, (rx (and "@" (in "a-zA-Z_.") (0+ (in "a-zA-Z0-9_.")))) + . font-lock-preprocessor-face) + + ;; reserved symbols + (scala-font-lock:mark-reserved-symbols 2 font-lock-keyword-face) + + ;; 'Symbols + (,scala-syntax:symbolLiteral-re 1 font-lock-string-face) + + ;; underscore + (scala-font-lock:mark-underscore 2 font-lock-keyword-face) + + ;; escapes inside strings + (scala-font-lock:mark-string-escapes (0 font-lock-constant-face prepend nil)) + + ;; object + (,(concat "\\<object[ \t]+\\(" + scala-syntax:id-re + "\\)") + 1 font-lock-constant-face) + + ;; class, trait, object + (,(concat "\\<\\(class\\|trait\\)[ \t]+\\(" + scala-syntax:id-re + "\\)") + 2 font-lock-type-face) + + ;; ;; extends, with, new + ;; (,(concat "\\<\\(extends\\|with\\|new\\)[ \t]+\\([(" + ;; scala-syntax:id-first-char-group "]\\)") + ;; (scala-font-lock:mark-simpleType (scala-font-lock:limit-simpleType + ;; (goto-char (match-beginning 2))) + ;; nil + ;; (0 font-lock-type-face nil t))) + + ;; ;; ':' + ;; (,scala-syntax:colon-re + ;; (scala-font-lock:mark-simpleType (scala-font-lock:limit-simpleType + ;; (goto-char (match-end 2))) + ;; nil + ;; (0 font-lock-type-face nil t))) + + ;; def + (,(concat "\\<def[ \t]+\\(" scala-syntax:id-re "\\)") 1 font-lock-function-name-face) + + ;; VarDcl + ("\\<val[ \t]+\\([^:]\\)" + (scala-font-lock:mark-pattern1-part (scala-font-lock:limit-pattern2-list + (goto-char (match-beginning 1))) + nil + (1 font-lock-variable-name-face nil t) + (2 font-lock-constant-face nil t) + (3 font-lock-type-face nil t))) + + ("\\<var[ \t]+\\([^:]\\)" + (scala-font-lock:mark-pattern1-part (scala-font-lock:limit-pattern2-list + (goto-char (match-beginning 1))) + nil + (1 scala-font-lock:var-face nil t) + (2 font-lock-constant-face nil t) + (3 font-lock-type-face nil t) + )) + + ;; case (but not case class|object) + ("\\<case[ \t]+\\([^:]\\)" + (scala-font-lock:mark-pattern-part (scala-font-lock:limit-pattern + (goto-char (match-beginning 1))) + nil + (1 font-lock-variable-name-face nil t) + (2 font-lock-constant-face nil t) + (3 font-lock-type-face nil t))) + + ;; type ascription (: followed by alpha type name) + (,(rx + (or (not (in "!#%&*+-/:<=>?@\\^|~")) line-start) + (group ":") + (0+ space) + (group (in "a-zA-Z_") + (0+ (in "a-zA-Z0-9_")) + (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~")))))) + (1 font-lock-keyword-face) (2 font-lock-type-face)) + + ;; type ascription (: followed by punctuation type name) + (,(rx + (or (not (in "!#%&*+-/:<=>?@\\^|~")) line-start) + (group ":") + (1+ space) + (group (1+ (in "-!#%&*+/:<=>?@\\^|~")))) + (1 font-lock-keyword-face) (2 font-lock-type-face)) + + ;; extends followed by type + (,(rx symbol-start + (group "extends") + (1+ space) + (group (or + (and (in "a-zA-Z_") + (0+ (in "a-zA-Z0-9_")) + (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) + (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) + (1 font-lock-keyword-face) (2 font-lock-type-face)) + + ;; with followed by type + (,(rx symbol-start + (group "with") + (1+ space) + (group (or + (and (in "a-zA-Z_") + (0+ (in "a-zA-Z0-9_")) + (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) + (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) + (1 font-lock-keyword-face) (2 font-lock-type-face)) + + ;; new followed by type + (,(rx symbol-start + (group "new") + (1+ space) + (group (or + (and (in "a-zA-Z_") + (0+ (in "a-zA-Z0-9_")) + (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) + (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) + (1 font-lock-keyword-face) (2 font-lock-type-face)) + + ;; uppercase means a type or object + (,(rx symbol-start + (and (in "A-Z") + (0+ (in "a-zA-Z0-9_")) + (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~")))))) + . font-lock-constant-face) + ;; . font-lock-type-face) + ; uncomment this to go back to highlighting objects as types + + ;; uppercase + (,(rx symbol-start + (group + (and (in "A-Z") + (0+ (in "a-zA-Z0-9_")) + (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))))) + . font-lock-constant-face) + + ;; package name + (,(rx symbol-start + (group "package") + (1+ space) + (group (and (in "a-zA-Z_.") (0+ (in "a-zA-Z0-9_."))))) + (1 font-lock-keyword-face) (2 font-lock-string-face)) + + ;; number literals (have to be here so that other rules take precedence) + (scala-font-lock:mark-floatingPointLiteral . font-lock-constant-face) + (scala-font-lock:mark-integerLiteral . font-lock-constant-face) + + (scala-syntax:interpolation-matcher 0 font-lock-variable-name-face t) + )) +(defun scala-font-lock:syntactic-face-function (state) + "Return correct face for string or comment" + (if (and (integerp (nth 4 state)) + (save-excursion + (goto-char (nth 8 state)) + (looking-at "/\\*\\*\\($\\|[^*]\\)"))) + ;; scaladoc (starts with /** only) + font-lock-doc-face + (if (nth 3 state) font-lock-string-face font-lock-comment-face))) + +(defface scala-font-lock:var-face + '((t (:inherit font-lock-warning-face))) + "Font Lock mode face used to highlight scala variable names." + :group 'scala) -(defvar scala-font-lock-syntactic-keywords - `((,scala-char-re (0 "\"" t nil)) - (scala-search-special-identifier-forward (0 "w" nil nil)))) +(defvar scala-font-lock:var-face 'scala-font-lock:var-face + "Face for scala variable names.") +(defface scala-font-lock:private-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the private keyword." + :group 'scala) +(defvar scala-font-lock:private-face 'scala-font-lock:private-face + "Face for the scala private keyword.") +(defface scala-font-lock:protected-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the protected keyword." + :group 'scala) + +(defvar scala-font-lock:protected-face 'scala-font-lock:protected-face + "Face for the scala protected keyword.") + +(defface scala-font-lock:override-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the override keyword." + :group 'scala) + +(defvar scala-font-lock:override-face 'scala-font-lock:override-face + "Face for the scala override keyword.") + +(defface scala-font-lock:sealed-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the sealed keyword." + :group 'scala) + +(defvar scala-font-lock:sealed-face 'scala-font-lock:sealed-face + "Face for the scala sealed keyword.") + +(defface scala-font-lock:abstract-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the abstract keyword." + :group 'scala) + +(defvar scala-font-lock:abstract-face 'scala-font-lock:abstract-face + "Face for the scala abstract keyword.") + +(defface scala-font-lock:final-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the final keyword." + :group 'scala) + +(defvar scala-font-lock:final-face 'scala-font-lock:final-face + "Face for the scala final keyword.") + +(defface scala-font-lock:implicit-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the implicit keyword." + :group 'scala) + +(defvar scala-font-lock:implicit-face 'scala-font-lock:implicit-face + "Face for the scala implicit keyword.") + +(defface scala-font-lock:lazy-face + '((t (:inherit font-lock-builtin-face))) + "Font Lock mode face used for the lazy keyword." + :group 'scala) + +(defvar scala-font-lock:lazy-face 'scala-font-lock:lazy-face + "Face for the scala lazy keyword.") + +(defface scala-font-lock:var-keyword-face + '((t (:inherit font-lock-keyword-face))) + "Font Lock mode face used for the var keyword." + :group 'scala) + +(defvar scala-font-lock:var-keyword-face 'scala-font-lock:var-keyword-face + "Face for the scala var keyword.") + +(provide 'scala-mode-fontlock) diff --git a/scala-mode-imenu.el b/scala-mode-imenu.el new file mode 100644 index 0000000..f06a52e --- /dev/null +++ b/scala-mode-imenu.el @@ -0,0 +1,134 @@ +;;; scala-mode-imenu.el - Major mode for editing scala +;;; Copyright (c) 2014 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +;;; Code: + +(require 'scala-mode-syntax) + +;; Make lambdas proper clousures (only in this file) +(make-local-variable 'lexical-binding) +(setq lexical-binding t) + +(defcustom scala-imenu:should-flatten-index t + "Controls whether or not the imenu index is flattened or hierarchical." + :type 'boolean + :safe #'booleanp + :group 'scala) +(defcustom scala-imenu:build-imenu-candidate + 'scala-imenu:default-build-imenu-candidate + "Controls whether or not the imenu index has definition type information." + :type 'function + :group 'scala) +(defcustom scala-imenu:cleanup-hooks nil + "Functions that will be run after the construction of each imenu" + :type 'hook + :group 'scala) + +(defun scala-imenu:flatten-list (incoming-list &optional predicate) + (when (not predicate) (setq predicate 'listp)) + (cl-mapcan (lambda (x) (if (funcall predicate x) + (scala-imenu:flatten-list x predicate) (list x))) incoming-list)) + +(defun scala-imenu:flatten-imenu-index (index) + (cl-mapcan (lambda (x) (if (listp (cdr x)) + (scala-imenu:flatten-imenu-index (cdr x)) + (list x))) index)) + +(defun scala-imenu:create-imenu-index () + (let ((imenu-index (cl-mapcar 'scala-imenu:build-imenu-candidates + (scala-imenu:create-index)))) + (dolist (cleanup-hook scala-imenu:cleanup-hooks) + (funcall cleanup-hook)) + (if scala-imenu:should-flatten-index + (scala-imenu:flatten-imenu-index imenu-index) + imenu-index))) + +(defun scala-imenu:build-imenu-candidates (member-info &optional parents) + (if (listp (car member-info)) + (let* ((current-member-info (car member-info)) + (child-member-infos (cdr member-info)) + (current-member-result + (scala-imenu:destructure-for-build-imenu-candidate + current-member-info parents)) + (current-member-name (car current-member-result))) + (if child-member-infos + (let ((current-member-members + (scala-imenu:build-child-members + (append parents `(,current-member-info)) + (cdr member-info)))) + `(,current-member-name . + ,(cons current-member-result current-member-members))) + current-member-result)) + (scala-imenu:destructure-for-build-imenu-candidate member-info parents))) + +(defun scala-imenu:build-child-members (parents child-members) + (cl-mapcar (lambda (child) (scala-imenu:build-imenu-candidates + child parents)) child-members)) + +(defun scala-imenu:destructure-for-build-imenu-candidate (member-info parents) + (cl-destructuring-bind (member-name definition-type marker) + member-info (funcall scala-imenu:build-imenu-candidate + member-name definition-type marker parents))) + + +(defun scala-imenu:default-build-imenu-candidate (member-name definition-type + marker parents) + (let* ((all-names + (append (cl-mapcar (lambda (parent) (car parent)) parents) + `(,member-name))) + (member-string (mapconcat 'identity all-names "."))) + `(,(format "(%s)%s" definition-type member-string) . ,marker))) + +(defun scala-imenu:create-index () + (let ((class nil) (index nil)) + (goto-char (point-max)) + (while (setq class (scala-imenu:parse-nested-from-end)) + (setq index (cons class index))) + index)) + +(defun scala-imenu:parse-nested-from-end () + (let ((last-point (point)) (class-name nil) (definition-type nil)) + (scala-syntax:beginning-of-definition) + ;; We're done if scala-syntax:beginning-of-definition has no effect. + (if (eq (point) last-point) nil + (progn (looking-at scala-syntax:all-definition-re) + (setq class-name (match-string-no-properties 2)) + (setq definition-type (match-string-no-properties 1))) + `(,`(,class-name ,definition-type ,(point-marker)) . + ,(scala-imenu:nested-members))))) + +(defun scala-imenu:parse-nested-from-beginning () + (scala-syntax:end-of-definition) + (scala-imenu:parse-nested-from-end)) + +(defun scala-imenu:nested-members () + (let ((start-point (point))) + (save-excursion + (scala-syntax:end-of-definition) + ;; This gets us inside of the class definition + ;; It seems like there should be a better way + ;; to do this. + (backward-char) + (reverse (scala-imenu:get-nested-members start-point))))) + +(defvar scala-imenu:nested-definition-types '("class" "object" "trait")) + +(defun scala-imenu:get-nested-members (parent-start-point) + (scala-syntax:beginning-of-definition) + (if (< parent-start-point (point)) + (cons (scala-imenu:get-member-info-at-point) + (scala-imenu:get-nested-members parent-start-point)) + nil)) + +(defun scala-imenu:get-member-info-at-point () + (looking-at scala-syntax:all-definition-re) + (let* ((member-name (match-string-no-properties 2)) + (definition-type (match-string-no-properties 1))) + (if (member definition-type scala-imenu:nested-definition-types) + (save-excursion (scala-imenu:parse-nested-from-beginning)) + `(,member-name ,definition-type ,(point-marker))))) + + +(provide 'scala-mode-imenu) +;;; scala-mode-imenu.el ends here diff --git a/scala-mode-indent.el b/scala-mode-indent.el index ac3ec3d..d2237bd 100644 --- a/scala-mode-indent.el +++ b/scala-mode-indent.el @@ -1,228 +1,963 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-indent.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; scala-mode.el - Major mode for editing scala, indenting +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file -(provide 'scala-mode-indent) +(require 'scala-mode-syntax) +(require 'scala-mode-lib) + +(eval-when-compile + (defvar scala-indent:effective-run-on-strategy) + (defvar scala-indent:previous-indent-pos)) -(defcustom scala-mode-indent:step 2 - "Indentation step." +(defcustom scala-indent:step 2 + "The number of spaces an indentation step should be. The actual +indentation will be one or two steps depending on context." :type 'integer + :safe #'integerp + :group 'scala) + +(defcustom scala-indent:indent-value-expression nil + "Whether or not to indent multi-line value expressions, with +one extra step. When true, indenting will be + +val x = try { + some() + } catch { + case e => other + } finally { + clean-up() + } + +When nil, the same will indent as + +val x = try { + some() +} catch { + case e => other +} finally { + clean-up() +} +" + :type 'boolean + :group 'scala) + +(defcustom scala-indent:align-parameters nil + "Whether or not to indent parameter lists so that next + parameter lines always align under the first parameter. When + non-nil, indentation will be + +def foo(x: Int, y: List[Int] + z: Int) + +val x = foo(1, List(1, 2, 3) map (i => + i + 1 + ), 2) + +When nil, the same will indent as + +def foo(x: Int, y: List[Int] + z: Int) + +val x = foo(1, List(1, 2, 3) map (i => + i + 1 + ), 2) +" + :type 'boolean + :safe #'booleanp + :group 'scala) + +(defcustom scala-indent:align-forms nil + "Whether or not to align 'else', 'yield', 'catch', 'finally' +below their respective expression start. When non-nil, identing +will be + +val x = if (foo) + bar + else + zot + +when nil, the same will indent as + +val x = if (foo) + bar + else + zot +" + :type 'boolean + :group 'scala) + +(defconst scala-indent:eager-strategy 0 + "See 'scala-indent:run-on-strategy'") +(defconst scala-indent:operator-strategy 1 + "See 'scala-indent:run-on-strategy'") +(defconst scala-indent:reluctant-strategy 2 + "See 'scala-indent:run-on-strategy'") +(defconst scala-indent:keywords-only-strategy 3 + "A strategy used internally by indent engine") + +(defcustom scala-indent:default-run-on-strategy 2 + "What strategy to use for detecting run-on lines, i.e. lines +that continue a statement from the previous line. Possible values +are: + +'reluctant', which marks only lines that begin with -- or +that follow a line that ends with -- a reserved word that cannot start +or end a line, such as 'with'. + +'operators', which extends the previous strategy by marking also +lines that begin with -- or that follow a line that ends with -- +an operator character. For example, '+', '-', etc. + +'eager', which marks all rows which could be run-ons, i.e. which +are not ruled out by the language specification. +" + :type `(choice (const :tag "eager" ,scala-indent:eager-strategy) + (const :tag "operators" ,scala-indent:operator-strategy) + (const :tag "reluctant" ,scala-indent:reluctant-strategy)) + :group 'scala) + +(make-variable-buffer-local 'scala-indent:effective-run-on-strategy) + +(defcustom scala-indent:add-space-for-scaladoc-asterisk t + "When non-nil, a space will be added after a scaladoc asterisk, +when it is added to an empty line." + :type 'boolean + :safe #'booleanp + :group 'scala) + +(defcustom scala-indent:use-javadoc-style nil + "When non-nil, multi-line comments are indented according to Javadoc +style (i.e. indented to the first asterisk). This overrides the +Scaladoc behavior of indenting comment lines to the second asterisk." + :type 'boolean + :safe #'booleanp :group 'scala) +(defun scala-indent:run-on-strategy () + "Returns the currently effecti run-on strategy" + (or scala-indent:effective-run-on-strategy + scala-indent:default-run-on-strategy + scala-indent:eager-strategy)) + +(defun scala-indent:toggle-effective-run-on-strategy () + "If effective run-on strategy is not set, it is set as follows: +- if default is eager or operators, then it is set to reluctant +- if default is reluctant, then it is set to eager. If it is set, +it is nilled." + (if scala-indent:effective-run-on-strategy + (setq scala-indent:effective-run-on-strategy nil) + (let ((new-strategy + (cond ((= (scala-indent:run-on-strategy) + scala-indent:reluctant-strategy) + scala-indent:eager-strategy) + ((or (= (scala-indent:run-on-strategy) + scala-indent:operator-strategy) + (= (scala-indent:run-on-strategy) + scala-indent:eager-strategy)) + scala-indent:reluctant-strategy)))) + (setq scala-indent:effective-run-on-strategy new-strategy)))) + +(defun scala-indent:reset-effective-run-on-strategy () + (setq scala-indent:effective-run-on-strategy nil)) + +(defun scala-indent:rotate-run-on-strategy () + (interactive) + (let ((new-strategy + (cond ((= scala-indent:default-run-on-strategy + scala-indent:reluctant-strategy) + scala-indent:operator-strategy) + ((= scala-indent:default-run-on-strategy + scala-indent:operator-strategy) + scala-indent:eager-strategy) + ((= scala-indent:default-run-on-strategy + scala-indent:eager-strategy) + scala-indent:reluctant-strategy)))) + (setq scala-indent:default-run-on-strategy new-strategy) +; (message "scala-indent:default-run-on-strategy set to %s" scala-indent:default-run-on-strategy) + )) + +(defun scala-indent:backward-sexp-to-beginning-of-line () + "Skip sexps backwards until reaches beginning of line (i.e. the +point is at the first non whitespace or comment character). It +does not move outside enclosin list. Returns the current point or +nil if the beginning of line could not be reached because of +enclosing list." + (let ((code-beg (scala-lib:point-after + (scala-syntax:beginning-of-code-line)))) + (ignore-errors + (while (> (point) code-beg) + (scala-syntax:backward-sexp) + (skip-syntax-backward ".") + (when (< (point) code-beg) + ;; moved to previous line, set new target + (setq code-beg (scala-lib:point-after + (scala-syntax:beginning-of-code-line)))))) + (unless (> (point) code-beg) + (point)))) + +(defun scala-indent:align-anchor () + "Go to beginning of line, if a) scala-indent:align-parameters +is nil or backward-sexp-to-beginning-of-line is non-nil. This has +the effect of staying within lists if +scala-indent:align-parameters is non-nil." + (when (or (scala-indent:backward-sexp-to-beginning-of-line) + (not scala-indent:align-parameters)) + (back-to-indentation))) + +(defun scala-indent:value-expression-lead (start anchor &optional not-block-p) + ;; calculate an indent lead. The lead is one indent step if there is + ;; a '=' between anchor and start, otherwise 0. + (if (and scala-indent:indent-value-expression + (ignore-errors + (save-excursion + (let ((block-beg (if not-block-p + start + (nth 1 (syntax-ppss start))))) + (goto-char anchor) + (scala-syntax:has-char-before ?= block-beg))))) + scala-indent:step 0)) + +;;; +;;; Run-on +;;; + +(defconst scala-indent:mustNotTerminate-keywords-re + (regexp-opt '("extends" "forSome" "match" "with") 'words) + "Some keywords which occure only in the middle of an +expression") + +(defconst scala-indent:mustNotTerminate-line-beginning-re + (concat "\\(" scala-indent:mustNotTerminate-keywords-re + "\\|:\\(" scala-syntax:after-reserved-symbol-re "\\)\\)") + "All keywords and symbols that cannot terminate a expression +and must be handled by run-on. Reserved-symbols not included.") + +(defconst scala-indent:mustTerminate-re + (concat "\\([,;\u21D2]\\|=>?" scala-syntax:end-of-code-line-re + "\\|\\s(\\|" scala-syntax:empty-line-re "\\)") + "Symbols that must terminate an expression or start a +sub-expression, i.e the following expression cannot be a +run-on. This includes only parenthesis, '=', '=>', ',' and ';' +and the empty line") + +(defconst scala-indent:mustNotContinue-re + (regexp-opt '("abstract" "catch" "case" "class" "def" "do" "else" "final" + "finally" "for" "if" "implicit" "import" "lazy" "new" "object" + "override" "package" "private" "protected" "return" "sealed" + "throw" "trait" "try" "type" "val" "var" "while" "yield" "inline") + 'words) + "Words that we don't want to continue the previous line") + +(defconst scala-indent:mustBeContinued-line-end-re + (concat "\\(" scala-syntax:other-keywords-unsafe-re + "\\|:" scala-syntax:end-of-code-line-re "\\)") + "All keywords and symbols that cannot terminate a expression +and are infact a sign of run-on. Reserved-symbols not included.") + +(defun scala-indent:run-on-p (&optional point strategy) + "Returns t if the current point is in the middle of an expression" + ;; use default strategy if none given + (when (not strategy) (setq strategy (scala-indent:run-on-strategy))) + (save-excursion + (when point (goto-char point)) + (unless (eobp) + ;; Note: ofcourse this 'cond' could be written as one big boolean + ;; expression, but I doubt that would be so readable and + ;; maintainable + (cond + ;; NO: this line starts with close parenthesis + ((= (char-syntax (char-after)) ?\)) + nil) + ;; NO: the previous line must terminate + ((save-excursion + (scala-syntax:skip-backward-ignorable) + (or (bobp) + (scala-syntax:looking-back-empty-line-p) + (scala-syntax:looking-back-token scala-indent:mustTerminate-re))) + nil) + ;; YES: in a region where newlines are disabled + ((and (scala-syntax:newlines-disabled-p) + (not (= strategy scala-indent:keywords-only-strategy))) + t) + ;; NO: this line starts with a keyword that starts a new + ;; expression (e.g. 'def' or 'class') + ((looking-at scala-indent:mustNotContinue-re) + nil) + ;; NO: this line is the start of value body + ((scala-indent:body-p) + nil) + ;; YES: eager strategy can stop here, everything is a run-on if no + ;; counter evidence + ((= strategy scala-indent:eager-strategy) + t) + ;; YES: this line must not terminate because it starts with a + ;; middle of expression keyword + ((looking-at scala-indent:mustNotTerminate-line-beginning-re) + t) + ;; YES: end of prev line must not terminate + ((let ((case-fold-search nil)) + (scala-syntax:looking-back-token + scala-indent:mustBeContinued-line-end-re)) + t) + ;; YES: this line starts with type param + ((= (char-after) ?\[) + t) + ;; YES: this line starts with open paren and the expression + ;; after all parens is a run-on + ((and (= (char-after) ?\() + (save-excursion (scala-syntax:forward-parameter-groups) + (scala-syntax:skip-forward-ignorable) + (or (= (char-after) ?=) + (= (char-after) ?{) + (scala-indent:run-on-p nil strategy)))) + t) + ;; NO: that's all for keywords-only strategy + ((= strategy scala-indent:keywords-only-strategy) + nil) + ;; YES: this line starts with punctuation + ((= (char-after) ?\.) + t) + ;; YES: prev line ended with punctuation + ((scala-syntax:looking-back-token ".*[.]") + t) + ;; NO: that's all for reluctant-strategy + ((= strategy scala-indent:reluctant-strategy) + nil) + ;; YES: this line starts with opchars + ((save-excursion + (< 0 (skip-chars-forward scala-syntax:opchar-group))) + t) + ;; YES: prev line ends with opchars + ((save-excursion + (scala-syntax:skip-backward-ignorable) + (> 0 (skip-chars-backward scala-syntax:opchar-group))) + t) + ;; NO: else nil (only operator strategy should reach here) + (t nil))))) + +(defun scala-indent:run-on-line-p (&optional point strategy) + "Returns t if the current point (or point at 'point) is on a +line that is a run-on from a previous line." + (save-excursion + (when point (goto-char point)) + (scala-syntax:beginning-of-code-line) + (scala-indent:run-on-p nil strategy))) + +(defun scala-indent:goto-run-on-anchor (&optional point strategy) + "Moves back to the point whose column will be used as the +anchor relative to which indenting for current point (or point +'point') is calculated. Returns the new point or nil if the point +is not on a run-on line." + (when (scala-indent:run-on-line-p point strategy) + (when point (goto-char point)) + (scala-syntax:beginning-of-code-line) + (while (and (scala-indent:run-on-line-p nil strategy) + (scala-syntax:skip-backward-ignorable) + (scala-indent:backward-sexp-to-beginning-of-line))) + (scala-indent:align-anchor) + (point))) + +(defconst scala-indent:double-indent-re + (concat (regexp-opt '("with" "extends" "forSome") 'words) + "\\|:\\(" scala-syntax:after-reserved-symbol-re "\\)")) + +(defun scala-indent:resolve-run-on-step (start &optional anchor) + "Resolves the appropriate indent step for run-on line at position +'start'" + (save-excursion + (goto-char anchor) + (if (scala-syntax:looking-at-case-p) + ;; case run-on lines get double indent, except '|' which get + ;; special indents + (progn (goto-char start) + (- (* 2 scala-indent:step) + (skip-chars-forward "|"))) + (goto-char start) + (cond + ;; some keywords get double indent + ((or (looking-at scala-indent:double-indent-re) + (scala-syntax:looking-back-token scala-indent:double-indent-re)) + (* 2 scala-indent:step)) + ;; no indent if the previous line is just close parens + ;; ((save-excursion + ;; (scala-syntax:skip-backward-ignorable) + ;; (let ((end (point))) + ;; (scala-syntax:beginning-of-code-line) + ;; (skip-syntax-forward ")") + ;; (= (point) end))) + ;; 0) + ;; else normal indent + (t (+ (if scala-indent:align-parameters 0 + (scala-indent:value-expression-lead start anchor)) + scala-indent:step)))))) + +(defconst scala-indent:forms-align-re + (regexp-opt '("yield" "else" "catch" "finally") 'words)) + +(defun scala-indent:forms-align-p (&optional point) + "Returns scala-syntax:beginning-of-code-line for the line on +which current point (or point 'point') is, if the line starts +with one of 'yield', 'else', 'catch' and 'finally', otherwise +nil. Also, the previous line must not be with '}'" + (save-excursion + (when point (goto-char point)) + (scala-syntax:beginning-of-code-line) + (when (looking-at scala-indent:forms-align-re) + (goto-char (match-beginning 0)) + (point)))) + + +(defun scala-indent:goto-forms-align-anchor (&optional point) + "Moves back to the point whose column will be used as the +anchor relative to which indenting of special words on beginning +of the line on which point (or point 'point') is, or nul if not +special word found. Special words include 'yield', 'else', +'catch' and 'finally'" + (let ((special-beg (scala-indent:forms-align-p point))) + (when special-beg + (goto-char special-beg) + (if (and (scala-syntax:looking-back-token "}") + (save-excursion + (goto-char (match-beginning 0)) + (= (match-beginning 0) (scala-lib:point-after (scala-syntax:beginning-of-code-line))))) + (goto-char (match-beginning 0)) + (let ((anchor + (cond ((looking-at "\\<yield\\>") + ;; align with 'for' + (if (scala-syntax:search-backward-sexp "\\<for\\>") + (point) + (message "matching 'for' not found") + nil)) + ((looking-at "\\<else\\>") + ;; align with 'if' or 'else if' + (if (scala-syntax:search-backward-sexp "\\<if\\>") + (if (scala-syntax:looking-back-token "\\<else\\>") + (goto-char (match-beginning 0)) + (point)) + nil)) + ((looking-at "\\<catch\\>") + ;; align with 'try' + (if (scala-syntax:search-backward-sexp "\\<try\\>") + (point) + (message "matching 'try' not found") + nil)) + ((looking-at "\\<finally\\>") + ;; align with 'try' + (if (scala-syntax:search-backward-sexp "\\<try\\>") + (point) + (message "matching 'try' not found") + nil))))) + (if scala-indent:align-forms + anchor + (when anchor + (scala-indent:align-anchor) + (point)))))))) + +(defun scala-indent:resolve-forms-align-step (start anchor) + (if scala-indent:align-forms + 0 + (scala-indent:value-expression-lead start anchor t))) + +;;; +;;; Lists and enumerators +;;; -(defun scala-parse-partial-sexp () - (parse-partial-sexp (point-min) (point))) +(defun scala-indent:goto-list-anchor-impl (point) + (goto-char point) + ;; find the first element of the list + (if (not scala-indent:align-parameters) + (progn (back-to-indentation) (point)) + (forward-comment (buffer-size)) + (if (= (line-number-at-pos point) + (line-number-at-pos)) + (goto-char point) + (beginning-of-line)) -(defun scala-in-comment-p () - "Return t iff the point is inside a comment." - ;; The two branches of the "if" below do not have the same behaviour - ;; when the point is on the comment beginning/ending character(s). - (or (scala-in-multi-line-comment-p) - (scala-in-single-line-comment-p))) + ;; align list with first non-whitespace character + (skip-syntax-forward " ") + (point))) -(defun scala-in-single-line-comment-p () - "Return t iff the point is inside a single line comment." +(defun scala-indent:goto-list-anchor (&optional point) + "Moves back to the point whose column will be used to indent +list rows at current point (or point 'point'). Returns the new +point or nil if the point is not in a list element > 1." + (let ((list-beg (scala-syntax:list-p point))) + (when list-beg + (scala-indent:goto-list-anchor-impl list-beg)))) + +(defun scala-indent:resolve-list-step (start anchor) + (if scala-indent:align-parameters + 0 + (scala-indent:resolve-block-step start anchor))) + +(defun scala-indent:for-enumerators-p (&optional point) + "Returns the point after opening parentheses if the current +point (or point 'point') is in a block of enumerators. Return nil +if not in a list of enumerators or at the first enumerator." + (unless point (setq point (point))) + (save-excursion + (goto-char point) + (scala-syntax:beginning-of-code-line) + (let ((state (syntax-ppss point))) + (unless (or (eobp) (= (char-syntax (char-after)) ?\))) + (when (and state (nth 1 state)) + (goto-char (nth 1 state)) + (when (scala-syntax:looking-back-token scala-syntax:for-re) + (forward-char) + (forward-comment (buffer-size)) + (when (< (point) point) + (1+ (nth 1 state))))))))) + +(defun scala-indent:goto-for-enumerators-anchor (&optional point) + "Moves back to the point whose column will be used to indent +for enumerator at current point (or point 'point'). Returns the new +point or nil if the point is not in a enumerator element > 1." + (let ((enumerators-beg (scala-indent:for-enumerators-p point))) + (when enumerators-beg + (scala-indent:goto-list-anchor-impl enumerators-beg)))) + +;;; +;;; Body +;;; + +(defconst scala-indent:control-keywords-cond-re + (regexp-opt '("if" "while" "for") 'words) + "All the flow control keywords that are followed by a +condition (or generators in the case of 'for') in parentheses.") + +(defconst scala-indent:control-keywords-other-re + (regexp-opt '("else" "do" "yield" "try" "finally" "catch") 'words) + "Other flow control keywords (not followed by parentheses)") + +(defconst scala-indent:control-keywords-re + (concat "\\(" scala-indent:control-keywords-cond-re + "\\|" scala-indent:control-keywords-other-re "\\)")) + +(defun scala-indent:body-p (&optional point) + "Returns the position of '=' symbol, or one of the +scala-indent:control-keywords-re or +scala-indent:control-keywords-cond-re keywords if current +point (or point 'point) is on a line that follows said symbol or +keyword, or nil if not." + (save-excursion + (when point (goto-char point)) + (scala-syntax:beginning-of-code-line) + (or (scala-syntax:looking-back-token scala-syntax:body-start-re 3) + (let ((case-fold-search nil)) + (scala-syntax:looking-back-token scala-indent:control-keywords-other-re)) + (progn + ;; if, else if + (when (scala-syntax:looking-back-token ")" 1) + (goto-char (match-end 0)) + (backward-list)) + (when (scala-syntax:looking-back-token scala-indent:control-keywords-cond-re) + (goto-char (match-beginning 0)) + (when (and (looking-at "\\<if\\>") + (scala-syntax:looking-back-token "\\<else\\>")) + (goto-char (match-beginning 0))) + (when (not scala-indent:align-forms) + (scala-indent:align-anchor)) + (point)))))) + +(defun scala-indent:goto-body-anchor (&optional point) + (let ((declaration-end (scala-indent:body-p point))) + (when declaration-end + (goto-char declaration-end) + (if (let ((case-fold-search nil)) + (looking-at scala-indent:control-keywords-re)) + (point) + (when (scala-indent:backward-sexp-to-beginning-of-line) + (scala-indent:goto-run-on-anchor + nil + scala-indent:keywords-only-strategy)) + (scala-indent:align-anchor) + (point))))) + +(defun scala-indent:resolve-body-step (start &optional anchor) + (if (and (not (= start (point-max))) (= (char-after start) ?\{)) + 0 + (+ (scala-indent:value-expression-lead start anchor t) + scala-indent:step))) + +;;; +;;; Block +;;; + +(defun scala-indent:goto-block-anchor (&optional point) + "Moves back to the point whose column will be used as the +anchor for calculating block indent for current point (or point +'point'). Returns point or (point-min) if not inside a block." + (let ((block-beg (nth 1 (syntax-ppss + (scala-lib:point-after (beginning-of-line)))))) + (when block-beg + ;; check if the opening paren is the first on the line, + ;; if so, it is the anchor. If not, then go back to the + ;; start of the line + (goto-char block-beg) + (if (= (point) (scala-lib:point-after + (scala-syntax:beginning-of-code-line))) + (point) + (goto-char (or (scala-syntax:looking-back-token + scala-syntax:body-start-re 3) + (point))) + (scala-syntax:backward-parameter-groups) + (when (scala-indent:backward-sexp-to-beginning-of-line) + (scala-indent:goto-run-on-anchor nil + scala-indent:keywords-only-strategy)) + (scala-indent:align-anchor) + (point))))) + +(defun scala-indent:resolve-block-step (start anchor) + "Resolves the appropriate indent step for block line at position +'start' relative to the block anchor 'anchor'." (let - (begin - end - subst - match) + ((lead (scala-indent:value-expression-lead start anchor))) + (cond + ;; at end of buffer + ((= start (point-max)) (+ scala-indent:step lead)) + ;; block close parentheses line up with anchor in normal case + ((= (char-syntax (char-after start)) ?\)) + (+ 0 lead)) + ;; case-lines indent normally, regardless of where they are + ((scala-syntax:looking-at-case-p start) + (+ scala-indent:step lead)) + ;; other than case-line in case-block get double indent + ((save-excursion + (goto-char (1+ (or (nth 1 (syntax-ppss start)) 0))) + (forward-comment (buffer-size)) + (and (scala-syntax:looking-at-case-p) + (> (line-number-at-pos) (line-number-at-pos anchor)) + (> start (match-beginning 0)))) + (+ (* 2 scala-indent:step) lead)) + ;; normal block line + (t (+ scala-indent:step lead))))) + +;;; +;;; Open parentheses +;;; + +(defun scala-indent:open-parentheses-line-p (&optional point) + "Returns the position of the first character of the line, +if the current point (or point 'point') is on a line that starts +with an opening parentheses, or nil if not." + (save-excursion + (when point (goto-char point)) + (scala-syntax:beginning-of-code-line) + (if (looking-at "\\s(") (point) nil))) + +(defun scala-indent:goto-open-parentheses-anchor (&optional point) + "Moves back to the point whose column will be used as the +anchor for calculating opening parenthesis indent for the current +point (or point 'point'). Returns point or nil, if line does not +start with opening parenthesis." + ;; There are five cases we need to consider: + ;; 1. curry parentheses, i.e. 2..n parentheses groups. + ;; 2. value body parentheses (follows '='). + ;; 3. parameters, etc on separate line (who would be so mad?) + ;; 4. non-value body parentheses (follows class, trait, new, def, etc). + (let ((parentheses-beg (scala-indent:open-parentheses-line-p point))) + (when parentheses-beg + (goto-char parentheses-beg) + (cond + ;; case 1 + ((and scala-indent:align-parameters + (= (char-after) ?\() + (scala-indent:run-on-p) + (scala-syntax:looking-back-token ")" 1)) + (scala-syntax:backward-parameter-groups) + (let ((curry-beg (point))) + (forward-char) + (forward-comment (buffer-size)) + (if (= (line-number-at-pos curry-beg) + (line-number-at-pos)) + (goto-char curry-beg) + nil))) + ;; case 2 + ((scala-syntax:looking-back-token "=" 1) + nil) ; let body rule handle it + ;; case 4 + ((and (= (char-after) ?\{) + (scala-indent:goto-run-on-anchor + nil scala-indent:keywords-only-strategy)) ; use customized strategy + (point)) + ;; case 3 + ;;((scala-indent:run-on-p) + ;; (scala-syntax:skip-backward-ignorable) + ;; (back-to-indentation) + ;; (point)) + (t + nil) + )))) + +(defun scala-indent:resolve-open-parentheses-step (start anchor) + "Resolves the appropriate indent step for an open paren +anchored at 'anchor'." + (cond ((scala-syntax:looking-back-token ")") +; (message "curry") + 0) + ((save-excursion + (goto-char anchor) + ;; find = + (scala-syntax:has-char-before ?= start)) +; (message "=") + scala-indent:step) + (t +; (message "normal at %d" (current-column)) + 0))) + +(defun scala-indent:goto-line-comment-anchor (&optional point) + "Goto and return the position relative to which a line comment +will be indented. This will be the start of the line-comment on +previous line, if any." + (let ((pos (point))) + (when (save-excursion + (when point (goto-char point)) + (when (and (looking-at "\\s *//") + (not (scala-syntax:looking-back-empty-line-p)) + (forward-comment -1)) + (setq pos (point)))) + (goto-char pos)))) + +;;; +;;; Indentation engine +;;; + +(defun scala-indent:apply-indent-rules (rule-indents &optional point) + "Evaluates each rule, until one returns non-nil value. Returns +the sum of the value and the respective indent step, or nil if +nothing was applied." + (when rule-indents + (save-excursion + (when point (goto-char point)) + (let* ((pos (scala-syntax:beginning-of-code-line)) + (rule-indent (car rule-indents)) + (rule-statement (car rule-indent)) + (indent-statement (cadr rule-indent)) + (anchor (funcall rule-statement point))) + (if anchor + (progn + (if scala-mode:debug-messages + (message "indenting acording to %s at %d for pos %d for point %s" rule-statement anchor pos point)) + (when (/= anchor (point)) + (error (format "Assertion error: anchor=%d, point=%d" anchor (point)))) + (+ (current-column) + (save-excursion + (if (functionp indent-statement) + (funcall indent-statement pos anchor) + (eval indent-statement))))) + (scala-indent:apply-indent-rules (cdr rule-indents))))))) + +(defun scala-indent:calculate-indent-for-line (&optional point) + "Calculate the appropriate indent for the current point or the +point 'point'. Returns the new column, or nil if the indent +cannot be determined." + (or (scala-indent:apply-indent-rules + `((scala-indent:goto-line-comment-anchor 0) + (scala-indent:goto-open-parentheses-anchor scala-indent:resolve-open-parentheses-step) + (scala-indent:goto-for-enumerators-anchor scala-indent:resolve-list-step) + (scala-indent:goto-forms-align-anchor scala-indent:resolve-forms-align-step) + (scala-indent:goto-list-anchor scala-indent:resolve-list-step) + (scala-indent:goto-body-anchor scala-indent:resolve-body-step) + (scala-indent:goto-run-on-anchor scala-indent:resolve-run-on-step) + (scala-indent:goto-block-anchor scala-indent:resolve-block-step) + ) + point) + 0)) + +(defun scala-indent:indent-line-to (column) + "Indent the line to column and move cursor to the indent +column, if it was at the left margin." + (when column + (if (<= (current-column) (current-indentation)) + (indent-line-to column) + (save-excursion (indent-line-to column))))) + +(make-variable-buffer-local 'scala-indent:previous-indent-pos) + +(defun scala-indent:remove-indent-from-previous-empty-line () + "Handles removing of whitespace from a previosly indented code +line that was left empty (i.e. whitespaces only). Also clears the +scala-indent:previous-indent-pos variable that controls the process." + (when (and scala-indent:previous-indent-pos + (/= scala-indent:previous-indent-pos (point))) (save-excursion - (setq end (point)) - (beginning-of-line) - (setq begin (point)) - (setq subst (buffer-substring begin end)) - (setq match (string-match "//" subst)) - (if match t nil)))) - -(defun scala-in-multi-line-comment-p () - "Return t iff the point is inside a multi line comment." - (if font-lock-mode - (and (not (scala-in-single-line-comment-p)) - (eq (get-text-property (point) 'face) 'font-lock-comment-face)) - nil)) - - -(defun scala-in-string-p () - "Return t iff the point is inside a string." - (if font-lock-mode - (eq (get-text-property (point) 'face) 'font-lock-string-face) - (let ((limit (point))) (beginning-of-line) - (loop while (search-forward-regexp "\\(^\\|[^\\\\]\\)\"" limit 'move) - count (not (scala-in-comment-p)) into quotes - finally return (oddp quotes))))) + (if (= scala-indent:previous-indent-pos + (point)) + (setq scala-indent:previous-indent-pos + (when (looking-at "^\\s +$") (point))) + (goto-char scala-indent:previous-indent-pos) + (when (looking-at "^\\s +$") + (delete-region (match-beginning 0) (match-end 0))) + (setq scala-indent:previous-indent-pos nil))))) + +(defun scala-indent:indent-code-line (&optional strategy) + "Indent a line of code. Expect to be outside of any comments or +strings" + (if strategy + (setq scala-indent:effective-run-on-strategy strategy) + (if (eq last-command this-command) + (scala-indent:toggle-effective-run-on-strategy) + (scala-indent:reset-effective-run-on-strategy))) +; (message "run-on-strategy is %s" (scala-indent:run-on-strategy)) + (scala-indent:indent-line-to (scala-indent:calculate-indent-for-line)) + (scala-lib:delete-trailing-whitespace) + (setq scala-indent:previous-indent-pos + (save-excursion + (beginning-of-line) + (when (looking-at "^\\s +$") (point))))) + +(defun scala-indent:indent-line (&optional strategy) + "Indents the current line." + (interactive "*") + (let ((state (save-excursion (syntax-ppss (line-beginning-position))))) + (if (not (nth 8 state)) ;; 8 = start pos of comment or string, nil if none + (scala-indent:indent-code-line strategy) + (scala-indent:indent-line-to + (cond ((integerp (nth 4 state)) ;; 4 = nesting level of multi-line comment + (scala-indent:scaladoc-indent (nth 8 state))) + ((eq t (nth 3 state)) ;; 3 = t for multi-line string + (or (save-excursion + (beginning-of-line) + (when (and (looking-at "\\s *|") + (progn (goto-char (nth 8 state)) + (looking-at "\\(\"\"\"\\)|"))) + (goto-char (match-end 1)) + (current-column))) + (current-indentation))) + (t (current-indentation))))))) + +(defun scala-indent:indent-with-reluctant-strategy () + (interactive "*") + (scala-indent:indent-line scala-indent:reluctant-strategy)) -(defun scala-indentation () - "Return the suggested indentation for the current line." +(defun scala-indent:scaladoc-indent (comment-start-pos) + "Calculate indent for a multi-line comment. Scaladoc +lines (starting with /**) are indented under the second +aseterix. Other multi-line comment rows are indented undet the +first asterisk. + +Note: start line is indented as code since the start of the +comment is outside the comment region. " (save-excursion - (beginning-of-line) - (or (and (scala-in-comment-p) - (not (= (char-after) ?\/)) - (scala-comment-indentation)) - (scala-indentation-from-following) - (scala-indentation-from-preceding) - (scala-indentation-from-block) - 0))) - -(defun scala-comment-indentation () - ;; Return suggested indentation inside of a comment. - (forward-line -1) - (beginning-of-line) - (skip-syntax-forward " ") - (if (looking-at "/\\*") - (+ 1 (current-column)) - (current-column))) - -(defun scala-block-indentation () - (let ((block-start-eol (scala-point-after (end-of-line))) - (block-after-spc (scala-point-after (scala-forward-spaces)))) - (if (> block-after-spc block-start-eol) - (progn - (beginning-of-line) - (when (search-forward ")" block-start-eol t) - (scala-forward-spaces) - (backward-sexp)) - (+ (current-indentation) scala-mode-indent:step)) + (goto-char comment-start-pos) + (when (looking-at "/\\*+") + (goto-char + (if (and (not scala-indent:use-javadoc-style) + (= (- (match-end 0) (match-beginning 0)) 3)) + (- (match-end 0) 1) + (+ (match-beginning 0) 1))) (current-column)))) -(defun scala-indentation-from-following () - ;; Return suggested indentation based on the following part of the - ;; current expression. Return nil if indentation cannot be guessed. - (save-excursion - (scala-forward-spaces (scala-point-after (end-of-line))) - (cond - ((eobp) nil) - ((= (char-syntax (char-after)) ?\)) - (let ((parse-sexp-ignore-comments t)) - (goto-char (1+ (scan-sexps (1+ (point)) -1)))) - (- (scala-block-indentation) scala-mode-indent:step)) - ((looking-at scala-expr-middle-re) - ;; [...] this is a somewhat of a hack. - (let ((matching-kw (cdr (assoc (match-string-no-properties 0) - scala-expr-starter)))) - (while (and (search-backward-regexp matching-kw nil t) - (or (scala-in-comment-p) (scala-in-string-p))))) - (scala-move-if (backward-word 1) - (looking-at scala-compound-expr-re)) - (current-column))))) - -(defun scala-indentation-from-preceding () - ;; Return suggested indentation based on the preceding part of the - ;; current expression. Return nil if indentation cannot be guessed. - (save-excursion - (scala-backward-spaces) - (and (not (bobp)) - (if (eq (char-syntax (char-before)) ?\() - (scala-block-indentation) - (progn - (when (eq (char-before) ?\)) - (backward-sexp) - (scala-backward-spaces)) - (scala-looking-at-backward scala-expr-start-re))) - (+ (current-indentation) scala-mode-indent:step)))) - - -(defun scala-indentation-from-block () - ;; Return suggested indentation based on the current block. +(defun scala-indent:indent-on-parentheses () + (when (and (= (char-syntax (char-before)) ?\)) + (= (save-excursion (back-to-indentation) (point)) (1- (point)))) + (scala-indent:indent-line))) + +(defconst scala-indent:indent-on-words-re + (concat "^\\s *" + (regexp-opt '("catch" "case" "else" "finally" "yield") 'words))) + +(defun scala-indent:indent-on-special-words () + "This function is meant to be used with post-self-insert-hook. + +Indents the line if position is right after a space that is after +a word that needs to be indented specially." + ;; magic numbers used 4 = length of "case", 7 = length of "finally" + (when (and (> (current-column) 4) + (= (char-before) ?\s) + (= (char-syntax (char-before (- (point) 1))) ?w) + (save-excursion (backward-char) + (looking-back scala-indent:indent-on-words-re 7)) + (not (nth 8 (syntax-ppss)))) + (scala-indent:indent-line-to (scala-indent:calculate-indent-for-line)))) + +(defun scala-indent:indent-on-scaladoc-asterisk () + "This function is meant to be used with post-self-insert-hook. + +Indents the line if position is right after an asterisk in a +multi-line comment block and there is only whitespace before the asterisk. + +If scala-indent:add-space-for-scaladoc-asterisk is t, also adds a +space after the asterisk if the asterisk is the last character on +the line." + (let ((state (syntax-ppss))) + (when (and (integerp (nth 4 state)) + (looking-back "^\\s *\\*" (line-beginning-position))) + (when scala-indent:add-space-for-scaladoc-asterisk + (insert " ")) + (scala-indent:indent-line-to (scala-indent:scaladoc-indent (nth 8 state)))))) + +(defun scala-indent:fix-scaladoc-close () + "This function is meant to be used with post-self-insert-hook. + +Changes 'asterisk space slash' to 'asterisk slash' in a +multi-line comment if position is right after that slash and +scala-indent:add-space-for-scaladoc-asterisk is t." + (let ((state (syntax-ppss))) + (when (and scala-indent:add-space-for-scaladoc-asterisk + (integerp (nth 4 state)) + (looking-back "^\\s *\\*\\s /" (line-beginning-position))) + (delete-region (- (point) 2) (- (point) 1))))) + +(defun scala-indent:insert-asterisk-on-multiline-comment () + "Insert an asterisk at the end of the current line when at the beginning +of a line inside a multi-line comment " + (let* ((state (syntax-ppss)) + (comment-start-pos (nth 8 state))) + (when (and (integerp (nth 4 state)) + ; Ensure that we're inside a scaladoc comment + (string-match-p "^/\\*\\*[^\\*]" + (buffer-substring-no-properties + comment-start-pos + (+ comment-start-pos 4))) + ; Ensure that the previous line had a leading asterisk or was the comment start. + (let ((prev-line (buffer-substring-no-properties + (line-beginning-position 0) + (line-end-position 0)))) + (or + (string-match-p "^\\s-*\\*" prev-line) + (string-match-p "\\s-*/\\*\\*" prev-line)))) + (skip-syntax-forward " ") + (insert "*") + (scala-indent:indent-on-scaladoc-asterisk)))) + +(defun scala-mode:indent-scaladoc-asterisk (&optional insert-space-p) + (message "scala-mode:indent-scaladoc-asterisk has been deprecated")) + + +(defun scala-indent:fixup-whitespace () + "scala-mode version of `fixup-whitespace'" + (interactive "*") (save-excursion - (let* ((state (scala-parse-partial-sexp)) - (block-start (nth 1 state))) - (if (not block-start) - 0 - (goto-char (1+ block-start)) - (scala-block-indentation))))) - -(defun scala-indent-line-to (column) - "Indent current line to COLUMN and perhaps move point. -The point is moved iff it is currently in the indentation, in which -case it is brought to the end of that indentation. Otherwise it does -not move." - (if (<= (current-column) (current-indentation)) - (indent-line-to column) - (save-excursion (indent-line-to column)))) - -(defun scala-indent-line () - "Indent current line as smartly as possible. -When called repeatedly, indent each time one stop further on the right." - (interactive) - (if (or (eq last-command this-command) - (eq last-command 'scala-undent-line)) - (scala-indent-line-to (+ (current-indentation) scala-mode-indent:step)) - (let - ((indentation (scala-indentation))) - (scala-indent-line-to indentation)))) - -(defun scala-undent-line () - "Indent line to previous tab stop." - (interactive) - (scala-indent-line-to (max 0 (- (current-indentation) scala-mode-indent:step)))) + (delete-horizontal-space) + (if (or (looking-at "^\\|[]):.]") + (save-excursion (forward-char -1) + (if (nth 4 (syntax-ppss)) + (looking-at "$\\|\\s(") + (looking-at "$\\|[[(.]"))) + (and (= (char-before) ?{) (= (char-after) ?}))) + nil + (insert ?\s)))) -(defun scala-electric-brace () - "Insert a brace, and if alone on a non-comment line, reindent." - (interactive) - (let ((on-empty-line-p (save-excursion - (beginning-of-line) - (looking-at "^\\s *$")))) - ;; Calling self-insert-command will blink to the matching open-brace - ;; (if blink-matching-paren is enabled); we first indent, then - ;; call self-insert-command, so that the close-brace is correctly - ;; positioned during the blink. - (when on-empty-line-p - (insert "}") - (scala-indent-line) - (delete-backward-char 1)) - (call-interactively 'self-insert-command))) - - -(defun scala-newline () - (interactive) - (if (scala-in-multi-line-comment-p) - (progn - (newline-and-indent) - (insert "* ")) - (newline))) +(defun scala-indent:join-line (&optional arg) + "scala-mode version of `join-line', i.e. `delete-indentation'" + (interactive "*P") + (beginning-of-line) + (if arg (forward-line 1)) + (when (= (preceding-char) ?\n) + (delete-region (point) (1- (point))) + (delete-horizontal-space) + (let ((state (syntax-ppss))) + (cond + ((and (integerp (nth 4 state)) ; nestable comment (i.e. with *) + (looking-at " *\\*\\($\\|[^/]\\)") + (save-excursion (goto-char (max (nth 8 state) (line-beginning-position))) + (looking-at "\\s */?\\*"))) + (delete-char 2)) + ((and (nth 4 state) ; row comment (i.e. with //) + (looking-at " //")) + (delete-char 3)))) + (scala-indent:fixup-whitespace))) + +(provide 'scala-mode-indent) diff --git a/scala-mode-inf.el b/scala-mode-inf.el deleted file mode 100644 index 1047714..0000000 --- a/scala-mode-inf.el +++ /dev/null @@ -1,201 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-inf.el - Interaction with a Scala interpreter. - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-inf) - - -(require 'comint) - -(defgroup scala-mode-inf - nil - "Mode to interact with a Scala interpreter." - :group 'scala - :tag "Inferior Scala") - -(defcustom scala-interpreter "scala" - "The interpreter that `run-scala' should run. This should - be a program in your PATH or the full pathname of the scala interpreter." - :type 'string - :group 'scala-mode-inf) - -(defconst scala-inf-buffer-name "*inferior-scala*") - -(define-derived-mode scala-mode-inf comint-mode "Inferior Scala" - "Major mode for interacting with a Scala interpreter. - -\\{inferior-scala-mode-map\\}" - (define-key scala-mode-inf-map [(meta return)] 'comint-accumulate) - - ;; Comint configuration - (make-local-variable 'comint-input-sender) - (setq comint-input-sender 'scala-input-sender)) - -(defun scala-input-sender (proc string) - (comint-send-string proc string) - ;; (comint-send-string proc "\nemacs:end\n")) ;; Heineman's contrib (06/03/2007) - (comint-send-string proc "\n")) - -;;;###autoload -(defun scala-interpreter-running-p-1 () - ;; True iff a Scala interpreter is currently running in a buffer. - (comint-check-proc scala-inf-buffer-name)) - -(defun scala-check-interpreter-running () - (unless (scala-interpreter-running-p-1) - (error "Scala interpreter not running"))) - -;;;###autoload -(defun scala-run-scala (cmd-line) - "Run a Scala interpreter in an Emacs buffer" - (interactive (list (if current-prefix-arg - (read-string "Scala interpreter: " scala-interpreter) - scala-interpreter))) - (unless (scala-interpreter-running-p-1) - (setq scala-interpreter cmd-line) - (let ((cmd/args (split-string cmd-line))) - (set-buffer - (apply 'make-comint "inferior-scala" (car cmd/args) nil (cdr cmd/args)))) - (scala-mode-inf) - (pop-to-buffer scala-inf-buffer-name))) - -(defun scala-send-string (str &rest args) - ;; Send string to interpreter - (comint-send-string scala-inf-buffer-name (apply 'format str args)) - ;; (comint-send-string scala-inf-buffer-name "\nemacs:end\n")) Heineman's contrib (06/03/2007) - (comint-send-string scala-inf-buffer-name "\n")) - -;;;###autoload -(defun scala-switch-to-interpreter () - "Switch to buffer containing the interpreter" - (interactive) - (scala-check-interpreter-running) - (switch-to-buffer scala-inf-buffer-name)) - -(defvar scala-tmp-file nil) - -;;;###autoload -(defun scala-eval-region (start end) - "Send current region to Scala interpreter." - (interactive "r") - (scala-check-interpreter-running) - (comint-send-region scala-inf-buffer-name start end) - (comint-send-string scala-inf-buffer-name "\n")) - -;;;###autoload -(defun scala-eval-definition () - "Send the current 'definition' to the Scala interpreter. -This function's idea of a definition is the block of text ending -in the current line (or the first non-empty line going -backwards), and begins in the first line that is not empty and -does not start with whitespace or '{'. - -For example: - -println( \"aja\") -println( \"hola\" ) - -if the cursor is somewhere in the second print statement, the -interpreter should output 'hola'. - -In the following case, if the cursor is in the second line, then -the complete function definition will be send to the interpreter: - -def foo = - 1 + 2 -" - (interactive) - (save-excursion - ;; find the first non-empty line - (beginning-of-line) - (while (and (not (= (point) (point-min))) - (looking-at "\\s-*$")) - (next-line -1)) - (end-of-line) - (let ((end (point))) - ;; now we need to find the start - (beginning-of-line) - (while (and (not (= (point) (point-min))) - (looking-at (mapconcat '(lambda (x) x) - '("^$" ; empty lines - "^\\s-+" ; empty lines or lines that start with whitespace - "^\\s-*}") ; lines that start with a '}' - "\\|"))) - (next-line -1) - (beginning-of-line)) - (message "region %s %s" (point) end) - (scala-eval-region (point) end)))) - -;;;###autoload -(defun scala-eval-buffer () - "Send whole buffer to Scala interpreter." - (interactive) - (scala-eval-region (point-min) (point-max))) - -(defvar scala-prev-l/c-dir/file nil - "Caches the last (directory . file) pair. -Caches the last pair used in the last scala-load-file. -Used for determining the default in the next one.") - -;;;###autoload -(defun scala-load-file (file-name) - "Load a file in the Scala interpreter." - (interactive (comint-get-source "Load Scala file: " scala-prev-l/c-dir/file - '(scala-mode) t)) - (scala-check-interpreter-running) - (comint-check-source file-name) - (setq scala-prev-l/c-dir/file (cons (file-name-directory file-name) - (file-name-nondirectory file-name))) - (scala-send-string ":load %s" file-name)) - -;;;###autoload -(defun scala-quit-interpreter () - "Quit Scala interpreter." - (interactive) - (scala-check-interpreter-running) - (scala-send-string "\n:quit")) - diff --git a/scala-mode-lib.el b/scala-mode-lib.el index ef2a6b2..538f3a0 100644 --- a/scala-mode-lib.el +++ b/scala-mode-lib.el @@ -1,114 +1,26 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-lib.el - Libraries and macroes used by the scala mode. +;;; scala-mode-lib.el - Major mode for editing scala, common functions +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop +(defvar scala-mode:debug-messages + "If true, some debug messages may be printed printed." + nil) -;;; License +(defmacro scala-lib:column-after (&rest body) + `(save-excursion + ,@body + (current-column))) -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. +(defmacro scala-lib:point-after (&rest body) + `(save-excursion + ,@body + (point))) -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-lib) - -(eval-when-compile - (require 'scala-mode-constants)) - - -(defmacro scala-mode-lib:define-keys (key-map &rest key-funcs) - "Define key bindings for KEY-MAP (create KEY-MAP, if it does -not exist." - `(progn - (unless (boundp ',key-map) - (setf ,key-map (make-keymap))) - ,@(mapcar - #'(lambda (key-func) - `(define-key ,key-map ,(first key-func) ,(second key-func))) - key-funcs))) - - -(defun scala-special-char-p (char) - (and char - (string-match scala-all-special-char-re (string char)))) - -(defun scala-looking-at-special-identifier (regexp) - (and (not (scala-special-char-p (char-before))) - (looking-at regexp) - (not (scala-special-char-p (char-after (match-end 0)))))) - - -(defun scala-search-special-identifier-forward (limit) - (ignore-errors - (while (and (search-forward-regexp scala-special-ident-re limit) - (save-match-data - (string-match scala-comment-begin-or-end-re - (match-string-no-properties 0))))) - t)) - - -(defun scala-mode-find-clstrtobj-name-doc () +(defun scala-lib:delete-trailing-whitespace () (save-excursion - (if (re-search-forward "\\(class\\|object\\|trait\\)[ \t\n]+\\([a-zA-Z0-9_:=]+\\)[ \t\n]*" nil t) - - (buffer-substring (match-beginning 2) (match-end 2)) - "NONAME"))) + (end-of-line) + (skip-syntax-backward " ") + (unless (bolp) + (delete-char (- (line-end-position) (point)))))) - -(defun scala-mode-def-and-args-doc () - (save-excursion - (if (re-search-forward - (concat - ;; function name - "[ \t\n]*def[ \t\n]+\\([a-zA-Z0-9_:=]+\\)[ \t\n]*" - - ;; arguments - "\\((\\([a-zA-Z0-9_:* \t\n]*\\))\\)?" - ) nil t) - - ;; TODO: output args in a sane format to use in yasnippet, look at doxymancs line 1441 - (let* ((func (buffer-substring (match-beginning 1) (match-end 1))) - ;(args (buffer-substring (match-beginning 3) (match-end 3))) - ) - (concat "${1:" func "} $0")) - "${1:name} $0"))) - - -(defun scala-mode-file-doc () - (file-name-nondirectory buffer-file-name)) +(provide 'scala-mode-lib) diff --git a/scala-mode-map.el b/scala-mode-map.el new file mode 100644 index 0000000..615c8fd --- /dev/null +++ b/scala-mode-map.el @@ -0,0 +1,28 @@ +;;; scala-mode-map.el - Major mode for editing scala, keyboard map +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +(require 'scala-mode-indent) + +(defvar scala-mode-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map prog-mode-map) + ;(substitute-key-definition 'delete-indentation 'scala-indent:join-line map global-map) + map) + "Local key map used for scala mode") + +(defun scala-mode-map:add-self-insert-hooks () + (add-hook 'post-self-insert-hook + 'scala-indent:indent-on-parentheses) + (add-hook 'post-self-insert-hook + 'scala-indent:indent-on-special-words) + (add-hook 'post-self-insert-hook + 'scala-indent:indent-on-scaladoc-asterisk) + (add-hook 'post-self-insert-hook + 'scala-indent:fix-scaladoc-close)) + +(defun scala-mode-map:add-remove-indent-hook () + (add-hook 'post-command-hook + 'scala-indent:remove-indent-from-previous-empty-line)) + +(provide 'scala-mode-map) diff --git a/scala-mode-navigation.el b/scala-mode-navigation.el deleted file mode 100644 index ba4d586..0000000 --- a/scala-mode-navigation.el +++ /dev/null @@ -1,193 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-navigation.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-navigation) - -(require 'scala-mode-constants) - -(defun scala-when-looking-at* (regexp &optional thunk) - (let ((saved-match-data (match-data))) - (if (looking-at regexp) - (progn (goto-char (match-end 0)) - (set-match-data saved-match-data) - (or (not thunk) (funcall thunk))) - (set-match-data saved-match-data) - nil))) - -(defmacro scala-when-looking-at (regexp &rest body) - (if body - `(scala-when-looking-at* ,regexp (lambda () ,@body)) - `(scala-when-looking-at* ,regexp))) - -(defun scala-forward-spaces (&optional limit) - (if limit - (save-restriction - (narrow-to-region (point) limit) - (forward-comment 100000)) - (forward-comment 100000))) - -(defun scala-backward-spaces () - (forward-comment -100000)) - -(defun scala-looking-at-backward (re) - (save-excursion - (when (= 0 (skip-syntax-backward "w_")) (backward-char)) - (looking-at re))) - -(defmacro scala-point-after (&rest body) - `(save-excursion - ,@body - (point))) - - -(defmacro scala-move-if (&rest body) - (let ((pt-sym (make-symbol "point")) - (res-sym (make-symbol "result"))) - `(let ((,pt-sym (point)) - (,res-sym ,(cons 'progn body))) - (unless ,res-sym (goto-char ,pt-sym)) - ,res-sym))) - -(defun scala-forward-ident () - ;; Move forward over an identifier. - (scala-forward-spaces) - (if (looking-at scala-ident-re) - (goto-char (match-end 0)) - (forward-char)) - t) - -(defun scala-backward-ident () - ;; Move backward over an identifier. - (scala-backward-spaces) - (if (scala-looking-at-backward scala-ident-re) - (goto-char (match-beginning 0)) - (backward-char)) - t) - -(defun scala-forward-qual-ident () - ;; Move forward over a qualifier identifier. - (scala-forward-spaces) - (if (looking-at scala-qual-ident-re) - (goto-char (match-end 0)) - (forward-char)) - t) - -(defun scala-backward-qual-ident () - ;; Move backward over a qualifier identifier. - (scala-backward-spaces) - (if (scala-looking-at-backward scala-qual-ident-re) - (goto-char (match-beginning 0)) - (backward-char)) - t) - -(defun scala-forward-simple-type () - ;; Move forward over a simple type (as defined by the grammar). - ;; Works only when point is at the beginning of a simple type - ;; (modulo initial spaces/comments). - (cond ((eobp) nil) - ((= (char-after) ?\() - ;; Parenthesized type - (forward-sexp) - t) - (t - ;; Type designator - (scala-forward-qual-ident) - (scala-forward-spaces) - (cond ((eobp) nil) - ((= (char-after) ?\[) - ;; Type arguments - (forward-sexp)) - ((= (char-after) ?\#) - ;; Type selection - (forward-char) - (scala-forward-ident))) - t))) - -(defun scala-forward-type1 () - ;; Move forward over a type1 (as defined by the grammar). - ;; Works only when point is at the beginning of a type (modulo - ;; initial spaces/comments). - (scala-forward-spaces) - (scala-when-looking-at "\\<class\\>" - (forward-word 1) (scala-forward-spaces)) - (scala-forward-simple-type) - (while (scala-when-looking-at "\\s *\\<with\\>\\s *") - (if (and (not (eobp)) (= (char-after) ?\{)) - (forward-sexp) ;skip refinement - (scala-forward-simple-type))) - t) - -(defun scala-forward-type () - ;; Move forward over a type. - (cond ((eobp) nil) - ((= (char-after) ?\() - ;; Function type (several arguments) - (forward-sexp) - (scala-when-looking-at "\\s *=>\\s *" (scala-forward-type)) - t) - (t - ;; Type1 or function type with one argument - (scala-forward-type1) - (scala-when-looking-at "\\s *=>\\s *" (scala-forward-type)) - t))) - -(defun scala-forward-type-param () - ;; Move over a type parameter - ;; variance - (scala-when-looking-at "\\s *[-+]\\s *") - (scala-forward-ident) - ;; bounds - (while (scala-when-looking-at "\\s *[<>][:%]\\s *") - (scala-forward-type)) - t) - -(defun scala-forward-literal () - ;; Move forward over an integer, float, character or string literal. - (scala-forward-spaces) - (scala-when-looking-at scala-literal-re) - t) diff --git a/scala-mode-paragraph.el b/scala-mode-paragraph.el new file mode 100644 index 0000000..06fc7b2 --- /dev/null +++ b/scala-mode-paragraph.el @@ -0,0 +1,110 @@ +;;; scala-mode-paragraph.el - Major mode for editing scala, paragraph +;;; detection and fill +;;; Copyright (c) 2012 Heikki Vesalainen For information on the License, +;;; see the LICENSE file + +;;; Based on Scala Language Specification (SLS) Version 2.9 + +;;; Provides paragraph navigation and fill for scaladocs and +;;; multi-line strings. + +(defconst scala-paragraph:paragraph-line-start-re + (concat "\\(?:\\s-*" ; whitespace + "\\(?://+\\|\\*\\|/\\*+" ; comment start + "\\||\\)?" ; multi-line margin | + "\\s-*\\)")) ; whitespace + +(defconst scala-paragraph:scaladoc-list-start-re + (concat "\\(?:-" ; unordered liststs + "\\|[1IiAa]\\." ; ordered lists + "\\)\\s-*")) + +(defconst scala-paragraph:fill-first-line-re + (concat "\\s-*\\(//+\\|\\*\\||\\)?\\s-*" + "\\(?:" scala-paragraph:scaladoc-list-start-re "\\)?")) + +(defconst scala-paragraph:paragraph-start-re + (concat scala-paragraph:paragraph-line-start-re + "\\(?:$" ; empty line + "\\|==*[^=]+==*[ ]*$" ; headings + "\\|" + scala-paragraph:scaladoc-list-start-re + "\\|{{{" ; code block start + "\\|}}}" ; code block end + "\\|@[a-zA-Z]+\\>" ; annotations + "\\)" + "\\|\\(?:\\s-*\\*/\\)" ; end of comment + )) + +(defconst scala-paragraph:paragraph-separate-re + (concat scala-paragraph:paragraph-line-start-re + "\\(?:$\\)" + "\\|\\(?:\\s *\\*/\\)" ; end of comment + )) + +(defun scala-paragraph:fill-function () + (let (fill) + (save-restriction + (save-excursion + (widen) + (beginning-of-line) + (cond ((looking-at "\\s-*/?\\*+\\s-*") + (setq fill (replace-regexp-in-string + "/\\*+" + (lambda (str) (if (= (length str) 3) " *" " *")) + (match-string-no-properties 0))) + (goto-char (match-end 0)) + (when (looking-at scala-paragraph:scaladoc-list-start-re) + (setq fill + (concat fill (make-string (- (match-end 0) + (match-beginning 0)) ?\s))))) + ((or (re-search-forward "\"\"\"|" (line-end-position) t) + (and (eq (nth 3 (syntax-ppss)) t) + (re-search-forward "^\\s-*|" (line-end-position) t))) + (setq fill (concat (make-string (- (current-column) 1) ?\s) "|")) + (setq fill (concat fill (make-string (skip-syntax-forward " ") ?\s))) + (when (looking-at scala-paragraph:scaladoc-list-start-re) + (setq fill + (concat fill (make-string (- (match-end 0) + (match-beginning 0)) ?\s)))))))) + fill)) + +(defun scala-paragraph:fill-paragraph (&rest args) + ;; move to inside multi-line comment or multi-line string, if outside + (when (looking-at "\\s-*\\(?:/\\**\\|\"\"\"\\)\\s-*") + (goto-char (match-end 0))) + (let ((state (syntax-ppss)) + (fill-paragraph-function + ;; Avoid infinite recursion, set fill-paragraph-function to + ;; nil if it is 'scala-paragraph:fill-paragraph + (unless (eq fill-paragraph-function 'scala-paragraph:fill-paragraph) + fill-paragraph-function))) + (cond ((integerp (nth 4 state)) + ;; mask multi-line comments and fill + (save-restriction + (narrow-to-region (nth 8 state) + (save-excursion (goto-char (nth 8 state)) + (if (forward-comment 1) + (point) + (point-max)))) + (apply #'fill-paragraph args)) + t) + ((eq (nth 4 state) t) + ;; line comment, let normal fill-function handle this + nil) + ((eq (nth 3 state) t) + ;; mask multi-line strings and fill. + (save-restriction + (narrow-to-region (nth 8 state) + (save-excursion (goto-char (nth 8 state)) + (or (ignore-errors + (forward-sexp) + (point)) + (point-max)))) + (apply #'fill-paragraph args)) + t) + ;; TODO: fill lists + ;; the rest should not be filled (code, etc) + (t t)))) + +(provide 'scala-mode-paragraph) diff --git a/scala-mode-prettify-symbols.el b/scala-mode-prettify-symbols.el new file mode 100644 index 0000000..73dd259 --- /dev/null +++ b/scala-mode-prettify-symbols.el @@ -0,0 +1,94 @@ +;;; scala-mode-prettify-symbols.el --- Prettifying scala symbols -*- coding: utf-8; -*- + +;; Copyright (c) 2016 Merlin Göttlinger +;; License: http://www.gnu.org/licenses/gpl.html + +;;; Commentary: +;; +;; Suggested `prettify-symbols' for Scala editing, enable +;; `prettify-symbols-mode' and `setq' an alist of your choice +;; for `prettify-symbols-alist'. + +;;; Code: + +(defconst + scala-mode-pretty-bool-alist + '(("<=" . ?≤) + (">=" . ?≥) + ("==" . ?≡) + ("===" . ?≣) + ("!" . ?¬) + ("!=" . ?≢) + ("&&" . ?∧) + ("||" . ?∨) + ("true" . ?⊤) + ("false" . ?⊥) + ("Boolean" . ?𝔹)) + "Prettify rules for boolean related operations.") + +(defconst + scala-mode-pretty-collection-alist + '(("empty" . ?∅) + ("sum" . ?∑) + ("product" . ?∏) + ("contains" . ?∍) + ("forall" . ?∀) + ("any" . ?∃) + ("intersect" . ?∩) + ("union" . ?∪) + ("diff" . ?≏) + ("subsetOf" . ?⊆) + ("++" . ?⧺) + ("::" . ?⸬) + ("--" . ?╌)) + "Prettify rules for collections related operations.") + +(defconst + scala-mode-pretty-arrows-alist + '(("->" . ?→) + ("<-" . ?←) + ("=>" . ?⇒) + ("<=>" . ?⇔) + ("-->" . ?⟶) + ("<->" . ?↔) + ("<--" . ?⟵) + ("<-->" . ?⟷) + ("==>" . ?⟹) + ("<==" . ?⟸) + ("<==>" . ?⟺) + ("~>" . ?⇝) + ("<~" . ?⇜)) + "Prettify rules for arrow related code pieces.") + +(defconst + scala-mode-pretty-misc-alist + '(("Unit" . ?∅) + ("Int" . ?ℤ) + ("assert" . ?⊦) + (":=" . ?≔)) + "Prettify rules for other mixed code pieces.") + +(defconst + scala-mode-pretty-categories-alist + '(("flatMap" . ?⤜) + (">>=" . ?⤜) + ("bind" . ?⤜) + (">>" . ?≫) + ("followedBy" . ?≫) + ("<+>" . ?⊕)) + "Prettify rules for category theory related operators (for use with cats/scalaz/...).") + +(defcustom + scala-prettify-symbols-alist + (append + scala-mode-pretty-bool-alist + scala-mode-pretty-collection-alist + scala-mode-pretty-arrows-alist + scala-mode-pretty-misc-alist + scala-mode-pretty-categories-alist) + "All prettify rules to be applied in scala code." + :type 'alist + :group 'scala) + +(provide 'scala-mode-prettify-symbols) +;;; scala-mode-prettify-symbols.el ends here diff --git a/scala-mode-syntax.el b/scala-mode-syntax.el new file mode 100644 index 0000000..1817868 --- /dev/null +++ b/scala-mode-syntax.el @@ -0,0 +1,1047 @@ +;;;; scala-mode-syntax.el - Major mode for editing scala, syntax +;;; Copyright (c) 2012 Heikki Vesalainen +;;; For information on the License, see the LICENSE file + +;;; Based on Scala Language Specification (SLS) Version 2.9 + +;;;; +;;;; Scala syntax regular expressions +;;;; + +;;; Based on the Scala language specification 2.9. Note: order is not +;;; the same as in the document, as here things are declared before +;;; used. + +;;; A note on naming. Things that end with '-re' are regular +;;; expressions. Things that end with '-group' are regular expression +;;; character groups without the enclosing [], i.e. they are not +;;; regular expressions, but can be used in declaring one. + +;; single letter matching groups (Chapter 1) +(defconst scala-syntax:hexDigit-group "0-9A-Fa-f") +(defconst scala-syntax:UnicodeEscape-re (concat "\\\\u[" scala-syntax:hexDigit-group "]\\{4\\}")) + +(defconst scala-syntax:upper-group "[:upper:]\\$") ;; missing _ to make ids work +(defconst scala-syntax:upperAndUnderscore-group (concat "_" scala-syntax:upper-group )) +(defconst scala-syntax:lower-group "[:lower:]") +(defconst scala-syntax:letter-group (concat scala-syntax:lower-group scala-syntax:upper-group)) ;; TODO: add Lt, Lo, Nl +(defconst scala-syntax:digit-group "0-9") +(defconst scala-syntax:letterOrDigit-group (concat + scala-syntax:upperAndUnderscore-group + scala-syntax:lower-group + scala-syntax:digit-group)) +(defconst scala-syntax:opchar-safe-group "!%&*+/?\\\\^|~-") ;; TODO: Sm, So +(defconst scala-syntax:opchar-unsafe-group "#:<=>@") +(defconst scala-syntax:opchar-group (concat scala-syntax:opchar-unsafe-group + scala-syntax:opchar-safe-group)) + +;; Scala delimiters (Chapter 1), but no quotes +(defconst scala-syntax:delimiter-group ".,;") + +;; Integer Literal (Chapter 1.3.1) +(defconst scala-syntax:nonZeroDigit-group "1-9") +(defconst scala-syntax:octalDigit-group "0-7") +(defconst scala-syntax:decimalNumeral-re + (concat "0" + "\\|[" scala-syntax:nonZeroDigit-group "][" scala-syntax:digit-group "]*")) +(defconst scala-syntax:hexNumeral-re (concat "0x[" scala-syntax:hexDigit-group "]+")) +(defconst scala-syntax:octalNumeral-re (concat "0[" scala-syntax:octalDigit-group "]+")) +(defconst scala-syntax:integerLiteral-re (concat "-?" ;; added from definition of literal + "\\(" scala-syntax:hexNumeral-re + "\\|" scala-syntax:octalNumeral-re + "\\|" scala-syntax:decimalNumeral-re + "\\)[Ll]?")) + + +;; Floating Point Literal (Chapter 1.3.2) +(defconst scala-syntax:exponentPart-re (concat "\\([eE][+-]?[" scala-syntax:digit-group "]+\\)")) +(defconst scala-syntax:floatType-re "[fFdD]") +(defconst scala-syntax:floatingPointLiteral-re + (concat "-?" ;; added from definition of literal + "\\([" scala-syntax:digit-group "]+\\.[" scala-syntax:digit-group "]*" + scala-syntax:exponentPart-re "?" scala-syntax:floatType-re "?" + "\\|" "\\.[" scala-syntax:digit-group "]+" + scala-syntax:exponentPart-re "?" scala-syntax:floatType-re "?" + "\\|" "[" scala-syntax:digit-group "]+" scala-syntax:exponentPart-re + "\\|" "[" scala-syntax:digit-group "]+" scala-syntax:floatType-re "\\)")) + +(defconst scala-syntax:number-safe-start-re + (concat "[^_" scala-syntax:letter-group "]")) + +;; Boolean Literals (Chapter 1.3.3) +(defconst scala-syntax:booleanLiteral-re "true|false") + +;; Escape Sequences (Chapter 1.3.6) +(defconst scala-syntax:escapeSequence-re "\\\\['btnfr\"\\\\]") + +;; Octal Escape Sequences (Chapter 1.3.6) +(defconst scala-syntax:octalEscape-re (concat "\\\\[" scala-syntax:octalDigit-group "\\]\\{1,3\\}")) + +;; Character Literals (Chapter 1.3.4) +(defconst scala-syntax:characterLiteral-re + (concat "\\('\\)\\(" "[^\\\\]" ;; should be just printable char, but this is faster + "\\|" scala-syntax:escapeSequence-re + "\\|" scala-syntax:octalEscape-re + "\\|" scala-syntax:UnicodeEscape-re "\\)\\('\\)")) + +(defconst scala-syntax:string-escape-re + (concat scala-syntax:escapeSequence-re + "\\|" scala-syntax:octalEscape-re + "\\|" scala-syntax:UnicodeEscape-re)) + +;; String Literals (Chapter 1.3.5) +(defconst scala-syntax:stringElement-re + (concat "\\(" "[^\n\"\\\\]" + "\\|" scala-syntax:string-escape-re "\\)")) +(defconst scala-syntax:oneLineStringLiteral-re (concat "\\(\"\\)" scala-syntax:stringElement-re "*\\(\"\\)")) +(defconst scala-syntax:multiLineStringLiteral-start-re + "\\(\"\\)\"\"\\(\"?\"?[^\"]\\)*") +(defconst scala-syntax:multiLineStringLiteral-end-re + "\"\"+\\(\"\\)") +(defconst scala-syntax:multiLineStringLiteral-re + (concat scala-syntax:multiLineStringLiteral-start-re + scala-syntax:multiLineStringLiteral-end-re)) +(defconst scala-syntax:stringLiteral-re + (concat "\\(" scala-syntax:multiLineStringLiteral-re + "\\|" scala-syntax:oneLineStringLiteral-re "\\)" )) + +;; If you change this or any of the used regex, be sure to +;; maintain this or update propertize function accordingly: +;; group 1 = char start, 3 = char end +;; group 4 = multi-line string start, 6 = end +;; group 7 = string start, 9 = end +(defconst scala-syntax:relaxed-char-and-string-literal-re + (concat scala-syntax:characterLiteral-re + "\\|" scala-syntax:multiLineStringLiteral-start-re + "\\(?:" scala-syntax:multiLineStringLiteral-end-re "\\)?" + "\\|\\(\"\\)" "\\(\\\\.\\|[^\"\n\\]\\)*" "\\(\"\\)")) + +;; Identifiers (Chapter 1.1) +(defconst scala-syntax:op-re + (concat "[" scala-syntax:opchar-group "]+" )) +(defconst scala-syntax:idrest-re + ;; Eagerness of regexp causes problems with _. The following is a workaround, + ;; but the resulting regexp matches only what SLS demands. + (concat "\\(" "[_]??" "[" scala-syntax:letter-group scala-syntax:digit-group "]+" "\\)*" + "\\(" "_+" scala-syntax:op-re "\\|" "_" "\\)?")) +(defconst scala-syntax:varid-re (concat "[" scala-syntax:lower-group "]" scala-syntax:idrest-re)) +(defconst scala-syntax:capitalid-re (concat "[" scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re)) +;; alphaid introduce by SIP11 +(defconst scala-syntax:alphaid-re (concat "\\(" "[" scala-syntax:lower-group scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re "\\)")) +(defconst scala-syntax:plainid-re (concat "\\(" scala-syntax:alphaid-re "\\|" scala-syntax:op-re "\\)")) +;; stringlit is referred to, but not defined Scala Language Specification 2.9 +;; we define it as consisting of anything but '`' and newline +(defconst scala-syntax:stringlit-re "[^`\n\r]") +(defconst scala-syntax:quotedid-re (concat "`" scala-syntax:stringlit-re "+`")) +(defconst scala-syntax:id-re (concat "\\(" scala-syntax:plainid-re + "\\|" scala-syntax:quotedid-re "\\)")) +(defconst scala-syntax:id-first-char-group + (concat scala-syntax:lower-group + scala-syntax:upperAndUnderscore-group + scala-syntax:opchar-group)) + +;; Symbol literals (Chapter 1.3.7) +(defconst scala-syntax:symbolLiteral-re + ;; must end with non-' to not conflict with scala-syntax:characterLiteral-re + (concat "\\('" scala-syntax:plainid-re "\\)\\([^']\\|$\\)")) + +;; Literals (Chapter 1.3) +(defconst scala-syntax:literal-re + (concat "\\(" scala-syntax:integerLiteral-re + "\\|" scala-syntax:floatingPointLiteral-re + "\\|" scala-syntax:booleanLiteral-re + "\\|" scala-syntax:characterLiteral-re + "\\|" scala-syntax:stringLiteral-re + "\\|" scala-syntax:symbolLiteral-re + "\\|" "null" "\\)")) + +(defconst scala-syntax:interpolation-re + (concat "\\(" "\\$" scala-syntax:id-re "\\|" "\\${[^}\n\\\\]*}" "\\)")) + +(defun scala-syntax:interpolation-matcher (end) + (let* ((pos nil) + (syntax nil) + (str-start nil) + (char-before-str nil)) + (while (and + (setq pos (re-search-forward scala-syntax:interpolation-re end t)) + (setq syntax (syntax-ppss pos)) + (if (nth 3 syntax) ;; "is string" + (progn + (setq str-start (nth 8 syntax)) + ;; s"foo" + ;; ^-- `char-before-str', must be identifier + (setq char-before-str (char-after (1- str-start))) + ;; break if match + (null (string-match-p + scala-syntax:id-re (string char-before-str)))) + t))) ;; keep going + pos)) + +;; Paths (Chapter 3.1) +;; emacs has a problem with these regex, don't use them +;; (defconst scala-syntax:classQualifier-re (concat "[[]" scala-syntax:id-re "[]]")) +;; (defconst scala-syntax:stableId-re +;; (concat "\\(\\(" "this" +;; "\\|" "super" scala-syntax:classQualifier-re +;; "\\|" scala-syntax:id-re +;; "\\)\\.\\)*" +;; scala-syntax:id-re)) +;; (defconst scala-syntax:path-re +;; (concat "\\(" scala-syntax:stableId-re +;; "\\|" "\\(" scala-syntax:id-re "\\." "\\)?" "this" "\\)")) + +(defun scala-syntax:looking-at-super () + (save-excursion + (when (looking-at "\\<super\\>") + (let ((beg (match-beginning 0))) + (when (and (goto-char (match-end 0)) + (or (when (= (char-after) ?.) + (forward-char) + t) + (and (when (and (not (eobp)) (= (char-after) ?\[)) + (forward-char) + t) + (progn (scala-syntax:skip-forward-ignorable) + (looking-at scala-syntax:id-re)) + (progn (goto-char (match-end 0)) + (scala-syntax:skip-forward-ignorable) + (when (and (not (eobp)) (= (char-after) ?\])) + (forward-char) + t)) + (when (and (not (eobp)) (= (char-after) ?.)) + (forward-char) + t))) + (looking-at scala-syntax:id-re)) + (set-match-data `(,beg ,(match-end 0))) + t))))) + +(defun scala-syntax:looking-at-stableIdOrPath (&optional path-p beg) + (unless beg (setq beg (point))) + (save-excursion + (cond ((looking-at "\\<this\\>") + (goto-char (match-end 0)) + (if (and (not (eobp)) (= (char-after) ?.)) + (progn (forward-char) + (scala-syntax:looking-at-stableIdOrPath path-p beg)) + path-p)) + ((or (scala-syntax:looking-at-super) + (and (not (or (looking-at scala-syntax:keywords-unsafe-re) + (scala-syntax:looking-at-reserved-symbol nil))) + (looking-at scala-syntax:id-re))) + (goto-char (match-end 0)) + (if (and (not (eobp)) (= (char-after) ?.)) + (progn (forward-char) + (scala-syntax:looking-at-stableIdOrPath path-p beg)) + (set-match-data `(,beg ,(match-end 0))) + (point)))))) + +(defun scala-syntax:looking-at-simplePattern-beginning () + (or (looking-at "[_(]") + (looking-at scala-syntax:literal-re) + (scala-syntax:looking-at-stableIdOrPath))) + + +(defun scala-syntax:regexp-for-id (id) + (let ((prefix-regex + (if (string-match scala-syntax:alphaid-re id) + "\\b" (concat "\\(^\\|[^" scala-syntax:opchar-group "]\\)"))) + (suffix-regex + (if (string-match scala-syntax:op-re (substring id -1 nil)) + (concat "\\([^" scala-syntax:opchar-group "]\\|$\\)") "\\b"))) + (concat prefix-regex id suffix-regex))) + +;;; +;;; Other regular expressions +;;; + +(defconst scala-syntax:preamble-start-re + "\#\!") + +(defconst scala-syntax:empty-line-re + "^\\s *$") + +(defconst scala-syntax:comment-start-re + "/[/*]") + +(defconst scala-syntax:end-of-code-line-re + (concat "\\([ ]\\|$\\|" scala-syntax:comment-start-re "\\)") + "A special regexp that can be concatenated to an other regular + expression when used with scala-syntax:looking-back-token. Not + meaningfull in other contexts.") + +(defconst scala-syntax:path-keywords-unsafe-re + (regexp-opt '("super" "this") 'words)) + +(defconst scala-syntax:path-keywords-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:path-keywords-unsafe-re "\\)")) + +(defconst scala-syntax:value-keywords-unsafe-re + (regexp-opt '("false" "null" "true") 'words)) + +(defconst scala-syntax:value-keywords-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:value-keywords-unsafe-re "\\)")) + +(defconst scala-syntax:other-keywords-unsafe-re + (regexp-opt '("abstract" "case" "catch" "class" "def" "do" "else" "extends" + "final" "finally" "for" "forSome" "if" "implicit" "import" + "lazy" "match" "new" "object" "override" "package" "private" + "protected" "return" "sealed" "throw" "trait" "try" "type" + "val" "var" "while" "with" "yield" "inline") 'words)) + +(defconst scala-syntax:other-keywords-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:other-keywords-unsafe-re "\\)")) + +(defconst scala-syntax:keywords-unsafe-re + (concat "\\(" scala-syntax:path-keywords-unsafe-re + "\\|" scala-syntax:value-keywords-unsafe-re + "\\|" scala-syntax:other-keywords-unsafe-re + "\\)")) + +;; TODO: remove +;; (defconst scala-syntax:keywords-re +;; (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:value-keywords-unsafe-re +;; "\\|" scala-syntax:path-keywords-unsafe-re +;; "\\|" scala-syntax:other-keywords-unsafe-re "\\)")) + + +(defconst scala-syntax:after-reserved-symbol-underscore-re + (concat "$\\|" scala-syntax:comment-start-re + "\\|[^" scala-syntax:letterOrDigit-group "]")) + +(defconst scala-syntax:reserved-symbol-underscore-re + ;; reserved symbol _ + (concat "\\(^\\|[^" scala-syntax:letterOrDigit-group "]\\)" + "\\(_\\)" + "\\(" scala-syntax:after-reserved-symbol-underscore-re "\\)")) + +(defconst scala-syntax:reserved-symbols-unsafe-re + ;; reserved symbols. The regexp is unsafe as it does not + ;; check the context. + "\\([:#@\u21D2\u2190]\\|=>?\\|<[:%!?\\-]\\|>:\\)" ) + +(defconst scala-syntax:double-arrow-unsafe-re + "\\(=>\\|\u21D2\\)") + +(defconst scala-syntax:after-reserved-symbol-re + (concat "\\($\\|" scala-syntax:comment-start-re + "\\|[^" scala-syntax:opchar-group "]\\)")) + +(defconst scala-syntax:reserved-symbols-re + ;; reserved symbols and XML starts ('<!' and '<?') + (concat "\\(^\\|[^" scala-syntax:opchar-group "]\\)" + scala-syntax:reserved-symbols-unsafe-re + "\\(" scala-syntax:after-reserved-symbol-re "\\)")) + +(defconst scala-syntax:colon-re + (concat "\\(^\\|[^" scala-syntax:opchar-group "]\\)" + "\\(:\\)" + "\\(" scala-syntax:after-reserved-symbol-re "\\)")) + + +(defconst scala-syntax:override-unsafe-re + (regexp-opt '("override") 'words)) + +(defconst scala-syntax:override-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:override-unsafe-re "\\)")) + +(defconst scala-syntax:abstract-unsafe-re + (regexp-opt '("abstract") 'words)) + +(defconst scala-syntax:abstract-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:abstract-unsafe-re "\\)")) + +(defconst scala-syntax:final-unsafe-re + (regexp-opt '("final") 'words)) + +(defconst scala-syntax:final-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:final-unsafe-re "\\)")) + +(defconst scala-syntax:sealed-unsafe-re + (regexp-opt '("sealed") 'words)) + +(defconst scala-syntax:sealed-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:sealed-unsafe-re "\\)")) + +(defconst scala-syntax:implicit-unsafe-re + (regexp-opt '("implicit") 'words)) + +(defconst scala-syntax:implicit-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:implicit-unsafe-re "\\)")) + +(defconst scala-syntax:lazy-unsafe-re + (regexp-opt '("lazy") 'words)) + +(defconst scala-syntax:lazy-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:lazy-unsafe-re "\\)")) + +(defconst scala-syntax:private-unsafe-re + (regexp-opt '("private") 'words)) + +(defconst scala-syntax:private-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:private-unsafe-re "\\)")) + +(defconst scala-syntax:protected-unsafe-re + (regexp-opt '("protected") 'words)) + +(defconst scala-syntax:protected-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:protected-unsafe-re "\\)")) + +(defconst scala-syntax:modifiers-unsafe-re + (regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy" + "private" "protected") 'words)) + +(defconst scala-syntax:modifiers-re + (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:modifiers-unsafe-re "\\)")) + +(defconst scala-syntax:body-start-re + (concat "=" scala-syntax:end-of-code-line-re) + "A regexp for detecting if a line ends with '='") + +(defconst scala-syntax:list-keywords-re + (regexp-opt '("var" "val" "import") 'words) + ("Keywords that can start a list")) + +(defconst scala-syntax:case-re + "\\<case\\>") + +(defconst scala-syntax:for-re + "\\<for\\>") + +(defconst scala-syntax:class-or-object-re + (regexp-opt '("class" "object") 'words)) + + +;;;; +;;;; Character syntax table and related syntax-propertize functions +;;;; + +;;; The syntax table relies havily on the syntax-propertize-functions being +;;; run. Hence this syntax requires at least emacs 24, which introduced +;;; this new facility. + +(defvar scala-syntax:syntax-table nil + "Syntax table used in `scala-mode' buffers.") +(when (not scala-syntax:syntax-table) + (let ((syntab (make-syntax-table))) + ;; 1. start by reseting the syntax table: only (){}[] are + ;; parentheses, so all others marked as parentheses in the parent + ;; table must be marked as symbols, nothing is a punctuation + ;; unless otherwise stated + (map-char-table + #'(lambda (key value) + (when (or (= (syntax-class value) 4) ; open + (= (syntax-class value) 5) ; close + (= (syntax-class value) 1)) ; punctuation + (modify-syntax-entry key "_" syntab))) + (char-table-parent syntab)) + + ;; Below 'space', everything is either illegal or whitespace. + ;; Consider as whitespace, unless otherwise stated below. + (modify-syntax-entry '(0 . 32) " " syntab) + + ;; The scala parentheses + (modify-syntax-entry ?\( "()" syntab) + (modify-syntax-entry ?\[ "(]" syntab) + (modify-syntax-entry ?\{ "(}" syntab) + (modify-syntax-entry ?\) ")(" syntab) + (modify-syntax-entry ?\] ")[" syntab) + (modify-syntax-entry ?\} "){" syntab) + + ;; _ is upper-case letter, but will be modified to be symbol + ;; constituent when in reserved symbol position by + ;; syntax-propertize-function + (modify-syntax-entry ?\_ "w" syntab) + + ;; by default all opchars are punctuation, but they will be + ;; modified by syntax-propertize-function to be symbol + ;; constituents when a part of varid or capitalid + (dolist (char (mapcar 'identity "!#%&*+/:<=>?@^|~-\u21D2\u2190")) ;; TODO: Sm, So + (modify-syntax-entry char "." syntab)) + + ;; for clarity, the \ is alone here and not in the string above + (modify-syntax-entry ?\\ "." syntab) + + ;; scala strings cannot span lines, so we mark + ;; " as punctuation, but do the real stuff + ;; in syntax-propertize-function for properly + ;; formatted strings. + (modify-syntax-entry ?\" "." syntab) + + ;; backquote is given paired delimiter syntax so that + ;; quoted ids are parsed as one sexp. Fontification + ;; is done separately. + (modify-syntax-entry ?\` "$" syntab) + + ;; ' is considered an expression prefix, since it can + ;; both start a Symbol and is a char quote. It + ;; will be given string syntax by syntax-propertize-function + ;; for properly formatted char literals. + (modify-syntax-entry ?\' "'" syntab) + + ;; punctuation as specified by SLS + (modify-syntax-entry ?\. "." syntab) + (modify-syntax-entry ?\; "." syntab) + (modify-syntax-entry ?\, "." syntab) + + ;; comments + ;; the `n' means that comments can be nested + (modify-syntax-entry ?\/ ". 124b" syntab) + (modify-syntax-entry ?\* ". 23n" syntab) + (modify-syntax-entry ?\n "> b" syntab) + (modify-syntax-entry ?\r "> b" syntab) + + (setq scala-syntax:syntax-table syntab))) + +(defun scala-syntax:propertize-extend-region (start end) + "See syntax-propertize-extend-region-functions" + ;; nothing yet + nil) + +(defmacro scala-syntax:put-syntax-table-property (match-group value) + "Add 'syntax-table entry 'value' to the region marked by the +match-group 'match-group'" + `(put-text-property (match-beginning ,match-group) + (match-end ,match-group) + 'syntax-table + ,value)) + +(defun scala-syntax:propertize-char-and-string-literals (start end) + "Mark start and end of character literals as well as one-line +and multi-line string literals. One-line strings and characters +use syntax class 7 (string quotes), while multi-line strings are +marked with 15 (generic string delimiter). Multi-line string +literals are marked even if they are unbalanced. One-line string +literals have to be balanced to get marked. This means invalid +characters and one-line strings will not be fontified." + + (let* ((string-state (nth 3 (syntax-ppss start))) + (unbalanced-p (eq string-state t))) + + (if (and string-state (not unbalanced-p)) + ;; a normal string is open, let's de-propertize + (remove-text-properties start end '(syntax-table nil)) + (save-excursion + (goto-char start) + ;; close the closing for the unbalanced multi-line literal + (when (and unbalanced-p + (re-search-forward scala-syntax:multiLineStringLiteral-end-re end t)) + (scala-syntax:put-syntax-table-property 1 '(15 . nil))) + ;; match any balanced one-line or multi-line literals + (catch 'break + (while (re-search-forward + scala-syntax:relaxed-char-and-string-literal-re end t) + ;; Expects the following groups: + ;; group 1 = char start, 3 = char end + ;; group 4 = multi-line string start, 6 = end + ;; group 7 = string start, 9 = end + (cond + ((match-beginning 1) + (scala-syntax:put-syntax-table-property 1 '(7 . nil)) + (scala-syntax:put-syntax-table-property 3 '(7 . nil))) + ((match-beginning 4) ;; start of multi-line literal + (scala-syntax:put-syntax-table-property 4 '(15 . nil)) + (if (match-beginning 6) + ;; balanced multi-line + (scala-syntax:put-syntax-table-property 6 '(15 . nil)) + ;; un-balanced multi-line + (throw 'break nil))) + ((or + ;; normal string, content is not empty + (match-beginning 8) + ;; empty string at line end + (= (match-end 9) (line-end-position)) + ;; no " after empty string + (not (= (char-after (match-end 10)) ?\"))) + (when (save-excursion + (goto-char (match-beginning 7)) + ;; really valid? + (looking-at-p scala-syntax:oneLineStringLiteral-re)) + (scala-syntax:put-syntax-table-property 7 '(7 . nil)) + (scala-syntax:put-syntax-table-property 9 '(7 . nil)))) + (t (throw 'break nil))))))))) + +(defun scala-syntax:propertize-shell-preamble (start end) + "Mark a shell preamble (#!) at the beginning of a script as a line comment." + (save-excursion + (goto-char start) + (when (and (= start 1) + (looking-at scala-syntax:preamble-start-re)) + (scala-syntax:put-syntax-table-property 0 '(11 . nil)) + (end-of-line) + (when (re-search-forward "\n" end t) + (scala-syntax:put-syntax-table-property 0 '(12 . nil)))))) + +(defun scala-syntax:propertize-underscore-and-idrest (start end) + "Mark all underscores (_) as symbol constituents (syntax 3) or +upper case letter (syntax 2). Also mark opchars in idrest as +symbol constituents (syntax 3)." + (save-excursion + (goto-char start) + (while (re-search-forward "_" end t) + (let ((match-beg (match-beginning 0)) + (match-end (match-end 0))) + (put-text-property + match-beg match-end 'syntax-table + (if (= match-beg (line-beginning-position)) + (if (looking-at scala-syntax:after-reserved-symbol-underscore-re) + '(3 . nil) ; symbol constituent + '(2 . nil)) ; word syntax + (save-excursion + (goto-char (1- match-beg)) + (if (looking-at scala-syntax:reserved-symbol-underscore-re) + '(3 . nil) ; symbol constituent + ;; check for opchars that should be marked as symbol constituents (3) + (goto-char match-end) + (when (looking-at scala-syntax:op-re) + (scala-syntax:put-syntax-table-property 0 '(3 . nil))) + '(3 . nil))))))))) ;; symbol constituent syntax (3) also for the '_' + +(defun scala-syntax:propertize-special-symbols (start end) + (save-excursion + (goto-char start) + (while (re-search-forward (concat "[" scala-syntax:opchar-group "]" scala-syntax:op-re) end t) + (let ((match-beg (match-beginning 0)) + (match-end (match-end 0)) + (match (match-string 0))) + (unless (or + (string-suffix-p "*/" match) + (member match '("</")) + (member 0 (mapcar (lambda (regexp) (string-match regexp match)) '("^*+/$" "^//.*$" "^/\\*+$"))) + (equal 2 (syntax-class (syntax-after match-end))) + (equal 2 (syntax-class (syntax-after (1- match-beg))))) + (put-text-property match-beg match-end 'syntax-table '(3 . nil))))))) + +(defun scala-syntax:propertize-quotedid (start end) + "Mark all `scala-syntax:quotedid-re' as symbol constituents (syntax 3)" + (save-excursion + (goto-char start) + (while (re-search-forward scala-syntax:quotedid-re end t) + (scala-syntax:put-syntax-table-property 0 '(3 . nil))))) + +(defun scala-syntax:propertize-dollar (start end) + "Mark all $ occurences as punctuation (syntax 1)" + (save-excursion + (goto-char start) + (while (re-search-forward "\\$" end t) + (scala-syntax:put-syntax-table-property 0 '(1 . nil))))) + +(defun scala-syntax:propertize (start end) + "See syntax-propertize-function" + (scala-syntax:propertize-char-and-string-literals start end) + (scala-syntax:propertize-shell-preamble start end) + (scala-syntax:propertize-underscore-and-idrest start end) + (scala-syntax:propertize-special-symbols start end) + (scala-syntax:propertize-quotedid start end) + (scala-syntax:propertize-dollar start end)) + +;;;; +;;;; Syntax navigation functions +;;;; + +(defun scala-syntax:beginning-of-code-line () + (interactive) + "Move to the beginning of code on the line, or to the end of +the line, if the line is empty. Return the new point. Not to be +called on a line whose start is inside a comment, i.e. a comment +begins on the previous line and continues past the start of this +line." + ;; TODO: make it work even if the start IS inside a comment + (beginning-of-line) + (let ((eol (line-end-position)) + (pos (point))) + + (while (and (forward-comment 1) + (< (point) eol)) + (setq pos (point))) + ;; Now we are either on a different line or at eol. + ;; Pos is the last point one the starting line. + (if (> (point) eol) + (goto-char pos) + (skip-syntax-forward " " eol) + (point)))) + +(defun scala-syntax:looking-at-varid-p (&optional point) + "Return true if looking-at varid, and it is not the start of a +stableId" + (save-excursion + (when point (goto-char point)) + (scala-syntax:skip-forward-ignorable) + (let ((case-fold-search nil)) + (when (looking-at scala-syntax:varid-re) + (save-match-data + (if (or (= (char-after (match-end 0)) ?.) + (looking-at "\\<\\(this\\|super\\)\\>")) + nil + t)))))) + +(defun scala-syntax:looking-at-empty-line-p () + (save-excursion + (or (bolp) + (skip-syntax-forward " >" (1+ (line-end-position)))) + (looking-at scala-syntax:empty-line-re))) + +(defun scala-syntax:looking-at-reserved-symbol (re &optional point) + (interactive) + (unless re (setq re scala-syntax:reserved-symbols-unsafe-re)) + (save-excursion + (when point (goto-char point)) + (scala-syntax:skip-forward-ignorable) + (and (looking-at re) + (goto-char (match-end 0)) + (looking-at-p scala-syntax:after-reserved-symbol-re)))) + +(defun scala-syntax:looking-at-case-p (&optional point) + (save-excursion + (when point (goto-char point)) + (scala-syntax:skip-forward-ignorable) + (and (looking-at scala-syntax:case-re) + (goto-char (match-end 0)) + (scala-syntax:skip-forward-ignorable) + (not (looking-at-p scala-syntax:class-or-object-re))))) + +(defun scala-syntax:looking-back-empty-line-p () + "Return t if the previous line is empty" + (save-excursion + (skip-syntax-backward " " (line-beginning-position)) + (and (bolp) + (forward-line -1) + (looking-at-p scala-syntax:empty-line-re)))) + +(defun scala-syntax:skip-forward-ignorable () + "Moves forward over ignorable whitespace and comments. A +completely empty line is not ignorable and will not be mobed over." + (interactive) + (save-match-data + (while (and (not (scala-syntax:looking-at-empty-line-p)) + (forward-comment 1))) + (skip-syntax-forward " " (line-end-position)))) + +(defun scala-syntax:skip-backward-ignorable () + "Move backwards over ignorable whitespace and comments. A +completely empty line is not ignorable and will not be moved +over. Returns the number of points moved (will be negative)." + (save-match-data + (while (and (not (scala-syntax:looking-back-empty-line-p)) + (forward-comment -1))) + (skip-syntax-backward " " (line-beginning-position)))) + +(defun scala-syntax:looking-at (re) + "Return the end position of the matched re, if the current +position is followed by it, or nil if not. All ignorable comments +and whitespace are skipped before matching." + (save-excursion + (scala-syntax:skip-forward-ignorable) + (looking-at re))) + +(defun scala-syntax:looking-back-token (re &optional max-chars) + "Return the start position of the token matched by re, if the +current position is preceeded by it, or nil if not. All ignorable +comments and whitespace are ignored, i.e. does not search past an +empty line. Expects to be outside of comment. A limit for the +search is calculated based on max-chars. The function won't look +further than max-chars starting after skipping any ignorable." + (save-excursion + ;; skip back all comments + (scala-syntax:skip-backward-ignorable) + (let ((end (point)) + (limit (when max-chars (- (point) max-chars)))) + ;; skip back punctuation or ids (words and related symbols and delimiters) + (if (or (/= 0 (skip-chars-backward scala-syntax:delimiter-group limit)) + (/= 0 (skip-syntax-backward "." limit)) + (/= 0 (skip-syntax-backward "(" limit)) + (/= 0 (skip-syntax-backward ")" limit)) + (/= 0 (skip-syntax-backward "w_'$" limit))) + (if (looking-at re) (point) nil) + nil)))) + +(defun scala-syntax:backward-parameter-groups () + "Move back over all parameter groups to the start of the first +one." + (save-match-data + (while (scala-syntax:looking-back-token "[])]" 1) + (backward-list)))) + +(defun scala-syntax:forward-parameter-groups () + "Move back over all parameter groups to the end of the last +one." + (save-match-data + (while (scala-syntax:looking-at "[[(]") + (forward-list)))) + +(defun scala-syntax:forward-modifiers () + "Move forward over any modifiers." + (save-match-data + (while (scala-syntax:looking-at scala-syntax:modifiers-re) + (scala-syntax:forward-sexp) + (when (scala-syntax:looking-at "[[]") + (forward-list))))) + +(defun scala-syntax:looking-back-else-if-p () + ;; TODO: rewrite using (scala-syntax:if-skipped (scala:syntax:skip-backward-else-if)) + (save-excursion + (if (and (scala-syntax:looking-back-token "\\s)" 1) + (backward-list) + (prog1 (scala-syntax:looking-back-token "if") + (goto-char (match-beginning 0))) + (prog1 (scala-syntax:looking-back-token "else") + (goto-char (match-beginning 0)))) + (point) nil))) + +(defun scala-syntax:newlines-disabled-p (&optional point) + "Return true if newlines are disabled at the current point (or +point 'point') as specified by SLS chapter 1.2" + ;; newlines are disabled if + ;; - in '()' or '[]' + ;; - between 'case' and '=>' + ;; - XML mode (not implemented here) + (unless point (setq point (point))) + (save-excursion + (let* ((state (syntax-ppss point)) + (parenthesisPos (nth 1 state))) + (when parenthesisPos ;; if no parenthesis, then this cannot be a case block either + (goto-char parenthesisPos) + (or + ;; the trivial cases of being inside ( or [ + (= (char-after) ?\() + (= (char-after) ?\[) + ;; else we have to see about case + (progn + (forward-char) + (forward-comment (buffer-size)) + (skip-syntax-forward " >") + (when (looking-at scala-syntax:case-re) + (let ((limit (match-beginning 0))) + (goto-char (or (nth 8 state) point)) + ;; go to the start of => or 'case' + (while (> (point) limit) + (scala-syntax:backward-sexp) + (when (or (looking-at scala-syntax:case-re) + (scala-syntax:looking-at-reserved-symbol + scala-syntax:double-arrow-unsafe-re)) + (setq limit (point)))) + ;; unless we found '=>', check if we found 'case' (but + ;; 'case class' or 'case object') + (unless (scala-syntax:looking-at-reserved-symbol + scala-syntax:double-arrow-unsafe-re) + (scala-syntax:forward-sexp) + + (and (<= (point) point) ;; check that we were inside in the first place + (progn (scala-syntax:skip-forward-ignorable) + (not (looking-at scala-syntax:class-or-object-re))))))))))))) + +(defun scala-syntax:forward-sexp () + "Move forward one scala expression. It can be: parameter list (value or type), +id, reserved symbol, keyword, block, or literal. Punctuation (.,;) +and comments are skipped silently. Position is placed at the +end of the skipped expression." + (interactive) + (syntax-propertize (point-max)) + ;; emacs knows how to properly skip: lists, varid, capitalid, + ;; strings, symbols, chars, quotedid. What we have to handle here is + ;; most of all ids made of op chars + + ;; skip comments, whitespace and scala delimiter chars .,; so we + ;; will be at the start of something interesting + (forward-comment (buffer-size)) + (while (< 0 (+ (skip-syntax-forward " ") + (skip-chars-forward scala-syntax:delimiter-group)))) + + ;; emacs can handle everything but opchars + (when (= (skip-syntax-forward ".") 0) + (goto-char (or (scan-sexps (point) 1) (buffer-end 1))))) + +(defun scala-syntax:forward-token () + "Move forward one scala token, comment word or string word. It +can be: start or end of list (value or type), id, reserved +symbol, keyword, block, or literal. Punctuation (.,;), comment +delimiters and string delimiters are skipped silently. Position +is placed at the end of the skipped token." + (interactive) + (syntax-propertize (point-max)) + (skip-syntax-forward " >" (point-max)) + (when (looking-at + (concat "\\([#@:]\\|" scala-syntax:double-arrow-unsafe-re + "\\|:>\\|<:\\)" scala-syntax:after-reserved-symbol-re)) + (goto-char (match-end 1))) + (let ((syntax (char-syntax (char-after))) + (state (syntax-ppss))) + (cond + ((or (nth 4 state) (nth 3 state)) + ;; inside a string or comment, skip words as normal unless that + ;; would end up outside the string. Then leave point at end of + ;; string delimiter. + (let ((start (nth 8 state)) + (end (save-excursion (forward-word) (point)))) + (if (eq (nth 8 (save-excursion (syntax-ppss end))) start) + (goto-char end) + (while (eq (nth 8 (syntax-ppss)) start) + (forward-char))))) + ;; list start or end + ((or (= syntax ?\)) (= syntax ?\()) (forward-char)) + ;; comment or string start is skipped + ((looking-at "\\(//\\|/\\*+\\|\"\\(\"\"\\)?\\)") + (goto-char (match-end 1))) + ;; otherwise forward-sexp + (t (forward-sexp))))) + +(defun scala-syntax:backward-sexp () + "Move backward one scala expression. It can be: parameter + list (value or type), id, reserved symbol, keyword, block, or + literal. Delimiters (.,;) and comments are skipped + silently. Position is placed at the beginning of the skipped + expression." + (interactive) + (syntax-propertize (point)) + ;; for implementation comments, see scala-syntax:forward-sexp + (forward-comment (- (buffer-size))) + (while (> 0 (+ (skip-syntax-backward " ") + (skip-chars-backward scala-syntax:delimiter-group)))) + + (when (= (skip-syntax-backward ".") 0) + (goto-char (or (scan-sexps (point) -1) (buffer-end -1))) + (backward-prefix-chars))) + +(defun scala-syntax:has-char-before (char end) + (save-excursion + (while (and (< (point) end) + (or (bobp) + (/= (char-before) char))) + (scala-syntax:forward-sexp)) + (when (= (char-before) char) + (scala-syntax:skip-forward-ignorable) + (> end (point))))) + +(defun scala-syntax:search-backward-sexp (re) + "Searches backward sexps until it reaches re, empty line or ;. +If re is found, point is set to beginning of re and the position +is returned, otherwise nil is returned" + (let ((found (save-excursion + (while (not (or (bobp) + (scala-syntax:looking-back-empty-line-p) + (scala-syntax:looking-back-token "[;,]") + (looking-at re))) + (scala-syntax:backward-sexp)) + (if (looking-at re) + (point) + nil)))) + (when found (goto-char found)))) + +(defun scala-syntax:list-p (&optional point) + "Returns the start of the list, if the current point (or point +'point') is on the first line of a list element > 1, or nil if +not. A list must be either enclosed in parentheses or start with +'val', 'var' or 'import'." + (save-excursion + ;; first check that the previous line ended with ',' + (when point (goto-char point)) + (scala-syntax:beginning-of-code-line) + (when (scala-syntax:looking-back-token "," 1) + (goto-char (match-beginning 0)) + (let ((parenpoint (nth 1 (syntax-ppss)))) + (if (and parenpoint (or (= (char-after parenpoint) ?\() + (= (char-after parenpoint) ?\[))) + (1+ parenpoint) + (ignore-errors ; catches when we get at parentheses + (while (not (or (bobp) + (looking-at scala-syntax:list-keywords-re) + (scala-syntax:looking-back-empty-line-p) + (scala-syntax:looking-back-token ";"))) + (scala-syntax:backward-sexp))) + (when (looking-at scala-syntax:list-keywords-re) + (goto-char (match-end 0)))))))) + +;; Functions to help with finding the beginning and end of scala definitions. + +(defconst scala-syntax:modifiers-re + (regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy" + "private" "protected" "case") 'words)) + +(defconst scala-syntax:whitespace-delimeted-modifiers-re + (concat "\\(?:" scala-syntax:modifiers-re "\\(?: *\\)" "\\)*")) + +(defconst scala-syntax:definition-words-re + (mapconcat 'regexp-quote '("class" "object" "trait" "val" "var" "def" "type") "\\|")) + +(defun scala-syntax:build-definition-re (words-re) + (concat " *" + scala-syntax:whitespace-delimeted-modifiers-re + words-re + "\\(?: *\\)" + "\\(?2:" + scala-syntax:id-re + "\\)")) + +(defconst scala-syntax:all-definition-re + (scala-syntax:build-definition-re + (concat "\\(?1:" scala-syntax:definition-words-re "\\)\\b"))) + +;; Functions to help with beginning and end of definitions. + +(defun scala-syntax:backward-sexp-forcing () + (condition-case ex (backward-sexp) ('error (backward-char)))) + +(defun scala-syntax:forward-sexp-or-next-line () + (interactive) + (cond ((looking-at "\n") (forward-line 1) (beginning-of-line)) + (t (forward-sexp)))) + +(defun scala-syntax:beginning-of-definition () + "This function may not work properly with certain types of scala definitions. +For example, no care has been taken to support multiple assignments to vals such as + +val a, b = (1, 2) +" + (interactive) + (let ((found-position + (save-excursion + (scala-syntax:backward-sexp-forcing) + (scala-syntax:movement-function-until-re scala-syntax:all-definition-re + 'scala-syntax:backward-sexp-forcing)))) + (when found-position (progn (goto-char found-position) (back-to-indentation))))) + +(defun scala-syntax:end-of-definition () + "This function may not work properly with certain types of scala definitions. +For example, no care has been taken to support multiple assignments to vals such as + +val a, b = (1, 2) +" + (interactive) + (re-search-forward scala-syntax:all-definition-re) + (scala-syntax:find-brace-equals-or-next) + (scala-syntax:handle-brace-equals-or-next)) + +(defun scala-syntax:find-brace-equals-or-next () + (scala-syntax:go-to-pos + (save-excursion + (scala-syntax:movement-function-until-cond-function + (lambda () (or (looking-at "[[:space:]]*[{=]") + (looking-at scala-syntax:all-definition-re))) + (lambda () (condition-case ex (scala-syntax:forward-sexp-or-next-line) ('error nil))))))) + +(defun scala-syntax:handle-brace-equals-or-next () + (cond ((eobp) nil) + ((looking-at "[[:space:]]*{") (forward-sexp)) + ((looking-at "[[:space:]]*=") (scala-syntax:forward-sexp-or-next-line) + (scala-syntax:handle-brace-equals-or-next)) + ((looking-at scala-syntax:all-definition-re) nil) + ((looking-at "[[:space:]]*\n[[:space:]]*}") (skip-syntax-forward "[[:space:]]*\n[[:space:]]*}")) + (t (scala-syntax:forward-sexp-or-next-line) + (scala-syntax:handle-brace-equals-or-next)))) + +(defun scala-syntax:movement-function-until-re (re movement-function) + (save-excursion + (scala-syntax:movement-function-until-cond-function + (lambda () (looking-at re)) movement-function))) + +(defun scala-syntax:movement-function-until-cond-function (cond-function movement-function) + (let ((last-point (point))) + (if (not (funcall cond-function)) + (progn (funcall movement-function) + (if (equal last-point (point)) nil + (scala-syntax:movement-function-until-cond-function + cond-function movement-function))) last-point))) + +(defun scala-syntax:go-to-pos (pos) (when pos (goto-char pos))) + +(provide 'scala-mode-syntax) diff --git a/scala-mode-ui.el b/scala-mode-ui.el deleted file mode 100644 index 8dd355a..0000000 --- a/scala-mode-ui.el +++ /dev/null @@ -1,159 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-ui.el - Menu entries and keyboard shortcuts for scala mode - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-ui) - -(require 'easymenu) -(require 'scala-mode-lib) - -(eval-when-compile - (require 'scala-mode-inf)) - -(eval-and-compile - (defcustom scala-mode-ui:prefix-key "\C-c" - "Key prefix for scala mode." - :group 'scala)) - -(defmacro scala-mode-ui:key (key) - "Simple macro for appending 'scala-mode-prefix-key' to key commands" - `(kbd ,(concat scala-mode-ui:prefix-key " " key))) - -;;; Helper functions - -(defun scala-mode-ui:interpreter-running-p () - "True iff a Scala interpreter is currently running in a buffer." - ;; The following makes sure that we do not autoload - ;; scala-mode-inf just to check if the interpreter is running. - (and (fboundp 'scala-mode-inf) - (let ((ism-def (symbol-function 'scala-mode-inf))) - (not (and (consp ism-def) (eq (car ism-def) 'autoload)))) - (scala-interpreter-running-p-1))) - -;;; Menubar - -(scala-mode-lib:define-keys scala-mode-menu-bar-map - - ([scala] (cons "Scala" (make-sparse-keymap "ScalaMode"))) - - ([scala version] '(menu-item "Version" (lambda () (interactive) (message "Using scala mode version %s (%s)" scala-mode-version scala-mode-svn-revision)) )) - ([scala report-bug] '(menu-item "Report bug" scala-mode:report-bug)) - ([scala customize] '(menu-item "Customize" (lambda () (interactive) (customize-group 'scala)))) - ([scala browse-api] '(menu-item "Browse Scala API" scala-mode:browse-api)) - ([scala browse-website] '(menu-item "Browse Scala Website" scala-mode:browse-web-site)) - - ([scala sep0] '("---")) - - ([scala feature] (cons "Features" (make-sparse-keymap "Features"))) - - ([scala feature apropos] '(menu-item "Tag apropos" tags-apropos)) - ([scala feature search] '(menu-item "Tag search" tags-search)) - ([scala feature find] '(menu-item "Tag find" find-tag)) - ([scala feature comp] '(menu-item "Tag complete word" scala-mode-feature-tags-complete)) - ([scala feature load] '(menu-item "Load TAGS file" scala-mode-feature-tags-load)) - ([scala feature create] '(menu-item "Create TAGS file" scala-mode-feature-tags-create)) - - ([scala feature sep1] '("---")) - - ([scala feature speedbar] '(menu-item "Speedbar Focus" speedbar-get-focus)) - - ([scala feature sep0] '("---")) - - ([scala feature electric] '(menu-item "Toggle Scala Electric Mode" scala-electric-mode - :button (:toggle . (scala-mode-feature-electric-active-p)) - :help "Toggle on/off the electric insert mode for Scala files")) - - ([scala sep1] '("---")) - - ([scala eval-buf] '(menu-item "Evaluate buffer" scala-eval-buffer :enable (scala-mode-ui:interpreter-running-p) )) - ([scala eval-reg] '(menu-item "Evaluate region" scala-eval-region :enable (and (scala-mode-ui:interpreter-running-p) mark-active))) - ([scala switch-interp] '(menu-item "Switch to interpreter" scala-switch-to-interpreter :enable (scala-mode-ui:interpreter-running-p) )) - ([scala load-file] '(menu-item "Load file in interpreter" scala-load-file :enable (scala-mode-ui:interpreter-running-p) )) - ([scala quit-interp] '(menu-item "Quit interpreter" scala-quit-interpreter :enable (scala-mode-ui:interpreter-running-p) )) - ([scala run-interp] '(menu-item "Run interpreter..." scala-run-scala :enable (not (scala-mode-ui:interpreter-running-p)) )) - -) - - -;;; Shortcuts - -(defvar scala-mode-map - (let ((map (make-keymap))) - map)) - -(scala-mode-lib:define-keys scala-mode-map - - ;; Attach Menubar - ([menu-bar] scala-mode-menu-bar-map) - - ;; Attach keyboard Shortcuts - ([(control tab)] 'scala-undent-line) - ([backspace] 'backward-delete-char-untabify) - - ("\r" 'scala-newline) - - ([f1] 'speedbar-get-focus) - - ([(control c)(control l)] 'scala-load-file) - ([(control c)(control r)] 'scala-eval-region) - ([(control c)(control b)] 'scala-eval-buffer) - - ([(control c)(control c)] 'comment-region) - - ("}" 'scala-electric-brace) - - ((scala-mode-ui:key "t n") 'scala-mode-feature-tags-create) - ((scala-mode-ui:key "t l") 'scala-mode-feature-tags-load) - ((scala-mode-ui:key "t c") 'scala-mode-feature-tags-complete) - ((scala-mode-ui:key "t s") 'tags-search) - ((scala-mode-ui:key "t a") 'tags-apropos) - ) - - - - - diff --git a/scala-mode-variables.el b/scala-mode-variables.el deleted file mode 100644 index 6aa37e5..0000000 --- a/scala-mode-variables.el +++ /dev/null @@ -1,55 +0,0 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode-feature.el - - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(provide 'scala-mode-variables) - -;; Feature specific variables that need to be shared! - -; define scala-mode-hook -(defvar scala-mode-hook nil - "Hook to run after installing scala mode") diff --git a/scala-mode.el b/scala-mode.el index 2ff0f9b..bfe55f3 100644 --- a/scala-mode.el +++ b/scala-mode.el @@ -1,207 +1,182 @@ -;;; -*-Emacs-Lisp-*- -;;; scala-mode.el - Major mode for editing Scala code. - -;; Copyright (C) 2009-2011 Scala Dev Team at EPFL -;; Authors: See AUTHORS file -;; Keywords: scala languages oop - -;;; License - -;; SCALA LICENSE -;; -;; Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. -;; All rights reserved. -;; -;; This software was developed by the Programming Methods Laboratory of the -;; Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. -;; -;; Permission to use, copy, modify, and distribute this software in source -;; or binary form for any purpose with or without fee is hereby granted, -;; provided that the following conditions are met: -;; -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. -;; -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. -;; -;; 3. Neither the name of the EPFL nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. -;; -;; -;; THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -;; ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -;; SUCH DAMAGE. - -;;; Code -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; scala-mode.el --- Major mode for editing Scala -(provide 'scala-mode) +;; Copyright (c) 2012 Heikki Vesalainen + +;; Homepage: https://github.com/hvesalai/emacs-scala-mode +;; Keywords: languages +;; Package-Version: 0.23 +;; Package-Requires: () -(require 'cl) +;;; Commentary: +;; +;;; Code: -(require 'scala-mode-constants) -(require 'scala-mode-variables) (require 'scala-mode-lib) -(require 'scala-mode-navigation) +(require 'scala-mode-syntax) +(require 'scala-mode-paragraph) (require 'scala-mode-indent) (require 'scala-mode-fontlock) -(require 'scala-mode-ui) -(require 'scala-mode-feature) +(require 'scala-mode-map) +(require 'scala-mode-imenu) +(require 'scala-mode-prettify-symbols) -;;; Customization and Variables -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defvar fixup-whitespace) ;; for compilation +(defvar delete-indentation) ;; for compilation + +;; Tested only for emacs 24 +(unless (<= 24 emacs-major-version) + (error + (format "The Scala mode has been tested only on Emacs version 24.2 (and not your Emacs version %s.%s)" + emacs-major-version emacs-minor-version))) (defgroup scala nil - "Mode for editing Scala code." + "A programming mode for the Scala language 2.9" :group 'languages) -(defcustom scala-mode:api-url "http://www.scala-lang.org/docu/files/api/index.html" - "URL to the online Scala documentation" - :type 'string - :group 'scala) - -(defconst scala-mode-version "0.5.99.5") -(defconst scala-mode-svn-revision "$Revision: 21917 $") -(defconst scala-bug-e-mail "scala@listes.epfl.ch") -(defconst scala-web-url "http://scala-lang.org/") - - -;;; Helper functions/macroes -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defun scala-mode:browse-web-site () - "Browse the Scala home-page" - (interactive) - (require 'browse-url) - (browse-url scala-web-url)) - - -(defun scala-mode:browse-api () - "Browse the Scala API" - (interactive) - (require 'browse-url) - (browse-url scala-mode:api-url)) +(defmacro scala-mode:make-local-variables (&rest quoted-names) + (cons 'progn (mapcar #'(lambda (quoted-name) `(make-local-variable ,quoted-name)) quoted-names))) + +(defun scala-mode:find-tag () + "Determine default tag to search for, based on text at point. +If there is no plausible default, return nil." + (let (from to) + (when (and (progn + ;; Look at text around `point'. + (save-excursion + (if (< 0 (skip-chars-backward scala-syntax:opchar-group)) + (if (= (char-before) ?_) + (skip-syntax-backward "w_")) + (skip-syntax-backward "w_")) + (setq from (point))) + (save-excursion + (skip-syntax-forward "w_.") (setq to (point))) + (save-excursion + (ignore-errors (scala-syntax:backward-sexp)) (setq from (max from (point)))) + (save-excursion + (goto-char from) + (ignore-errors (scala-syntax:forward-sexp)) (setq to (min to (point)))) + (> to from)) + (save-excursion + (goto-char from) + (and (looking-at scala-syntax:id-re) + (not (looking-at scala-syntax:keywords-unsafe-re))))) + (buffer-substring-no-properties from to)))) + + +(defun scala-mode:forward-sexp-function (&optional count) + (unless count (setq count 1)) + (if (< count 0) + (dotimes (n (abs count)) + (scala-syntax:backward-sexp)) + (dotimes (n count) + (scala-syntax:forward-sexp)))) +;;;###autoload +(defun scala-mode:set-scala-syntax-mode () + "Sets the syntax-table and other related variables for the current buffer to those of scala-mode. Can be used to make some other major mode (such as sbt-mode) use scala syntax-table." + (set-syntax-table scala-syntax:syntax-table) + (scala-mode:make-local-variables + 'syntax-propertize-function + 'parse-sexp-lookup-properties + 'forward-sexp-function) + + (add-hook 'syntax-propertize-extend-region-functions + 'scala-syntax:propertize-extend-region) + (setq syntax-propertize-function 'scala-syntax:propertize + parse-sexp-lookup-properties t + forward-sexp-function 'scala-mode:forward-sexp-function)) -(defun scala-mode:report-bug () - "Report a bug to the author of the Scala mode via e-mail. -The package used to edit and send the e-mail is the one selected -through `mail-user-agent'." +;;;###autoload +(defun scala-mode:goto-start-of-code () + "Go to the start of the real code in the file: object, class or trait." (interactive) - (require 'reporter) - (let ((reporter-prompt-for-summary-p t)) - (reporter-submit-bug-report - scala-bug-e-mail - (concat "Emacs Scala mode v" scala-mode-version) - '(scala-indent-step)))) - - - - - -(defvar scala-mode-abbrev-table nil - "Abbrev table in use in `scala-mode' buffers.") -(define-abbrev-table 'scala-mode-abbrev-table nil) - - -(defvar scala-mode-syntax-table nil - "Syntax table used in `scala-mode' buffers.") -(when (not scala-mode-syntax-table) - (setq scala-mode-syntax-table (make-syntax-table)) - ;; strings and character literals - (modify-syntax-entry ?\" "\"" scala-mode-syntax-table) - (modify-syntax-entry ?\\ "\\" scala-mode-syntax-table) - - ;; different kinds of "parenthesis" - (modify-syntax-entry ?\( "()" scala-mode-syntax-table) - (modify-syntax-entry ?\[ "(]" scala-mode-syntax-table) - (modify-syntax-entry ?\{ "(}" scala-mode-syntax-table) - (modify-syntax-entry ?\) ")(" scala-mode-syntax-table) - (modify-syntax-entry ?\] ")[" scala-mode-syntax-table) - (modify-syntax-entry ?\} "){" scala-mode-syntax-table) - - ;; special characters - (modify-syntax-entry ?\_ "_" scala-mode-syntax-table) - - (dolist (char scala-all-special-chars) - (modify-syntax-entry char "." scala-mode-syntax-table)) - - (modify-syntax-entry ?\. "." scala-mode-syntax-table) - - ;; comments - ;; the `n' means that comments can be nested - (modify-syntax-entry ?\/ ". 124nb" scala-mode-syntax-table) - (modify-syntax-entry ?\* ". 23n" scala-mode-syntax-table) - (modify-syntax-entry ?\n "> bn" scala-mode-syntax-table) - (modify-syntax-entry ?\r "> bn" scala-mode-syntax-table)) + (let* ((case-fold-search nil)) + (search-forward-regexp "\\([[:space:]]+\\|^\\)\\(class\\|object\\|trait\\)" nil t) + (move-beginning-of-line nil))) +;;;###autoload +(define-derived-mode scala-mode prog-mode "Scala" + "Major mode for editing scala code. -;;; Mode -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +When started, runs `scala-mode-hook'. -;;;###autoload -(defun scala-mode () - "Major mode for editing Scala code. -When started, run `scala-mode-hook'. \\{scala-mode-map}" - (interactive) - ;; set up local variables - (kill-all-local-variables) - (make-local-variable 'font-lock-defaults) - (make-local-variable 'paragraph-separate) - (make-local-variable 'paragraph-start) - (make-local-variable 'paragraph-ignore-fill-prefix) - (make-local-variable 'require-final-newline) - (make-local-variable 'comment-start) - (make-local-variable 'comment-end) - (make-local-variable 'comment-start-skip) - (make-local-variable 'comment-end-skip) - (make-local-variable 'comment-column) - ;(make-local-variable 'comment-indent-function) - (make-local-variable 'indent-line-function) - ;; - (set-syntax-table scala-mode-syntax-table) - (setq major-mode 'scala-mode - mode-name "Scala" - local-abbrev-table scala-mode-abbrev-table - font-lock-defaults '(scala-font-lock-keywords - nil - nil - ((?\_ . "w")) - nil - (font-lock-syntactic-keywords . scala-font-lock-syntactic-keywords) - (parse-sexp-lookup-properties . t)) - paragraph-separate (concat "^\\s *$\\|" page-delimiter) - paragraph-start (concat "^\\s *$\\|" page-delimiter) - paragraph-ignore-fill-prefix t - require-final-newline t - comment-start "// " - comment-end "" - comment-start-skip "/\\*+ *\\|//+ *" - comment-end-skip " *\\*+/\\| *" - comment-column 40 -; comment-indent-function 'scala-comment-indent-function - indent-line-function 'scala-indent-line - ) - + :syntax-table scala-syntax:syntax-table +; :group +; :abbrev + + (scala-mode:make-local-variables + 'post-self-insert-hook + 'syntax-propertize-function + 'font-lock-syntactic-face-function + 'font-lock-defaults + 'paragraph-start + 'paragraph-separate + 'parse-sexp-lookup-properties + 'fill-paragraph-function + 'adaptive-fill-function + 'adaptive-fill-first-line-regexp + 'comment-start + 'comment-end + 'comment-start-skip + 'comment-column + 'comment-multi-line + 'forward-sexp-function + 'find-tag-default-function + 'indent-line-function + 'fixup-whitespace + 'delete-indentation + 'indent-tabs-mode + 'imenu-create-index-function + 'beginning-of-defun-function + 'end-of-defun-function) + + (add-hook 'syntax-propertize-extend-region-functions + 'scala-syntax:propertize-extend-region) + (setq scala-mode:debug-messages nil + + syntax-propertize-function 'scala-syntax:propertize + parse-sexp-lookup-properties t + + ;; TODO: font-lock + font-lock-defaults '(scala-font-lock:keywords + nil) + font-lock-syntactic-face-function 'scala-font-lock:syntactic-face-function + + ;; TODO: beginning-of-defun-function, end-of-defun-function + + ;; comments + paragraph-start scala-paragraph:paragraph-start-re + paragraph-separate scala-paragraph:paragraph-separate-re + fill-paragraph-function 'scala-paragraph:fill-paragraph + adaptive-fill-function 'scala-paragraph:fill-function + adaptive-fill-first-line-regexp scala-paragraph:fill-first-line-re + comment-start "// " + comment-end "" + comment-start-skip "\\(//+\\|/\\*+\\)[ \t]*" + comment-column 0 + comment-multi-line t + + forward-sexp-function 'scala-mode:forward-sexp-function + find-tag-default-function 'scala-mode:find-tag + indent-line-function 'scala-indent:indent-line + fixup-whitespace 'scala-indent:fixup-whitespace + delete-indentation 'scala-indent:join-line + indent-tabs-mode nil + beginning-of-defun-function #'scala-syntax:beginning-of-definition + end-of-defun-function #'scala-syntax:end-of-definition + imenu-create-index-function #'scala-imenu:create-imenu-index) (use-local-map scala-mode-map) - (turn-on-font-lock) - (scala-mode-feature-install) - (if scala-mode-hook - (run-hooks 'scala-mode-hook))) - - + ;; add indent functionality to some characters + (scala-mode-map:add-remove-indent-hook) + (scala-mode-map:add-self-insert-hooks)) +;; Attach .scala files to the scala-mode +;;;###autoload +(progn + (add-to-list 'auto-mode-alist + '("\\.\\(scala\\|sbt\\)\\'" . scala-mode)) + (modify-coding-system-alist 'file "\\.\\(scala\\|sbt\\)\\'" 'utf-8)) +(provide 'scala-mode) +;;; scala-mode.el ends here diff --git a/test/scala-mode-test.el b/test/scala-mode-test.el new file mode 100644 index 0000000..8a7a195 --- /dev/null +++ b/test/scala-mode-test.el @@ -0,0 +1,178 @@ +(defun smt:test (line exps expf) + "line - line of scala code +exps - expected codes of syntax class +expf - expected font-locks" + (let ((line-length (length line))) + (with-temp-buffer + (insert (format "package ensime + +object Ensime { + %s +}" line)) + (scala-mode) + (font-lock-ensure) + (re-search-backward (regexp-opt `(,line)) nil t) + (let ((end-point (+ (point) line-length)) + (acc-syntax "") + (acc-font "")) + (while (< (point) end-point) + (setq acc-syntax (concat acc-syntax (number-to-string (syntax-class (syntax-after (point)))))) + (setq acc-font (concat acc-font (font-lock-to-string (get-text-property (point) 'face)))) + (forward-char)) + (should (equal acc-syntax exps)) + (should (equal acc-font expf)))))) + +(defun font-lock-to-string (font-lock) + (pcase font-lock + (`nil "-") + ('font-lock-constant-face "C") + ('font-lock-variable-name-face "V") + ('font-lock-keyword-face "K") + ('font-lock-comment-face "O") + ('font-lock-comment-delimiter-face "D") + ('font-lock-doc-face "U") + ('font-lock-type-face "T") + ('font-lock-string-face "S") + (_ "?"))) + +(ert-deftest smt:syntax-class-and-font-lock-test-1 () + (smt:test + "val `tvw xyz/*` = `abc def/*` + 123 /* comment `abc` abc */ + 456" + "22203333333333301033333333333010222011022222220333330222011010222" + "KKK-VVVVVVVVVVV-K---------------CCC-DDDOOOOOOOOOOOOOOOOOOOO---CCC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-2 () + (smt:test + "val |+| = 123" + "2220333010222" + "KKK-VVV-K-CCC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-3 () + (smt:test + "val a_|+| = 123" + "222023333010222" + "KKK-VVVVV-K-CCC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-4 () + (smt:test + "val a = 123 /** hello */" + "222020102220111022222011" + "KKK-V-K-CCC-UUUUUUUUUUUU")) + +(ert-deftest smt:syntax-class-and-font-lock-test-5 () + (smt:test + "val a = <td>hello</td>" + "2220201012212222211221" + "KKK-V-K---------------")) + +(ert-deftest smt:syntax-class-and-font-lock-test-6 () + (smt:test + "// val |--| = 123" + "11022203333010222" + "DDDOOOOOOOOOOOOOO")) + +(ert-deftest smt:syntax-class-and-font-lock-test-7 () + (smt:test + "val xs = 1 :: 2 :: Nil" + "2220220102033020330222" + "KKK-VV-K-C----C----CCC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-8 () + (smt:test + "val xs = 1:: 2 :: Nil" + "222022010211020330222" + "KKK-VV-K-C---C----CCC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-9 () + (smt:test + "val xs = 1 ::2 :: Nil" + "222022010201120330222" + "KKK-VV-K-C---C----CCC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-10 () + (smt:test + "case a :: (2) :: Nil" + "22220203304250330222" + "KKKK-V-TT--C--CC-TTT")) + +(ert-deftest smt:syntax-class-and-font-lock-test-11 () + (smt:test + "abc :<: def" + "22203330222" + "--------KKK")) + +(ert-deftest smt:syntax-class-and-font-lock-test-12 () + (smt:test + "Foo<T>" + "222121" + "CCC-C-")) + +(ert-deftest smt:syntax-class-and-font-lock-test-13 () + (smt:test + "class X[T<:Mapper[T]](t: T){}" + "22222024211222222425542102545" + "KKKKK-T-CKKCCCCCC-C----K-T---")) + +(ert-deftest smt:syntax-class-and-font-lock-test-14 () + (smt:test + "class X[T <: Mapper[T]](t: T){}" + "2222202420330222222425542102545" + "KKKKK-T-C-KK-CCCCCC-C----K-T---")) + +(ert-deftest smt:syntax-class-and-font-lock-test-15 () + (smt:test + "val c = /* hello */ 20" + "2220201011022222011022" + "KKK-V-K-DDDOOOOOOOO-CC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-16 () + (smt:test + "val c = /* hello **/ 20" + "22202010110222220111022" + "KKK-V-K-DDDOOOOOOOOO-CC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-17 () + (smt:test + "val c = /**** hello */ 20" + "2220201011111022222011022" + "KKK-V-K-DDDDDDOOOOOOOO-CC")) + +(ert-deftest smt:syntax-class-and-font-lock-test-18 () + (smt:test + "val c = //**** hello */ 20" + "22202010111111022222011022" + "KKK-V-K-DDOOOOOOOOOOOOOOOO")) + +(ert-deftest smt:syntax-class-and-font-lock-test-19 () + (smt:test + "val c = 1 /////////// big comment" + "222020102011111111111022202222222" + "KKK-V-K-C-DDDDDDDDDDDDOOOOOOOOOOO")) + +(ert-deftest smt:syntax-class-and-font-lock-test-20 () + (smt:test + "val c = s\"result $sum\"" + "2220201027222222012227" + "KKK-V-K--SSSSSSSSVVVVS")) + +(ert-deftest smt:syntax-class-and-font-lock-test-21 () + (smt:test + "val c = s\"$sum-123\"" + "2220201027122212227" + "KKK-V-K--SVVVVSSSSS")) + +(ert-deftest smt:syntax-class-and-font-lock-test-22 () + (smt:test + "val c = s\"${sum.getOrElse(\"\")} - $sum\"" + "22202010271422212222222224775501012227" + "KKK-V-K--SSSSSSSSSSSSSSSSSSSSSSSSSSSSS")) + +(ert-deftest smt:syntax-class-and-font-lock-test-23 () + "Test that `[!%&*+/?\\\\^|~-#:<=>@]\*/` will be treated as punctuation and +_not_ a symbol. Doing so would cause comment strings such as `/* Comment &*/` to +not be recognized as a delimiter, causing the entire file to treated as a +comment. A concrete example may be viewed at https://github.com/scala/scala/blob/v2.11.11/src/reflect/scala/reflect/internal/Symbols.scala#L863" + (smt:test + "/* &*/" + "110111" + "DDDOOO")) diff --git a/test/test-helper.el b/test/test-helper.el new file mode 100644 index 0000000..7eeb038 --- /dev/null +++ b/test/test-helper.el @@ -0,0 +1 @@ +(require 'scala-mode) |