├── .ert-runner ├── .gitignore ├── Cask ├── LICENSE ├── Makefile ├── README.md ├── ob-scala.el ├── scala-mode-fontlock.el ├── scala-mode-imenu.el ├── scala-mode-indent.el ├── scala-mode-lib.el ├── scala-mode-map.el ├── scala-mode-paragraph.el ├── scala-mode-prettify-symbols.el ├── scala-mode-syntax.el ├── scala-mode.el └── test ├── scala-mode-test.el └── test-helper.el /.ert-runner: -------------------------------------------------------------------------------- 1 | -L . -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.elc 2 | .cask 3 | *-pkg.el 4 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | ;;-*- Mode: Emacs-Lisp -*- 2 | ;;; Cask --- project definition 3 | 4 | ;; Copyright (C) 2015 Sam Halliday 5 | 6 | ;; Author: Sam Halliday 7 | 8 | ;;; Commentary: 9 | ;; 10 | ;; Cask is a package manager for emacs lisp projects, this generates 11 | ;; the *-pkg.el file and could be our test runner in the future. 12 | ;; 13 | ;; See http://cask.readthedocs.org/en/latest/guide/dsl.html for more 14 | ;; information about Cask. 15 | ;; 16 | ;; cask pkg-file 17 | ;; 18 | ;; cask update 19 | ;; cask install 20 | ;; 21 | ;; are particularly useful commands. 22 | ;; 23 | ;; To run the tests: 24 | ;; cask exec ert-runner 25 | ;; 26 | ;;; Code: 27 | 28 | (source melpa-stable) 29 | 30 | (package-file "scala-mode.el") 31 | 32 | (development 33 | (depends-on "ert-runner") 34 | (depends-on "ecukes") 35 | (depends-on "espuds") 36 | (depends-on "undercover")) 37 | 38 | ;;; Cask ends here 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ############################################################-*-Makefile-*-#### 2 | # Makefile for compiling the major mode of Emacs 3 | ############################################################################## 4 | 5 | ## This Makefile has been copied from the scala project. 6 | ## See the LICENSE at the bottom of this file. 7 | 8 | ############################################################################## 9 | # Configuration 10 | 11 | ROOT = . 12 | 13 | SOURCE_DIR = $(ROOT) 14 | 15 | ############################################################################## 16 | # Variables 17 | 18 | MODE_NAME = scala-mode2 19 | 20 | # Emacs Lisp 21 | ELISP_COMMAND ?= emacs 22 | ELISP_OPTIONS += -batch -no-site-file -q 23 | ELISP_OPTIONS += -L $(ROOT) 24 | 25 | 26 | ELISP_FILES += $(MODE_NAME)-lib 27 | ELISP_FILES += $(MODE_NAME) 28 | ELISP_FILES += $(MODE_NAME)-syntax 29 | ELISP_FILES += $(MODE_NAME)-indent 30 | ELISP_FILES += $(MODE_NAME)-paragraph 31 | ELISP_FILES += $(MODE_NAME)-fontlock 32 | ELISP_FILES += $(MODE_NAME)-map 33 | ELISP_FILES += $(MODE_NAME)-sbt 34 | ELISP_SOURCES += $(ELISP_FILES:%=$(SOURCE_DIR)/%.el) 35 | 36 | PKG_FILE += $(SOURCE_DIR)/$(MODE_NAME)-pkg.el 37 | 38 | ############################################################################## 39 | 40 | RM ?= rm -f 41 | RMDIR ?= rmdir 42 | TOUCH ?= touch 43 | 44 | # Strip the version out of the pkg file 45 | VERSION := $(shell ${ELISP_COMMAND} $(ELISP_OPTIONS) --eval '(princ (format "%s\n" (nth 2 (read (find-file "$(PKG_FILE)")))))') 46 | MODE_NAME_VERSION = $(MODE_NAME)-$(VERSION) 47 | 48 | ############################################################################## 49 | # Commands 50 | 51 | all: .latest-build 52 | 53 | clean: 54 | $(RM) *.elc .latest-* autoloads.el $(MODE_NAME_VERSION).tar 55 | [ ! -d $(MODE_NAME_VERSION) ] || $(RM) $(MODE_NAME_VERSION)/* 56 | [ ! -d $(MODE_NAME_VERSION) ] || $(RMDIR) $(MODE_NAME_VERSION) 57 | 58 | .PHONY: all 59 | .PHONY: clean 60 | 61 | ############################################################################## 62 | # Rules 63 | 64 | .latest-build: $(ELISP_SOURCES) 65 | $(ELISP_COMMAND) $(ELISP_OPTIONS) -f batch-byte-compile $(ELISP_SOURCES) 66 | @$(TOUCH) $@ 67 | 68 | ############################################################################## 69 | 70 | autoloads: $(ELISP_SOURCES) 71 | $(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` 72 | 73 | package: 74 | mkdir -p $(MODE_NAME_VERSION) 75 | cp $(ELISP_SOURCES) $(PKG_FILE) $(MODE_NAME_VERSION) 76 | tar cf $(MODE_NAME_VERSION).tar $(MODE_NAME_VERSION) 77 | 78 | 79 | ## SCALA LICENSE 80 | ## 81 | ## Copyright (c) 2002-2011 EPFL, Lausanne, unless otherwise specified. 82 | ## All rights reserved. 83 | ## 84 | ## This software was developed by the Programming Methods Laboratory of the 85 | ## Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. 86 | ## 87 | ## Permission to use, copy, modify, and distribute this software in source 88 | ## or binary form for any purpose with or without fee is hereby granted, 89 | ## provided that the following conditions are met: 90 | ## 91 | ## 1. Redistributions of source code must retain the above copyright 92 | ## notice, this list of conditions and the following disclaimer. 93 | ## 94 | ## 2. Redistributions in binary form must reproduce the above copyright 95 | ## notice, this list of conditions and the following disclaimer in the 96 | ## documentation and/or other materials provided with the distribution. 97 | ## 98 | ## 3. Neither the name of the EPFL nor the names of its contributors 99 | ## may be used to endorse or promote products derived from this 100 | ## software without specific prior written permission. 101 | ## 102 | ## 103 | ## THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 104 | ## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 105 | ## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 106 | ## ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 107 | ## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 108 | ## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 109 | ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 110 | ## CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 111 | ## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 112 | ## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 113 | ## SUCH DAMAGE. 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # emacs-scala-mode 2 | 3 | The mode intends to provide basic emacs support for the Scala language, including: 4 | 5 | - local indenting of code, comments and multi-line strings 6 | - motion commands 7 | - highlighting 8 | 9 | See also (emacs-sbt-mode)[https://github.com/hvesalai/emacs-sbt-mode]. 10 | 11 | ## Installation 12 | 13 | The preferred mechanism is via MELPA and `use-package` as per our 14 | [Learning Emacs](/editors/emacs/learning) guide: 15 | 16 | ```elisp 17 | (use-package scala-mode 18 | :interpreter 19 | ("scala" . scala-mode)) 20 | ``` 21 | 22 | ## Multi-line comments 23 | 24 | The start of a multi-line comment is indented to the same level with 25 | code. 26 | 27 | By default, if a multi-line comment begins with `/*` it is considered 28 | to be a Scaladoc comment. Scaladoc comments are indented according to 29 | the Scaladoc style guide. 30 | 31 | ```scala 32 | /** This is a Scaladoc comment. 33 | * 2nd line. 34 | */ 35 | ``` 36 | 37 | Alternatively, if the configurable variable 38 | *scala-indent:use-javadoc-style* is set to `t`, multi-line comments 39 | beginning with `/**` will be indented according to the Javadoc style, 40 | wherein all following lines are indented under the first asterisk. 41 | 42 | ```scala 43 | /** 44 | * This is a Javadoc-style comment. 45 | * 2nd line. 46 | */ 47 | ``` 48 | 49 | All other multi-line comments are indented under the first asterisk. 50 | 51 | ``` 52 | /** 53 | * Supercalifragilistic- 54 | * expialidocious! 55 | */ 56 | 57 | /* 58 | A comment 59 | */ 60 | ``` 61 | 62 | Typing an asterisk in multi-line comment region, at the start of a 63 | line, will trigger indent. Furthermore, if the configurable variable 64 | `scala-indent:add-space-for-scaladoc-asterisk` is `t` (default) and 65 | the asterisk was the last character on the line, a space will be 66 | inserted after it. If you type a forward slash after the automatically 67 | inserted space, the space is deleted again so that you can end the 68 | comment without deleting the space manually. 69 | 70 | 71 | ## Filling (i.e. word wrap) 72 | 73 | Paragraph `filling` is supported for comments and multi-line strings. 74 | Auto-fill is not supported yet. 75 | 76 | To re-fill a paragraph, use the `fill-paragraph` command ( `M-q` ). As 77 | always, the column at which to wrap is controlled by the `fill-column` 78 | variable, which you set it with the `set-fill-column` command. To set 79 | the default, you use the `customize-variable` command or a mode-hook. 80 | 81 | 82 | ## Motion 83 | 84 | Emacs commands `forward-sexp` and `backward-sexp` ( `M-C-f`, `M-C-b` ) 85 | motion commands will move over reserved words, literals, ids and 86 | lists. 87 | 88 | Text paragraph motion (i.e. `forward-paragraph`, `backward-paragraph`) 89 | works inside comments and multi-line strings, and it respect 90 | Scaladoc's wiki-style markup. 91 | 92 | `scala-syntax:beginning-of-definition` and 93 | `scala-syntax:end-of-definition` move the cursor forward and backward 94 | over class, trait, object, def, val, var, and type definitions. These 95 | functions are assigned to the buffer local variables 96 | `beginning-of-defun-function` and `end-of-defun-function` which makes 97 | it so that the `beginning-of-defun` and `end-of-defun` functions 98 | behave in a way that is appropriate to scala. These functions are not 99 | currently able to support some of the more advanced scala definition 100 | types. 101 | 102 | 103 | ## Highlighting 104 | 105 | The highlighting of variable definitions, such as 106 | 107 | ```var test = "some mutable variable"``` 108 | 109 | now result in the variable name ("test" above) to be highlighted using 110 | the variable scala-font-lock:var-face. Per default, the value of 111 | scala-font-lock:var-face is 'font-lock-warning-face. You can always 112 | change the highlighting of vars by changing scala-font-lock:var-face 113 | through the Emacs face customization (use `M-x` *customize-face*). 114 | 115 | Very complex scala files may need the following in your emacs init 116 | (.emacs, etc): 117 | 118 | ```lisp 119 | ;; For complex scala files 120 | (setq max-lisp-eval-depth 50000) 121 | (setq max-specpdl-size 5000) 122 | ``` 123 | 124 | ## imenu 125 | 126 | scala-mode supports imenu, a library for accessing locations in 127 | documents that is included in emacs 24. The custom variable 128 | `scala-imenu:should-flatten-index` controls whether or not the imenu 129 | index will be hierarchical or completely flat. The current iMenu 130 | implementation only goes one level deep i.e. nested classes are not 131 | traversed. scala-mode's imenu support depends heavily on the 132 | `scala-syntax:end-of-definition` and 133 | `scala-syntax:beginning-of-definition` functions, and as such, it 134 | shares their limitations. 135 | 136 | ## Joining lines (delete indentation) and removing horizontal whitespace 137 | 138 | Scala-mode defines its own `scala-indent:join-line` and 139 | `scala-indent:fixup-whitespace` functions. 140 | 141 | Unlike the normal `join-line` (aka `delete-indentation`), 142 | `scala-indent:join-line` detects the comment fill-prefix and removes 143 | it. 144 | 145 | The `scala-indent:fixup-whitespace` first removes all horizontal 146 | whitespace, then adds one space the context requires none to be 147 | present (before semicolon, around dot, after `(` or `[`, before `)` or 148 | `]`, etc). 149 | 150 | ## Indenting 151 | 152 | **Where four developers meet, there are four opinions on how code should be indented**. 153 | 154 | `scala-mode` supports 2^4 different ways of applying local heuristics to indentation. 155 | 156 | Note that when using `sbt-scalariform`, your local indentation rules will be overwritten. 157 | 158 | ### Run-on lines 159 | 160 | Provided by `scala-indent:default-run-on-strategy` 161 | 162 | The indenting engine has three modes for handling run-on lines. The 163 | `reluctant` (default) mode is geared toward a general style of coding 164 | and the `eager` for strictly functional style. A third mode called 165 | `operators` is between the two. 166 | 167 | The difference between the modes is how they treat run-on lines. For 168 | example, the `eager` mode will indent `map` in the following code 169 | 170 | ```scala 171 | val x = List(1, 2, 3) 172 | map(x => x + 1) 173 | ``` 174 | 175 | The `operators` and `eager` modes will indent the second row in the 176 | following code, as the first line ends with an operator character. 177 | 178 | ```scala 179 | val x = 20 + 180 | 21 181 | ``` 182 | 183 | The `reluctant` mode (default) will not indent the line in either 184 | case. However, all three modes will indent the second line in these 185 | examples as it is clear that the first line cannot terminate a 186 | statement. 187 | 188 | ```scala 189 | val x = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9). 190 | map (x => x + 1) // last token of previous line cannot terminate a statement 191 | 192 | val y = (List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 193 | map (x => x + 1)) // inside 'newlines disabled' region 194 | ``` 195 | 196 | You can use empty lines in the `eager` mode to stop it from indenting 197 | a line. For example 198 | 199 | ```scala 200 | val x = foo("bar") 201 | ("zot", "kala") // indented as curry 202 | 203 | val y = foo("bar") 204 | 205 | ("zot", "kala") // a tuple 206 | ``` 207 | 208 | However, in all three modes pressing the `tab` key repeatedly on a 209 | line will toggle between the modes. 210 | 211 | ### Value expressions 212 | 213 | Provided by `scala-indent:indent-value-expression` 214 | 215 | When this variable is set to *nil* (default), body of a value 216 | expressions will be indented in the traditional way. 217 | 218 | ```scala 219 | val x = try { 220 | some() 221 | } catch { 222 | case e => other 223 | } finally { 224 | clean-up() 225 | } 226 | ``` 227 | 228 | However, when the variable is set to `t`, the body will be indented 229 | one extra step to make the `val`, `var` or `def` stand out. For 230 | example: 231 | 232 | ```scala 233 | val x = try { 234 | some() 235 | } catch { 236 | case e => other 237 | } finally { 238 | clean-up() 239 | } 240 | ``` 241 | 242 | ### Parameter lists 243 | 244 | Provided by `scala-indent:align-parameters` 245 | 246 | When this variable is set to `nil` (default), parameters and run-on 247 | lines in parameter lists will not align under or according to the 248 | first parameter. 249 | 250 | ```scala 251 | val y = List( "Alpha", "Bravo", 252 | "Charlie" ) 253 | 254 | val x = equals(List(1,2,3) map (x => 255 | x + 1)) 256 | ``` 257 | 258 | When the variable is set to `t`, the same will be indented as: 259 | 260 | ```scala 261 | val y = List( "Alpha", "Bravo", 262 | "Charlie" ) 263 | 264 | val x = equals(List(1,2,3) map (x => 265 | x + 1)) 266 | ``` 267 | 268 | ### Expression forms: if, for, try 269 | 270 | Provided by `scala-indent:align-forms` 271 | 272 | When this variable is set to `nil` (default), `if`, `for` and `try` 273 | forms are not aligned specially. 274 | 275 | ```scala 276 | val x = if (kala) 277 | foo 278 | else if (koira) 279 | bar 280 | else 281 | zot 282 | 283 | val x = try "1".toInt 284 | catch { case e => 0} 285 | finally { println("hello") } 286 | 287 | val xs = for (i <- 1 to 10) 288 | yield i 289 | ``` 290 | 291 | When the variable is set to `t`, the same will be indented as: 292 | 293 | ```scala 294 | val x = if (kala) 295 | foo 296 | else if (koira) 297 | bar 298 | else 299 | zot 300 | 301 | val x = try "1".toInt 302 | catch { case e => 0} 303 | finally { println("hello") } 304 | 305 | val xs = for (i <- 1 to 10) 306 | yield i 307 | ``` 308 | 309 | ## Prettify-Symbols 310 | 311 | Scala-mode has a preconfigured list of prettify-symbols rules. The 312 | `prettify-symbols-mode` minor-mode (included with emacs from version 313 | 24.4 onwards) displays text in your buffer as (usually) unicode 314 | symbols that express the same thing to improve readability. A good 315 | example would be displaying the boolean operators as their unicode 316 | equivalents. 317 | 318 | To enable the feature just add these lines to the `scala-mode-hook`: 319 | 320 | ```elisp 321 | (setq prettify-symbols-alist scala-prettify-symbols-alist) 322 | (prettify-symbols-mode) 323 | ``` 324 | 325 | Also feel free to customise the prettify rules by adding or removing 326 | from the `scala-prettify-symbols-alist` alist. 327 | 328 | Libre fonts that seems to work well with this feature are: 329 | 330 | - [Source Code Pro](https://github.com/adobe-fonts/source-code-pro) 331 | - [Hack](https://github.com/chrissimpkins/Hack) 332 | -------------------------------------------------------------------------------- /ob-scala.el: -------------------------------------------------------------------------------- 1 | ;;; ob-scala.el --- org-babel functions for scala evaluation 2 | 3 | ;; Copyright (C) Simon Hafner 4 | 5 | ;;; License: 6 | 7 | ;; This program is free software; you can redistribute it and/or modify 8 | ;; it under the terms of the GNU General Public License as published by 9 | ;; the Free Software Foundation; either version 3, or (at your option) 10 | ;; any later version. 11 | ;; 12 | ;; This program is distributed in the hope that it will be useful, 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ;; GNU General Public License for more details. 16 | ;; 17 | ;; You should have received a copy of the GNU General Public License 18 | ;; along with GNU Emacs; see the file COPYING. If not, write to the 19 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 | ;; Boston, MA 02110-1301, USA. 21 | 22 | ;;; Code: 23 | (require 'ob) 24 | (require 'ob-ref) 25 | (require 'ob-comint) 26 | (require 'ob-eval) 27 | (require 'ensime) 28 | ;; possibly require modes required for your language 29 | 30 | ;; optionally define a file extension for this language 31 | (add-to-list 'org-babel-tangle-lang-exts '("scala" . "scala")) 32 | 33 | ;; optionally declare default header arguments for this language 34 | (defvar org-babel-default-header-args:scala '()) 35 | 36 | (defun org-babel-expand-body:scala (body params &optional processed-params) 37 | "Expand BODY according to PARAMS, return the expanded body." 38 | (require 'ensime-inf) 39 | (let ((vars (assoc-default :vars (or processed-params (org-babel-process-params params))))) 40 | (concat 41 | (mapconcat ;; define any variables 42 | (lambda (pair) 43 | (format "val %s=%S" 44 | (car pair) (org-babel-scala-var-to-scala (cdr pair)))) vars "\n") 45 | "\n" body "\nprint(\"\\nob_scala_eol\")"))) 46 | 47 | 48 | ;; This is the main function which is called to evaluate a code 49 | ;; block. 50 | ;; 51 | ;; So far, only result :output is supported. The session is always 52 | ;; interactive. Manual start of `ensime-inf-buffer-name` via 53 | ;; `ensime-inf-run-scala` is recommended. 54 | (defun org-babel-execute:scala (body params) 55 | "Execute a block of Scalacode with org-babel. 56 | This function is called by `org-babel-execute-src-block'" 57 | (message "executing Scala source code block") 58 | (let* ((processed-params (org-babel-process-params params)) 59 | ;; set the session 60 | (session (org-babel-scala-initiate-session (assoc-default :session processed-params))) 61 | ;; variables assigned for use in the block 62 | (vars (assoc-default :vars processed-params)) 63 | (result-params (assoc-default :result-params processed-params)) 64 | ;; either OUTPUT or VALUE which should behave as described above 65 | (result-type (assoc-default :result-type processed-params)) 66 | ;; expand the body with `org-babel-expand-body:scala' 67 | (full-body (org-babel-expand-body:scala 68 | body params processed-params))) 69 | (ensime-inf-assert-running) 70 | (org-babel-scala-table-or-string 71 | (let ((temp-file (make-temp-file "scala-eval"))) 72 | (message temp-file) 73 | (with-temp-file temp-file 74 | (insert full-body)) 75 | (let ((output 76 | (org-babel-comint-with-output (ensime-inf-buffer-name "ob_scala_eol") 77 | (ensime-inf-send-string (concat ":load " temp-file)) 78 | (comint-send-input nil t ) 79 | (sleep-for 0 5)))) 80 | (delete-file temp-file) 81 | output))))) 82 | 83 | ;; This function should be used to assign any variables in params in 84 | ;; the context of the session environment. 85 | (defun org-babel-prep-session:scala (session params) 86 | "Prepare SESSION according to the header arguments specified in PARAMS." 87 | ) 88 | 89 | (defun org-babel-scala-var-to-scala (var) 90 | "Convert an elisp var into a string of scala source code 91 | specifying a var of the same value." 92 | (format "%S" var)) 93 | 94 | (defun org-babel-scala-table-or-string (results) 95 | "If the results look like a table, then convert them into an 96 | Emacs-lisp table, otherwise return the results as a string." 97 | (message (format "%S" results)) 98 | (org-trim (mapconcat (lambda (element) 99 | (if (or 100 | (string-equal (org-trim element) "scala>") 101 | (string-equal (org-trim element) "ob_scala_eol")) 102 | "" 103 | element)) 104 | (cddr results) 105 | ""))) 106 | 107 | (defun org-babel-scala-initiate-session (&optional session) 108 | "If there is not a current inferior-process-buffer in SESSION then create. 109 | Return the initialized session." 110 | (unless (get-buffer ensime-inf-buffer-name) 111 | (ensime-inf-run-scala)) 112 | ensime-inf-buffer-name 113 | ) 114 | 115 | (provide 'ob-scala) 116 | ;;; ob-scala.el ends here 117 | -------------------------------------------------------------------------------- /scala-mode-fontlock.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode-fontlock.el - Major mode for editing scala, font-lock 2 | ;;; Copyright (c) 2012 Heikki Vesalainen 3 | ;;; For information on the License, see the LICENSE file 4 | 5 | (require 'scala-mode-syntax) 6 | 7 | (defcustom scala-font-lock:constant-list '() 8 | "A list of strigs that should be fontified in constant 9 | face. This customization property takes effect only after the 10 | scala-mode has been reloaded." 11 | :type '(repeat string) 12 | :group 'scala) 13 | 14 | (defun scala-font-lock:create-user-constant-re () 15 | (regexp-opt scala-font-lock:constant-list 'words)) 16 | 17 | (defun scala-font-lock:mark-reserved-symbols (limit) 18 | (when (re-search-forward scala-syntax:reserved-symbols-re limit t) 19 | (goto-char (match-end 2)))) ;; step back to the match (re matches futher) 20 | 21 | (defun scala-font-lock:mark-underscore (limit) 22 | (when (re-search-forward scala-syntax:reserved-symbol-underscore-re limit t) 23 | (goto-char (match-end 2)))) ;; step back to the match (re matches futher) 24 | 25 | ;(defun scala-font-lock:extend-region-function () 26 | 27 | (defun scala-font-lock:limit-pattern2 (&optional start) 28 | (save-excursion 29 | (when start (goto-char start)) 30 | (scala-syntax:skip-forward-ignorable) 31 | (ignore-errors 32 | (while (and (not (or (eobp) 33 | (looking-at scala-syntax:other-keywords-unsafe-re) 34 | (scala-syntax:looking-at-reserved-symbol nil))) 35 | (scala-syntax:looking-at-simplePattern-beginning)) 36 | ; (message "- now at %d" (point)) 37 | (if (= (char-after) ?\() 38 | (forward-list) 39 | ;; else 40 | (goto-char (match-end 0)) 41 | (scala-syntax:skip-forward-ignorable) 42 | ; (message "+ now at %d" (point)) 43 | (cond ((looking-at "(") 44 | (forward-list)) 45 | ((looking-at "@") 46 | (goto-char (match-end 0))) 47 | ((or (scala-syntax:looking-at-reserved-symbol nil) 48 | (looking-at scala-syntax:other-keywords-unsafe-re)) 49 | ; (messssage "saw reserved symbol or keyword") 50 | nil) 51 | ((looking-at scala-syntax:id-re) 52 | ; (message "saw id-re %d" (match-beginning 0)) 53 | (goto-char (match-end 0))) 54 | ; (t 55 | ; (message "nothing special here %s" (point))) 56 | )) 57 | (scala-syntax:skip-forward-ignorable))) 58 | ; (message "limit at %s" (point)) 59 | (point))) 60 | 61 | (defun scala-font-lock:limit-pattern2-list (&optional start) 62 | (let ((limit (scala-font-lock:limit-pattern2 start))) 63 | (while (= (char-after limit) ?,) 64 | (setq limit (scala-font-lock:limit-pattern2 (1+ limit)))) 65 | ; (message "list limit at %s" limit) 66 | limit)) 67 | 68 | (defun scala-font-lock:mark-pattern1-part (&optional limit pattern-p) 69 | "Parses a part of val, var and case pattern (or id). Always 70 | parses a variable or constant name first and then type, leaving 71 | the pointer at the next variablename, constant name, list or 72 | Pattern3, if any, and setting up match data 1 (variable), 73 | 2 (constant) and 3 (type) accordingly. If there is no variable 74 | name before the first type, then the match data for the variable 75 | name is nil. Returns t if something was matched or nil if nothing 76 | was found. 77 | 78 | If pattern-p is defined, then only varid is matched as variable 79 | and everything else is constant. 80 | 81 | Does not continue past limit. 82 | " 83 | ; (message "will stop at %d" limit) 84 | (cond 85 | ;; quit if we are past limit 86 | ((or (and limit (>= (point) limit)) 87 | (eobp)) 88 | ; (message "at limit %s" (point)) 89 | nil) 90 | ;; Type pattern, just skip the whole thing. It will end at ',' or ')'. 91 | ;; Note: forms starting with ':' are handled by a completely separete 92 | ;; font-lock matcher. 93 | ((scala-syntax:looking-at-reserved-symbol ":") 94 | ; (message ":") 95 | (while (not (or (eobp) 96 | (scala-syntax:looking-at "[,);]") 97 | (scala-syntax:looking-at-reserved-symbol "|") 98 | (scala-syntax:looking-at-reserved-symbol 99 | scala-syntax:double-arrow-unsafe-re) 100 | (scala-syntax:looking-at-empty-line-p))) 101 | (scala-syntax:forward-sexp) 102 | (scala-syntax:skip-forward-ignorable)) 103 | (set-match-data nil) 104 | t) 105 | ;; Binding part cannot start with reserved symbols. If they 106 | ;; are seen, we must quit. 107 | ((scala-syntax:looking-at-reserved-symbol nil) 108 | ; (message "symbol") 109 | nil) 110 | ((scala-syntax:looking-at-stableIdOrPath) 111 | ; (message "stableId") 112 | (let ((beg (match-beginning 0)) 113 | (end (match-end 0)) 114 | (varid (scala-syntax:looking-at-varid-p))) 115 | (goto-char end) 116 | (let ((new-match-data 117 | (cond 118 | ((= (char-after end) ?\() 119 | ;; matched type 120 | ; (message "it's a type") 121 | `(,beg ,end nil nil nil nil ,beg ,end)) 122 | ((progn (scala-syntax:backward-sexp) 123 | (= (char-before) ?.)) 124 | ;; matched constant 125 | `(,beg ,end nil nil ,(point) ,end nil nil)) 126 | ((or varid (not pattern-p)) 127 | ;; matched variable name or we can't be sure 128 | `(,beg ,end ,beg ,end nil nil nil nil)) 129 | (t 130 | ;; matched constant 131 | `(,beg ,end nil nil ,beg ,end nil nil))))) 132 | (goto-char end) 133 | (scala-syntax:skip-forward-ignorable) 134 | (cond 135 | ((and (not (or (scala-syntax:looking-at-reserved-symbol nil) 136 | (scala-syntax:looking-at-reserved-symbol "|"))) 137 | (scala-syntax:looking-at-stableIdOrPath)) 138 | (setq new-match-data 139 | (append (butlast new-match-data 2) 140 | `(,(match-beginning 0) 141 | ,(match-end 0)))) 142 | (goto-char (match-end 0)) 143 | (scala-syntax:skip-forward-ignorable)) 144 | ((= (char-after) ?@) 145 | (forward-char) 146 | (scala-syntax:skip-forward-ignorable))) 147 | (set-match-data new-match-data))) 148 | t) 149 | ;; Pattern3 can be a literal. Just skip them. 150 | ((looking-at scala-syntax:literal-re) 151 | ; (message "literal") 152 | (goto-char (match-end 0)) 153 | (scala-syntax:skip-forward-ignorable) 154 | (set-match-data nil) 155 | t) 156 | ;; Start of a patterns list or alternatives. Skip if alternatives or 157 | ;; else leave point at start of first element. 158 | ((= (char-after) ?\() 159 | ; (message "(") 160 | (let ((alternatives-p 161 | (save-excursion 162 | (forward-char) 163 | (ignore-errors 164 | ;; forward-sexp will terminate the loop with error 165 | ;; if '|' is not found before end of list ')' 166 | (while (not (or (eobp) 167 | (= (char-before) ?|) 168 | (scala-syntax:looking-at-empty-line-p))) 169 | (scala-syntax:forward-sexp)) 170 | t)))) 171 | (if alternatives-p 172 | (forward-list) 173 | (forward-char))) 174 | (scala-syntax:skip-forward-ignorable) 175 | (set-match-data nil) 176 | t) 177 | ;; continuation or end of list, just skip and position at the 178 | ;; next element 179 | ((or (= (char-after) ?,) 180 | (= (char-after) ?\))) 181 | ; (message ", or )") 182 | (forward-char) 183 | (scala-syntax:skip-forward-ignorable) 184 | (set-match-data nil) 185 | t) 186 | ;; none of the above, just stop 187 | (t 188 | ; (message "Cannot continue Pattern1 at %d" (point)) 189 | nil) 190 | )) 191 | 192 | (defun scala-font-lock:limit-pattern (&optional start) 193 | (save-excursion 194 | (goto-char (scala-font-lock:limit-pattern2 start)) 195 | ; (message "now at %d" (point)) 196 | (when (scala-syntax:looking-at-reserved-symbol ":") 197 | (while (not (or (eobp) 198 | (scala-syntax:looking-at-reserved-symbol "|") 199 | (scala-syntax:looking-at-reserved-symbol 200 | scala-syntax:double-arrow-unsafe-re) 201 | (scala-syntax:looking-at-empty-line-p))) 202 | (scala-syntax:forward-sexp) 203 | (scala-syntax:skip-forward-ignorable))) 204 | (if (or (/= (char-after) ?|) 205 | (scala-syntax:looking-at-reserved-symbol 206 | scala-syntax:double-arrow-unsafe-re)) 207 | (point) 208 | (forward-char) 209 | (scala-font-lock:limit-pattern)))) 210 | 211 | (defun scala-font-lock:mark-pattern-part (&optional limit) 212 | (when (scala-syntax:looking-at-reserved-symbol "|") 213 | ; (message "skipping |") 214 | (forward-char) 215 | (scala-syntax:skip-forward-ignorable)) 216 | (scala-font-lock:mark-pattern1-part limit t)) 217 | 218 | (defun scala-font-lock:limit-type (&optional start) 219 | start) 220 | 221 | 222 | (defun scala-font-lock:limit-simpleType (&optional start) 223 | (when start (goto-char start)) 224 | (scala-syntax:skip-forward-ignorable) 225 | (setq start (point)) 226 | 227 | (if (= (char-after) ?\() 228 | (ignore-errors (forward-list)) 229 | (scala-font-lock:mark-simpleType)) 230 | (when (and (not (eobp)) (= (char-after) ?#)) 231 | (scala-font-lock:mark-simpleType)) 232 | (when (and (not (eobp)) (= (char-after) ?\[)) 233 | (ignore-errors (forward-list)) 234 | (scala-syntax:skip-forward-ignorable)) 235 | (let ((limit (point))) 236 | (goto-char start) 237 | ; (message "simpeType limit at %d" limit) 238 | limit)) 239 | 240 | (defun scala-font-lock:mark-simpleType (&optional limit) 241 | ; (message "looking for simpleType at %d" (point)) 242 | (cond 243 | ;; stop at limit 244 | ((and limit (>= (point) limit)) 245 | nil) 246 | ;; just dive into lists 247 | ((> (skip-chars-forward "[(,)]") 0) 248 | ; (message "skipping list-marks") 249 | (scala-syntax:skip-forward-ignorable) 250 | (set-match-data nil) 251 | t) 252 | ;; jump over blocks 253 | ((= (char-after) ?\{) 254 | (ignore-errors 255 | (forward-list) 256 | (set-match-data nil) 257 | t)) 258 | ;; ignore arrows and reserved words and symbols 259 | ((or (scala-syntax:looking-at-reserved-symbol 260 | scala-syntax:double-arrow-unsafe-re) 261 | (scala-syntax:looking-at-reserved-symbol 262 | "<[:%]\\|>?:") 263 | (looking-at "\\")) 264 | ; (message "skipping reserved") 265 | (goto-char (match-end 0)) 266 | (scala-syntax:skip-forward-ignorable) 267 | (set-match-data nil) 268 | t) 269 | ;; color id after '#' 270 | ((= (char-after) ?#) 271 | ; (message "at #") 272 | (forward-char) 273 | (if (and (not (or (looking-at scala-syntax:keywords-unsafe-re) 274 | (scala-syntax:looking-at-reserved-symbol nil))) 275 | (looking-at scala-syntax:id-re)) 276 | (goto-char (match-end 0)) nil)) 277 | ;; color paths (including stableid) 278 | ((scala-syntax:looking-at-stableIdOrPath t) 279 | ; (message "at path") 280 | (let ((end (match-end 0))) 281 | (goto-char end) 282 | (while (scala-syntax:looking-back-token "this\\|type") 283 | (goto-char (match-beginning 0)) 284 | (skip-chars-backward ".")) 285 | (unless (scala-syntax:looking-back-token scala-syntax:id-re) 286 | (set-match-data nil)) 287 | (goto-char end)) 288 | (scala-syntax:skip-forward-ignorable) 289 | t) 290 | (t 291 | ; (message "Cannot continue simpleType at %d" (point)) 292 | nil))) 293 | 294 | (defun scala-font-lock:mark-string-escapes (limit) 295 | (when (re-search-forward scala-syntax:string-escape-re limit t) 296 | (goto-char (match-end 0)) 297 | (or (eq (nth 3 (save-excursion (syntax-ppss (match-beginning 0)))) ?\") 298 | (scala-font-lock:mark-string-escapes limit)))) 299 | 300 | (defun scala-font-lock:mark-numberLiteral (re limit) 301 | (when (re-search-forward re limit t) 302 | (if (string-match-p scala-syntax:number-safe-start-re 303 | ;; get char-before match or a magic ',', which is safe 304 | (string (or (char-before (match-beginning 0)) ?,))) 305 | t 306 | (scala-font-lock:mark-numberLiteral re limit)))) 307 | 308 | (defun scala-font-lock:mark-floatingPointLiteral (limit) 309 | (scala-font-lock:mark-numberLiteral 310 | scala-syntax:floatingPointLiteral-re 311 | limit)) 312 | 313 | (defun scala-font-lock:mark-integerLiteral (limit) 314 | (scala-font-lock:mark-numberLiteral 315 | scala-syntax:integerLiteral-re 316 | limit)) 317 | 318 | (defun scala-font-lock:keywords () 319 | ;; chars, string, comments are handled acording to syntax and 320 | ;; syntax propertize 321 | 322 | `(;; keywords 323 | (,scala-syntax:override-re 2 scala-font-lock:override-face) 324 | (,scala-syntax:abstract-re 2 scala-font-lock:abstract-face) 325 | (,scala-syntax:final-re 2 scala-font-lock:final-face) 326 | (,scala-syntax:sealed-re 2 scala-font-lock:sealed-face) 327 | (,scala-syntax:implicit-re 2 scala-font-lock:implicit-face) 328 | (,scala-syntax:lazy-re 2 scala-font-lock:lazy-face) 329 | (,scala-syntax:private-re 2 scala-font-lock:private-face) 330 | (,scala-syntax:protected-re 2 scala-font-lock:protected-face) 331 | (,scala-syntax:other-keywords-re 2 font-lock-keyword-face) 332 | (,scala-syntax:value-keywords-re 2 font-lock-constant-face) 333 | (,scala-syntax:path-keywords-re 2 font-lock-keyword-face) 334 | 335 | ;; User defined constants 336 | (,(scala-font-lock:create-user-constant-re) 0 font-lock-constant-face) 337 | 338 | ;; Annotations 339 | (, (rx (and "@" (in "a-zA-Z_.") (0+ (in "a-zA-Z0-9_.")))) 340 | . font-lock-preprocessor-face) 341 | 342 | ;; reserved symbols 343 | (scala-font-lock:mark-reserved-symbols 2 font-lock-keyword-face) 344 | 345 | ;; 'Symbols 346 | (,scala-syntax:symbolLiteral-re 1 font-lock-string-face) 347 | 348 | ;; underscore 349 | (scala-font-lock:mark-underscore 2 font-lock-keyword-face) 350 | 351 | ;; escapes inside strings 352 | (scala-font-lock:mark-string-escapes (0 font-lock-constant-face prepend nil)) 353 | 354 | ;; object 355 | (,(concat "\\?@\\^|~")) line-start) 414 | (group ":") 415 | (0+ space) 416 | (group (in "a-zA-Z_") 417 | (0+ (in "a-zA-Z0-9_")) 418 | (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~")))))) 419 | (1 font-lock-keyword-face) (2 font-lock-type-face)) 420 | 421 | ;; type ascription (: followed by punctuation type name) 422 | (,(rx 423 | (or (not (in "!#%&*+-/:<=>?@\\^|~")) line-start) 424 | (group ":") 425 | (1+ space) 426 | (group (1+ (in "-!#%&*+/:<=>?@\\^|~")))) 427 | (1 font-lock-keyword-face) (2 font-lock-type-face)) 428 | 429 | ;; extends followed by type 430 | (,(rx symbol-start 431 | (group "extends") 432 | (1+ space) 433 | (group (or 434 | (and (in "a-zA-Z_") 435 | (0+ (in "a-zA-Z0-9_")) 436 | (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) 437 | (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) 438 | (1 font-lock-keyword-face) (2 font-lock-type-face)) 439 | 440 | ;; with followed by type 441 | (,(rx symbol-start 442 | (group "with") 443 | (1+ space) 444 | (group (or 445 | (and (in "a-zA-Z_") 446 | (0+ (in "a-zA-Z0-9_")) 447 | (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) 448 | (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) 449 | (1 font-lock-keyword-face) (2 font-lock-type-face)) 450 | 451 | ;; new followed by type 452 | (,(rx symbol-start 453 | (group "new") 454 | (1+ space) 455 | (group (or 456 | (and (in "a-zA-Z_") 457 | (0+ (in "a-zA-Z0-9_")) 458 | (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) 459 | (1+ (in "!#%&*+-/:<=>?@\\^|~"))))) 460 | (1 font-lock-keyword-face) (2 font-lock-type-face)) 461 | 462 | ;; uppercase means a type or object 463 | (,(rx symbol-start 464 | (and (in "A-Z") 465 | (0+ (in "a-zA-Z0-9_")) 466 | (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~")))))) 467 | . font-lock-constant-face) 468 | ;; . font-lock-type-face) 469 | ; uncomment this to go back to highlighting objects as types 470 | 471 | ;; uppercase 472 | (,(rx symbol-start 473 | (group 474 | (and (in "A-Z") 475 | (0+ (in "a-zA-Z0-9_")) 476 | (\? (and "_" (1+ (in "!#%&*+-/:<=>?@\\^|~"))))))) 477 | . font-lock-constant-face) 478 | 479 | ;; package name 480 | (,(rx symbol-start 481 | (group "package") 482 | (1+ space) 483 | (group (and (in "a-zA-Z_.") (0+ (in "a-zA-Z0-9_."))))) 484 | (1 font-lock-keyword-face) (2 font-lock-string-face)) 485 | 486 | ;; number literals (have to be here so that other rules take precedence) 487 | (scala-font-lock:mark-floatingPointLiteral . font-lock-constant-face) 488 | (scala-font-lock:mark-integerLiteral . font-lock-constant-face) 489 | 490 | (scala-syntax:interpolation-matcher 0 font-lock-variable-name-face t) 491 | 492 | )) 493 | 494 | (defun scala-font-lock:syntactic-face-function (state) 495 | "Return correct face for string or comment" 496 | (if (and (integerp (nth 4 state)) 497 | (save-excursion 498 | (goto-char (nth 8 state)) 499 | (looking-at "/\\*\\*\\($\\|[^*]\\)"))) 500 | ;; scaladoc (starts with /** only) 501 | font-lock-doc-face 502 | (if (nth 3 state) font-lock-string-face font-lock-comment-face))) 503 | 504 | (defface scala-font-lock:var-face 505 | '((t (:inherit font-lock-warning-face))) 506 | "Font Lock mode face used to highlight scala variable names." 507 | :group 'scala) 508 | 509 | (defvar scala-font-lock:var-face 'scala-font-lock:var-face 510 | "Face for scala variable names.") 511 | 512 | (defface scala-font-lock:private-face 513 | '((t (:inherit font-lock-builtin-face))) 514 | "Font Lock mode face used for the private keyword." 515 | :group 'scala) 516 | 517 | (defvar scala-font-lock:private-face 'scala-font-lock:private-face 518 | "Face for the scala private keyword.") 519 | 520 | (defface scala-font-lock:protected-face 521 | '((t (:inherit font-lock-builtin-face))) 522 | "Font Lock mode face used for the protected keyword." 523 | :group 'scala) 524 | 525 | (defvar scala-font-lock:protected-face 'scala-font-lock:protected-face 526 | "Face for the scala protected keyword.") 527 | 528 | (defface scala-font-lock:override-face 529 | '((t (:inherit font-lock-builtin-face))) 530 | "Font Lock mode face used for the override keyword." 531 | :group 'scala) 532 | 533 | (defvar scala-font-lock:override-face 'scala-font-lock:override-face 534 | "Face for the scala override keyword.") 535 | 536 | (defface scala-font-lock:sealed-face 537 | '((t (:inherit font-lock-builtin-face))) 538 | "Font Lock mode face used for the sealed keyword." 539 | :group 'scala) 540 | 541 | (defvar scala-font-lock:sealed-face 'scala-font-lock:sealed-face 542 | "Face for the scala sealed keyword.") 543 | 544 | (defface scala-font-lock:abstract-face 545 | '((t (:inherit font-lock-builtin-face))) 546 | "Font Lock mode face used for the abstract keyword." 547 | :group 'scala) 548 | 549 | (defvar scala-font-lock:abstract-face 'scala-font-lock:abstract-face 550 | "Face for the scala abstract keyword.") 551 | 552 | (defface scala-font-lock:final-face 553 | '((t (:inherit font-lock-builtin-face))) 554 | "Font Lock mode face used for the final keyword." 555 | :group 'scala) 556 | 557 | (defvar scala-font-lock:final-face 'scala-font-lock:final-face 558 | "Face for the scala final keyword.") 559 | 560 | (defface scala-font-lock:implicit-face 561 | '((t (:inherit font-lock-builtin-face))) 562 | "Font Lock mode face used for the implicit keyword." 563 | :group 'scala) 564 | 565 | (defvar scala-font-lock:implicit-face 'scala-font-lock:implicit-face 566 | "Face for the scala implicit keyword.") 567 | 568 | (defface scala-font-lock:lazy-face 569 | '((t (:inherit font-lock-builtin-face))) 570 | "Font Lock mode face used for the lazy keyword." 571 | :group 'scala) 572 | 573 | (defvar scala-font-lock:lazy-face 'scala-font-lock:lazy-face 574 | "Face for the scala lazy keyword.") 575 | 576 | (defface scala-font-lock:var-keyword-face 577 | '((t (:inherit font-lock-keyword-face))) 578 | "Font Lock mode face used for the var keyword." 579 | :group 'scala) 580 | 581 | (defvar scala-font-lock:var-keyword-face 'scala-font-lock:var-keyword-face 582 | "Face for the scala var keyword.") 583 | 584 | (provide 'scala-mode-fontlock) 585 | -------------------------------------------------------------------------------- /scala-mode-imenu.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode-imenu.el - Major mode for editing scala 2 | ;;; Copyright (c) 2014 Heikki Vesalainen 3 | ;;; For information on the License, see the LICENSE file 4 | 5 | ;;; Code: 6 | 7 | (require 'scala-mode-syntax) 8 | 9 | ;; Make lambdas proper clousures (only in this file) 10 | (make-local-variable 'lexical-binding) 11 | (setq lexical-binding t) 12 | 13 | (defcustom scala-imenu:should-flatten-index t 14 | "Controls whether or not the imenu index is flattened or hierarchical.") 15 | (defcustom scala-imenu:build-imenu-candidate 16 | 'scala-imenu:default-build-imenu-candidate 17 | "Controls whether or not the imenu index has definition type information.") 18 | (defcustom scala-imenu:cleanup-hooks nil 19 | "Functions that will be run after the construction of each imenu") 20 | 21 | (defun scala-imenu:flatten-list (incoming-list &optional predicate) 22 | (when (not predicate) (setq predicate 'listp)) 23 | (cl-mapcan (lambda (x) (if (funcall predicate x) 24 | (scala-imenu:flatten-list x predicate) (list x))) incoming-list)) 25 | 26 | (defun scala-imenu:flatten-imenu-index (index) 27 | (cl-mapcan (lambda (x) (if (listp (cdr x)) 28 | (scala-imenu:flatten-imenu-index (cdr x)) 29 | (list x))) index)) 30 | 31 | (defun scala-imenu:create-imenu-index () 32 | (let ((imenu-index (cl-mapcar 'scala-imenu:build-imenu-candidates 33 | (scala-imenu:create-index)))) 34 | (dolist (cleanup-hook scala-imenu:cleanup-hooks) 35 | (funcall cleanup-hook)) 36 | (if scala-imenu:should-flatten-index 37 | (scala-imenu:flatten-imenu-index imenu-index) 38 | imenu-index))) 39 | 40 | (defun scala-imenu:build-imenu-candidates (member-info &optional parents) 41 | (if (listp (car member-info)) 42 | (let* ((current-member-info (car member-info)) 43 | (child-member-infos (cdr member-info)) 44 | (current-member-result 45 | (scala-imenu:destructure-for-build-imenu-candidate 46 | current-member-info parents)) 47 | (current-member-name (car current-member-result))) 48 | (if child-member-infos 49 | (let ((current-member-members 50 | (scala-imenu:build-child-members 51 | (append parents `(,current-member-info)) 52 | (cdr member-info)))) 53 | `(,current-member-name . 54 | ,(cons current-member-result current-member-members))) 55 | current-member-result)) 56 | (scala-imenu:destructure-for-build-imenu-candidate member-info parents))) 57 | 58 | (defun scala-imenu:build-child-members (parents child-members) 59 | (cl-mapcar (lambda (child) (scala-imenu:build-imenu-candidates 60 | child parents)) child-members)) 61 | 62 | (defun scala-imenu:destructure-for-build-imenu-candidate (member-info parents) 63 | (cl-destructuring-bind (member-name definition-type marker) 64 | member-info (funcall scala-imenu:build-imenu-candidate 65 | member-name definition-type marker parents))) 66 | 67 | 68 | (defun scala-imenu:default-build-imenu-candidate (member-name definition-type 69 | marker parents) 70 | (let* ((all-names 71 | (append (cl-mapcar (lambda (parent) (car parent)) parents) 72 | `(,member-name))) 73 | (member-string (mapconcat 'identity all-names "."))) 74 | `(,(format "(%s)%s" definition-type member-string) . ,marker))) 75 | 76 | (defun scala-imenu:create-index () 77 | (let ((class nil) (index nil)) 78 | (goto-char (point-max)) 79 | (while (setq class (scala-imenu:parse-nested-from-end)) 80 | (setq index (cons class index))) 81 | index)) 82 | 83 | (defun scala-imenu:parse-nested-from-end () 84 | (let ((last-point (point)) (class-name nil) (definition-type nil)) 85 | (scala-syntax:beginning-of-definition) 86 | ;; We're done if scala-syntax:beginning-of-definition has no effect. 87 | (if (eq (point) last-point) nil 88 | (progn (looking-at scala-syntax:all-definition-re) 89 | (setq class-name (match-string-no-properties 2)) 90 | (setq definition-type (match-string-no-properties 1))) 91 | `(,`(,class-name ,definition-type ,(point-marker)) . 92 | ,(scala-imenu:nested-members))))) 93 | 94 | (defun scala-imenu:parse-nested-from-beginning () 95 | (scala-syntax:end-of-definition) 96 | (scala-imenu:parse-nested-from-end)) 97 | 98 | (defun scala-imenu:nested-members () 99 | (let ((start-point (point))) 100 | (save-excursion 101 | (scala-syntax:end-of-definition) 102 | ;; This gets us inside of the class definition 103 | ;; It seems like there should be a better way 104 | ;; to do this. 105 | (backward-char) 106 | (reverse (scala-imenu:get-nested-members start-point))))) 107 | 108 | (defvar scala-imenu:nested-definition-types '("class" "object" "trait")) 109 | 110 | (defun scala-imenu:get-nested-members (parent-start-point) 111 | (scala-syntax:beginning-of-definition) 112 | (if (< parent-start-point (point)) 113 | (cons (scala-imenu:get-member-info-at-point) 114 | (scala-imenu:get-nested-members parent-start-point)) 115 | nil)) 116 | 117 | (defun scala-imenu:get-member-info-at-point () 118 | (looking-at scala-syntax:all-definition-re) 119 | (let* ((member-name (match-string-no-properties 2)) 120 | (definition-type (match-string-no-properties 1))) 121 | (if (member definition-type scala-imenu:nested-definition-types) 122 | (save-excursion (scala-imenu:parse-nested-from-beginning)) 123 | `(,member-name ,definition-type ,(point-marker))))) 124 | 125 | 126 | (provide 'scala-mode-imenu) 127 | ;;; scala-mode-imenu.el ends here 128 | -------------------------------------------------------------------------------- /scala-mode-indent.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode.el - Major mode for editing scala, indenting 2 | ;;; Copyright (c) 2012 Heikki Vesalainen 3 | ;;; For information on the License, see the LICENSE file 4 | 5 | (require 'scala-mode-syntax) 6 | (require 'scala-mode-lib) 7 | 8 | (eval-when-compile 9 | (defvar scala-indent:effective-run-on-strategy) 10 | (defvar scala-indent:previous-indent-pos)) 11 | 12 | (defcustom scala-indent:step 2 13 | "The number of spaces an indentation step should be. The actual 14 | indentation will be one or two steps depending on context." 15 | :type 'integer 16 | :safe #'integerp 17 | :group 'scala) 18 | 19 | (defcustom scala-indent:indent-value-expression nil 20 | "Whether or not to indent multi-line value expressions, with 21 | one extra step. When true, indenting will be 22 | 23 | val x = try { 24 | some() 25 | } catch { 26 | case e => other 27 | } finally { 28 | clean-up() 29 | } 30 | 31 | When nil, the same will indent as 32 | 33 | val x = try { 34 | some() 35 | } catch { 36 | case e => other 37 | } finally { 38 | clean-up() 39 | } 40 | " 41 | :type 'boolean 42 | :group 'scala) 43 | 44 | (defcustom scala-indent:align-parameters nil 45 | "Whether or not to indent parameter lists so that next 46 | parameter lines always align under the first parameter. When 47 | non-nil, indentation will be 48 | 49 | def foo(x: Int, y: List[Int] 50 | z: Int) 51 | 52 | val x = foo(1, List(1, 2, 3) map (i => 53 | i + 1 54 | ), 2) 55 | 56 | When nil, the same will indent as 57 | 58 | def foo(x: Int, y: List[Int] 59 | z: Int) 60 | 61 | val x = foo(1, List(1, 2, 3) map (i => 62 | i + 1 63 | ), 2) 64 | " 65 | :type 'boolean 66 | :safe #'booleanp 67 | :group 'scala) 68 | 69 | (defcustom scala-indent:align-forms nil 70 | "Whether or not to align 'else', 'yield', 'catch', 'finally' 71 | below their respective expression start. When non-nil, identing 72 | will be 73 | 74 | val x = if (foo) 75 | bar 76 | else 77 | zot 78 | 79 | when nil, the same will indent as 80 | 81 | val x = if (foo) 82 | bar 83 | else 84 | zot 85 | " 86 | :type 'boolean 87 | :group 'scala) 88 | 89 | (defconst scala-indent:eager-strategy 0 90 | "See 'scala-indent:run-on-strategy'") 91 | (defconst scala-indent:operator-strategy 1 92 | "See 'scala-indent:run-on-strategy'") 93 | (defconst scala-indent:reluctant-strategy 2 94 | "See 'scala-indent:run-on-strategy'") 95 | (defconst scala-indent:keywords-only-strategy 3 96 | "A strategy used internally by indent engine") 97 | 98 | (defcustom scala-indent:default-run-on-strategy 2 99 | "What strategy to use for detecting run-on lines, i.e. lines 100 | that continue a statement from the previous line. Possible values 101 | are: 102 | 103 | 'reluctant', which marks only lines that begin with -- or 104 | that follow a line that ends with -- a reserved word that cannot start 105 | or end a line, such as 'with'. 106 | 107 | 'operators', which extends the previous strategy by marking also 108 | lines that begin with -- or that follow a line that ends with -- 109 | an operator character. For example, '+', '-', etc. 110 | 111 | 'eager', which marks all rows which could be run-ons, i.e. which 112 | are not ruled out by the language specification. 113 | " 114 | :type `(choice (const :tag "eager" ,scala-indent:eager-strategy) 115 | (const :tag "operators" ,scala-indent:operator-strategy) 116 | (const :tag "reluctant" ,scala-indent:reluctant-strategy)) 117 | :group 'scala) 118 | 119 | (make-variable-buffer-local 'scala-indent:effective-run-on-strategy) 120 | 121 | (defcustom scala-indent:add-space-for-scaladoc-asterisk t 122 | "When non-nil, a space will be added after a scaladoc asterisk, 123 | when it is added to an empty line." 124 | :type 'boolean 125 | :safe #'booleanp 126 | :group 'scala) 127 | 128 | (defcustom scala-indent:use-javadoc-style nil 129 | "When non-nil, multi-line comments are indented according to Javadoc 130 | style (i.e. indented to the first asterisk). This overrides the 131 | Scaladoc behavior of indenting comment lines to the second asterisk." 132 | :type 'boolean 133 | :safe #'booleanp 134 | :group 'scala) 135 | 136 | (defun scala-indent:run-on-strategy () 137 | "Returns the currently effecti run-on strategy" 138 | (or scala-indent:effective-run-on-strategy 139 | scala-indent:default-run-on-strategy 140 | scala-indent:eager-strategy)) 141 | 142 | (defun scala-indent:toggle-effective-run-on-strategy () 143 | "If effective run-on strategy is not set, it is set as follows: 144 | - if default is eager or operators, then it is set to reluctant 145 | - if default is reluctant, then it is set to eager. If it is set, 146 | it is nilled." 147 | (if scala-indent:effective-run-on-strategy 148 | (setq scala-indent:effective-run-on-strategy nil) 149 | (let ((new-strategy 150 | (cond ((= (scala-indent:run-on-strategy) 151 | scala-indent:reluctant-strategy) 152 | scala-indent:eager-strategy) 153 | ((or (= (scala-indent:run-on-strategy) 154 | scala-indent:operator-strategy) 155 | (= (scala-indent:run-on-strategy) 156 | scala-indent:eager-strategy)) 157 | scala-indent:reluctant-strategy)))) 158 | (setq scala-indent:effective-run-on-strategy new-strategy)))) 159 | 160 | (defun scala-indent:reset-effective-run-on-strategy () 161 | (setq scala-indent:effective-run-on-strategy nil)) 162 | 163 | (defun scala-indent:rotate-run-on-strategy () 164 | (interactive) 165 | (let ((new-strategy 166 | (cond ((= scala-indent:default-run-on-strategy 167 | scala-indent:reluctant-strategy) 168 | scala-indent:operator-strategy) 169 | ((= scala-indent:default-run-on-strategy 170 | scala-indent:operator-strategy) 171 | scala-indent:eager-strategy) 172 | ((= scala-indent:default-run-on-strategy 173 | scala-indent:eager-strategy) 174 | scala-indent:reluctant-strategy)))) 175 | (setq scala-indent:default-run-on-strategy new-strategy) 176 | ; (message "scala-indent:default-run-on-strategy set to %s" scala-indent:default-run-on-strategy) 177 | )) 178 | 179 | (defun scala-indent:backward-sexp-to-beginning-of-line () 180 | "Skip sexps backwards until reaches beginning of line (i.e. the 181 | point is at the first non whitespace or comment character). It 182 | does not move outside enclosin list. Returns the current point or 183 | nil if the beginning of line could not be reached because of 184 | enclosing list." 185 | (let ((code-beg (scala-lib:point-after 186 | (scala-syntax:beginning-of-code-line)))) 187 | (ignore-errors 188 | (while (> (point) code-beg) 189 | (scala-syntax:backward-sexp) 190 | (skip-syntax-backward ".") 191 | (when (< (point) code-beg) 192 | ;; moved to previous line, set new target 193 | (setq code-beg (scala-lib:point-after 194 | (scala-syntax:beginning-of-code-line)))))) 195 | (unless (> (point) code-beg) 196 | (point)))) 197 | 198 | (defun scala-indent:align-anchor () 199 | "Go to beginning of line, if a) scala-indent:align-parameters 200 | is nil or backward-sexp-to-beginning-of-line is non-nil. This has 201 | the effect of staying within lists if 202 | scala-indent:align-parameters is non-nil." 203 | (when (or (scala-indent:backward-sexp-to-beginning-of-line) 204 | (not scala-indent:align-parameters)) 205 | (back-to-indentation))) 206 | 207 | (defun scala-indent:value-expression-lead (start anchor &optional not-block-p) 208 | ;; calculate an indent lead. The lead is one indent step if there is 209 | ;; a '=' between anchor and start, otherwise 0. 210 | (if (and scala-indent:indent-value-expression 211 | (ignore-errors 212 | (save-excursion 213 | (let ((block-beg (if not-block-p 214 | start 215 | (nth 1 (syntax-ppss start))))) 216 | (goto-char anchor) 217 | (scala-syntax:has-char-before ?= block-beg))))) 218 | scala-indent:step 0)) 219 | 220 | ;;; 221 | ;;; Run-on 222 | ;;; 223 | 224 | (defconst scala-indent:mustNotTerminate-keywords-re 225 | (regexp-opt '("extends" "forSome" "match" "with") 'words) 226 | "Some keywords which occure only in the middle of an 227 | expression") 228 | 229 | (defconst scala-indent:mustNotTerminate-line-beginning-re 230 | (concat "\\(" scala-indent:mustNotTerminate-keywords-re 231 | "\\|:\\(" scala-syntax:after-reserved-symbol-re "\\)\\)") 232 | "All keywords and symbols that cannot terminate a expression 233 | and must be handled by run-on. Reserved-symbols not included.") 234 | 235 | (defconst scala-indent:mustTerminate-re 236 | (concat "\\([,;\u21D2]\\|=>?" scala-syntax:end-of-code-line-re 237 | "\\|\\s(\\|" scala-syntax:empty-line-re "\\)") 238 | "Symbols that must terminate an expression or start a 239 | sub-expression, i.e the following expression cannot be a 240 | run-on. This includes only parenthesis, '=', '=>', ',' and ';' 241 | and the empty line") 242 | 243 | (defconst scala-indent:mustNotContinue-re 244 | (regexp-opt '("abstract" "catch" "case" "class" "def" "do" "else" "final" 245 | "finally" "for" "if" "implicit" "import" "lazy" "new" "object" 246 | "override" "package" "private" "protected" "return" "sealed" 247 | "throw" "trait" "try" "type" "val" "var" "while" "yield" "inline") 248 | 'words) 249 | "Words that we don't want to continue the previous line") 250 | 251 | (defconst scala-indent:mustBeContinued-line-end-re 252 | (concat "\\(" scala-syntax:other-keywords-unsafe-re 253 | "\\|:" scala-syntax:end-of-code-line-re "\\)") 254 | "All keywords and symbols that cannot terminate a expression 255 | and are infact a sign of run-on. Reserved-symbols not included.") 256 | 257 | (defun scala-indent:run-on-p (&optional point strategy) 258 | "Returns t if the current point is in the middle of an expression" 259 | ;; use default strategy if none given 260 | (when (not strategy) (setq strategy (scala-indent:run-on-strategy))) 261 | (save-excursion 262 | (when point (goto-char point)) 263 | (unless (eobp) 264 | ;; Note: ofcourse this 'cond' could be written as one big boolean 265 | ;; expression, but I doubt that would be so readable and 266 | ;; maintainable 267 | (cond 268 | ;; NO: this line starts with close parenthesis 269 | ((= (char-syntax (char-after)) ?\)) 270 | nil) 271 | ;; NO: the previous line must terminate 272 | ((save-excursion 273 | (scala-syntax:skip-backward-ignorable) 274 | (or (bobp) 275 | (scala-syntax:looking-back-empty-line-p) 276 | (scala-syntax:looking-back-token scala-indent:mustTerminate-re))) 277 | nil) 278 | ;; YES: in a region where newlines are disabled 279 | ((and (scala-syntax:newlines-disabled-p) 280 | (not (= strategy scala-indent:keywords-only-strategy))) 281 | t) 282 | ;; NO: this line starts with a keyword that starts a new 283 | ;; expression (e.g. 'def' or 'class') 284 | ((looking-at scala-indent:mustNotContinue-re) 285 | nil) 286 | ;; NO: this line is the start of value body 287 | ((scala-indent:body-p) 288 | nil) 289 | ;; YES: eager strategy can stop here, everything is a run-on if no 290 | ;; counter evidence 291 | ((= strategy scala-indent:eager-strategy) 292 | t) 293 | ;; YES: this line must not terminate because it starts with a 294 | ;; middle of expression keyword 295 | ((looking-at scala-indent:mustNotTerminate-line-beginning-re) 296 | t) 297 | ;; YES: end of prev line must not terminate 298 | ((let ((case-fold-search nil)) 299 | (scala-syntax:looking-back-token 300 | scala-indent:mustBeContinued-line-end-re)) 301 | t) 302 | ;; YES: this line starts with type param 303 | ((= (char-after) ?\[) 304 | t) 305 | ;; YES: this line starts with open paren and the expression 306 | ;; after all parens is a run-on 307 | ((and (= (char-after) ?\() 308 | (save-excursion (scala-syntax:forward-parameter-groups) 309 | (scala-syntax:skip-forward-ignorable) 310 | (or (= (char-after) ?=) 311 | (= (char-after) ?{) 312 | (scala-indent:run-on-p nil strategy)))) 313 | t) 314 | ;; NO: that's all for keywords-only strategy 315 | ((= strategy scala-indent:keywords-only-strategy) 316 | nil) 317 | ;; YES: this line starts with punctuation 318 | ((= (char-after) ?\.) 319 | t) 320 | ;; YES: prev line ended with punctuation 321 | ((scala-syntax:looking-back-token ".*[.]") 322 | t) 323 | ;; NO: that's all for reluctant-strategy 324 | ((= strategy scala-indent:reluctant-strategy) 325 | nil) 326 | ;; YES: this line starts with opchars 327 | ((save-excursion 328 | (< 0 (skip-chars-forward scala-syntax:opchar-group))) 329 | t) 330 | ;; YES: prev line ends with opchars 331 | ((save-excursion 332 | (scala-syntax:skip-backward-ignorable) 333 | (> 0 (skip-chars-backward scala-syntax:opchar-group))) 334 | t) 335 | ;; NO: else nil (only operator strategy should reach here) 336 | (t nil))))) 337 | 338 | (defun scala-indent:run-on-line-p (&optional point strategy) 339 | "Returns t if the current point (or point at 'point) is on a 340 | line that is a run-on from a previous line." 341 | (save-excursion 342 | (when point (goto-char point)) 343 | (scala-syntax:beginning-of-code-line) 344 | (scala-indent:run-on-p nil strategy))) 345 | 346 | (defun scala-indent:goto-run-on-anchor (&optional point strategy) 347 | "Moves back to the point whose column will be used as the 348 | anchor relative to which indenting for current point (or point 349 | 'point') is calculated. Returns the new point or nil if the point 350 | is not on a run-on line." 351 | (when (scala-indent:run-on-line-p point strategy) 352 | (when point (goto-char point)) 353 | (scala-syntax:beginning-of-code-line) 354 | (while (and (scala-indent:run-on-line-p nil strategy) 355 | (scala-syntax:skip-backward-ignorable) 356 | (scala-indent:backward-sexp-to-beginning-of-line))) 357 | (scala-indent:align-anchor) 358 | (point))) 359 | 360 | (defconst scala-indent:double-indent-re 361 | (concat (regexp-opt '("with" "extends" "forSome") 'words) 362 | "\\|:\\(" scala-syntax:after-reserved-symbol-re "\\)")) 363 | 364 | (defun scala-indent:resolve-run-on-step (start &optional anchor) 365 | "Resolves the appropriate indent step for run-on line at position 366 | 'start'" 367 | (save-excursion 368 | (goto-char anchor) 369 | (if (scala-syntax:looking-at-case-p) 370 | ;; case run-on lines get double indent, except '|' which get 371 | ;; special indents 372 | (progn (goto-char start) 373 | (- (* 2 scala-indent:step) 374 | (skip-chars-forward "|"))) 375 | (goto-char start) 376 | (cond 377 | ;; some keywords get double indent 378 | ((or (looking-at scala-indent:double-indent-re) 379 | (scala-syntax:looking-back-token scala-indent:double-indent-re)) 380 | (* 2 scala-indent:step)) 381 | ;; no indent if the previous line is just close parens 382 | ;; ((save-excursion 383 | ;; (scala-syntax:skip-backward-ignorable) 384 | ;; (let ((end (point))) 385 | ;; (scala-syntax:beginning-of-code-line) 386 | ;; (skip-syntax-forward ")") 387 | ;; (= (point) end))) 388 | ;; 0) 389 | ;; else normal indent 390 | (t (+ (if scala-indent:align-parameters 0 391 | (scala-indent:value-expression-lead start anchor)) 392 | scala-indent:step)))))) 393 | 394 | (defconst scala-indent:forms-align-re 395 | (regexp-opt '("yield" "else" "catch" "finally") 'words)) 396 | 397 | (defun scala-indent:forms-align-p (&optional point) 398 | "Returns scala-syntax:beginning-of-code-line for the line on 399 | which current point (or point 'point') is, if the line starts 400 | with one of 'yield', 'else', 'catch' and 'finally', otherwise 401 | nil. Also, the previous line must not be with '}'" 402 | (save-excursion 403 | (when point (goto-char point)) 404 | (scala-syntax:beginning-of-code-line) 405 | (when (looking-at scala-indent:forms-align-re) 406 | (goto-char (match-beginning 0)) 407 | (point)))) 408 | 409 | 410 | (defun scala-indent:goto-forms-align-anchor (&optional point) 411 | "Moves back to the point whose column will be used as the 412 | anchor relative to which indenting of special words on beginning 413 | of the line on which point (or point 'point') is, or nul if not 414 | special word found. Special words include 'yield', 'else', 415 | 'catch' and 'finally'" 416 | (let ((special-beg (scala-indent:forms-align-p point))) 417 | (when special-beg 418 | (goto-char special-beg) 419 | (if (and (scala-syntax:looking-back-token "}") 420 | (save-excursion 421 | (goto-char (match-beginning 0)) 422 | (= (match-beginning 0) (scala-lib:point-after (scala-syntax:beginning-of-code-line))))) 423 | (goto-char (match-beginning 0)) 424 | (let ((anchor 425 | (cond ((looking-at "\\") 426 | ;; align with 'for' 427 | (if (scala-syntax:search-backward-sexp "\\") 428 | (point) 429 | (message "matching 'for' not found") 430 | nil)) 431 | ((looking-at "\\") 432 | ;; align with 'if' or 'else if' 433 | (if (scala-syntax:search-backward-sexp "\\") 434 | (if (scala-syntax:looking-back-token "\\") 435 | (goto-char (match-beginning 0)) 436 | (point)) 437 | nil)) 438 | ((looking-at "\\") 439 | ;; align with 'try' 440 | (if (scala-syntax:search-backward-sexp "\\") 441 | (point) 442 | (message "matching 'try' not found") 443 | nil)) 444 | ((looking-at "\\") 445 | ;; align with 'try' 446 | (if (scala-syntax:search-backward-sexp "\\") 447 | (point) 448 | (message "matching 'try' not found") 449 | nil))))) 450 | (if scala-indent:align-forms 451 | anchor 452 | (when anchor 453 | (scala-indent:align-anchor) 454 | (point)))))))) 455 | 456 | (defun scala-indent:resolve-forms-align-step (start anchor) 457 | (if scala-indent:align-forms 458 | 0 459 | (scala-indent:value-expression-lead start anchor t))) 460 | 461 | ;;; 462 | ;;; Lists and enumerators 463 | ;;; 464 | 465 | (defun scala-indent:goto-list-anchor-impl (point) 466 | (goto-char point) 467 | ;; find the first element of the list 468 | (if (not scala-indent:align-parameters) 469 | (progn (back-to-indentation) (point)) 470 | (forward-comment (buffer-size)) 471 | (if (= (line-number-at-pos point) 472 | (line-number-at-pos)) 473 | (goto-char point) 474 | (beginning-of-line)) 475 | 476 | ;; align list with first non-whitespace character 477 | (skip-syntax-forward " ") 478 | (point))) 479 | 480 | (defun scala-indent:goto-list-anchor (&optional point) 481 | "Moves back to the point whose column will be used to indent 482 | list rows at current point (or point 'point'). Returns the new 483 | point or nil if the point is not in a list element > 1." 484 | (let ((list-beg (scala-syntax:list-p point))) 485 | (when list-beg 486 | (scala-indent:goto-list-anchor-impl list-beg)))) 487 | 488 | (defun scala-indent:resolve-list-step (start anchor) 489 | (if scala-indent:align-parameters 490 | 0 491 | (scala-indent:resolve-block-step start anchor))) 492 | 493 | (defun scala-indent:for-enumerators-p (&optional point) 494 | "Returns the point after opening parentheses if the current 495 | point (or point 'point') is in a block of enumerators. Return nil 496 | if not in a list of enumerators or at the first enumerator." 497 | (unless point (setq point (point))) 498 | (save-excursion 499 | (goto-char point) 500 | (scala-syntax:beginning-of-code-line) 501 | (let ((state (syntax-ppss point))) 502 | (unless (or (eobp) (= (char-syntax (char-after)) ?\))) 503 | (when (and state (nth 1 state)) 504 | (goto-char (nth 1 state)) 505 | (when (scala-syntax:looking-back-token scala-syntax:for-re) 506 | (forward-char) 507 | (forward-comment (buffer-size)) 508 | (when (< (point) point) 509 | (1+ (nth 1 state))))))))) 510 | 511 | (defun scala-indent:goto-for-enumerators-anchor (&optional point) 512 | "Moves back to the point whose column will be used to indent 513 | for enumerator at current point (or point 'point'). Returns the new 514 | point or nil if the point is not in a enumerator element > 1." 515 | (let ((enumerators-beg (scala-indent:for-enumerators-p point))) 516 | (when enumerators-beg 517 | (scala-indent:goto-list-anchor-impl enumerators-beg)))) 518 | 519 | ;;; 520 | ;;; Body 521 | ;;; 522 | 523 | (defconst scala-indent:control-keywords-cond-re 524 | (regexp-opt '("if" "while" "for") 'words) 525 | "All the flow control keywords that are followed by a 526 | condition (or generators in the case of 'for') in parentheses.") 527 | 528 | (defconst scala-indent:control-keywords-other-re 529 | (regexp-opt '("else" "do" "yield" "try" "finally" "catch") 'words) 530 | "Other flow control keywords (not followed by parentheses)") 531 | 532 | (defconst scala-indent:control-keywords-re 533 | (concat "\\(" scala-indent:control-keywords-cond-re 534 | "\\|" scala-indent:control-keywords-other-re "\\)")) 535 | 536 | (defun scala-indent:body-p (&optional point) 537 | "Returns the position of '=' symbol, or one of the 538 | scala-indent:control-keywords-re or 539 | scala-indent:control-keywords-cond-re keywords if current 540 | point (or point 'point) is on a line that follows said symbol or 541 | keyword, or nil if not." 542 | (save-excursion 543 | (when point (goto-char point)) 544 | (scala-syntax:beginning-of-code-line) 545 | (or (scala-syntax:looking-back-token scala-syntax:body-start-re 3) 546 | (let ((case-fold-search nil)) 547 | (scala-syntax:looking-back-token scala-indent:control-keywords-other-re)) 548 | (progn 549 | ;; if, else if 550 | (when (scala-syntax:looking-back-token ")" 1) 551 | (goto-char (match-end 0)) 552 | (backward-list)) 553 | (when (scala-syntax:looking-back-token scala-indent:control-keywords-cond-re) 554 | (goto-char (match-beginning 0)) 555 | (when (and (looking-at "\\") 556 | (scala-syntax:looking-back-token "\\")) 557 | (goto-char (match-beginning 0))) 558 | (when (not scala-indent:align-forms) 559 | (scala-indent:align-anchor)) 560 | (point)))))) 561 | 562 | (defun scala-indent:goto-body-anchor (&optional point) 563 | (let ((declaration-end (scala-indent:body-p point))) 564 | (when declaration-end 565 | (goto-char declaration-end) 566 | (if (let ((case-fold-search nil)) 567 | (looking-at scala-indent:control-keywords-re)) 568 | (point) 569 | (when (scala-indent:backward-sexp-to-beginning-of-line) 570 | (scala-indent:goto-run-on-anchor 571 | nil 572 | scala-indent:keywords-only-strategy)) 573 | (scala-indent:align-anchor) 574 | (point))))) 575 | 576 | (defun scala-indent:resolve-body-step (start &optional anchor) 577 | (if (and (not (= start (point-max))) (= (char-after start) ?\{)) 578 | 0 579 | (+ (scala-indent:value-expression-lead start anchor t) 580 | scala-indent:step))) 581 | 582 | ;;; 583 | ;;; Block 584 | ;;; 585 | 586 | (defun scala-indent:goto-block-anchor (&optional point) 587 | "Moves back to the point whose column will be used as the 588 | anchor for calculating block indent for current point (or point 589 | 'point'). Returns point or (point-min) if not inside a block." 590 | (let ((block-beg (nth 1 (syntax-ppss 591 | (scala-lib:point-after (beginning-of-line)))))) 592 | (when block-beg 593 | ;; check if the opening paren is the first on the line, 594 | ;; if so, it is the anchor. If not, then go back to the 595 | ;; start of the line 596 | (goto-char block-beg) 597 | (if (= (point) (scala-lib:point-after 598 | (scala-syntax:beginning-of-code-line))) 599 | (point) 600 | (goto-char (or (scala-syntax:looking-back-token 601 | scala-syntax:body-start-re 3) 602 | (point))) 603 | (scala-syntax:backward-parameter-groups) 604 | (when (scala-indent:backward-sexp-to-beginning-of-line) 605 | (scala-indent:goto-run-on-anchor nil 606 | scala-indent:keywords-only-strategy)) 607 | (scala-indent:align-anchor) 608 | (point))))) 609 | 610 | (defun scala-indent:resolve-block-step (start anchor) 611 | "Resolves the appropriate indent step for block line at position 612 | 'start' relative to the block anchor 'anchor'." 613 | (let 614 | ((lead (scala-indent:value-expression-lead start anchor))) 615 | (cond 616 | ;; at end of buffer 617 | ((= start (point-max)) (+ scala-indent:step lead)) 618 | ;; block close parentheses line up with anchor in normal case 619 | ((= (char-syntax (char-after start)) ?\)) 620 | (+ 0 lead)) 621 | ;; case-lines indent normally, regardless of where they are 622 | ((scala-syntax:looking-at-case-p start) 623 | (+ scala-indent:step lead)) 624 | ;; other than case-line in case-block get double indent 625 | ((save-excursion 626 | (goto-char (1+ (or (nth 1 (syntax-ppss start)) 0))) 627 | (forward-comment (buffer-size)) 628 | (and (scala-syntax:looking-at-case-p) 629 | (> (line-number-at-pos) (line-number-at-pos anchor)) 630 | (> start (match-beginning 0)))) 631 | (+ (* 2 scala-indent:step) lead)) 632 | ;; normal block line 633 | (t (+ scala-indent:step lead))))) 634 | 635 | ;;; 636 | ;;; Open parentheses 637 | ;;; 638 | 639 | (defun scala-indent:open-parentheses-line-p (&optional point) 640 | "Returns the position of the first character of the line, 641 | if the current point (or point 'point') is on a line that starts 642 | with an opening parentheses, or nil if not." 643 | (save-excursion 644 | (when point (goto-char point)) 645 | (scala-syntax:beginning-of-code-line) 646 | (if (looking-at "\\s(") (point) nil))) 647 | 648 | (defun scala-indent:goto-open-parentheses-anchor (&optional point) 649 | "Moves back to the point whose column will be used as the 650 | anchor for calculating opening parenthesis indent for the current 651 | point (or point 'point'). Returns point or nil, if line does not 652 | start with opening parenthesis." 653 | ;; There are five cases we need to consider: 654 | ;; 1. curry parentheses, i.e. 2..n parentheses groups. 655 | ;; 2. value body parentheses (follows '='). 656 | ;; 3. parameters, etc on separate line (who would be so mad?) 657 | ;; 4. non-value body parentheses (follows class, trait, new, def, etc). 658 | (let ((parentheses-beg (scala-indent:open-parentheses-line-p point))) 659 | (when parentheses-beg 660 | (goto-char parentheses-beg) 661 | (cond 662 | ;; case 1 663 | ((and scala-indent:align-parameters 664 | (= (char-after) ?\() 665 | (scala-indent:run-on-p) 666 | (scala-syntax:looking-back-token ")" 1)) 667 | (scala-syntax:backward-parameter-groups) 668 | (let ((curry-beg (point))) 669 | (forward-char) 670 | (forward-comment (buffer-size)) 671 | (if (= (line-number-at-pos curry-beg) 672 | (line-number-at-pos)) 673 | (goto-char curry-beg) 674 | nil))) 675 | ;; case 2 676 | ((scala-syntax:looking-back-token "=" 1) 677 | nil) ; let body rule handle it 678 | ;; case 4 679 | ((and (= (char-after) ?\{) 680 | (scala-indent:goto-run-on-anchor 681 | nil scala-indent:keywords-only-strategy)) ; use customized strategy 682 | (point)) 683 | ;; case 3 684 | ;;((scala-indent:run-on-p) 685 | ;; (scala-syntax:skip-backward-ignorable) 686 | ;; (back-to-indentation) 687 | ;; (point)) 688 | (t 689 | nil) 690 | )))) 691 | 692 | (defun scala-indent:resolve-open-parentheses-step (start anchor) 693 | "Resolves the appropriate indent step for an open paren 694 | anchored at 'anchor'." 695 | (cond ((scala-syntax:looking-back-token ")") 696 | ; (message "curry") 697 | 0) 698 | ((save-excursion 699 | (goto-char anchor) 700 | ;; find = 701 | (scala-syntax:has-char-before ?= start)) 702 | ; (message "=") 703 | scala-indent:step) 704 | (t 705 | ; (message "normal at %d" (current-column)) 706 | 0))) 707 | 708 | (defun scala-indent:goto-line-comment-anchor (&optional point) 709 | "Goto and return the position relative to which a line comment 710 | will be indented. This will be the start of the line-comment on 711 | previous line, if any." 712 | (let ((pos (point))) 713 | (when (save-excursion 714 | (when point (goto-char point)) 715 | (when (and (looking-at "\\s *//") 716 | (not (scala-syntax:looking-back-empty-line-p)) 717 | (forward-comment -1)) 718 | (setq pos (point)))) 719 | (goto-char pos)))) 720 | 721 | ;;; 722 | ;;; Indentation engine 723 | ;;; 724 | 725 | (defun scala-indent:apply-indent-rules (rule-indents &optional point) 726 | "Evaluates each rule, until one returns non-nil value. Returns 727 | the sum of the value and the respective indent step, or nil if 728 | nothing was applied." 729 | (when rule-indents 730 | (save-excursion 731 | (when point (goto-char point)) 732 | (let* ((pos (scala-syntax:beginning-of-code-line)) 733 | (rule-indent (car rule-indents)) 734 | (rule-statement (car rule-indent)) 735 | (indent-statement (cadr rule-indent)) 736 | (anchor (funcall rule-statement point))) 737 | (if anchor 738 | (progn 739 | (if scala-mode:debug-messages 740 | (message "indenting acording to %s at %d for pos %d for point %s" rule-statement anchor pos point)) 741 | (when (/= anchor (point)) 742 | (error (format "Assertion error: anchor=%d, point=%d" anchor (point)))) 743 | (+ (current-column) 744 | (save-excursion 745 | (if (functionp indent-statement) 746 | (funcall indent-statement pos anchor) 747 | (eval indent-statement))))) 748 | (scala-indent:apply-indent-rules (cdr rule-indents))))))) 749 | 750 | (defun scala-indent:calculate-indent-for-line (&optional point) 751 | "Calculate the appropriate indent for the current point or the 752 | point 'point'. Returns the new column, or nil if the indent 753 | cannot be determined." 754 | (or (scala-indent:apply-indent-rules 755 | `((scala-indent:goto-line-comment-anchor 0) 756 | (scala-indent:goto-open-parentheses-anchor scala-indent:resolve-open-parentheses-step) 757 | (scala-indent:goto-for-enumerators-anchor scala-indent:resolve-list-step) 758 | (scala-indent:goto-forms-align-anchor scala-indent:resolve-forms-align-step) 759 | (scala-indent:goto-list-anchor scala-indent:resolve-list-step) 760 | (scala-indent:goto-body-anchor scala-indent:resolve-body-step) 761 | (scala-indent:goto-run-on-anchor scala-indent:resolve-run-on-step) 762 | (scala-indent:goto-block-anchor scala-indent:resolve-block-step) 763 | ) 764 | point) 765 | 0)) 766 | 767 | (defun scala-indent:indent-line-to (column) 768 | "Indent the line to column and move cursor to the indent 769 | column, if it was at the left margin." 770 | (when column 771 | (if (<= (current-column) (current-indentation)) 772 | (indent-line-to column) 773 | (save-excursion (indent-line-to column))))) 774 | 775 | (make-variable-buffer-local 'scala-indent:previous-indent-pos) 776 | 777 | (defun scala-indent:remove-indent-from-previous-empty-line () 778 | "Handles removing of whitespace from a previosly indented code 779 | line that was left empty (i.e. whitespaces only). Also clears the 780 | scala-indent:previous-indent-pos variable that controls the process." 781 | (when (and scala-indent:previous-indent-pos 782 | (/= scala-indent:previous-indent-pos (point))) 783 | (save-excursion 784 | (beginning-of-line) 785 | (if (= scala-indent:previous-indent-pos 786 | (point)) 787 | (setq scala-indent:previous-indent-pos 788 | (when (looking-at "^\\s +$") (point))) 789 | (goto-char scala-indent:previous-indent-pos) 790 | (when (looking-at "^\\s +$") 791 | (delete-region (match-beginning 0) (match-end 0))) 792 | (setq scala-indent:previous-indent-pos nil))))) 793 | 794 | (defun scala-indent:indent-code-line (&optional strategy) 795 | "Indent a line of code. Expect to be outside of any comments or 796 | strings" 797 | (if strategy 798 | (setq scala-indent:effective-run-on-strategy strategy) 799 | (if (eq last-command this-command) 800 | (scala-indent:toggle-effective-run-on-strategy) 801 | (scala-indent:reset-effective-run-on-strategy))) 802 | ; (message "run-on-strategy is %s" (scala-indent:run-on-strategy)) 803 | (scala-indent:indent-line-to (scala-indent:calculate-indent-for-line)) 804 | (scala-lib:delete-trailing-whitespace) 805 | (setq scala-indent:previous-indent-pos 806 | (save-excursion 807 | (beginning-of-line) 808 | (when (looking-at "^\\s +$") (point))))) 809 | 810 | (defun scala-indent:indent-line (&optional strategy) 811 | "Indents the current line." 812 | (interactive "*") 813 | (let ((state (save-excursion (syntax-ppss (line-beginning-position))))) 814 | (if (not (nth 8 state)) ;; 8 = start pos of comment or string, nil if none 815 | (scala-indent:indent-code-line strategy) 816 | (scala-indent:indent-line-to 817 | (cond ((integerp (nth 4 state)) ;; 4 = nesting level of multi-line comment 818 | (scala-indent:scaladoc-indent (nth 8 state))) 819 | ((eq t (nth 3 state)) ;; 3 = t for multi-line string 820 | (or (save-excursion 821 | (beginning-of-line) 822 | (when (and (looking-at "\\s *|") 823 | (progn (goto-char (nth 8 state)) 824 | (looking-at "\\(\"\"\"\\)|"))) 825 | (goto-char (match-end 1)) 826 | (current-column))) 827 | (current-indentation))) 828 | (t (current-indentation))))))) 829 | 830 | (defun scala-indent:indent-with-reluctant-strategy () 831 | (interactive "*") 832 | (scala-indent:indent-line scala-indent:reluctant-strategy)) 833 | 834 | (defun scala-indent:scaladoc-indent (comment-start-pos) 835 | "Calculate indent for a multi-line comment. Scaladoc 836 | lines (starting with /**) are indented under the second 837 | aseterix. Other multi-line comment rows are indented undet the 838 | first asterisk. 839 | 840 | Note: start line is indented as code since the start of the 841 | comment is outside the comment region. " 842 | (save-excursion 843 | (goto-char comment-start-pos) 844 | (when (looking-at "/\\*+") 845 | (goto-char 846 | (if (and (not scala-indent:use-javadoc-style) 847 | (= (- (match-end 0) (match-beginning 0)) 3)) 848 | (- (match-end 0) 1) 849 | (+ (match-beginning 0) 1))) 850 | (current-column)))) 851 | 852 | (defun scala-indent:indent-on-parentheses () 853 | (when (and (= (char-syntax (char-before)) ?\)) 854 | (= (save-excursion (back-to-indentation) (point)) (1- (point)))) 855 | (scala-indent:indent-line))) 856 | 857 | (defconst scala-indent:indent-on-words-re 858 | (concat "^\\s *" 859 | (regexp-opt '("catch" "case" "else" "finally" "yield") 'words))) 860 | 861 | (defun scala-indent:indent-on-special-words () 862 | "This function is meant to be used with post-self-insert-hook. 863 | 864 | Indents the line if position is right after a space that is after 865 | a word that needs to be indented specially." 866 | ;; magic numbers used 4 = length of "case" 867 | (when (and (> (current-column) 4) 868 | (= (char-before) ?\s) 869 | (= (char-syntax (char-before (- (point) 1))) ?w) 870 | (save-excursion (backward-char) 871 | (looking-back scala-indent:indent-on-words-re)) 872 | (not (nth 8 (syntax-ppss)))) 873 | (scala-indent:indent-line-to (scala-indent:calculate-indent-for-line)))) 874 | 875 | (defun scala-indent:indent-on-scaladoc-asterisk () 876 | "This function is meant to be used with post-self-insert-hook. 877 | 878 | Indents the line if position is right after an asterisk in a 879 | multi-line comment block and there is only whitespace before the asterisk. 880 | 881 | If scala-indent:add-space-for-scaladoc-asterisk is t, also adds a 882 | space after the asterisk if the asterisk is the last character on 883 | the line." 884 | (let ((state (syntax-ppss))) 885 | (when (and (integerp (nth 4 state)) 886 | (looking-back "^\\s *\\*" (line-beginning-position))) 887 | (when scala-indent:add-space-for-scaladoc-asterisk 888 | (insert " ")) 889 | (scala-indent:indent-line-to (scala-indent:scaladoc-indent (nth 8 state)))))) 890 | 891 | (defun scala-indent:fix-scaladoc-close () 892 | "This function is meant to be used with post-self-insert-hook. 893 | 894 | Changes 'asterisk space slash' to 'asterisk slash' in a 895 | multi-line comment if position is right after that slash and 896 | scala-indent:add-space-for-scaladoc-asterisk is t." 897 | (let ((state (syntax-ppss))) 898 | (when (and scala-indent:add-space-for-scaladoc-asterisk 899 | (integerp (nth 4 state)) 900 | (looking-back "^\\s *\\*\\s /" (line-beginning-position))) 901 | (delete-region (- (point) 2) (- (point) 1))))) 902 | 903 | (defun scala-indent:insert-asterisk-on-multiline-comment () 904 | "Insert an asterisk at the end of the current line when at the beginning 905 | of a line inside a multi-line comment " 906 | (let* ((state (syntax-ppss)) 907 | (comment-start-pos (nth 8 state))) 908 | (when (and (integerp (nth 4 state)) 909 | ; Ensure that we're inside a scaladoc comment 910 | (string-match-p "^/\\*\\*[^\\*]" 911 | (buffer-substring-no-properties 912 | comment-start-pos 913 | (+ comment-start-pos 4))) 914 | ; Ensure that the previous line had a leading asterisk or was the comment start. 915 | (let ((prev-line (buffer-substring-no-properties 916 | (line-beginning-position 0) 917 | (line-end-position 0)))) 918 | (or 919 | (string-match-p "^\\s-*\\*" prev-line) 920 | (string-match-p "\\s-*/\\*\\*" prev-line)))) 921 | (skip-syntax-forward " ") 922 | (insert "*") 923 | (scala-indent:indent-on-scaladoc-asterisk)))) 924 | 925 | (defun scala-mode:indent-scaladoc-asterisk (&optional insert-space-p) 926 | (message "scala-mode:indent-scaladoc-asterisk has been deprecated")) 927 | 928 | 929 | (defun scala-indent:fixup-whitespace () 930 | "scala-mode version of `fixup-whitespace'" 931 | (interactive "*") 932 | (save-excursion 933 | (delete-horizontal-space) 934 | (if (or (looking-at "^\\|[]):.]") 935 | (save-excursion (forward-char -1) 936 | (if (nth 4 (syntax-ppss)) 937 | (looking-at "$\\|\\s(") 938 | (looking-at "$\\|[[(.]"))) 939 | (and (= (char-before) ?{) (= (char-after) ?}))) 940 | nil 941 | (insert ?\s)))) 942 | 943 | (defun scala-indent:join-line (&optional arg) 944 | "scala-mode version of `join-line', i.e. `delete-indentation'" 945 | (interactive "*P") 946 | (beginning-of-line) 947 | (if arg (forward-line 1)) 948 | (when (= (preceding-char) ?\n) 949 | (delete-region (point) (1- (point))) 950 | (delete-horizontal-space) 951 | (let ((state (syntax-ppss))) 952 | (cond 953 | ((and (integerp (nth 4 state)) ; nestable comment (i.e. with *) 954 | (looking-at " *\\*\\($\\|[^/]\\)") 955 | (save-excursion (goto-char (max (nth 8 state) (line-beginning-position))) 956 | (looking-at "\\s */?\\*"))) 957 | (delete-forward-char 2)) 958 | ((and (nth 4 state) ; row comment (i.e. with //) 959 | (looking-at " //")) 960 | (delete-forward-char 3)))) 961 | (scala-indent:fixup-whitespace))) 962 | 963 | (provide 'scala-mode-indent) 964 | -------------------------------------------------------------------------------- /scala-mode-lib.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode-lib.el - Major mode for editing scala, common functions 2 | ;;; Copyright (c) 2012 Heikki Vesalainen 3 | ;;; For information on the License, see the LICENSE file 4 | 5 | (defvar scala-mode:debug-messages 6 | "If true, some debug messages may be printed printed." 7 | nil) 8 | 9 | (defmacro scala-lib:column-after (&rest body) 10 | `(save-excursion 11 | ,@body 12 | (current-column))) 13 | 14 | (defmacro scala-lib:point-after (&rest body) 15 | `(save-excursion 16 | ,@body 17 | (point))) 18 | 19 | (defun scala-lib:delete-trailing-whitespace () 20 | (save-excursion 21 | (end-of-line) 22 | (skip-syntax-backward " ") 23 | (unless (bolp) 24 | (delete-char (- (line-end-position) (point)))))) 25 | 26 | (provide 'scala-mode-lib) 27 | -------------------------------------------------------------------------------- /scala-mode-map.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode-map.el - Major mode for editing scala, keyboard map 2 | ;;; Copyright (c) 2012 Heikki Vesalainen 3 | ;;; For information on the License, see the LICENSE file 4 | 5 | (require 'scala-mode-indent) 6 | 7 | (defvar scala-mode-map 8 | (let ((map (make-sparse-keymap))) 9 | (set-keymap-parent map prog-mode-map) 10 | ;(substitute-key-definition 'delete-indentation 'scala-indent:join-line map global-map) 11 | map) 12 | "Local key map used for scala mode") 13 | 14 | (defun scala-mode-map:add-self-insert-hooks () 15 | (add-hook 'post-self-insert-hook 16 | 'scala-indent:indent-on-parentheses) 17 | (add-hook 'post-self-insert-hook 18 | 'scala-indent:indent-on-special-words) 19 | (add-hook 'post-self-insert-hook 20 | 'scala-indent:indent-on-scaladoc-asterisk) 21 | (add-hook 'post-self-insert-hook 22 | 'scala-indent:fix-scaladoc-close)) 23 | 24 | (defun scala-mode-map:add-remove-indent-hook () 25 | (add-hook 'post-command-hook 26 | 'scala-indent:remove-indent-from-previous-empty-line)) 27 | 28 | (provide 'scala-mode-map) 29 | -------------------------------------------------------------------------------- /scala-mode-paragraph.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode-paragraph.el - Major mode for editing scala, paragraph 2 | ;;; detection and fill 3 | ;;; Copyright (c) 2012 Heikki Vesalainen For information on the License, 4 | ;;; see the LICENSE file 5 | 6 | ;;; Based on Scala Language Specification (SLS) Version 2.9 7 | 8 | ;;; Provides paragraph navigation and fill for scaladocs and 9 | ;;; multi-line strings. 10 | 11 | (defconst scala-paragraph:paragraph-line-start-re 12 | (concat "\\(?:\\s-*" ; whitespace 13 | "\\(?://+\\|\\*\\|/\\*+" ; comment start 14 | "\\||\\)?" ; multi-line margin | 15 | "\\s-*\\)")) ; whitespace 16 | 17 | (defconst scala-paragraph:scaladoc-list-start-re 18 | (concat "\\(?:-" ; unordered liststs 19 | "\\|[1IiAa]\\." ; ordered lists 20 | "\\)\\s-*")) 21 | 22 | (defconst scala-paragraph:fill-first-line-re 23 | (concat "\\s-*\\(//+\\|\\*\\||\\)?\\s-*" 24 | "\\(?:" scala-paragraph:scaladoc-list-start-re "\\)?")) 25 | 26 | (defconst scala-paragraph:paragraph-start-re 27 | (concat scala-paragraph:paragraph-line-start-re 28 | "\\(?:$" ; empty line 29 | "\\|==*[^=]+==*[ ]*$" ; headings 30 | "\\|" 31 | scala-paragraph:scaladoc-list-start-re 32 | "\\|{{{" ; code block start 33 | "\\|}}}" ; code block end 34 | "\\|@[a-zA-Z]+\\>" ; annotations 35 | "\\)" 36 | "\\|\\(?:\\s-*\\*/\\)" ; end of comment 37 | )) 38 | 39 | (defconst scala-paragraph:paragraph-separate-re 40 | (concat scala-paragraph:paragraph-line-start-re 41 | "\\(?:$\\)" 42 | "\\|\\(?:\\s *\\*/\\)" ; end of comment 43 | )) 44 | 45 | (defun scala-paragraph:fill-function () 46 | (let (fill) 47 | (save-restriction 48 | (save-excursion 49 | (widen) 50 | (beginning-of-line) 51 | (cond ((looking-at "\\s-*/?\\*+\\s-*") 52 | (setq fill (replace-regexp-in-string 53 | "/\\*+" 54 | (lambda (str) (if (= (length str) 3) " *" " *")) 55 | (match-string-no-properties 0))) 56 | (goto-char (match-end 0)) 57 | (when (looking-at scala-paragraph:scaladoc-list-start-re) 58 | (setq fill 59 | (concat fill (make-string (- (match-end 0) 60 | (match-beginning 0)) ?\s))))) 61 | ((or (re-search-forward "\"\"\"|" (line-end-position) t) 62 | (and (eq (nth 3 (syntax-ppss)) t) 63 | (re-search-forward "^\\s-*|" (line-end-position) t))) 64 | (setq fill (concat (make-string (- (current-column) 1) ?\s) "|")) 65 | (setq fill (concat fill (make-string (skip-syntax-forward " ") ?\s))) 66 | (when (looking-at scala-paragraph:scaladoc-list-start-re) 67 | (setq fill 68 | (concat fill (make-string (- (match-end 0) 69 | (match-beginning 0)) ?\s)))))))) 70 | fill)) 71 | 72 | (defun scala-paragraph:fill-paragraph (&rest args) 73 | ;; move to inside multi-line comment or multi-line string, if outside 74 | (when (looking-at "\\s-*\\(?:/\\**\\|\"\"\"\\)\\s-*") 75 | (goto-char (match-end 0))) 76 | (let ((state (syntax-ppss)) 77 | (fill-paragraph-function 78 | ;; Avoid infinite recursion, set fill-paragraph-function to 79 | ;; nil if it is 'scala-paragraph:fill-paragraph 80 | (unless (eq fill-paragraph-function 'scala-paragraph:fill-paragraph) 81 | fill-paragraph-function))) 82 | (cond ((integerp (nth 4 state)) 83 | ;; mask multi-line comments and fill 84 | (save-restriction 85 | (narrow-to-region (nth 8 state) 86 | (save-excursion (goto-char (nth 8 state)) 87 | (if (forward-comment 1) 88 | (point) 89 | (point-max)))) 90 | (apply #'fill-paragraph args)) 91 | t) 92 | ((eq (nth 4 state) t) 93 | ;; line comment, let normal fill-function handle this 94 | nil) 95 | ((eq (nth 3 state) t) 96 | ;; mask multi-line strings and fill. 97 | (save-restriction 98 | (narrow-to-region (nth 8 state) 99 | (save-excursion (goto-char (nth 8 state)) 100 | (or (ignore-errors 101 | (forward-sexp) 102 | (point)) 103 | (point-max)))) 104 | (apply #'fill-paragraph args)) 105 | t) 106 | ;; TODO: fill lists 107 | ;; the rest should not be filled (code, etc) 108 | (t t)))) 109 | 110 | (provide 'scala-mode-paragraph) 111 | -------------------------------------------------------------------------------- /scala-mode-prettify-symbols.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode-prettify-symbols.el --- Prettifying scala symbols -*- coding: utf-8; -*- 2 | 3 | ;; Copyright (c) 2016 Merlin Göttlinger 4 | ;; License: http://www.gnu.org/licenses/gpl.html 5 | 6 | ;;; Commentary: 7 | ;; 8 | ;; Suggested `prettify-symbols' for Scala editing, enable 9 | ;; `prettify-symbols-mode' and `setq' an alist of your choice 10 | ;; for `prettify-symbols-alist'. 11 | 12 | ;;; Code: 13 | 14 | (defconst 15 | scala-mode-pretty-bool-alist 16 | '(("<=" . ?≤) 17 | (">=" . ?≥) 18 | ("==" . ?≡) 19 | ("===" . ?≣) 20 | ("!" . ?¬) 21 | ("!=" . ?≢) 22 | ("&&" . ?∧) 23 | ("||" . ?∨) 24 | ("true" . ?⊤) 25 | ("false" . ?⊥) 26 | ("Boolean" . ?𝔹)) 27 | "Prettify rules for boolean related operations.") 28 | 29 | (defconst 30 | scala-mode-pretty-collection-alist 31 | '(("empty" . ?∅) 32 | ("sum" . ?∑) 33 | ("product" . ?∏) 34 | ("contains" . ?∍) 35 | ("forall" . ?∀) 36 | ("any" . ?∃) 37 | ("intersect" . ?∩) 38 | ("union" . ?∪) 39 | ("diff" . ?≏) 40 | ("subsetOf" . ?⊆) 41 | ("++" . ?⧺) 42 | ("::" . ?⸬) 43 | ("--" . ?╌)) 44 | "Prettify rules for collections related operations.") 45 | 46 | (defconst 47 | scala-mode-pretty-arrows-alist 48 | '(("->" . ?→) 49 | ("<-" . ?←) 50 | ("=>" . ?⇒) 51 | ("<=>" . ?⇔) 52 | ("-->" . ?⟶) 53 | ("<->" . ?↔) 54 | ("<--" . ?⟵) 55 | ("<-->" . ?⟷) 56 | ("==>" . ?⟹) 57 | ("<==" . ?⟸) 58 | ("<==>" . ?⟺) 59 | ("~>" . ?⇝) 60 | ("<~" . ?⇜)) 61 | "Prettify rules for arrow related code pieces.") 62 | 63 | (defconst 64 | scala-mode-pretty-misc-alist 65 | '(("Unit" . ?∅) 66 | ("Int" . ?ℤ) 67 | ("assert" . ?⊦) 68 | (":=" . ?≔)) 69 | "Prettify rules for other mixed code pieces.") 70 | 71 | (defconst 72 | scala-mode-pretty-categories-alist 73 | '(("flatMap" . ?⤜) 74 | (">>=" . ?⤜) 75 | ("bind" . ?⤜) 76 | (">>" . ?≫) 77 | ("followedBy" . ?≫) 78 | ("<+>" . ?⊕)) 79 | "Prettify rules for category theory related operators (for use with cats/scalaz/...).") 80 | 81 | (defcustom 82 | scala-prettify-symbols-alist 83 | (append 84 | scala-mode-pretty-bool-alist 85 | scala-mode-pretty-collection-alist 86 | scala-mode-pretty-arrows-alist 87 | scala-mode-pretty-misc-alist 88 | scala-mode-pretty-categories-alist) 89 | "All prettify rules to be applied in scala code." 90 | :type 'alist 91 | :group 'scala) 92 | 93 | (provide 'scala-mode-prettify-symbols) 94 | ;;; scala-mode-prettify-symbols.el ends here 95 | -------------------------------------------------------------------------------- /scala-mode-syntax.el: -------------------------------------------------------------------------------- 1 | ;;;; scala-mode-syntax.el - Major mode for editing scala, syntax 2 | ;;; Copyright (c) 2012 Heikki Vesalainen 3 | ;;; For information on the License, see the LICENSE file 4 | 5 | ;;; Based on Scala Language Specification (SLS) Version 2.9 6 | 7 | ;;;; 8 | ;;;; Scala syntax regular expressions 9 | ;;;; 10 | 11 | ;;; Based on the Scala language specification 2.9. Note: order is not 12 | ;;; the same as in the document, as here things are declared before 13 | ;;; used. 14 | 15 | ;;; A note on naming. Things that end with '-re' are regular 16 | ;;; expressions. Things that end with '-group' are regular expression 17 | ;;; character groups without the enclosing [], i.e. they are not 18 | ;;; regular expressions, but can be used in declaring one. 19 | 20 | ;; single letter matching groups (Chapter 1) 21 | (defconst scala-syntax:hexDigit-group "0-9A-Fa-f") 22 | (defconst scala-syntax:UnicodeEscape-re (concat "\\\\u[" scala-syntax:hexDigit-group "]\\{4\\}")) 23 | 24 | (defconst scala-syntax:upper-group "[:upper:]\\$") ;; missing _ to make ids work 25 | (defconst scala-syntax:upperAndUnderscore-group (concat "_" scala-syntax:upper-group )) 26 | (defconst scala-syntax:lower-group "[:lower:]") 27 | (defconst scala-syntax:letter-group (concat scala-syntax:lower-group scala-syntax:upper-group)) ;; TODO: add Lt, Lo, Nl 28 | (defconst scala-syntax:digit-group "0-9") 29 | (defconst scala-syntax:letterOrDigit-group (concat 30 | scala-syntax:upperAndUnderscore-group 31 | scala-syntax:lower-group 32 | scala-syntax:digit-group)) 33 | (defconst scala-syntax:opchar-safe-group "!%&*+/?\\\\^|~-") ;; TODO: Sm, So 34 | (defconst scala-syntax:opchar-unsafe-group "#:<=>@") 35 | (defconst scala-syntax:opchar-group (concat scala-syntax:opchar-unsafe-group 36 | scala-syntax:opchar-safe-group)) 37 | 38 | ;; Scala delimiters (Chapter 1), but no quotes 39 | (defconst scala-syntax:delimiter-group ".,;") 40 | 41 | ;; Integer Literal (Chapter 1.3.1) 42 | (defconst scala-syntax:nonZeroDigit-group "1-9") 43 | (defconst scala-syntax:octalDigit-group "0-7") 44 | (defconst scala-syntax:decimalNumeral-re 45 | (concat "0" 46 | "\\|[" scala-syntax:nonZeroDigit-group "][" scala-syntax:digit-group "]*")) 47 | (defconst scala-syntax:hexNumeral-re (concat "0x[" scala-syntax:hexDigit-group "]+")) 48 | (defconst scala-syntax:octalNumeral-re (concat "0[" scala-syntax:octalDigit-group "]+")) 49 | (defconst scala-syntax:integerLiteral-re (concat "-?" ;; added from definition of literal 50 | "\\(" scala-syntax:hexNumeral-re 51 | "\\|" scala-syntax:octalNumeral-re 52 | "\\|" scala-syntax:decimalNumeral-re 53 | "\\)[Ll]?")) 54 | 55 | 56 | ;; Floating Point Literal (Chapter 1.3.2) 57 | (defconst scala-syntax:exponentPart-re (concat "\\([eE][+-]?[" scala-syntax:digit-group "]+\\)")) 58 | (defconst scala-syntax:floatType-re "[fFdD]") 59 | (defconst scala-syntax:floatingPointLiteral-re 60 | (concat "-?" ;; added from definition of literal 61 | "\\([" scala-syntax:digit-group "]+\\.[" scala-syntax:digit-group "]*" 62 | scala-syntax:exponentPart-re "?" scala-syntax:floatType-re "?" 63 | "\\|" "\\.[" scala-syntax:digit-group "]+" 64 | scala-syntax:exponentPart-re "?" scala-syntax:floatType-re "?" 65 | "\\|" "[" scala-syntax:digit-group "]+" scala-syntax:exponentPart-re 66 | "\\|" "[" scala-syntax:digit-group "]+" scala-syntax:floatType-re "\\)")) 67 | 68 | (defconst scala-syntax:number-safe-start-re 69 | (concat "[^_" scala-syntax:letter-group "]")) 70 | 71 | ;; Boolean Literals (Chapter 1.3.3) 72 | (defconst scala-syntax:booleanLiteral-re "true|false") 73 | 74 | ;; Escape Sequences (Chapter 1.3.6) 75 | (defconst scala-syntax:escapeSequence-re "\\\\['btnfr\"\\\\]") 76 | 77 | ;; Octal Escape Sequences (Chapter 1.3.6) 78 | (defconst scala-syntax:octalEscape-re (concat "\\\\[" scala-syntax:octalDigit-group "\\]\\{1,3\\}")) 79 | 80 | ;; Character Literals (Chapter 1.3.4) 81 | (defconst scala-syntax:characterLiteral-re 82 | (concat "\\('\\)\\(" "[^\\\\]" ;; should be just printable char, but this is faster 83 | "\\|" scala-syntax:escapeSequence-re 84 | "\\|" scala-syntax:octalEscape-re 85 | "\\|" scala-syntax:UnicodeEscape-re "\\)\\('\\)")) 86 | 87 | (defconst scala-syntax:string-escape-re 88 | (concat scala-syntax:escapeSequence-re 89 | "\\|" scala-syntax:octalEscape-re 90 | "\\|" scala-syntax:UnicodeEscape-re)) 91 | 92 | ;; String Literals (Chapter 1.3.5) 93 | (defconst scala-syntax:stringElement-re 94 | (concat "\\(" "[^\n\"\\\\]" 95 | "\\|" scala-syntax:string-escape-re "\\)")) 96 | (defconst scala-syntax:oneLineStringLiteral-re (concat "\\(\"\\)" scala-syntax:stringElement-re "*\\(\"\\)")) 97 | (defconst scala-syntax:multiLineStringLiteral-start-re 98 | "\\(\"\\)\"\"\\(\"?\"?[^\"]\\)*") 99 | (defconst scala-syntax:multiLineStringLiteral-end-re 100 | "\"\"+\\(\"\\)") 101 | (defconst scala-syntax:multiLineStringLiteral-re 102 | (concat scala-syntax:multiLineStringLiteral-start-re 103 | scala-syntax:multiLineStringLiteral-end-re)) 104 | (defconst scala-syntax:stringLiteral-re 105 | (concat "\\(" scala-syntax:multiLineStringLiteral-re 106 | "\\|" scala-syntax:oneLineStringLiteral-re "\\)" )) 107 | 108 | ;; If you change this or any of the used regex, be sure to 109 | ;; maintain this or update propertize function accordingly: 110 | ;; group 1 = char start, 3 = char end 111 | ;; group 4 = multi-line string start, 6 = end 112 | ;; group 7 = string start, 9 = end 113 | (defconst scala-syntax:relaxed-char-and-string-literal-re 114 | (concat scala-syntax:characterLiteral-re 115 | "\\|" scala-syntax:multiLineStringLiteral-start-re 116 | "\\(?:" scala-syntax:multiLineStringLiteral-end-re "\\)?" 117 | "\\|\\(\"\\)" "\\(\\\\.\\|[^\"\n\\]\\)*" "\\(\"\\)")) 118 | 119 | ;; Identifiers (Chapter 1.1) 120 | (defconst scala-syntax:op-re 121 | (concat "[" scala-syntax:opchar-group "]+" )) 122 | (defconst scala-syntax:idrest-re 123 | ;; Eagerness of regexp causes problems with _. The following is a workaround, 124 | ;; but the resulting regexp matches only what SLS demands. 125 | (concat "\\(" "[_]??" "[" scala-syntax:letter-group scala-syntax:digit-group "]+" "\\)*" 126 | "\\(" "_+" scala-syntax:op-re "\\|" "_" "\\)?")) 127 | (defconst scala-syntax:varid-re (concat "[" scala-syntax:lower-group "]" scala-syntax:idrest-re)) 128 | (defconst scala-syntax:capitalid-re (concat "[" scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re)) 129 | ;; alphaid introduce by SIP11 130 | (defconst scala-syntax:alphaid-re (concat "\\(" "[" scala-syntax:lower-group scala-syntax:upperAndUnderscore-group "]" scala-syntax:idrest-re "\\)")) 131 | (defconst scala-syntax:plainid-re (concat "\\(" scala-syntax:alphaid-re "\\|" scala-syntax:op-re "\\)")) 132 | ;; stringlit is referred to, but not defined Scala Language Specification 2.9 133 | ;; we define it as consisting of anything but '`' and newline 134 | (defconst scala-syntax:stringlit-re "[^`\n\r]") 135 | (defconst scala-syntax:quotedid-re (concat "`" scala-syntax:stringlit-re "+`")) 136 | (defconst scala-syntax:id-re (concat "\\(" scala-syntax:plainid-re 137 | "\\|" scala-syntax:quotedid-re "\\)")) 138 | (defconst scala-syntax:id-first-char-group 139 | (concat scala-syntax:lower-group 140 | scala-syntax:upperAndUnderscore-group 141 | scala-syntax:opchar-group)) 142 | 143 | ;; Symbol literals (Chapter 1.3.7) 144 | (defconst scala-syntax:symbolLiteral-re 145 | ;; must end with non-' to not conflict with scala-syntax:characterLiteral-re 146 | (concat "\\('" scala-syntax:plainid-re "\\)\\([^']\\|$\\)")) 147 | 148 | ;; Literals (Chapter 1.3) 149 | (defconst scala-syntax:literal-re 150 | (concat "\\(" scala-syntax:integerLiteral-re 151 | "\\|" scala-syntax:floatingPointLiteral-re 152 | "\\|" scala-syntax:booleanLiteral-re 153 | "\\|" scala-syntax:characterLiteral-re 154 | "\\|" scala-syntax:stringLiteral-re 155 | "\\|" scala-syntax:symbolLiteral-re 156 | "\\|" "null" "\\)")) 157 | 158 | (defconst scala-syntax:interpolation-re 159 | (concat "\\(" "\\$" scala-syntax:id-re "\\|" "\\${[^}\n\\\\]*}" "\\)")) 160 | 161 | (defun scala-syntax:interpolation-matcher (end) 162 | (let* ((pos nil) 163 | (syntax nil) 164 | (str-start nil) 165 | (char-before-str nil)) 166 | (while (and 167 | (setq pos (re-search-forward scala-syntax:interpolation-re end t)) 168 | (setq syntax (syntax-ppss pos)) 169 | (if (nth 3 syntax) ;; "is string" 170 | (progn 171 | (setq str-start (nth 8 syntax)) 172 | ;; s"foo" 173 | ;; ^-- `char-before-str', must be identifier 174 | (setq char-before-str (char-after (1- str-start))) 175 | ;; break if match 176 | (null (string-match-p 177 | scala-syntax:id-re (string char-before-str)))) 178 | t))) ;; keep going 179 | pos)) 180 | 181 | ;; Paths (Chapter 3.1) 182 | ;; emacs has a problem with these regex, don't use them 183 | ;; (defconst scala-syntax:classQualifier-re (concat "[[]" scala-syntax:id-re "[]]")) 184 | ;; (defconst scala-syntax:stableId-re 185 | ;; (concat "\\(\\(" "this" 186 | ;; "\\|" "super" scala-syntax:classQualifier-re 187 | ;; "\\|" scala-syntax:id-re 188 | ;; "\\)\\.\\)*" 189 | ;; scala-syntax:id-re)) 190 | ;; (defconst scala-syntax:path-re 191 | ;; (concat "\\(" scala-syntax:stableId-re 192 | ;; "\\|" "\\(" scala-syntax:id-re "\\." "\\)?" "this" "\\)")) 193 | 194 | (defun scala-syntax:looking-at-super () 195 | (save-excursion 196 | (when (looking-at "\\") 197 | (let ((beg (match-beginning 0))) 198 | (when (and (goto-char (match-end 0)) 199 | (or (when (= (char-after) ?.) 200 | (forward-char) 201 | t) 202 | (and (when (and (not (eobp)) (= (char-after) ?\[)) 203 | (forward-char) 204 | t) 205 | (progn (scala-syntax:skip-forward-ignorable) 206 | (looking-at scala-syntax:id-re)) 207 | (progn (goto-char (match-end 0)) 208 | (scala-syntax:skip-forward-ignorable) 209 | (when (and (not (eobp)) (= (char-after) ?\])) 210 | (forward-char) 211 | t)) 212 | (when (and (not (eobp)) (= (char-after) ?.)) 213 | (forward-char) 214 | t))) 215 | (looking-at scala-syntax:id-re)) 216 | (set-match-data `(,beg ,(match-end 0))) 217 | t))))) 218 | 219 | (defun scala-syntax:looking-at-stableIdOrPath (&optional path-p beg) 220 | (unless beg (setq beg (point))) 221 | (save-excursion 222 | (cond ((looking-at "\\") 223 | (goto-char (match-end 0)) 224 | (if (and (not (eobp)) (= (char-after) ?.)) 225 | (progn (forward-char) 226 | (scala-syntax:looking-at-stableIdOrPath path-p beg)) 227 | path-p)) 228 | ((or (scala-syntax:looking-at-super) 229 | (and (not (or (looking-at scala-syntax:keywords-unsafe-re) 230 | (scala-syntax:looking-at-reserved-symbol nil))) 231 | (looking-at scala-syntax:id-re))) 232 | (goto-char (match-end 0)) 233 | (if (and (not (eobp)) (= (char-after) ?.)) 234 | (progn (forward-char) 235 | (scala-syntax:looking-at-stableIdOrPath path-p beg)) 236 | (set-match-data `(,beg ,(match-end 0))) 237 | (point)))))) 238 | 239 | (defun scala-syntax:looking-at-simplePattern-beginning () 240 | (or (looking-at "[_(]") 241 | (looking-at scala-syntax:literal-re) 242 | (scala-syntax:looking-at-stableIdOrPath))) 243 | 244 | 245 | (defun scala-syntax:regexp-for-id (id) 246 | (let ((prefix-regex 247 | (if (string-match scala-syntax:alphaid-re id) 248 | "\\b" (concat "\\(^\\|[^" scala-syntax:opchar-group "]\\)"))) 249 | (suffix-regex 250 | (if (string-match scala-syntax:op-re (substring id -1 nil)) 251 | (concat "\\([^" scala-syntax:opchar-group "]\\|$\\)") "\\b"))) 252 | (concat prefix-regex id suffix-regex))) 253 | 254 | ;;; 255 | ;;; Other regular expressions 256 | ;;; 257 | 258 | (defconst scala-syntax:preamble-start-re 259 | "\#\!") 260 | 261 | (defconst scala-syntax:empty-line-re 262 | "^\\s *$") 263 | 264 | (defconst scala-syntax:comment-start-re 265 | "/[/*]") 266 | 267 | (defconst scala-syntax:end-of-code-line-re 268 | (concat "\\([ ]\\|$\\|" scala-syntax:comment-start-re "\\)") 269 | "A special regexp that can be concatenated to an other regular 270 | expression when used with scala-syntax:looking-back-token. Not 271 | meaningfull in other contexts.") 272 | 273 | (defconst scala-syntax:path-keywords-unsafe-re 274 | (regexp-opt '("super" "this") 'words)) 275 | 276 | (defconst scala-syntax:path-keywords-re 277 | (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:path-keywords-unsafe-re "\\)")) 278 | 279 | (defconst scala-syntax:value-keywords-unsafe-re 280 | (regexp-opt '("false" "null" "true") 'words)) 281 | 282 | (defconst scala-syntax:value-keywords-re 283 | (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:value-keywords-unsafe-re "\\)")) 284 | 285 | (defconst scala-syntax:other-keywords-unsafe-re 286 | (regexp-opt '("abstract" "case" "catch" "class" "def" "do" "else" "extends" 287 | "final" "finally" "for" "forSome" "if" "implicit" "import" 288 | "lazy" "match" "new" "object" "override" "package" "private" 289 | "protected" "return" "sealed" "throw" "trait" "try" "type" 290 | "val" "var" "while" "with" "yield" "inline") 'words)) 291 | 292 | (defconst scala-syntax:other-keywords-re 293 | (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:other-keywords-unsafe-re "\\)")) 294 | 295 | (defconst scala-syntax:keywords-unsafe-re 296 | (concat "\\(" scala-syntax:path-keywords-unsafe-re 297 | "\\|" scala-syntax:value-keywords-unsafe-re 298 | "\\|" scala-syntax:other-keywords-unsafe-re 299 | "\\)")) 300 | 301 | ;; TODO: remove 302 | ;; (defconst scala-syntax:keywords-re 303 | ;; (concat "\\(^\\|[^`'_]\\)\\(" scala-syntax:value-keywords-unsafe-re 304 | ;; "\\|" scala-syntax:path-keywords-unsafe-re 305 | ;; "\\|" scala-syntax:other-keywords-unsafe-re "\\)")) 306 | 307 | 308 | (defconst scala-syntax:after-reserved-symbol-underscore-re 309 | (concat "$\\|" scala-syntax:comment-start-re 310 | "\\|[^" scala-syntax:letterOrDigit-group "]")) 311 | 312 | (defconst scala-syntax:reserved-symbol-underscore-re 313 | ;; reserved symbol _ 314 | (concat "\\(^\\|[^" scala-syntax:letterOrDigit-group "]\\)" 315 | "\\(_\\)" 316 | "\\(" scala-syntax:after-reserved-symbol-underscore-re "\\)")) 317 | 318 | (defconst scala-syntax:reserved-symbols-unsafe-re 319 | ;; reserved symbols. The regexp is unsafe as it does not 320 | ;; check the context. 321 | "\\([:#@\u21D2\u2190]\\|=>?\\|<[:%!?\\-]\\|>:\\)" ) 322 | 323 | (defconst scala-syntax:double-arrow-unsafe-re 324 | "\\(=>\\|\u21D2\\)") 325 | 326 | (defconst scala-syntax:after-reserved-symbol-re 327 | (concat "\\($\\|" scala-syntax:comment-start-re 328 | "\\|[^" scala-syntax:opchar-group "]\\)")) 329 | 330 | (defconst scala-syntax:reserved-symbols-re 331 | ;; reserved symbols and XML starts ('") 407 | 408 | (defconst scala-syntax:for-re 409 | "\\") 410 | 411 | (defconst scala-syntax:class-or-object-re 412 | (regexp-opt '("class" "object") 'words)) 413 | 414 | 415 | ;;;; 416 | ;;;; Character syntax table and related syntax-propertize functions 417 | ;;;; 418 | 419 | ;;; The syntax table relies havily on the syntax-propertize-functions being 420 | ;;; run. Hence this syntax requires at least emacs 24, which introduced 421 | ;;; this new facility. 422 | 423 | (defvar scala-syntax:syntax-table nil 424 | "Syntax table used in `scala-mode' buffers.") 425 | (when (not scala-syntax:syntax-table) 426 | (let ((syntab (make-syntax-table))) 427 | ;; 1. start by reseting the syntax table: only (){}[] are 428 | ;; parentheses, so all others marked as parentheses in the parent 429 | ;; table must be marked as symbols, nothing is a punctuation 430 | ;; unless otherwise stated 431 | (map-char-table 432 | #'(lambda (key value) 433 | (when (or (= (syntax-class value) 4) ; open 434 | (= (syntax-class value) 5) ; close 435 | (= (syntax-class value) 1)) ; punctuation 436 | (modify-syntax-entry key "_" syntab))) 437 | (char-table-parent syntab)) 438 | 439 | ;; Below 'space', everything is either illegal or whitespace. 440 | ;; Consider as whitespace, unless otherwise stated below. 441 | (modify-syntax-entry '(0 . 32) " " syntab) 442 | 443 | ;; The scala parentheses 444 | (modify-syntax-entry ?\( "()" syntab) 445 | (modify-syntax-entry ?\[ "(]" syntab) 446 | (modify-syntax-entry ?\{ "(}" syntab) 447 | (modify-syntax-entry ?\) ")(" syntab) 448 | (modify-syntax-entry ?\] ")[" syntab) 449 | (modify-syntax-entry ?\} "){" syntab) 450 | 451 | ;; _ is upper-case letter, but will be modified to be symbol 452 | ;; constituent when in reserved symbol position by 453 | ;; syntax-propertize-function 454 | (modify-syntax-entry ?\_ "w" syntab) 455 | 456 | ;; by default all opchars are punctuation, but they will be 457 | ;; modified by syntax-propertize-function to be symbol 458 | ;; constituents when a part of varid or capitalid 459 | (dolist (char (mapcar 'identity "!#%&*+/:<=>?@^|~-\u21D2\u2190")) ;; TODO: Sm, So 460 | (modify-syntax-entry char "." syntab)) 461 | 462 | ;; for clarity, the \ is alone here and not in the string above 463 | (modify-syntax-entry ?\\ "." syntab) 464 | 465 | ;; scala strings cannot span lines, so we mark 466 | ;; " as punctuation, but do the real stuff 467 | ;; in syntax-propertize-function for properly 468 | ;; formatted strings. 469 | (modify-syntax-entry ?\" "." syntab) 470 | 471 | ;; backquote is given paired delimiter syntax so that 472 | ;; quoted ids are parsed as one sexp. Fontification 473 | ;; is done separately. 474 | (modify-syntax-entry ?\` "$" syntab) 475 | 476 | ;; ' is considered an expression prefix, since it can 477 | ;; both start a Symbol and is a char quote. It 478 | ;; will be given string syntax by syntax-propertize-function 479 | ;; for properly formatted char literals. 480 | (modify-syntax-entry ?\' "'" syntab) 481 | 482 | ;; punctuation as specified by SLS 483 | (modify-syntax-entry ?\. "." syntab) 484 | (modify-syntax-entry ?\; "." syntab) 485 | (modify-syntax-entry ?\, "." syntab) 486 | 487 | ;; comments 488 | ;; the `n' means that comments can be nested 489 | (modify-syntax-entry ?\/ ". 124b" syntab) 490 | (modify-syntax-entry ?\* ". 23n" syntab) 491 | (modify-syntax-entry ?\n "> b" syntab) 492 | (modify-syntax-entry ?\r "> b" syntab) 493 | 494 | (setq scala-syntax:syntax-table syntab))) 495 | 496 | (defun scala-syntax:propertize-extend-region (start end) 497 | "See syntax-propertize-extend-region-functions" 498 | ;; nothing yet 499 | nil) 500 | 501 | (defmacro scala-syntax:put-syntax-table-property (match-group value) 502 | "Add 'syntax-table entry 'value' to the region marked by the 503 | match-group 'match-group'" 504 | `(put-text-property (match-beginning ,match-group) 505 | (match-end ,match-group) 506 | 'syntax-table 507 | ,value)) 508 | 509 | (defun scala-syntax:propertize-char-and-string-literals (start end) 510 | "Mark start and end of character literals as well as one-line 511 | and multi-line string literals. One-line strings and characters 512 | use syntax class 7 (string quotes), while multi-line strings are 513 | marked with 15 (generic string delimiter). Multi-line string 514 | literals are marked even if they are unbalanced. One-line string 515 | literals have to be balanced to get marked. This means invalid 516 | characters and one-line strings will not be fontified." 517 | 518 | (let* ((string-state (nth 3 (syntax-ppss start))) 519 | (unbalanced-p (eq string-state t))) 520 | 521 | (if (and string-state (not unbalanced-p)) 522 | ;; a normal string is open, let's de-propertize 523 | (remove-text-properties start end '(syntax-table nil)) 524 | (save-excursion 525 | (goto-char start) 526 | ;; close the closing for the unbalanced multi-line literal 527 | (when (and unbalanced-p 528 | (re-search-forward scala-syntax:multiLineStringLiteral-end-re end t)) 529 | (scala-syntax:put-syntax-table-property 1 '(15 . nil))) 530 | ;; match any balanced one-line or multi-line literals 531 | (catch 'break 532 | (while (re-search-forward 533 | scala-syntax:relaxed-char-and-string-literal-re end t) 534 | ;; Expects the following groups: 535 | ;; group 1 = char start, 3 = char end 536 | ;; group 4 = multi-line string start, 6 = end 537 | ;; group 7 = string start, 9 = end 538 | (cond 539 | ((match-beginning 1) 540 | (scala-syntax:put-syntax-table-property 1 '(7 . nil)) 541 | (scala-syntax:put-syntax-table-property 3 '(7 . nil))) 542 | ((match-beginning 4) ;; start of multi-line literal 543 | (scala-syntax:put-syntax-table-property 4 '(15 . nil)) 544 | (if (match-beginning 6) 545 | ;; balanced multi-line 546 | (scala-syntax:put-syntax-table-property 6 '(15 . nil)) 547 | ;; un-balanced multi-line 548 | (throw 'break nil))) 549 | ((or 550 | ;; normal string, content is not empty 551 | (match-beginning 8) 552 | ;; empty string at line end 553 | (= (match-end 9) (line-end-position)) 554 | ;; no " after empty string 555 | (not (= (char-after (match-end 10)) ?\"))) 556 | (when (save-excursion 557 | (goto-char (match-beginning 7)) 558 | ;; really valid? 559 | (looking-at-p scala-syntax:oneLineStringLiteral-re)) 560 | (scala-syntax:put-syntax-table-property 7 '(7 . nil)) 561 | (scala-syntax:put-syntax-table-property 9 '(7 . nil)))) 562 | (t (throw 'break nil))))))))) 563 | 564 | (defun scala-syntax:propertize-shell-preamble (start end) 565 | "Mark a shell preamble (#!) at the beginning of a script as a line comment." 566 | (save-excursion 567 | (goto-char start) 568 | (when (and (= start 1) 569 | (looking-at scala-syntax:preamble-start-re)) 570 | (scala-syntax:put-syntax-table-property 0 '(11 . nil)) 571 | (end-of-line) 572 | (when (re-search-forward "\n" end t) 573 | (scala-syntax:put-syntax-table-property 0 '(12 . nil)))))) 574 | 575 | (defun scala-syntax:propertize-underscore-and-idrest (start end) 576 | "Mark all underscores (_) as symbol constituents (syntax 3) or 577 | upper case letter (syntax 2). Also mark opchars in idrest as 578 | symbol constituents (syntax 3)." 579 | (save-excursion 580 | (goto-char start) 581 | (while (re-search-forward "_" end t) 582 | (let ((match-beg (match-beginning 0)) 583 | (match-end (match-end 0))) 584 | (put-text-property 585 | match-beg match-end 'syntax-table 586 | (if (= match-beg (line-beginning-position)) 587 | (if (looking-at scala-syntax:after-reserved-symbol-underscore-re) 588 | '(3 . nil) ; symbol constituent 589 | '(2 . nil)) ; word syntax 590 | (save-excursion 591 | (goto-char (1- match-beg)) 592 | (if (looking-at scala-syntax:reserved-symbol-underscore-re) 593 | '(3 . nil) ; symbol constituent 594 | ;; check for opchars that should be marked as symbol constituents (3) 595 | (goto-char match-end) 596 | (when (looking-at scala-syntax:op-re) 597 | (scala-syntax:put-syntax-table-property 0 '(3 . nil))) 598 | '(3 . nil))))))))) ;; symbol constituent syntax (3) also for the '_' 599 | 600 | (defun scala-syntax:propertize-special-symbols (start end) 601 | (save-excursion 602 | (goto-char start) 603 | (while (re-search-forward (concat "[" scala-syntax:opchar-group "]" scala-syntax:op-re) end t) 604 | (let ((match-beg (match-beginning 0)) 605 | (match-end (match-end 0)) 606 | (match (match-string 0))) 607 | (unless (or 608 | (string-suffix-p "*/" match) 609 | (member match '(" (point) eol) 660 | (goto-char pos) 661 | (skip-syntax-forward " " eol) 662 | (point)))) 663 | 664 | (defun scala-syntax:looking-at-varid-p (&optional point) 665 | "Return true if looking-at varid, and it is not the start of a 666 | stableId" 667 | (save-excursion 668 | (when point (goto-char point)) 669 | (scala-syntax:skip-forward-ignorable) 670 | (let ((case-fold-search nil)) 671 | (when (looking-at scala-syntax:varid-re) 672 | (save-match-data 673 | (if (or (= (char-after (match-end 0)) ?.) 674 | (looking-at "\\<\\(this\\|super\\)\\>")) 675 | nil 676 | t)))))) 677 | 678 | (defun scala-syntax:looking-at-empty-line-p () 679 | (save-excursion 680 | (or (bolp) 681 | (skip-syntax-forward " >" (1+ (line-end-position)))) 682 | (looking-at scala-syntax:empty-line-re))) 683 | 684 | (defun scala-syntax:looking-at-reserved-symbol (re &optional point) 685 | (interactive) 686 | (unless re (setq re scala-syntax:reserved-symbols-unsafe-re)) 687 | (save-excursion 688 | (when point (goto-char point)) 689 | (scala-syntax:skip-forward-ignorable) 690 | (and (looking-at re) 691 | (goto-char (match-end 0)) 692 | (looking-at-p scala-syntax:after-reserved-symbol-re)))) 693 | 694 | (defun scala-syntax:looking-at-case-p (&optional point) 695 | (save-excursion 696 | (when point (goto-char point)) 697 | (scala-syntax:skip-forward-ignorable) 698 | (and (looking-at scala-syntax:case-re) 699 | (goto-char (match-end 0)) 700 | (scala-syntax:skip-forward-ignorable) 701 | (not (looking-at-p scala-syntax:class-or-object-re))))) 702 | 703 | (defun scala-syntax:looking-back-empty-line-p () 704 | "Return t if the previous line is empty" 705 | (save-excursion 706 | (skip-syntax-backward " " (line-beginning-position)) 707 | (and (bolp) 708 | (forward-line -1) 709 | (looking-at-p scala-syntax:empty-line-re)))) 710 | 711 | (defun scala-syntax:skip-forward-ignorable () 712 | "Moves forward over ignorable whitespace and comments. A 713 | completely empty line is not ignorable and will not be mobed over." 714 | (interactive) 715 | (save-match-data 716 | (while (and (not (scala-syntax:looking-at-empty-line-p)) 717 | (forward-comment 1))) 718 | (skip-syntax-forward " " (line-end-position)))) 719 | 720 | (defun scala-syntax:skip-backward-ignorable () 721 | "Move backwards over ignorable whitespace and comments. A 722 | completely empty line is not ignorable and will not be moved 723 | over. Returns the number of points moved (will be negative)." 724 | (save-match-data 725 | (while (and (not (scala-syntax:looking-back-empty-line-p)) 726 | (forward-comment -1))) 727 | (skip-syntax-backward " " (line-beginning-position)))) 728 | 729 | (defun scala-syntax:looking-at (re) 730 | "Return the end position of the matched re, if the current 731 | position is followed by it, or nil if not. All ignorable comments 732 | and whitespace are skipped before matching." 733 | (save-excursion 734 | (scala-syntax:skip-forward-ignorable) 735 | (looking-at re))) 736 | 737 | (defun scala-syntax:looking-back-token (re &optional max-chars) 738 | "Return the start position of the token matched by re, if the 739 | current position is preceeded by it, or nil if not. All ignorable 740 | comments and whitespace are ignored, i.e. does not search past an 741 | empty line. Expects to be outside of comment. A limit for the 742 | search is calculated based on max-chars. The function won't look 743 | further than max-chars starting after skipping any ignorable." 744 | (save-excursion 745 | ;; skip back all comments 746 | (scala-syntax:skip-backward-ignorable) 747 | (let ((end (point)) 748 | (limit (when max-chars (- (point) max-chars)))) 749 | ;; skip back punctuation or ids (words and related symbols and delimiters) 750 | (if (or (/= 0 (skip-chars-backward scala-syntax:delimiter-group limit)) 751 | (/= 0 (skip-syntax-backward "." limit)) 752 | (/= 0 (skip-syntax-backward "(" limit)) 753 | (/= 0 (skip-syntax-backward ")" limit)) 754 | (/= 0 (skip-syntax-backward "w_'$" limit))) 755 | (if (looking-at re) (point) nil) 756 | nil)))) 757 | 758 | (defun scala-syntax:backward-parameter-groups () 759 | "Move back over all parameter groups to the start of the first 760 | one." 761 | (save-match-data 762 | (while (scala-syntax:looking-back-token "[])]" 1) 763 | (backward-list)))) 764 | 765 | (defun scala-syntax:forward-parameter-groups () 766 | "Move back over all parameter groups to the end of the last 767 | one." 768 | (save-match-data 769 | (while (scala-syntax:looking-at "[[(]") 770 | (forward-list)))) 771 | 772 | (defun scala-syntax:forward-modifiers () 773 | "Move forward over any modifiers." 774 | (save-match-data 775 | (while (scala-syntax:looking-at scala-syntax:modifiers-re) 776 | (scala-syntax:forward-sexp) 777 | (when (scala-syntax:looking-at "[[]") 778 | (forward-list))))) 779 | 780 | (defun scala-syntax:looking-back-else-if-p () 781 | ;; TODO: rewrite using (scala-syntax:if-skipped (scala:syntax:skip-backward-else-if)) 782 | (save-excursion 783 | (if (and (scala-syntax:looking-back-token "\\s)" 1) 784 | (backward-list) 785 | (prog1 (scala-syntax:looking-back-token "if") 786 | (goto-char (match-beginning 0))) 787 | (prog1 (scala-syntax:looking-back-token "else") 788 | (goto-char (match-beginning 0)))) 789 | (point) nil))) 790 | 791 | (defun scala-syntax:newlines-disabled-p (&optional point) 792 | "Return true if newlines are disabled at the current point (or 793 | point 'point') as specified by SLS chapter 1.2" 794 | ;; newlines are disabled if 795 | ;; - in '()' or '[]' 796 | ;; - between 'case' and '=>' 797 | ;; - XML mode (not implemented here) 798 | (unless point (setq point (point))) 799 | (save-excursion 800 | (let* ((state (syntax-ppss point)) 801 | (parenthesisPos (nth 1 state))) 802 | (when parenthesisPos ;; if no parenthesis, then this cannot be a case block either 803 | (goto-char parenthesisPos) 804 | (or 805 | ;; the trivial cases of being inside ( or [ 806 | (= (char-after) ?\() 807 | (= (char-after) ?\[) 808 | ;; else we have to see about case 809 | (progn 810 | (forward-char) 811 | (forward-comment (buffer-size)) 812 | (skip-syntax-forward " >") 813 | (when (looking-at scala-syntax:case-re) 814 | (let ((limit (match-beginning 0))) 815 | (goto-char (or (nth 8 state) point)) 816 | ;; go to the start of => or 'case' 817 | (while (> (point) limit) 818 | (scala-syntax:backward-sexp) 819 | (when (or (looking-at scala-syntax:case-re) 820 | (scala-syntax:looking-at-reserved-symbol 821 | scala-syntax:double-arrow-unsafe-re)) 822 | (setq limit (point)))) 823 | ;; unless we found '=>', check if we found 'case' (but 824 | ;; 'case class' or 'case object') 825 | (unless (scala-syntax:looking-at-reserved-symbol 826 | scala-syntax:double-arrow-unsafe-re) 827 | (scala-syntax:forward-sexp) 828 | 829 | (and (<= (point) point) ;; check that we were inside in the first place 830 | (progn (scala-syntax:skip-forward-ignorable) 831 | (not (looking-at scala-syntax:class-or-object-re))))))))))))) 832 | 833 | (defun scala-syntax:forward-sexp () 834 | "Move forward one scala expression. It can be: parameter list (value or type), 835 | id, reserved symbol, keyword, block, or literal. Punctuation (.,;) 836 | and comments are skipped silently. Position is placed at the 837 | end of the skipped expression." 838 | (interactive) 839 | (syntax-propertize (point-max)) 840 | ;; emacs knows how to properly skip: lists, varid, capitalid, 841 | ;; strings, symbols, chars, quotedid. What we have to handle here is 842 | ;; most of all ids made of op chars 843 | 844 | ;; skip comments, whitespace and scala delimiter chars .,; so we 845 | ;; will be at the start of something interesting 846 | (forward-comment (buffer-size)) 847 | (while (< 0 (+ (skip-syntax-forward " ") 848 | (skip-chars-forward scala-syntax:delimiter-group)))) 849 | 850 | ;; emacs can handle everything but opchars 851 | (when (= (skip-syntax-forward ".") 0) 852 | (goto-char (or (scan-sexps (point) 1) (buffer-end 1))))) 853 | 854 | (defun scala-syntax:forward-token () 855 | "Move forward one scala token, comment word or string word. It 856 | can be: start or end of list (value or type), id, reserved 857 | symbol, keyword, block, or literal. Punctuation (.,;), comment 858 | delimiters and string delimiters are skipped silently. Position 859 | is placed at the end of the skipped token." 860 | (interactive) 861 | (syntax-propertize (point-max)) 862 | (skip-syntax-forward " >" (point-max)) 863 | (when (looking-at 864 | (concat "\\([#@:]\\|" scala-syntax:double-arrow-unsafe-re 865 | "\\|:>\\|<:\\)" scala-syntax:after-reserved-symbol-re)) 866 | (goto-char (match-end 1))) 867 | (let ((syntax (char-syntax (char-after))) 868 | (state (syntax-ppss))) 869 | (cond 870 | ((or (nth 4 state) (nth 3 state)) 871 | ;; inside a string or comment, skip words as normal unless that 872 | ;; would end up outside the string. Then leave point at end of 873 | ;; string delimiter. 874 | (let ((start (nth 8 state)) 875 | (end (save-excursion (forward-word) (point)))) 876 | (if (eq (nth 8 (save-excursion (syntax-ppss end))) start) 877 | (goto-char end) 878 | (while (eq (nth 8 (syntax-ppss)) start) 879 | (forward-char))))) 880 | ;; list start or end 881 | ((or (= syntax ?\)) (= syntax ?\()) (forward-char)) 882 | ;; comment or string start is skipped 883 | ((looking-at "\\(//\\|/\\*+\\|\"\\(\"\"\\)?\\)") 884 | (goto-char (match-end 1))) 885 | ;; otherwise forward-sexp 886 | (t (forward-sexp))))) 887 | 888 | (defun scala-syntax:backward-sexp () 889 | "Move backward one scala expression. It can be: parameter 890 | list (value or type), id, reserved symbol, keyword, block, or 891 | literal. Delimiters (.,;) and comments are skipped 892 | silently. Position is placed at the beginning of the skipped 893 | expression." 894 | (interactive) 895 | (syntax-propertize (point)) 896 | ;; for implementation comments, see scala-syntax:forward-sexp 897 | (forward-comment (- (buffer-size))) 898 | (while (> 0 (+ (skip-syntax-backward " ") 899 | (skip-chars-backward scala-syntax:delimiter-group)))) 900 | 901 | (when (= (skip-syntax-backward ".") 0) 902 | (goto-char (or (scan-sexps (point) -1) (buffer-end -1))) 903 | (backward-prefix-chars))) 904 | 905 | (defun scala-syntax:has-char-before (char end) 906 | (save-excursion 907 | (while (and (< (point) end) 908 | (or (bobp) 909 | (/= (char-before) char))) 910 | (scala-syntax:forward-sexp)) 911 | (when (= (char-before) char) 912 | (scala-syntax:skip-forward-ignorable) 913 | (> end (point))))) 914 | 915 | (defun scala-syntax:search-backward-sexp (re) 916 | "Searches backward sexps until it reaches re, empty line or ;. 917 | If re is found, point is set to beginning of re and the position 918 | is returned, otherwise nil is returned" 919 | (let ((found (save-excursion 920 | (while (not (or (bobp) 921 | (scala-syntax:looking-back-empty-line-p) 922 | (scala-syntax:looking-back-token "[;,]") 923 | (looking-at re))) 924 | (scala-syntax:backward-sexp)) 925 | (if (looking-at re) 926 | (point) 927 | nil)))) 928 | (when found (goto-char found)))) 929 | 930 | (defun scala-syntax:list-p (&optional point) 931 | "Returns the start of the list, if the current point (or point 932 | 'point') is on the first line of a list element > 1, or nil if 933 | not. A list must be either enclosed in parentheses or start with 934 | 'val', 'var' or 'import'." 935 | (save-excursion 936 | ;; first check that the previous line ended with ',' 937 | (when point (goto-char point)) 938 | (scala-syntax:beginning-of-code-line) 939 | (when (scala-syntax:looking-back-token "," 1) 940 | (goto-char (match-beginning 0)) 941 | (let ((parenpoint (nth 1 (syntax-ppss)))) 942 | (if (and parenpoint (or (= (char-after parenpoint) ?\() 943 | (= (char-after parenpoint) ?\[))) 944 | (1+ parenpoint) 945 | (ignore-errors ; catches when we get at parentheses 946 | (while (not (or (bobp) 947 | (looking-at scala-syntax:list-keywords-re) 948 | (scala-syntax:looking-back-empty-line-p) 949 | (scala-syntax:looking-back-token ";"))) 950 | (scala-syntax:backward-sexp))) 951 | (when (looking-at scala-syntax:list-keywords-re) 952 | (goto-char (match-end 0)))))))) 953 | 954 | ;; Functions to help with finding the beginning and end of scala definitions. 955 | 956 | (defconst scala-syntax:modifiers-re 957 | (regexp-opt '("override" "abstract" "final" "sealed" "implicit" "lazy" 958 | "private" "protected" "case") 'words)) 959 | 960 | (defconst scala-syntax:whitespace-delimeted-modifiers-re 961 | (concat "\\(?:" scala-syntax:modifiers-re "\\(?: *\\)" "\\)*")) 962 | 963 | (defconst scala-syntax:definition-words-re 964 | (mapconcat 'regexp-quote '("class" "object" "trait" "val" "var" "def" "type") "\\|")) 965 | 966 | (defun scala-syntax:build-definition-re (words-re) 967 | (concat " *" 968 | scala-syntax:whitespace-delimeted-modifiers-re 969 | words-re 970 | "\\(?: *\\)" 971 | "\\(?2:" 972 | scala-syntax:id-re 973 | "\\)")) 974 | 975 | (defconst scala-syntax:all-definition-re 976 | (scala-syntax:build-definition-re 977 | (concat "\\(?1:" scala-syntax:definition-words-re "\\)\\b"))) 978 | 979 | ;; Functions to help with beginning and end of definitions. 980 | 981 | (defun scala-syntax:backward-sexp-forcing () 982 | (condition-case ex (backward-sexp) ('error (backward-char)))) 983 | 984 | (defun scala-syntax:forward-sexp-or-next-line () 985 | (interactive) 986 | (cond ((looking-at "\n") (next-line) (beginning-of-line)) 987 | (t (forward-sexp)))) 988 | 989 | (defun scala-syntax:beginning-of-definition () 990 | "This function may not work properly with certain types of scala definitions. 991 | For example, no care has been taken to support multiple assignments to vals such as 992 | 993 | val a, b = (1, 2) 994 | " 995 | (interactive) 996 | (let ((found-position 997 | (save-excursion 998 | (scala-syntax:backward-sexp-forcing) 999 | (scala-syntax:movement-function-until-re scala-syntax:all-definition-re 1000 | 'scala-syntax:backward-sexp-forcing)))) 1001 | (when found-position (progn (goto-char found-position) (back-to-indentation))))) 1002 | 1003 | (defun scala-syntax:end-of-definition () 1004 | "This function may not work properly with certain types of scala definitions. 1005 | For example, no care has been taken to support multiple assignments to vals such as 1006 | 1007 | val a, b = (1, 2) 1008 | " 1009 | (interactive) 1010 | (re-search-forward scala-syntax:all-definition-re) 1011 | (scala-syntax:find-brace-equals-or-next) 1012 | (scala-syntax:handle-brace-equals-or-next)) 1013 | 1014 | (defun scala-syntax:find-brace-equals-or-next () 1015 | (scala-syntax:go-to-pos 1016 | (save-excursion 1017 | (scala-syntax:movement-function-until-cond-function 1018 | (lambda () (or (looking-at "[[:space:]]*[{=]") 1019 | (looking-at scala-syntax:all-definition-re))) 1020 | (lambda () (condition-case ex (scala-syntax:forward-sexp-or-next-line) ('error nil))))))) 1021 | 1022 | (defun scala-syntax:handle-brace-equals-or-next () 1023 | (cond ((eobp) nil) 1024 | ((looking-at "[[:space:]]*{") (forward-sexp)) 1025 | ((looking-at "[[:space:]]*=") (scala-syntax:forward-sexp-or-next-line) 1026 | (scala-syntax:handle-brace-equals-or-next)) 1027 | ((looking-at scala-syntax:all-definition-re) nil) 1028 | ((looking-at "[[:space:]]*\n[[:space:]]*}") (skip-syntax-forward "[[:space:]]*\n[[:space:]]*}")) 1029 | (t (scala-syntax:forward-sexp-or-next-line) 1030 | (scala-syntax:handle-brace-equals-or-next)))) 1031 | 1032 | (defun scala-syntax:movement-function-until-re (re movement-function) 1033 | (save-excursion 1034 | (scala-syntax:movement-function-until-cond-function 1035 | (lambda () (looking-at re)) movement-function))) 1036 | 1037 | (defun scala-syntax:movement-function-until-cond-function (cond-function movement-function) 1038 | (let ((last-point (point))) 1039 | (if (not (funcall cond-function)) 1040 | (progn (funcall movement-function) 1041 | (if (equal last-point (point)) nil 1042 | (scala-syntax:movement-function-until-cond-function 1043 | cond-function movement-function))) last-point))) 1044 | 1045 | (defun scala-syntax:go-to-pos (pos) (when pos (goto-char pos))) 1046 | 1047 | (provide 'scala-mode-syntax) 1048 | -------------------------------------------------------------------------------- /scala-mode.el: -------------------------------------------------------------------------------- 1 | ;;; scala-mode.el --- Major mode for editing Scala 2 | 3 | ;; Copyright (c) 2012 Heikki Vesalainen 4 | 5 | ;; Homepage: https://github.com/hvesalai/emacs-scala-mode 6 | ;; Keywords: languages 7 | ;; Package-Version: 0.23 8 | ;; Package-Requires: () 9 | 10 | ;;; Commentary: 11 | ;; 12 | ;;; Code: 13 | 14 | (require 'scala-mode-lib) 15 | (require 'scala-mode-syntax) 16 | (require 'scala-mode-paragraph) 17 | (require 'scala-mode-indent) 18 | (require 'scala-mode-fontlock) 19 | (require 'scala-mode-map) 20 | (require 'scala-mode-imenu) 21 | (require 'scala-mode-prettify-symbols) 22 | 23 | ;; Tested only for emacs 24 24 | (unless (<= 24 emacs-major-version) 25 | (error 26 | (format "The Scala mode has been tested only on Emacs version 24.2 (and not your Emacs version %s.%s)" 27 | emacs-major-version emacs-minor-version))) 28 | 29 | (defgroup scala nil 30 | "A programming mode for the Scala language 2.9" 31 | :group 'languages) 32 | 33 | (defmacro scala-mode:make-local-variables (&rest quoted-names) 34 | (cons 'progn (mapcar #'(lambda (quoted-name) `(make-local-variable ,quoted-name)) quoted-names))) 35 | 36 | (defun scala-mode:find-tag () 37 | "Determine default tag to search for, based on text at point. 38 | If there is no plausible default, return nil." 39 | (let (from to) 40 | (when (and (progn 41 | ;; Look at text around `point'. 42 | (save-excursion 43 | (if (< 0 (skip-chars-backward scala-syntax:opchar-group)) 44 | (if (= (char-before) ?_) 45 | (skip-syntax-backward "w_")) 46 | (skip-syntax-backward "w_")) 47 | (setq from (point))) 48 | (save-excursion 49 | (skip-syntax-forward "w_.") (setq to (point))) 50 | (save-excursion 51 | (ignore-errors (scala-syntax:backward-sexp)) (setq from (max from (point)))) 52 | (save-excursion 53 | (goto-char from) 54 | (ignore-errors (scala-syntax:forward-sexp)) (setq to (min to (point)))) 55 | (> to from)) 56 | (save-excursion 57 | (goto-char from) 58 | (and (looking-at scala-syntax:id-re) 59 | (not (looking-at scala-syntax:keywords-unsafe-re))))) 60 | (buffer-substring-no-properties from to)))) 61 | 62 | 63 | (defun scala-mode:forward-sexp-function (&optional count) 64 | (unless count (setq count 1)) 65 | (if (< count 0) 66 | (dotimes (n (abs count)) 67 | (scala-syntax:backward-sexp)) 68 | (dotimes (n count) 69 | (scala-syntax:forward-sexp)))) 70 | 71 | ;;;###autoload 72 | (defun scala-mode:set-scala-syntax-mode () 73 | "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." 74 | (set-syntax-table scala-syntax:syntax-table) 75 | (scala-mode:make-local-variables 76 | 'syntax-propertize-function 77 | 'parse-sexp-lookup-properties 78 | 'forward-sexp-function) 79 | 80 | (add-hook 'syntax-propertize-extend-region-functions 81 | 'scala-syntax:propertize-extend-region) 82 | (setq syntax-propertize-function 'scala-syntax:propertize 83 | parse-sexp-lookup-properties t 84 | forward-sexp-function 'scala-mode:forward-sexp-function)) 85 | 86 | ;;;###autoload 87 | (defun scala-mode:goto-start-of-code () 88 | "Go to the start of the real code in the file: object, class or trait." 89 | (interactive) 90 | (let* ((case-fold-search nil)) 91 | (search-forward-regexp "\\([[:space:]]+\\|^\\)\\(class\\|object\\|trait\\)" nil t) 92 | (move-beginning-of-line nil))) 93 | 94 | ;;;###autoload 95 | (define-derived-mode scala-mode prog-mode "Scala" 96 | "Major mode for editing scala code. 97 | 98 | When started, runs `scala-mode-hook'. 99 | 100 | \\{scala-mode-map}" 101 | :syntax-table scala-syntax:syntax-table 102 | ; :group 103 | ; :abbrev 104 | 105 | (scala-mode:make-local-variables 106 | 'post-self-insert-hook 107 | 'syntax-propertize-function 108 | 'font-lock-syntactic-face-function 109 | 'font-lock-defaults 110 | 'paragraph-start 111 | 'paragraph-separate 112 | 'parse-sexp-lookup-properties 113 | 'fill-paragraph-function 114 | 'adaptive-fill-function 115 | 'adaptive-fill-first-line-regexp 116 | 'comment-start 117 | 'comment-end 118 | 'comment-start-skip 119 | 'comment-column 120 | 'comment-multi-line 121 | 'forward-sexp-function 122 | 'find-tag-default-function 123 | 'indent-line-function 124 | 'fixup-whitespace 125 | 'delete-indentation 126 | 'indent-tabs-mode 127 | 'imenu-create-index-function 128 | 'beginning-of-defun-function 129 | 'end-of-defun-function) 130 | 131 | (add-hook 'syntax-propertize-extend-region-functions 132 | 'scala-syntax:propertize-extend-region) 133 | (setq scala-mode:debug-messages nil 134 | 135 | syntax-propertize-function 'scala-syntax:propertize 136 | parse-sexp-lookup-properties t 137 | 138 | ;; TODO: font-lock 139 | font-lock-defaults '(scala-font-lock:keywords 140 | nil) 141 | font-lock-syntactic-face-function 'scala-font-lock:syntactic-face-function 142 | 143 | ;; TODO: beginning-of-defun-function, end-of-defun-function 144 | 145 | ;; comments 146 | paragraph-start scala-paragraph:paragraph-start-re 147 | paragraph-separate scala-paragraph:paragraph-separate-re 148 | fill-paragraph-function 'scala-paragraph:fill-paragraph 149 | adaptive-fill-function 'scala-paragraph:fill-function 150 | adaptive-fill-first-line-regexp scala-paragraph:fill-first-line-re 151 | comment-start "// " 152 | comment-end "" 153 | comment-start-skip "\\(//+\\|/\\*+\\)[ \t]*" 154 | comment-column 0 155 | comment-multi-line t 156 | 157 | forward-sexp-function 'scala-mode:forward-sexp-function 158 | find-tag-default-function 'scala-mode:find-tag 159 | indent-line-function 'scala-indent:indent-line 160 | fixup-whitespace 'scala-indent:fixup-whitespace 161 | delete-indentation 'scala-indent:join-line 162 | indent-tabs-mode nil 163 | beginning-of-defun-function #'scala-syntax:beginning-of-definition 164 | end-of-defun-function #'scala-syntax:end-of-definition 165 | imenu-create-index-function #'scala-imenu:create-imenu-index) 166 | (use-local-map scala-mode-map) 167 | ;; add indent functionality to some characters 168 | (scala-mode-map:add-remove-indent-hook) 169 | (scala-mode-map:add-self-insert-hooks)) 170 | 171 | ;; Attach .scala files to the scala-mode 172 | ;;;###autoload 173 | (progn 174 | (add-to-list 'auto-mode-alist 175 | '("\\.\\(scala\\|sbt\\)\\'" . scala-mode)) 176 | (modify-coding-system-alist 'file "\\.\\(scala\\|sbt\\)\\'" 'utf-8)) 177 | 178 | (provide 'scala-mode) 179 | ;;; scala-mode.el ends here 180 | -------------------------------------------------------------------------------- /test/scala-mode-test.el: -------------------------------------------------------------------------------- 1 | (defun smt:test (line exps expf) 2 | "line - line of scala code 3 | exps - expected codes of syntax class 4 | expf - expected font-locks" 5 | (let ((line-length (length line))) 6 | (with-temp-buffer 7 | (insert (format "package ensime 8 | 9 | object Ensime { 10 | %s 11 | }" line)) 12 | (scala-mode) 13 | (font-lock-ensure) 14 | (re-search-backward (regexp-opt `(,line)) nil t) 15 | (let ((end-point (+ (point) line-length)) 16 | (acc-syntax "") 17 | (acc-font "")) 18 | (while (< (point) end-point) 19 | (setq acc-syntax (concat acc-syntax (number-to-string (syntax-class (syntax-after (point)))))) 20 | (setq acc-font (concat acc-font (font-lock-to-string (get-text-property (point) 'face)))) 21 | (forward-char)) 22 | (should (equal acc-syntax exps)) 23 | (should (equal acc-font expf)))))) 24 | 25 | (defun font-lock-to-string (font-lock) 26 | (pcase font-lock 27 | (`nil "-") 28 | ('font-lock-constant-face "C") 29 | ('font-lock-variable-name-face "V") 30 | ('font-lock-keyword-face "K") 31 | ('font-lock-comment-face "O") 32 | ('font-lock-comment-delimiter-face "D") 33 | ('font-lock-doc-face "U") 34 | ('font-lock-type-face "T") 35 | ('font-lock-string-face "S") 36 | (_ "?"))) 37 | 38 | (ert-deftest smt:syntax-class-and-font-lock-test-1 () 39 | (smt:test 40 | "val `tvw xyz/*` = `abc def/*` + 123 /* comment `abc` abc */ + 456" 41 | "22203333333333301033333333333010222011022222220333330222011010222" 42 | "KKK-VVVVVVVVVVV-K---------------CCC-DDDOOOOOOOOOOOOOOOOOOOO---CCC")) 43 | 44 | (ert-deftest smt:syntax-class-and-font-lock-test-2 () 45 | (smt:test 46 | "val |+| = 123" 47 | "2220333010222" 48 | "KKK-VVV-K-CCC")) 49 | 50 | (ert-deftest smt:syntax-class-and-font-lock-test-3 () 51 | (smt:test 52 | "val a_|+| = 123" 53 | "222023333010222" 54 | "KKK-VVVVV-K-CCC")) 55 | 56 | (ert-deftest smt:syntax-class-and-font-lock-test-4 () 57 | (smt:test 58 | "val a = 123 /** hello */" 59 | "222020102220111022222011" 60 | "KKK-V-K-CCC-UUUUUUUUUUUU")) 61 | 62 | (ert-deftest smt:syntax-class-and-font-lock-test-5 () 63 | (smt:test 64 | "val a = hello" 65 | "2220201012212222211221" 66 | "KKK-V-K---------------")) 67 | 68 | (ert-deftest smt:syntax-class-and-font-lock-test-6 () 69 | (smt:test 70 | "// val |--| = 123" 71 | "11022203333010222" 72 | "DDDOOOOOOOOOOOOOO")) 73 | 74 | (ert-deftest smt:syntax-class-and-font-lock-test-7 () 75 | (smt:test 76 | "val xs = 1 :: 2 :: Nil" 77 | "2220220102033020330222" 78 | "KKK-VV-K-C----C----CCC")) 79 | 80 | (ert-deftest smt:syntax-class-and-font-lock-test-8 () 81 | (smt:test 82 | "val xs = 1:: 2 :: Nil" 83 | "222022010211020330222" 84 | "KKK-VV-K-C---C----CCC")) 85 | 86 | (ert-deftest smt:syntax-class-and-font-lock-test-9 () 87 | (smt:test 88 | "val xs = 1 ::2 :: Nil" 89 | "222022010201120330222" 90 | "KKK-VV-K-C---C----CCC")) 91 | 92 | (ert-deftest smt:syntax-class-and-font-lock-test-10 () 93 | (smt:test 94 | "case a :: (2) :: Nil" 95 | "22220203304250330222" 96 | "KKKK-V-TT--C--CC-TTT")) 97 | 98 | (ert-deftest smt:syntax-class-and-font-lock-test-11 () 99 | (smt:test 100 | "abc :<: def" 101 | "22203330222" 102 | "--------KKK")) 103 | 104 | (ert-deftest smt:syntax-class-and-font-lock-test-12 () 105 | (smt:test 106 | "Foo" 107 | "222121" 108 | "CCC-C-")) 109 | 110 | (ert-deftest smt:syntax-class-and-font-lock-test-13 () 111 | (smt:test 112 | "class X[T<:Mapper[T]](t: T){}" 113 | "22222024211222222425542102545" 114 | "KKKKK-T-CKKCCCCCC-C----K-T---")) 115 | 116 | (ert-deftest smt:syntax-class-and-font-lock-test-14 () 117 | (smt:test 118 | "class X[T <: Mapper[T]](t: T){}" 119 | "2222202420330222222425542102545" 120 | "KKKKK-T-C-KK-CCCCCC-C----K-T---")) 121 | 122 | (ert-deftest smt:syntax-class-and-font-lock-test-15 () 123 | (smt:test 124 | "val c = /* hello */ 20" 125 | "2220201011022222011022" 126 | "KKK-V-K-DDDOOOOOOOO-CC")) 127 | 128 | (ert-deftest smt:syntax-class-and-font-lock-test-16 () 129 | (smt:test 130 | "val c = /* hello **/ 20" 131 | "22202010110222220111022" 132 | "KKK-V-K-DDDOOOOOOOOO-CC")) 133 | 134 | (ert-deftest smt:syntax-class-and-font-lock-test-17 () 135 | (smt:test 136 | "val c = /**** hello */ 20" 137 | "2220201011111022222011022" 138 | "KKK-V-K-DDDDDDOOOOOOOO-CC")) 139 | 140 | (ert-deftest smt:syntax-class-and-font-lock-test-18 () 141 | (smt:test 142 | "val c = //**** hello */ 20" 143 | "22202010111111022222011022" 144 | "KKK-V-K-DDOOOOOOOOOOOOOOOO")) 145 | 146 | (ert-deftest smt:syntax-class-and-font-lock-test-19 () 147 | (smt:test 148 | "val c = 1 /////////// big comment" 149 | "222020102011111111111022202222222" 150 | "KKK-V-K-C-DDDDDDDDDDDDOOOOOOOOOOO")) 151 | 152 | (ert-deftest smt:syntax-class-and-font-lock-test-20 () 153 | (smt:test 154 | "val c = s\"result $sum\"" 155 | "2220201027222222012227" 156 | "KKK-V-K--SSSSSSSSVVVVS")) 157 | 158 | (ert-deftest smt:syntax-class-and-font-lock-test-21 () 159 | (smt:test 160 | "val c = s\"$sum-123\"" 161 | "2220201027122212227" 162 | "KKK-V-K--SVVVVSSSSS")) 163 | 164 | (ert-deftest smt:syntax-class-and-font-lock-test-22 () 165 | (smt:test 166 | "val c = s\"${sum.getOrElse(\"\")} - $sum\"" 167 | "22202010271422212222222224775501012227" 168 | "KKK-V-K--SSSSSSSSSSSSSSSSSSSSSSSSSSSSS")) 169 | 170 | (ert-deftest smt:syntax-class-and-font-lock-test-23 () 171 | "Test that `[!%&*+/?\\\\^|~-#:<=>@]\*/` will be treated as punctuation and 172 | _not_ a symbol. Doing so would cause comment strings such as `/* Comment &*/` to 173 | not be recognized as a delimiter, causing the entire file to treated as a 174 | 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" 175 | (smt:test 176 | "/* &*/" 177 | "110111" 178 | "DDDOOO")) 179 | -------------------------------------------------------------------------------- /test/test-helper.el: -------------------------------------------------------------------------------- 1 | (require 'scala-mode) 2 | --------------------------------------------------------------------------------