├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── docs.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── Eask ├── LICENSE ├── Makefile ├── README.md ├── docs └── stylesheets │ └── extra.css ├── icons ├── book-dark.png ├── book-light.png ├── bug-dark.png ├── bug-light.png ├── cancel-dark.png ├── cancel-light.png ├── cascade-dark.png ├── cascade-light.png ├── class.png ├── clean-all-dark.png ├── clean-all-light.png ├── clean-dark.png ├── clean-light.png ├── close-dark.png ├── close-light.png ├── compile-dark.png ├── compile-light.png ├── connect-dark.png ├── connect-light.png ├── debug-stop-dark.png ├── debug-stop-light.png ├── discord-dark.png ├── discord-light.png ├── doctor.png ├── empty-window-dark.png ├── empty-window-light.png ├── enum.png ├── exception-dark.png ├── exception-light.png ├── field.png ├── focus-dark.png ├── focus-light.png ├── github-dark.png ├── github-light.png ├── gitter-dark.png ├── gitter-light.png ├── info-dark.png ├── info-light.png ├── interface.png ├── issue-opened-dark.png ├── issue-opened-light.png ├── location-dark.png ├── location-light.png ├── logo.png ├── method.png ├── object.png ├── sync-dark.png ├── sync-light.png ├── trait.png ├── twitter-dark.png ├── twitter-light.png ├── val.png └── var.png ├── images └── logo.png ├── lsp-metals-protocol.el ├── lsp-metals-treeview.el ├── lsp-metals.el └── mkdocs.yml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior(sample project + file which can be used to reproduce the issue with.) 12 | 13 | **Expected behavior** 14 | A clear and concise description of what you expected to happen. 15 | 16 | **Screenshots** 17 | If applicable, add screenshots to help explain your problem. 18 | 19 | **Logs** 20 | Please include the debug stack trace (if there is an error) and the content of Messages buffer with `lsp-print-io` set to t in case the bug is related to client->server communication. 21 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | with: 15 | persist-credentials: false 16 | 17 | - name: MkDocs 18 | run: | 19 | cp -rf README.md CHANGELOG.md images docs 20 | 21 | docker login docker.pkg.github.com --username $GITHUB_ACTOR --password ${{ secrets.GITHUB_TOKEN }} 22 | docker run --rm -v ${PWD}:/docs docker.pkg.github.com/emacs-lsp/docs-image/docs-image -- build 23 | 24 | - name: Deploy 25 | uses: peaceiris/actions-gh-pages@v3 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: ./site 29 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | continue-on-error: ${{ matrix.experimental }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] 19 | emacs-version: 20 | - 28.2 21 | - 29.4 22 | - 30.1 23 | experimental: [false] 24 | include: 25 | - os: ubuntu-latest 26 | emacs-version: snapshot 27 | experimental: true 28 | - os: macos-latest 29 | emacs-version: snapshot 30 | experimental: true 31 | - os: windows-latest 32 | emacs-version: snapshot 33 | experimental: true 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - uses: jcs090218/setup-emacs@master 39 | with: 40 | version: ${{ matrix.emacs-version }} 41 | 42 | - name: Setup Eask (x64) 43 | uses: emacs-eask/setup-eask@master 44 | if: matrix.os != 'ubuntu-24.04-arm' 45 | with: 46 | version: 'snapshot' 47 | architecture: 'x64' 48 | 49 | - name: Setup Eask (arm64) 50 | uses: emacs-eask/setup-eask@master 51 | if: matrix.os == 'ubuntu-24.04-arm' 52 | with: 53 | version: 'snapshot' 54 | architecture: 'arm64' 55 | 56 | - name: Run tests 57 | run: 'make test' 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eask 2 | /dist 3 | *.elc 4 | tmp/ 5 | flycheck* 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Master 4 | * Add reset-choice command. 5 | * Drop emacs 26 support. 6 | 7 | ## 1.1.0 8 | * Migrate to lsp-protocol. 9 | * Implement metals/windowStateDidChange notification. 10 | * Drop emacs 25 support. 11 | 12 | ## 1.0.0 13 | * Migrate from `lsp-mode`. 14 | -------------------------------------------------------------------------------- /Eask: -------------------------------------------------------------------------------- 1 | ;; -*- mode: eask; lexical-binding: t -*- 2 | 3 | (package "lsp-metals" 4 | "1.0.0" 5 | "Scala Client settings") 6 | 7 | (website-url "https://github.com/emacs-lsp/lsp-metals") 8 | (keywords "languages" "extensions") 9 | 10 | (package-file "lsp-metals.el") 11 | 12 | (files "*.el") 13 | 14 | (script "test" "echo \"Error: no test specified\" && exit 1") 15 | 16 | (source 'gnu) 17 | (source 'melpa) 18 | 19 | (depends-on "emacs" "28.1") 20 | (depends-on "scala-mode") 21 | (depends-on "lsp-mode") 22 | (depends-on "lsp-treemacs") 23 | (depends-on "dap-mode") 24 | (depends-on "dash") 25 | (depends-on "f") 26 | (depends-on "ht") 27 | (depends-on "treemacs") 28 | 29 | (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 30 | -------------------------------------------------------------------------------- /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 | SHELL=/usr/bin/env bash 2 | 3 | EMACS ?= emacs 4 | EASK ?= eask 5 | 6 | test: build compile checkdoc lint 7 | 8 | build: 9 | $(EASK) package 10 | $(EASK) install 11 | 12 | compile: 13 | @echo "Compiling..." 14 | $(EASK) compile 15 | 16 | checkdoc: 17 | $(EASK) lint checkdoc 18 | 19 | lint: 20 | @echo "package linting..." 21 | $(EASK) lint package 22 | 23 | clean: 24 | $(EASK) clean all 25 | 26 | tag: 27 | $(eval TAG := $(filter-out $@,$(MAKECMDGOALS))) 28 | sed -i "s/;; Version: [0-9].[0-9].[0-9]/;; Version: $(TAG)/g" lsp-metals.el 29 | git add lsp-metals.el 30 | git commit -m "Bump lsp-metals: $(TAG)" 31 | git tag $(TAG) 32 | git push origin HEAD 33 | git push origin --tags 34 | 35 | # Allow args to make commands 36 | %: 37 | @: 38 | 39 | .PHONY : test compile checkdoc lint clean tag 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MELPA](https://melpa.org/packages/lsp-metals-badge.svg)](https://melpa.org/#/lsp-metals) 2 | [![MELPA Stable](https://stable.melpa.org/packages/lsp-metals-badge.svg)](https://stable.melpa.org/#/lsp-metals) 3 | metals_logo 4 | 5 | # lsp-metals 6 | 7 | [![CI](https://github.com/emacs-lsp/lsp-metals/workflows/CI/badge.svg)](https://github.com/emacs-lsp/lsp-metals/actions) 8 | [![Gitter](https://badges.gitter.im/emacs-lsp/lsp-mode.svg)](https://gitter.im/emacs-lsp/lsp-mode) 9 | 10 | Emacs Scala IDE using [lsp-mode](https://github.com/emacs-lsp/lsp-mode) to connect to [Metals](https://scalameta.org/metals). 11 | 12 | ## Quickstart 13 | 14 | An example to setup `lsp-metals` using `use-package`: 15 | 16 | ```elisp 17 | (use-package lsp-metals 18 | :ensure t 19 | :custom 20 | ;; You might set metals server options via -J arguments. This might not always work, for instance when 21 | ;; metals is installed using nix. In this case you can use JAVA_TOOL_OPTIONS environment variable. 22 | (lsp-metals-server-args '(;; Metals claims to support range formatting by default but it supports range 23 | ;; formatting of multiline strings only. You might want to disable it so that 24 | ;; emacs can use indentation provided by scala-mode. 25 | "-J-Dmetals.allow-multiline-string-formatting=off" 26 | ;; Enable unicode icons. But be warned that emacs might not render unicode 27 | ;; correctly in all cases. 28 | "-J-Dmetals.icons=unicode")) 29 | ;; In case you want semantic highlighting. This also has to be enabled in lsp-mode using 30 | ;; `lsp-semantic-tokens-enable' variable. Also you might want to disable highlighting of modifiers 31 | ;; setting `lsp-semantic-tokens-apply-modifiers' to `nil' because metals sends `abstract' modifier 32 | ;; which is mapped to `keyword' face. 33 | (lsp-metals-enable-semantic-highlighting t) 34 | :hook (scala-mode . lsp)) 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --md-primary-fg-color: #087E8B; 3 | --md-accent-fg-color: #1B555C; 4 | --md-default-fg-color: #24292E; 5 | --md-default-fg-color--light: #1B555C; 6 | } 7 | 8 | .md-grid { 9 | max-width: 70rem; 10 | } 11 | 12 | .md-typeset a { 13 | color: #087E8B; 14 | } 15 | -------------------------------------------------------------------------------- /icons/book-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/book-dark.png -------------------------------------------------------------------------------- /icons/book-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/book-light.png -------------------------------------------------------------------------------- /icons/bug-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/bug-dark.png -------------------------------------------------------------------------------- /icons/bug-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/bug-light.png -------------------------------------------------------------------------------- /icons/cancel-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/cancel-dark.png -------------------------------------------------------------------------------- /icons/cancel-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/cancel-light.png -------------------------------------------------------------------------------- /icons/cascade-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/cascade-dark.png -------------------------------------------------------------------------------- /icons/cascade-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/cascade-light.png -------------------------------------------------------------------------------- /icons/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/class.png -------------------------------------------------------------------------------- /icons/clean-all-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/clean-all-dark.png -------------------------------------------------------------------------------- /icons/clean-all-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/clean-all-light.png -------------------------------------------------------------------------------- /icons/clean-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/clean-dark.png -------------------------------------------------------------------------------- /icons/clean-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/clean-light.png -------------------------------------------------------------------------------- /icons/close-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/close-dark.png -------------------------------------------------------------------------------- /icons/close-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/close-light.png -------------------------------------------------------------------------------- /icons/compile-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/compile-dark.png -------------------------------------------------------------------------------- /icons/compile-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/compile-light.png -------------------------------------------------------------------------------- /icons/connect-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/connect-dark.png -------------------------------------------------------------------------------- /icons/connect-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/connect-light.png -------------------------------------------------------------------------------- /icons/debug-stop-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/debug-stop-dark.png -------------------------------------------------------------------------------- /icons/debug-stop-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/debug-stop-light.png -------------------------------------------------------------------------------- /icons/discord-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/discord-dark.png -------------------------------------------------------------------------------- /icons/discord-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/discord-light.png -------------------------------------------------------------------------------- /icons/doctor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/doctor.png -------------------------------------------------------------------------------- /icons/empty-window-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/empty-window-dark.png -------------------------------------------------------------------------------- /icons/empty-window-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/empty-window-light.png -------------------------------------------------------------------------------- /icons/enum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/enum.png -------------------------------------------------------------------------------- /icons/exception-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/exception-dark.png -------------------------------------------------------------------------------- /icons/exception-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/exception-light.png -------------------------------------------------------------------------------- /icons/field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/field.png -------------------------------------------------------------------------------- /icons/focus-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/focus-dark.png -------------------------------------------------------------------------------- /icons/focus-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/focus-light.png -------------------------------------------------------------------------------- /icons/github-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/github-dark.png -------------------------------------------------------------------------------- /icons/github-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/github-light.png -------------------------------------------------------------------------------- /icons/gitter-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/gitter-dark.png -------------------------------------------------------------------------------- /icons/gitter-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/gitter-light.png -------------------------------------------------------------------------------- /icons/info-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/info-dark.png -------------------------------------------------------------------------------- /icons/info-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/info-light.png -------------------------------------------------------------------------------- /icons/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/interface.png -------------------------------------------------------------------------------- /icons/issue-opened-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/issue-opened-dark.png -------------------------------------------------------------------------------- /icons/issue-opened-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/issue-opened-light.png -------------------------------------------------------------------------------- /icons/location-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/location-dark.png -------------------------------------------------------------------------------- /icons/location-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/location-light.png -------------------------------------------------------------------------------- /icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/logo.png -------------------------------------------------------------------------------- /icons/method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/method.png -------------------------------------------------------------------------------- /icons/object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/object.png -------------------------------------------------------------------------------- /icons/sync-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/sync-dark.png -------------------------------------------------------------------------------- /icons/sync-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/sync-light.png -------------------------------------------------------------------------------- /icons/trait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/trait.png -------------------------------------------------------------------------------- /icons/twitter-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/twitter-dark.png -------------------------------------------------------------------------------- /icons/twitter-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/twitter-light.png -------------------------------------------------------------------------------- /icons/val.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/val.png -------------------------------------------------------------------------------- /icons/var.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/icons/var.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-lsp/lsp-metals/e1d9d04f3bab7e6e74916054b36ab1a87e831367/images/logo.png -------------------------------------------------------------------------------- /lsp-metals-protocol.el: -------------------------------------------------------------------------------- 1 | ;;; lsp-metals-protocol.el --- LSP Metals custom protocol definitions -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2020 Evgeny Kurnevsky 4 | 5 | ;; Version: 1.0.0 6 | ;; Author: Evgeny Kurnevsky 7 | ;; Keywords: languages, extensions 8 | ;; URL: https://github.com/emacs-lsp/lsp-metals 9 | 10 | ;; This program is free software; you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;; lsp-metals custom protocol definitions 26 | 27 | ;;; Code: 28 | 29 | (require 'lsp-protocol) 30 | 31 | (lsp-interface 32 | (PublishDecorationsParams (:uri :options) nil) 33 | (MetalsStatusParams (:text) (:show :hide :tooltip :command)) 34 | (MetalsQuickPickParams (:items) (:matchOnDescription :matchOnDetail :placeHolder :ignoreFocusOut)) 35 | (MetalsQuickPickItem (:id :label) (:description :detail :alwaysShow)) 36 | (MetalsInputBoxParams (:prompt) (:value :placeHolder :password :ignoreFocusOut)) 37 | (DecorationOptions (:range :renderOptions) (:hoverMessage)) 38 | (ThemableDecorationInstanceRenderOption nil (:after)) 39 | (ThemableDecorationAttachmentRenderOptions nil (:contentText :color :fontStyle)) 40 | (DebugSession (:name :uri) nil) 41 | (TreeViewNode (:viewId :label) (:nodeUri :command :icon :tooltip :collapseState)) 42 | (TreeViewCommand (:title :command) (:tooltip :arguments)) 43 | (TreeViewChildrenResult (:nodes) nil) 44 | (TreeViewDidChangeParams (:nodes) nil) 45 | (TreeViewRevealResult (:viewId :uriChain) nil)) 46 | 47 | (provide 'lsp-metals-protocol) 48 | ;;; lsp-metals-protocol.el ends here 49 | -------------------------------------------------------------------------------- /lsp-metals-treeview.el: -------------------------------------------------------------------------------- 1 | ;;; lsp-metals-treeview.el --- LSP Scala Metals Treeview -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2019 Darren Syzling , Evgeny Kurnevsky 4 | 5 | ;; Version: 1.0.0 6 | ;; Author: Darren Syzling 7 | ;; Evgeny Kurnevsky 8 | ;; Keywords: languages, extensions 9 | ;; URL: https://github.com/emacs-lsp/lsp-metals 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; lsp-metals treeview ui client - handles a treeview for project tree, 27 | ;; compilation tree etc. 28 | ;; See the Metals treeview provider spec for further details: 29 | ;; https://scalameta.org/metals/docs/editors/tree-view-protocol.html 30 | ;; 31 | ;; Current treeview interaction is: 32 | ;; tab key to expand/collapse nodes which is default treemacs behaviour. 33 | ;; ret will execute the command associated with the current node via Metals. 34 | ;; Note you need -Dmetals.execute-client-command enabled for this to work 35 | ;; and may require you to upgrade Metals post 0.7 for Emacs. 36 | ;; 37 | ;; mouse left double click - will execute the command on a node. 38 | ;; 39 | ;; Metals allows classes to be expanded and the action executed on the same 40 | ;; node - metals.goto (goto definition) we can't therefore use return to 41 | ;; expand/collapse and execute actions. The existing implementation provides 42 | ;; a simple starting point to test the treeview with metals and we can evolve 43 | ;; to a Hydra like interface to provide a richer keyboard experience in future. 44 | ;; 45 | 46 | ;;; Code: 47 | 48 | (require 'ht) 49 | (require 'json) 50 | (require 'dash) 51 | (require 'f) 52 | (require 'seq) 53 | (require 'pcase) 54 | (require 'treemacs-treelib) 55 | (require 'lsp-mode) 56 | (require 'lsp-treemacs) 57 | (require 'lsp-metals-protocol) 58 | 59 | (dolist (method '("metals/treeViewChildren" 60 | "metals/treeViewVisibilityDidChange" 61 | "metals/treeViewNodeCollapseDidChange" 62 | "metals/treeViewReveal")) 63 | (add-to-list 'lsp-method-requirements 64 | `(,method :check-command (lambda (workspace) 65 | (eq (lsp--workspace-server-id workspace) 'metals))) 66 | t)) 67 | 68 | (defcustom lsp-metals-treeview-logging nil 69 | "If non nil log treeview trace/debug messages to the `lsp-log' for debugging." 70 | :group 'lsp-metals-treeview 71 | :type 'boolean) 72 | 73 | (defcustom lsp-metals-treeview-workspace-switch-delay 0.2 74 | "Delay in seconds for switching treeview between workspaces. 75 | The delay occurs after `buffer-list-update-hook' is called before 76 | triggering a switch of treeview when navigating between buffers in 77 | different workspaces." 78 | :group 'lsp-metals-treeview 79 | :type 'float) 80 | 81 | (defcustom lsp-metals-treeview-views '("metalsPackages" 82 | "metalsBuild" 83 | "metalsCompile" 84 | "metalsHelp") 85 | "List of views to display." 86 | :group 'lsp-metals-treeview 87 | :type '(repeat string) 88 | :package-version '(lsp-metals . "1.2")) 89 | 90 | (defcustom lsp-metals-treeview-theme 91 | (pcase (frame-parameter nil 'background-mode) 92 | ('dark 'Metals-dark) 93 | (_ 'Metals-light)) 94 | "The theme for treeview icons." 95 | :group 'lsp-metals-treeview 96 | :type '(choice 97 | (const :tag "Light" Metals-light) 98 | (const :tag "Dark" Metals-dark)) 99 | :package-version '(lsp-metals . "1.2")) 100 | 101 | (defvar-local lsp-metals-treeview--current-workspace nil 102 | "Associate lsp workspace with the metals treeview buffer. 103 | Needed to make async calls to the lsp server from treemacs buffers.") 104 | 105 | (defvar-local lsp-metals-treeview--view-id nil 106 | "Metals treeview id associated with the treeview buffer.") 107 | 108 | (defconst lsp-metals-treeview--icon-dir "icons" 109 | "Directory containing Metals treeview icon theme. 110 | Directory is relative to lsp-metals.") 111 | 112 | (defconst lsp-metals-treeview--buffer-prefix " *Metals" 113 | "Prefix for all Metals treeview buffers. 114 | Note the space prefix which hides the buffers within the buffer 115 | list in Emacs.") 116 | 117 | ;; Root directory of our lisp files so that we can find icons 118 | ;; relative to installation. 119 | (defconst lsp-metals-treeview--dir 120 | (-> (if load-file-name 121 | (file-name-directory load-file-name) 122 | default-directory) 123 | (expand-file-name)) 124 | "The directory lsp-metals-treeview.el is stored in.") 125 | 126 | (defconst lsp-metals-treeview--buffers-key "metals-treeview-buffers" 127 | "Metadata key to store treeview buffers within workspace.") 128 | 129 | (defconst lsp-metals-treeview--metals-server-id 'metals 130 | "Server id metals lsp client should be registered from within lsp-metals.") 131 | 132 | ;; 133 | ;; Treemacs doesn't support a unique key - :-key-form isn't actually defined as 134 | ;; being unique and you cannot search by this key - only by path. Since Metals 135 | ;; sends us nodeUri unique keys we need someway of mapping nodeUris to 136 | ;; treemacs paths - so we can use treemacs-find-node. 137 | ;; 138 | (defvar-local lsp-metals-treeview--treemacs-node-index (make-hash-table :test 'equal)) 139 | 140 | (defvar lsp-metals-treeview--active-view-workspace nil 141 | "Workspace associated with the active treeview instance. 142 | When the treeview is displayed and visible this variable 143 | will hold the workspace.") 144 | 145 | (defun lsp-metals-treeview--position (slot) 146 | "Side window position of Metals treeview with the given SLOT. 147 | Uses defaults for treemacs position and width." 148 | `((side . ,treemacs-position) 149 | (slot . ,slot) 150 | (window-width . ,treemacs-width))) 151 | 152 | (defun lsp-metals-treeview--buffer-changed () 153 | "Active buffer has changed check if current treeview is valid. 154 | When the buffer is switched check to see if a treeview 155 | is currently being displayed and whether we need to show 156 | an alternative workspace's treeview." 157 | (with-current-buffer (current-buffer) 158 | (when (and (eq major-mode 'scala-mode) 159 | (lsp-find-workspace lsp-metals-treeview--metals-server-id nil) 160 | lsp-metals-treeview--active-view-workspace 161 | (not (member lsp-metals-treeview--active-view-workspace 162 | (lsp-workspaces)))) 163 | 164 | ;; hide current treeview and show new window associated with 165 | ;; the current workspace of file in buffer. 166 | (lsp-metals-treeview--hide-window lsp-metals-treeview--active-view-workspace) 167 | (lsp-metals-treeview--show-window (car (lsp-workspaces)))))) 168 | 169 | (defun lsp-metals-treeview--buffer-list-update () 170 | "Active buffer changed check if treeview needs to be changed. 171 | If the user switches buffers in different workspaces we need to 172 | swap the treeview to show the new workspace's treeview." 173 | (run-with-idle-timer lsp-metals-treeview-workspace-switch-delay 174 | nil 175 | #'lsp-metals-treeview--buffer-changed)) 176 | 177 | (defun lsp-metals-treeview--add-workspace-switch-hook () 178 | "Add hook to swap treeviews between workspace buffers. 179 | Add a `buffer-list-update-hook' to hide/show the active treeview 180 | - if currently displayed - when the user switches buffers that are 181 | within another workspace." 182 | (add-hook 'buffer-list-update-hook 183 | #'lsp-metals-treeview--buffer-list-update)) 184 | 185 | (defun lsp-metals-treeview--remove-workspace-switch-hook () 186 | "Remove the hook to swap treeviews between workspace buffers. 187 | Remove the `buffer-list-update-hook' which deals with switching 188 | treeview when the active buffer switches between different 189 | workspaces." 190 | (remove-hook 'buffer-list-update-hook 191 | #'lsp-metals-treeview--buffer-list-update)) 192 | 193 | (defun lsp-metals-treeview--log (format &rest args) 194 | "Log treeview tracing/debug messages to the lsp log. 195 | Use the FORMAT formatting string and ARGS arguments to format 196 | the message and parameters." 197 | (when lsp-metals-treeview-logging 198 | (apply #'lsp-log format args))) 199 | 200 | (defun lsp-metals-treeview--add-buffer (workspace view-id buffer) 201 | "Add BUFFER with key VIEW-ID to treeview buffers stored in the WORKSPACE." 202 | (push (cons view-id buffer) (ht-get (lsp--workspace-metadata workspace) lsp-metals-treeview--buffers-key))) 203 | 204 | (defun lsp-metals-treeview--remove-buffers (workspace) 205 | "Clear treeview buffers stored in the WORKSPACE." 206 | (setf (ht-get (lsp--workspace-metadata workspace) lsp-metals-treeview--buffers-key) nil)) 207 | 208 | (defun lsp-metals-treeview--get-buffers (workspace) 209 | "Return buffers stored in the WORKSPACE." 210 | (-map #'cdr (ht-get (lsp--workspace-metadata workspace) lsp-metals-treeview--buffers-key))) 211 | 212 | (defun lsp-metals-treeview--get-buffer-by-id (workspace view-id) 213 | "Return buffer with key VIEW-ID stored in the WORKSPACE." 214 | (alist-get view-id (ht-get (lsp--workspace-metadata workspace) lsp-metals-treeview--buffers-key) nil nil #'equal)) 215 | 216 | (defun lsp-metals-treeview--view-name (view-id) 217 | "Return a view name from the VIEW-ID." 218 | (replace-regexp-in-string "metals" "" view-id)) 219 | 220 | (defun lsp-metals-treeview--buffer-name (workspace view-id) 221 | "Return buffer name of the treeview from WORKSPACE and VIEW-ID." 222 | (format "%s %s %s*" 223 | lsp-metals-treeview--buffer-prefix 224 | (lsp-metals-treeview--view-name view-id) 225 | (file-name-nondirectory 226 | (directory-file-name (lsp--workspace-root workspace))))) 227 | 228 | (defun lsp-metals-treeview--hide-window (&optional workspace) 229 | "Hide the Metals treeview window associated with the WORKSPACE. 230 | The window will be deleted but the treeview buffers will still 231 | be live in the background." 232 | (interactive) 233 | (-when-let (cur-workspace (or workspace lsp-metals-treeview--current-workspace)) 234 | (-map (lambda (buffer) 235 | ;; Notify Metals that visibility of the view has changed 236 | (with-current-buffer buffer 237 | (lsp-metals-treeview--send-visibility-did-change 238 | cur-workspace lsp-metals-treeview--view-id nil)) 239 | (delete-window (get-buffer-window buffer))) 240 | (lsp-metals-treeview--get-buffers cur-workspace)) 241 | (setq lsp-metals-treeview--active-view-workspace nil) 242 | ;; Only keep this treeview switching hook live when absolutely necessary 243 | (lsp-metals-treeview--remove-workspace-switch-hook))) 244 | 245 | (defun lsp-metals-treeview--get-visible-buffers () 246 | "Retrieve buffers associated with the current selected frame. 247 | Check to see if any of these buffers are metals treeview buffers and 248 | if so return the buffers." 249 | ;; retrieve any treeview buffers that are visible 250 | (->> (window-list (selected-frame)) 251 | (-keep (lambda (window) 252 | (let ((buffer (window-buffer window))) 253 | (when (s-starts-with? lsp-metals-treeview--buffer-prefix 254 | (buffer-name buffer)) 255 | buffer)))))) 256 | 257 | (defun lsp-metals-treeview--visible? (workspace) 258 | "Is the metals treeview associated with the WORKSPACE currently visible?" 259 | (-when-let* ((visible-buffers (lsp-metals-treeview--get-visible-buffers)) 260 | (workspace-buffers (lsp-metals-treeview--get-buffers workspace))) 261 | (equal visible-buffers workspace-buffers))) 262 | 263 | (defun lsp-metals-treeview--exists? (workspace) 264 | "Does a Metals Treeview exist for the WORKSPACE? 265 | The treeview may not be visible but still exists in the background." 266 | (-when-let (buffers (lsp-metals-treeview--get-buffers workspace)) 267 | (-all-p 'buffer-live-p buffers))) 268 | 269 | (defun lsp-metals-treeview--hidden? (workspace) 270 | "Does the metals treeview associated with WORKSPACE exist but not visible?" 271 | (and (lsp-metals-treeview--exists? workspace) 272 | (not (lsp-metals-treeview--visible? workspace)))) 273 | 274 | (defun lsp-metals-treeview--get-visibility (workspace) 275 | "Return visibility status of metals treeview associated with WORKSPACE. 276 | Return `visible', `hidden', `none' depending on state of treeview." 277 | (cond 278 | ((lsp-metals-treeview--visible? workspace) 'visible) 279 | ((lsp-metals-treeview--exists? workspace) 'hidden) 280 | (t 'none))) 281 | 282 | (defun lsp-metals-treeview--show-window (workspace &optional select-window?) 283 | "Show metals treeview window associated with WORKSPACE. 284 | Optionally select the window based on SELECT-WINDOW? being True. 285 | If the treeview window is hidden or not visible (not created) 286 | then show the window." 287 | (let ((visibility (lsp-metals-treeview--get-visibility workspace))) 288 | (when (or (eq 'hidden visibility) (eq 'none visibility)) 289 | (lsp-metals-treeview--show-views workspace 0 select-window?)))) 290 | 291 | (defun lsp-metals-treeview--delete-window (&optional workspace workspace-shutdown?) 292 | "Delete the metals treeview window associated with the WORKSPACE. 293 | If WORKSPACE is not provided the current treeview buffer local variable 294 | WORKSPACE will be used. This function is also called from an lsp hook 295 | which will be called when the workspace is shutdown - in this case we 296 | won't notify Metals of view being hidden if WORKSPACE-SHUTDOWN? is 297 | t." 298 | (let ((cur-workspace (or workspace lsp-metals-treeview--current-workspace))) 299 | (-map (lambda (treeview-buffer) 300 | (switch-to-buffer treeview-buffer) 301 | ;; Tell metals the view is no longer visible but only if 302 | ;; the workspace isn't in the process of shutting down or 303 | ;; not initialised. 304 | (when (and lsp-metals-treeview--view-id 305 | (not workspace-shutdown?) 306 | (equal 'initialized (lsp--workspace-status cur-workspace))) 307 | (lsp-metals-treeview--send-visibility-did-change 308 | lsp-metals-treeview--current-workspace 309 | lsp-metals-treeview--view-id 310 | nil)) 311 | (kill-buffer treeview-buffer)) 312 | (lsp-metals-treeview--get-buffers cur-workspace)) 313 | (lsp-metals-treeview--remove-buffers cur-workspace) 314 | (setq lsp-metals-treeview--active-view-workspace nil) 315 | ;; Only keep this treeview switching hook live when absolutely necessary. 316 | (lsp-metals-treeview--remove-workspace-switch-hook) 317 | (remove-hook 'lsp-after-uninitialized-functions #'lsp-metals-treeview--delete-window))) 318 | 319 | (defun lsp-metals-treeview--on-workspace-shutdown (workspace) 320 | "Handler for lsp WORKSPACE shutdown. 321 | Ensure we close our treeview windows/buffers. Under this scenario we 322 | shouldn't contact Metals to update view visibility status, so we pass 323 | through workspace-shutdown true so that the `delete-window' function has 324 | the context of the window closing." 325 | (lsp-metals-treeview--delete-window workspace t)) 326 | 327 | ;; 328 | ;; Minor mode for metals treeview window and keymap to control 329 | ;; functions such as closing window. 330 | ;; 331 | 332 | (defvar lsp-metals-treeview-mode-map 333 | (let ((m (make-sparse-keymap))) 334 | (define-key m (kbd "q") #'lsp-metals-treeview--hide-window) 335 | m) 336 | "Keymap for `lsp-metals-treeview-mode'.") 337 | 338 | (define-minor-mode lsp-metals-treeview-mode 339 | "LSP Metals Treeview minor mode." 340 | :keymap lsp-metals-treeview-mode-map 341 | :group 'lsp-metals-treeview) 342 | 343 | 344 | (defun lsp-metals-treeview--show-view (workspace view-id position) 345 | "Show or create the side window and treeview. 346 | The window will be created for the Metals VIEW-ID within the current WORKSPACE. 347 | The window will be positioned as a side window by POSITION and is of the 348 | form `((side left))'." 349 | (let ((buffer-name (lsp-metals-treeview--buffer-name workspace 350 | (lsp-metals-treeview--view-name view-id)))) 351 | ;; When opening or refreshing the view do temporarily switch focus but restore 352 | ;; after window has been created. User will then not be diverted away from their 353 | ;; current focus.. 354 | (-if-let (buffer (get-buffer buffer-name)) 355 | (with-selected-window (display-buffer-in-side-window buffer position) 356 | ;; update the root of the tree with the view. 357 | (lsp-metals-treeview--log "Refreshing tree %s" view-id) 358 | (treemacs-update-node '(metals-root) t) 359 | (set-window-dedicated-p (selected-window) t) 360 | ;; When closing other windows after splitting, prevent our treeview closing. 361 | (set-window-parameter (selected-window) 'no-delete-other-windows t)) 362 | 363 | (let* ((buffer (get-buffer-create buffer-name)) 364 | (window (display-buffer-in-side-window buffer position))) 365 | 366 | (with-lsp-workspace workspace 367 | (with-selected-window window 368 | (set-window-dedicated-p window t) 369 | (treemacs-initialize metals-root 370 | :and-do (progn 371 | (setq-local lsp-metals-treeview--current-workspace workspace) 372 | (setq-local lsp-metals-treeview--view-id view-id))) 373 | 374 | (setq-local mode-line-format (concat 375 | (treemacs-get-icon-value 'root nil "Metals") 376 | (lsp-metals-treeview--view-name view-id))) 377 | 378 | ;; Add buffer to list of treeview buffers associated with this workspace. 379 | (lsp-metals-treeview--add-buffer workspace view-id buffer) 380 | 381 | ;; When closing other windows after splitting, prevent our treeview closing. 382 | (set-window-parameter window 'no-delete-other-windows t) 383 | (lsp-metals-treeview-mode 1) 384 | 385 | (setq-local treemacs-default-visit-action 'treemacs-RET-action) 386 | (setq-local treemacs-doubleclick-actions-config '((treemacs-metals-node-open . treemacs-RET-action) 387 | (treemacs-metals-node-closed . treemacs-RET-action))))))))) 388 | 389 | 390 | (defun lsp-metals-treeview--display-views (workspace views slot) 391 | "Recursive function to display each view in VIEWS. 392 | The views will be associated with the WORKSPACE and displayed in the side 393 | window based based on an increasing SLOT number position." 394 | (when-let ((view-id (car views))) 395 | (lsp-metals-treeview--show-view workspace 396 | view-id 397 | (lsp-metals-treeview--position slot)) 398 | (lsp-metals-treeview--send-visibility-did-change workspace view-id t) 399 | (lsp-metals-treeview--display-views workspace (cdr views) (+ 1 slot)))) 400 | 401 | (defun lsp-metals-treeview--select-window (workspace) 402 | "Switch focus to the treeview window in the given WORKSPACE. 403 | Select the first view/buffer in the treeview window." 404 | (select-window (get-buffer-window 405 | (car (lsp-metals-treeview--get-buffers workspace))))) 406 | 407 | (defun lsp-metals-treeview--show-views (workspace slot &optional select-treeview-window) 408 | "Display each metals view in our sidebar treeview window. 409 | Views are displayed for this WORKSPACE. SLOT is a numeric position starting 410 | from 0 where the treeview will be positioned relative to the others. when 411 | SELECT-TREEVIEW-WINDOW is t the treeview window will be selected and have 412 | focus." 413 | (lsp-metals-treeview--display-views workspace lsp-metals-treeview-views slot) 414 | 415 | (when select-treeview-window 416 | (lsp-metals-treeview--select-window workspace)) 417 | 418 | (setq lsp-metals-treeview--active-view-workspace workspace) 419 | 420 | ;; When user switches between files in workspaces automatically switch 421 | ;; the treeview to the appropriate one. 422 | (lsp-metals-treeview--add-workspace-switch-hook) 423 | 424 | ;; Add hook to close our treeview when the workspace is shutdown. 425 | (add-hook 'lsp-after-uninitialized-functions #'lsp-metals-treeview--on-workspace-shutdown)) 426 | 427 | 428 | (defun lsp-metals-treeview--cache-add-nodes (metals-nodes current-treemacs-node) 429 | "Build an index of treemacs nodes nodeUri -> treemacs path. 430 | We can use this to find nodes within the tree based on nodeUri which 431 | Metals will send us. METALS-NODES contains a list of new nodes added to 432 | the tree to be displayed, CURRENT-TREEMACS-NODE is the paren to the new 433 | nodes." 434 | (let ((parent-path (treemacs-button-get current-treemacs-node :path))) 435 | (-map (lambda (metals-node) 436 | (-when-let ((&TreeViewNode :node-uri?) metals-node) 437 | (ht-set lsp-metals-treeview--treemacs-node-index 438 | node-uri? 439 | (append parent-path (list node-uri?))))) 440 | metals-nodes))) 441 | 442 | (defun lsp-metals-treeview--find-node (node-uri) 443 | "Find treemacs node based on NODE-URI via our local index. 444 | If the node cannot be found in the tree make sure we cleanup the cache 445 | and remove it." 446 | (-if-let* ((path (ht-get lsp-metals-treeview--treemacs-node-index node-uri)) 447 | (found-node (treemacs-find-node path))) 448 | found-node 449 | ;; Otherwise remove node form cache it's no longer in the tree. 450 | (ht-remove lsp-metals-treeview--treemacs-node-index node-uri) 451 | nil)) 452 | 453 | 454 | (lsp-defun lsp-metals-treeview--did-change (workspace (&TreeViewDidChangeParams :nodes)) 455 | "Metals treeview changed notification. 456 | Nodes that have been changed will be provided within the 457 | PARAMS message with their viewIds. WORKSPACE will be the current 458 | workspace of the project." 459 | (lsp-metals-treeview--log "In lsp-metals-treeview--did-change %s\n%s" 460 | (lsp--workspace-root workspace) 461 | (lsp--json-serialize nodes)) 462 | 463 | (mapc (lambda (node) 464 | (-when-let* (((&TreeViewNode :view-id :label) node) 465 | (buffer (lsp-metals-treeview--get-buffer-by-id workspace view-id))) 466 | (with-current-buffer buffer 467 | (-if-let ((&TreeViewNode :node-uri?) node) 468 | (-if-let (tree-node (lsp-metals-treeview--find-node node-uri?)) 469 | (progn 470 | ;; replace label in our node attached to the tree node. 471 | (lsp:set-tree-view-node-label (treemacs-button-get tree-node :node) label) 472 | 473 | ;; Currently the only way to re-render the label of an item is 474 | ;; for the parent to call render-node on its children. So 475 | ;; we update the parent of the node we're changing. 476 | ;; An enhancement to treemacs is in the works where the label 477 | ;; can be updated directly. 478 | (treemacs-update-node (treemacs-button-get tree-node :parent) nil)) 479 | (lsp-metals-treeview--log "Failed to find node in treeview")) 480 | (treemacs-update-node '(metals-root) t))))) 481 | nodes)) 482 | 483 | 484 | (defun lsp-metals-treeview--send-treeview-children (view-id &optional node-uri) 485 | "Query children in the view given by VIEW-ID. 486 | An optional NODE-URI can be used to query children of a specific node 487 | within the view. This call is synchronous and will return the response 488 | from the call to metas/treeViewChildren. Under the hood LSP-REQUEST will 489 | send the request asynchronously and wait for the response." 490 | (lsp-metals-treeview--log "Sending metals/treeViewChildren") 491 | (lsp-request "metals/treeViewChildren" 492 | (append `(:viewId ,view-id) 493 | (if node-uri `(:nodeUri ,node-uri) nil)))) 494 | 495 | 496 | (defun lsp-metals-treeview--send-visibility-did-change (workspace view-id visible?) 497 | "Send metals/treeViewVisibilityDidChange when views are shown/hidden. 498 | We need to keep Metals informed when views are shown/hidden so it can optimise 499 | the notification messages it sends us. WORKSPACE is the current lsp workspace, 500 | VIEW-ID is the view for which the visibility has changed described by the 501 | boolean value VISIBLE - t or nil." 502 | (lsp-metals-treeview--log "view visibility changed %s %s" view-id visible?) 503 | (let ((params (list :viewId view-id 504 | :visible visible?))) 505 | (with-lsp-workspace workspace 506 | (lsp-request-async "metals/treeViewVisibilityDidChange" params 507 | (lambda (response) 508 | (lsp-metals-treeview--log (lsp--json-serialize response))) 509 | :mode 'detached)))) 510 | 511 | (defun lsp-metals-treeview--send-node-collapse-did-change (workspace view-id node-uri collapsed?) 512 | "Send metals/treeViewNodeCollapseDidChange when a node has collapsed/expanded. 513 | WORKSPACE is the current workspace, VIEW-ID the id of the view containing the 514 | node with NODE-URI which has been collapsed or expanded based on the boolean 515 | COLLAPSED? either t or nil." 516 | (lsp-metals-treeview--log "sending metals/treeViewNodeCollapseDidChange viewId %s nodeUri %s collapsed? %s" 517 | view-id node-uri collapsed?) 518 | (let ((params (list :viewId view-id 519 | :nodeUri node-uri 520 | :collapsed (if collapsed? 521 | t 522 | json-false)))) 523 | (with-lsp-workspace workspace 524 | (lsp-request-async "metals/treeViewNodeCollapseDidChange" params 525 | (lambda (response) 526 | (lsp-metals-treeview--log "metals/treeViewNodeCollapseDidChange response:\n %s" 527 | (lsp--json-serialize response))) 528 | :mode 'detached)))) 529 | 530 | (defun lsp-metals-treeview--get-children (view-id &optional node-uri) 531 | "Retrieve children of the view given by the VIEW-ID and optionally the node. 532 | Children of the view can be returned and optionally by specifying a NODE-URI 533 | this function will return the node's children. Without a NODE-URI the top 534 | level child items will be returned for the view. Returns a list of nodes 535 | with values converted from json to hash tables." 536 | (with-lsp-workspace lsp-metals-treeview--current-workspace 537 | ;; return nodes element and convert from vector to list. 538 | (-let* ((current-tree-node (treemacs-node-at-point)) 539 | ((&TreeViewChildrenResult :nodes) (lsp-metals-treeview--send-treeview-children view-id node-uri)) 540 | (children (append nodes nil))) 541 | (lsp-metals-treeview--log "Children returned:\n%s" (lsp--json-serialize nodes)) 542 | (when (and (-non-nil children) current-tree-node) 543 | (lsp-metals-treeview--cache-add-nodes children current-tree-node)) 544 | children))) 545 | 546 | ;; 547 | ;; UI tree view using treemacs 548 | ;; 549 | 550 | (defun lsp-metals-treeview--xpm-pad (width) 551 | "Return an XPM image with the specified WIDTH." 552 | (format "/* XPM */ 553 | static char * pad_xpm[] = { 554 | \"%d 1 1 1\", 555 | \". c none\", 556 | \"%s\" 557 | }; 558 | " width (make-string width ?.))) 559 | 560 | (defun lsp-metals-treeview--icon (metals-node open-form?) 561 | "Return icon based on METALS-NODE. 562 | The icon will depend on the individual METALS-NODE and whether the 563 | node is expanding based on OPEN-FORM? being True. Check if icon matches 564 | one of our icons for the Metals theme and if not display a standard +/- 565 | if this is an expandable node. If the node isn't expandable for now 566 | do not show an icon." 567 | (-if-let* (((&TreeViewNode :icon?) metals-node) 568 | (icon (treemacs-get-icon-value icon? nil lsp-metals-treeview-theme))) 569 | icon 570 | (if (lsp-get metals-node :collapseState) 571 | (treemacs-get-icon-value 572 | (if open-form? 'expanded 'collapsed) 573 | nil 574 | lsp-treemacs-theme) 575 | 576 | ;; leaf node without an icon 577 | (concat (propertize " " 'display (create-image 578 | (lsp-metals-treeview--xpm-pad treemacs--icon-size) 579 | 'xpm t 580 | :mask 'heuristic)) 581 | " ")))) 582 | 583 | (defun lsp-metals-treeview--send-execute-command-async (command &optional args) 584 | "Create and send a `workspace/executeCommand'. 585 | The message will contain the COMMAND and optional ARGS. Send the 586 | command asynchronously rather than the default `lsp-mode' of synchronous." 587 | (lsp-request-async "workspace/executeCommand" 588 | (if args 589 | (list :command command :arguments args) 590 | (list :command command)) 591 | #'ignore 592 | :mode 'detached)) 593 | 594 | (defun lsp-metals-treeview--exec-node-action (&rest _) 595 | "Execute the action associated with the treeview node." 596 | (-when-let* ((node (treemacs-button-get (treemacs-current-button) :node)) 597 | ((&TreeViewNode :command?) node) 598 | ((&TreeViewCommand :command) command?)) 599 | (with-lsp-workspace lsp-metals-treeview--current-workspace 600 | (pcase command 601 | ;; TODO: use `seq-first' after switching to emacs 27. 602 | (`"metals-echo-command" (lsp-metals-treeview--send-execute-command-async (seq-elt (lsp-get command? :arguments) 0))) 603 | (c (lsp-metals-treeview--send-execute-command-async c (lsp-get command? :arguments))))))) 604 | 605 | (lsp-defun lsp-metals-treeview--on-node-collapsed ((&TreeViewNode :node-uri?) collapsed?) 606 | "Send metals/treeViewNodeCollapseDidChange to indicate collapsed/expanded. 607 | Metals node is a node attached to treemacs in the :node key - passed as item 608 | during render. COLLAPSED? either t or nil dependong on if the node has been 609 | collapsed or expanded." 610 | (lsp-metals-treeview--send-node-collapse-did-change lsp-metals-treeview--current-workspace 611 | lsp-metals-treeview--view-id 612 | node-uri? 613 | collapsed?)) 614 | 615 | ;; 616 | ;; Icon theme for Metals treeview 617 | ;; Icons are partially taken from VSCode and partially from Metals: 618 | ;; https://github.com/scalameta/metals-vscode/tree/master/icons 619 | ;; https://github.com/microsoft/vscode-codicons/tree/main/src/icons 620 | ;; 621 | (treemacs-create-theme "Metals" 622 | :icon-directory (f-join lsp-metals-treeview--dir lsp-metals-treeview--icon-dir) 623 | :config 624 | (progn 625 | (treemacs-create-icon :file "logo.png" :extensions (root)) 626 | (treemacs-create-icon :file "method.png" :extensions ("method")) 627 | (treemacs-create-icon :file "class.png" :extensions ("class")) 628 | (treemacs-create-icon :file "object.png" :extensions ("object")) 629 | (treemacs-create-icon :file "enum.png" :extensions ("enum")) 630 | (treemacs-create-icon :file "field.png" :extensions ("field")) 631 | (treemacs-create-icon :file "interface.png" :extensions ("interface")) 632 | (treemacs-create-icon :file "trait.png" :extensions ("trait")) 633 | (treemacs-create-icon :file "val.png" :extensions ("val")) 634 | (treemacs-create-icon :file "var.png" :extensions ("var")) 635 | (treemacs-create-icon :file "doctor.png" :extensions ("doctor")))) 636 | 637 | (treemacs-create-theme "Metals-dark" 638 | :icon-directory (f-join lsp-metals-treeview--dir lsp-metals-treeview--icon-dir) 639 | :extends "Metals" 640 | :config 641 | (progn 642 | (treemacs-create-icon :file "book-dark.png" :extensions ("book")) 643 | (treemacs-create-icon :file "bug-dark.png" :extensions ("bug")) 644 | (treemacs-create-icon :file "github-dark.png" :extensions ("github")) 645 | (treemacs-create-icon :file "gitter-dark.png" :extensions ("gitter")) 646 | (treemacs-create-icon :file "issue-opened-dark.png" :extensions ("issue-opened")) 647 | (treemacs-create-icon :file "twitter-dark.png" :extensions ("twitter")) 648 | (treemacs-create-icon :file "discord-dark.png" :extensions ("discord")) 649 | (treemacs-create-icon :file "cancel-dark.png" :extensions ("cancel")) 650 | (treemacs-create-icon :file "cascade-dark.png" :extensions ("cascade")) 651 | (treemacs-create-icon :file "clean-all-dark.png" :extensions ("clean-all")) 652 | (treemacs-create-icon :file "clean-dark.png" :extensions ("clean")) 653 | (treemacs-create-icon :file "compile-dark.png" :extensions ("compile")) 654 | (treemacs-create-icon :file "connect-dark.png" :extensions ("connect")) 655 | (treemacs-create-icon :file "exception-dark.png" :extensions ("exception")) 656 | (treemacs-create-icon :file "focus-dark.png" :extensions ("focus")) 657 | (treemacs-create-icon :file "info-dark.png" :extensions ("info")) 658 | (treemacs-create-icon :file "location-dark.png" :extensions ("location")) 659 | (treemacs-create-icon :file "empty-window-dark.png" :extensions ("empty-window")) 660 | (treemacs-create-icon :file "sync-dark.png" :extensions ("sync")) 661 | (treemacs-create-icon :file "debug-stop-dark.png" :extensions ("debug-stop")) 662 | (treemacs-create-icon :file "close-dark.png" :extensions ("notifications-clear")))) 663 | 664 | (treemacs-create-theme "Metals-light" 665 | :icon-directory (f-join lsp-metals-treeview--dir lsp-metals-treeview--icon-dir) 666 | :extends "Metals" 667 | :config 668 | (progn 669 | (treemacs-create-icon :file "book-light.png" :extensions ("book")) 670 | (treemacs-create-icon :file "bug-light.png" :extensions ("bug")) 671 | (treemacs-create-icon :file "github-light.png" :extensions ("github")) 672 | (treemacs-create-icon :file "gitter-light.png" :extensions ("gitter")) 673 | (treemacs-create-icon :file "issue-opened-light.png" :extensions ("issue-opened")) 674 | (treemacs-create-icon :file "twitter-light.png" :extensions ("twitter")) 675 | (treemacs-create-icon :file "discord-light.png" :extensions ("discord")) 676 | (treemacs-create-icon :file "cancel-light.png" :extensions ("cancel")) 677 | (treemacs-create-icon :file "cascade-light.png" :extensions ("cascade")) 678 | (treemacs-create-icon :file "clean-all-light.png" :extensions ("clean-all")) 679 | (treemacs-create-icon :file "clean-light.png" :extensions ("clean")) 680 | (treemacs-create-icon :file "compile-light.png" :extensions ("compile")) 681 | (treemacs-create-icon :file "connect-light.png" :extensions ("connect")) 682 | (treemacs-create-icon :file "exception-light.png" :extensions ("exception")) 683 | (treemacs-create-icon :file "focus-light.png" :extensions ("focus")) 684 | (treemacs-create-icon :file "info-light.png" :extensions ("info")) 685 | (treemacs-create-icon :file "location-light.png" :extensions ("location")) 686 | (treemacs-create-icon :file "empty-window-light.png" :extensions ("empty-window")) 687 | (treemacs-create-icon :file "sync-light.png" :extensions ("sync")) 688 | (treemacs-create-icon :file "debug-stop-light.png" :extensions ("debug-stop")) 689 | (treemacs-create-icon :file "close-light.png" :extensions ("notifications-clear")))) 690 | 691 | (treemacs-define-expandable-node-type metals-node 692 | :open-icon (lsp-metals-treeview--icon item t) 693 | :closed-icon (lsp-metals-treeview--icon item nil) 694 | :label (propertize (lsp-get item :label) 'face 'default 'help-echo (lsp-get item :tooltip)) 695 | :key (lsp-get item :nodeUri) 696 | :ret-action 'lsp-metals-treeview--exec-node-action 697 | :children (-when-let* ((node (treemacs-button-get btn :node)) 698 | ((&TreeViewNode :view-id :node-uri?) node)) 699 | (lsp-metals-treeview--get-children view-id node-uri?)) 700 | :child-type 'metals-node 701 | :more-properties `(:node 702 | ,item 703 | :leaf 704 | ,(not (lsp-get item :collapseState))) 705 | :on-expand (lsp-metals-treeview--on-node-collapsed 706 | (treemacs-button-get btn :node) nil) 707 | :on-collapse (lsp-metals-treeview--on-node-collapsed 708 | (treemacs-button-get btn :node) t)) 709 | 710 | (treemacs-define-variadic-entry-node-type metals-root 711 | :key 'metals-root 712 | :children (lsp-metals-treeview--get-children lsp-metals-treeview--view-id) 713 | :child-type 'metals-node) 714 | 715 | (defun lsp-metals-treeview (&optional workspace) 716 | "Display the Metals treeview window for the WORKSPACE (optional). 717 | If WORKSPACE is not specified obtain the current workspace for the file in 718 | the current buffer." 719 | (interactive) 720 | (-if-let* ((workspace 721 | (or workspace 722 | (lsp-find-workspace lsp-metals-treeview--metals-server-id 723 | (buffer-file-name))))) 724 | (lsp-metals-treeview--show-window workspace t) 725 | (message "Current buffer is not within Metals workspace"))) 726 | 727 | (defun lsp-metals-treeview-reveal () 728 | "Find the current buffer file in the treeview." 729 | (interactive) 730 | (-let (((&TreeViewRevealResult :view-id :uri-chain) (lsp-request "metals/treeViewReveal" (lsp--text-document-position-params)))) 731 | (-when-let* ((workspace (lsp-find-workspace lsp-metals-treeview--metals-server-id (buffer-file-name))) 732 | (treeview-buffer-name (lsp-metals-treeview--buffer-name workspace view-id))) 733 | (with-current-buffer treeview-buffer-name 734 | (mapc (lambda (uri) 735 | (-when-let (tree-node (lsp-metals-treeview--find-node uri)) 736 | (goto-char (marker-position tree-node)) 737 | (treemacs-update-node (treemacs-button-get tree-node :path) t))) 738 | (reverse uri-chain)) 739 | (-when-let (buffer-window (get-buffer-window)) 740 | (set-window-point buffer-window (point)) 741 | (select-window buffer-window)))))) 742 | 743 | 744 | ;; Debug helpers to track down issues with treemacs and aid development. 745 | (defun lsp-metals-treeview--debug-node () 746 | "Debug helper function to display treemacs node information." 747 | (interactive) 748 | (-let [node (treemacs-node-at-point)] 749 | (message 750 | "Label: %s 751 | Depth: %s 752 | Key: %s 753 | Path: %s 754 | State: %s 755 | Parent: %s 756 | eldoc: %s 757 | Metals Item: %s" 758 | (treemacs--get-label-of node) 759 | (treemacs-button-get node :depth) 760 | (treemacs-button-get node :key) 761 | (treemacs-button-get node :path) 762 | (treemacs-button-get node :state) 763 | (-some-> node (treemacs-button-get :parent) (treemacs--get-label-of)) 764 | (treemacs-button-get node :eldoc) 765 | (-some-> node (treemacs-button-get :node))))) 766 | 767 | 768 | (provide 'lsp-metals-treeview) 769 | ;;; lsp-metals-treeview.el ends here 770 | 771 | ;; Local Variables: 772 | ;; End: 773 | -------------------------------------------------------------------------------- /lsp-metals.el: -------------------------------------------------------------------------------- 1 | ;;; lsp-metals.el --- Scala Client settings -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2018-2019 Ross A. Baker , Evgeny Kurnevsky 4 | 5 | ;; Version: 1.0.0 6 | ;; Package-Requires: ((emacs "28.1") (scala-mode "0.23") (lsp-mode "7.0") (lsp-treemacs "0.2") (dap-mode "0.3") (dash "2.18.0") (f "0.20.0") (ht "2.0") (treemacs "3.1")) 7 | ;; Author: Ross A. Baker 8 | ;; Evgeny Kurnevsky 9 | ;; Keywords: languages, extensions 10 | ;; URL: https://github.com/emacs-lsp/lsp-metals 11 | 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | 27 | ;; lsp-metals client 28 | 29 | ;;; Code: 30 | 31 | (require 'lsp-mode) 32 | (require 'dap-mode) 33 | (require 'lsp-lens) 34 | (require 'lsp-metals-protocol) 35 | (require 'lsp-metals-treeview) 36 | (require 'view) 37 | 38 | (defgroup lsp-metals nil 39 | "LSP support for Scala, using Metals." 40 | :group 'lsp-mode 41 | :link '(url-link "https://scalameta.org/metals") 42 | :package-version '(lsp-metals . "1.0")) 43 | 44 | (defcustom lsp-metals-server-command "metals" 45 | "The command to launch the Scala language server." 46 | :group 'lsp-metals 47 | :type 'file 48 | :package-version '(lsp-metals . "1.0")) 49 | 50 | (defcustom lsp-metals-server-args '() 51 | "Extra arguments for the Scala language server." 52 | :group 'lsp-metals 53 | :type '(repeat string) 54 | :package-version '(lsp-metals . "1.0")) 55 | 56 | (defcustom lsp-metals-server-install-dir 57 | (f-join lsp-server-install-dir "metals/") 58 | "Installation directory for Metals server." 59 | :group 'lsp-metals 60 | :type 'directory 61 | :package-version '(lsp-metals . "1.2")) 62 | 63 | (defcustom lsp-metals-coursier-store-path 64 | (f-join lsp-metals-server-install-dir "coursier") 65 | "The path where Coursier will be stored." 66 | :group 'lsp-metals 67 | :type 'file 68 | :package-version '(lsp-metals . "1.2")) 69 | 70 | (defcustom lsp-metals-metals-store-path 71 | (f-join lsp-metals-server-install-dir "metals") 72 | "The path where Metals will be stored." 73 | :group 'lsp-metals 74 | :type 'file 75 | :package-version '(lsp-metals . "1.2")) 76 | 77 | (defcustom lsp-metals-coursier-download-url 78 | (pcase system-type 79 | (`windows-nt "https://github.com/coursier/launchers/raw/master/cs-x86_64-pc-win32.zip") 80 | (`darwin "https://github.com/coursier/launchers/raw/master/cs-x86_64-apple-darwin.gz") 81 | (_ "https://github.com/coursier/launchers/raw/master/cs-x86_64-pc-linux.gz")) 82 | "Download url for coursier." 83 | :group 'lsp-metals 84 | :type 'string 85 | :package-version '(lsp-metals . "1.2")) 86 | 87 | (defcustom lsp-metals-coursier-decompress 88 | (pcase system-type 89 | (`windows-nt :zip) 90 | (_ :gzip)) 91 | "Compression type of the downloaded coursier binary." 92 | :group 'lsp-metals 93 | :type 'string 94 | :package-version '(lsp-metals . "1.3")) 95 | 96 | (defcustom lsp-metals-install-scala-version "2.13" 97 | "Metals scala version to install." 98 | :group 'lsp-metals 99 | :type 'string 100 | :package-version '(lsp-metals . "1.3")) 101 | 102 | (defcustom lsp-metals-install-version "latest.release" 103 | "Metals version to install." 104 | :group 'lsp-metals 105 | :type 'string 106 | :package-version '(lsp-metals . "1.2")) 107 | 108 | (defcustom lsp-metals-java-home "" 109 | "The Java Home directory. 110 | It's used for indexing JDK sources and locating the `java' binary." 111 | :type '(string) 112 | :group 'lsp-metals 113 | :package-version '(lsp-metals . "1.0")) 114 | 115 | (defcustom lsp-metals-scalafmt-config-path "" 116 | "Optional custom path to the .scalafmt.conf file. 117 | Should be an absolute path and use forward slashes / for file 118 | separators (even on Windows)." 119 | :type '(string) 120 | :group 'lsp-metals 121 | :package-version '(lsp-metals . "1.0")) 122 | 123 | (defcustom lsp-metals-scalafix-config-path "" 124 | "Optional custom path to the .scalafix.conf file. 125 | Should be an absolute path and use forward slashes / for file 126 | separators (even on Windows)." 127 | :type '(string) 128 | :group 'lsp-metals 129 | :package-version '(lsp-metals . "1.0")) 130 | 131 | (defcustom lsp-metals-sbt-script "" 132 | "Optional absolute path to an `sbt' executable. 133 | By default, Metals uses `java -jar sbt-launch.jar' with an embedded 134 | launcher while respecting `.jvmopts' and `.sbtopts'. Update this 135 | setting if your `sbt' script requires more customizations like using 136 | environment variables." 137 | :type '(string) 138 | :group 'lsp-metals 139 | :package-version '(lsp-metals . "1.0")) 140 | 141 | (defcustom lsp-metals-gradle-script "" 142 | "Optional absolute path to a `gradle' executable. 143 | By default, Metals uses gradlew with 5.3.1 gradle version. Update 144 | this setting if your `gradle' script requires more customizations like 145 | using environment variables." 146 | :type '(string) 147 | :group 'lsp-metals 148 | :package-version '(lsp-metals . "1.0")) 149 | 150 | (defcustom lsp-metals-maven-script "" 151 | "Optional absolute path to a `maven' executable. 152 | By default, Metals uses mvnw maven wrapper with 3.6.1 maven version. 153 | Update this setting if your `maven' script requires more 154 | customizations." 155 | :type '(string) 156 | :group 'lsp-metals 157 | :package-version '(lsp-metals . "1.0")) 158 | 159 | (defcustom lsp-metals-mill-script "" 160 | "Optional absolute path to a `mill' executable. 161 | By default, Metals uses mill wrapper script with 0.5.0 mill version. 162 | Update this setting if your mill script requires more customizations 163 | like using environment variables." 164 | :type '(string) 165 | :group 'lsp-metals 166 | :package-version '(lsp-metals . "1.0")) 167 | 168 | (defcustom lsp-metals-pants-targets "" 169 | "Space separated list of Pants targets to export. 170 | For example, `src/main/scala:: src/main/java::'. Syntax such as 171 | `src/{main,test}::' is not supported." 172 | :type '(string) 173 | :group 'lsp-metals 174 | :package-version '(lsp-metals . "1.0")) 175 | 176 | (make-obsolete-variable 177 | 'lsp-metals-pants-targets 178 | "metals.pants-targets is no longer a valid configuration option, using it will have no effect." 179 | "1.3") 180 | 181 | (defcustom lsp-metals-bloop-sbt-already-installed nil 182 | "If true, Metals will not generate a `project/metals.sbt' file. 183 | This assumes that sbt-bloop is already manually installed in the sbt 184 | build. Build import will fail with a `not valid command bloopInstall' 185 | error in case Bloop is not manually installed in the build when using 186 | this option." 187 | :type 'boolean 188 | :group 'lsp-metals 189 | :package-version '(lsp-metals . "1.0")) 190 | 191 | (defcustom lsp-metals-bloop-version nil 192 | "The version of Bloop to use. 193 | This version will be used for the Bloop build tool plugin, for any 194 | supported build tool, while importing in Metals as well as for running 195 | the embedded server." 196 | :type '(choice 197 | (const :tag "Default" nil) 198 | (string :tag "Version")) 199 | :group 'lsp-metals 200 | :package-version '(lsp-metals . "1.0")) 201 | 202 | (defcustom lsp-metals-super-method-lenses-enabled nil 203 | "If True, super method lenses will be shown. 204 | Super method lenses are visible above methods definition that override 205 | another methods. Clicking on a lens jumps to super method definition. 206 | Disabled lenses are not calculated for opened documents which might 207 | speed up document processing." 208 | :type 'boolean 209 | :group 'lsp-metals 210 | :package-version '(lsp-metals . "1.0")) 211 | 212 | (defcustom lsp-metals-ammonite-jvm-properties nil 213 | "Optional vector of JVM properties to pass along to the Ammonite server. 214 | Each property needs to be a separate item. 215 | 216 | Example: -Xmx1G or -Xms100M." 217 | :type '(lsp-repeatable-vector string) 218 | :group 'lsp-metals 219 | :package-version '(lsp-metals . "1.3")) 220 | 221 | (defcustom lsp-metals-enable-indent-on-paste nil 222 | "Indent snippets when pasted. 223 | 224 | When this option is enabled, when a snippet is pasted into a Scala file, 225 | Metals will try to adjust the indentation to that of the current cursor." 226 | :type 'boolean 227 | :group 'lsp-metals 228 | :package-version '(lsp-metals . "1.3")) 229 | 230 | (defcustom lsp-metals-fallback-scala-version "automatic" 231 | "The Scala compiler version that is used as the default or fallback. 232 | Used when a file doesn't belong to any build target or the specified Scala 233 | version isn't supported by Metals. This applies to standalone Scala files, 234 | worksheets, and Ammonite scripts." 235 | :type 'string 236 | :group 'lsp-metals 237 | :package-version '(lsp-metals . "1.3")) 238 | 239 | (defcustom lsp-metals-test-user-interface "Code Lenses" 240 | "Default way of handling tests and test suites." 241 | :type 'string 242 | :group 'lsp-metals 243 | :package-version '(lsp-metals . "1.3")) 244 | 245 | (defcustom lsp-metals-java-format.eclipse-config-path "" 246 | "Optional custom path to the eclipse-formatter.xml file. 247 | 248 | Should be an absolute path and use forward slashes / for file separators (even 249 | on Windows)." 250 | :type 'string 251 | :group 'lsp-metals 252 | :package-version '(lsp-metals . "1.3")) 253 | 254 | (defcustom lsp-metals-java-format.eclipse-profile "" 255 | "The eclipse profile name. 256 | 257 | If the Eclipse formatter file contains more than one profile, this option can be 258 | used to control which is used." 259 | :type 'string 260 | :group 'lsp-metals 261 | :package-version '(lsp-metals . "1.3")) 262 | 263 | (defcustom lsp-metals-scala-cli-launcher "" 264 | "Optional absolute path to a scala-cli executable. 265 | 266 | The executable will be used for running a Scala CLI BSP server. By default, 267 | Metals uses the scala-cli from the PATH, or if it's not found, downloads and 268 | runs Scala CLI on the JVM (slower than native Scala CLI). Update this if you 269 | want to use a custom Scala CLI launcher, not available in PATH." 270 | :type 'string 271 | :group 'lsp-metals 272 | :package-version '(lsp-metals . "1.3")) 273 | 274 | (defcustom lsp-metals-enable-semantic-highlighting nil 275 | "Use semantic tokens highlight. 276 | 277 | When this option is enabled, Metals will provide semantic tokens for clients 278 | that support it. The feature is still experimental and does not work for all 279 | sources." 280 | :type 'boolean 281 | :group 'lsp-metals 282 | :package-version '(lsp-metals . "1.3")) 283 | 284 | (defcustom lsp-metals-inlay-hints-enable-inferred-types nil 285 | "Should display type annotations for inferred types. 286 | 287 | When this option is enabled, each method that can have inferred types has them 288 | displayed either as additional decorations." 289 | :type 'boolean 290 | :group 'lsp-metals 291 | :package-version '(lsp-metals . "1.3")) 292 | 293 | (defcustom lsp-metals-inlay-hints-enable-implicit-conversions nil 294 | "Should display implicit conversion at usage sites. 295 | 296 | When this option is enabled, each place where an implicit method or class is 297 | used has it displayed either as additional decorations." 298 | :type 'boolean 299 | :group 'lsp-metals 300 | :package-version '(lsp-metals . "1.3")) 301 | 302 | (defcustom lsp-metals-inlay-hints-enable-implicit-arguments nil 303 | "Should display implicit parameter at usage sites. 304 | 305 | When this option is enabled, each method that has implicit arguments has them 306 | displayed either as additional decorations." 307 | :type 'boolean 308 | :group 'lsp-metals 309 | :package-version '(lsp-metals . "1.3")) 310 | 311 | (defcustom lsp-metals-inlay-hints-enable-type-parameters nil 312 | "Should display type annotations for type parameters. 313 | 314 | When this option is enabled, each place when a type parameter is applied has it 315 | displayed either as additional decorations." 316 | :type 'boolean 317 | :group 'lsp-metals 318 | :package-version '(lsp-metals . "1.3")) 319 | 320 | (defcustom lsp-metals-inlay-hints-enable-hints-in-pattern-match nil 321 | "Should display type annotations in pattern matches. 322 | 323 | When this option is enabled, each place when a type is inferred in a pattern 324 | match has it displayed either as additional decorations." 325 | :type 'boolean 326 | :group 'lsp-metals 327 | :package-version '(lsp-metals . "1.3")) 328 | 329 | (defcustom lsp-metals-remote-language-server "" 330 | "A URL pointing to a remote language server." 331 | :type '(string) 332 | :group 'lsp-metals 333 | :package-version '(lsp-metals . "1.0")) 334 | 335 | (defcustom lsp-metals-multi-root t 336 | "If non nil, `metals' will be started in multi-root mode." 337 | :type 'boolean 338 | :group 'lsp-metals 339 | :package-version '(lsp-metals . "1.3")) 340 | 341 | (defface lsp-metals-face-overlay 342 | '((t :inherit font-lock-comment-face)) 343 | "Face used for metals decoration overlays." 344 | :group 'lsp-metals) 345 | 346 | (defconst lsp-metals--javap-format-id "javap") 347 | 348 | (defconst lsp-metals--javap-verbose-format-id "javap-verbose") 349 | 350 | (defconst lsp-metals--semanticdb-compact-format-id "semanticdb-compact") 351 | 352 | (defconst lsp-metals--semanticdb-detailed-format-id "semanticdb-detailed") 353 | 354 | (defconst lsp-metals--tasty-decoded-format-id "tasty-decoded") 355 | 356 | (defconst lsp-metals--all-format-ids (list lsp-metals--javap-format-id 357 | lsp-metals--javap-verbose-format-id 358 | lsp-metals--semanticdb-compact-format-id 359 | lsp-metals--semanticdb-detailed-format-id 360 | lsp-metals--tasty-decoded-format-id)) 361 | 362 | (lsp-register-custom-settings 363 | '(("metals.java-home" lsp-metals-java-home) 364 | ("metals.sbt-script" lsp-metals-sbt-script) 365 | ("metals.gradle-script" lsp-metals-gradle-script) 366 | ("metals.maven-script" lsp-metals-maven-script) 367 | ("metals.mill-script" lsp-metals-mill-script) 368 | ("metals.scalafmt-config-path" lsp-metals-scalafmt-config-path) 369 | ("metals.scalafix-config-path" lsp-metals-scalafix-config-path) 370 | ("metals.ammonite-jvm-properties" lsp-metals-ammonite-jvm-properties) 371 | ("metals.bloop-sbt-already-installed" lsp-metals-bloop-sbt-already-installed t) 372 | ("metals.bloop-version" lsp-metals-bloop-version) 373 | ("metals.super-method-lenses-enabled" lsp-metals-super-method-lenses-enabled t) 374 | ("metals.enable-indent-on-paste" lsp-metals-enable-indent-on-paste t) 375 | ("metals.remote-language-server" lsp-metals-remote-language-server) 376 | ("metals.fallback-scala-version" lsp-metals-fallback-scala-version) 377 | ("metals.test-user-interface" lsp-metals-test-user-interface) 378 | ("metals.java-format.eclipse-config-path" lsp-metals-java-format.eclipse-config-path) 379 | ("metals.java-format.eclipse-profile" lsp-metals-java-format.eclipse-profile) 380 | ("metals.scala-cli-launcher" lsp-metals-scala-cli-launcher) 381 | ("metals.enable-semantic-highlighting" lsp-metals-enable-semantic-highlighting t) 382 | ("inlay-hints.inferredTypes.enable" lsp-metals-inlay-hints-enable-inferred-types t) 383 | ("inlay-hints.implicitConversions.enable" lsp-metals-inlay-hints-enable-implicit-conversions t) 384 | ("inlay-hints.implicitArguments.enable" lsp-metals-inlay-hints-enable-implicit-arguments t) 385 | ("inlay-hints.typeParameters.enable" lsp-metals-inlay-hints-enable-type-parameters t) 386 | ("inlay-hints.hintsInPatternMatch.enable" lsp-metals-inlay-hints-enable-hints-in-pattern-match t))) 387 | 388 | (lsp-dependency 389 | 'coursier 390 | '(:system "cs") 391 | '(:system "coursier") 392 | `(:download :url ,lsp-metals-coursier-download-url 393 | :store-path ,lsp-metals-coursier-store-path 394 | :decompress ,lsp-metals-coursier-decompress 395 | :set-executable? t)) 396 | 397 | (lsp-dependency 398 | 'metals 399 | `(:system ,lsp-metals-server-command) 400 | `(:system ,lsp-metals-metals-store-path)) 401 | 402 | (defun lsp-metals--server-command () 403 | "Generate the Scala language server startup command." 404 | `(,(lsp-package-path 'metals) ,@lsp-metals-server-args)) 405 | 406 | (defun lsp-metals--download-server (_client callback error-callback _update?) 407 | "Install metals server via coursier. 408 | Will invoke CALLBACK on success, ERROR-CALLBACK on error." 409 | (lsp-package-ensure 410 | 'coursier 411 | (lambda () 412 | (call-process 413 | (lsp-package-path 'coursier) 414 | nil 415 | (get-buffer-create "*Coursier log*") 416 | t 417 | "bootstrap" 418 | "--java-opt" 419 | "-Xss4m" 420 | "--java-opt" 421 | "-Xms100m" 422 | (concat "org.scalameta:metals_" lsp-metals-install-scala-version ":" lsp-metals-install-version) 423 | "-r" 424 | "bintray:scalacenter/releases" 425 | "-r" 426 | "sonatype:snapshots" 427 | "-o" 428 | lsp-metals-metals-store-path 429 | "-f") 430 | (funcall callback)) 431 | error-callback)) 432 | 433 | (defun lsp-metals-build-import () 434 | "Unconditionally run `sbt bloopInstall` and re-connect to the build server." 435 | (interactive) 436 | (lsp-metals-treeview--send-execute-command-async "build-import" ())) 437 | 438 | (defun lsp-metals-build-connect () 439 | "Unconditionally cancel existing build server connection and re-connect." 440 | (interactive) 441 | (lsp-metals-treeview--send-execute-command-async "build-connect" ())) 442 | 443 | (defun lsp-metals-cancel-compilation () 444 | "Cancel the currently ongoing compilation, if any." 445 | (interactive) 446 | (lsp-metals-treeview--send-execute-command-async "compile-cancel" ())) 447 | 448 | (defun lsp-metals-cascade-compile () 449 | "Cascade compile all open files." 450 | (interactive) 451 | (lsp-metals-treeview--send-execute-command-async "compile-cascade")) 452 | 453 | (defun lsp-metals-clean-compile () 454 | "Recompile all build targets in this workspace." 455 | (interactive) 456 | (lsp-metals-treeview--send-execute-command-async "compile-clean")) 457 | 458 | (defun lsp-metals-restart-build-server () 459 | "Unconditionally stop the current running Bloop server and start a new one." 460 | (interactive) 461 | (lsp-metals-treeview--send-execute-command-async "build-restart")) 462 | 463 | (defun lsp-metals-new-scala-file () 464 | "Create a new file either a class, object, trait, package object or worksheet." 465 | (interactive) 466 | (lsp-send-execute-command "new-scala-file" (concat "file://" default-directory))) 467 | 468 | (defun lsp-metals-new-scala-project () 469 | "Create a new Scala project using one of the available g8 templates." 470 | (interactive) 471 | (lsp-send-execute-command "new-scala-project")) 472 | 473 | (defun lsp-metals-doctor-run () 474 | "Open the Metals doctor to troubleshoot potential build problems." 475 | (interactive) 476 | (lsp-send-execute-command "doctor-run" ())) 477 | 478 | (defun lsp-metals-sources-scan () 479 | "Walk all files in the workspace and index where symbols are defined." 480 | (interactive) 481 | (lsp-metals-treeview--send-execute-command-async "sources-scan" ())) 482 | 483 | (defun lsp-metals-reset-choice () 484 | "Reset a decision you made about different settings. 485 | E.g. If you choose to import workspace with sbt you can decide to reset and 486 | change it again." 487 | (interactive) 488 | (lsp-send-execute-command "reset-choice" ())) 489 | 490 | (defun lsp-metals-copy-worksheet-output () 491 | "Copy worksheet with evaluated results as comments." 492 | (interactive) 493 | (let ((command-result (lsp-send-execute-command "metals.copy-worksheet-output" (lsp--buffer-uri)))) 494 | (when-let ((value (lsp-get command-result :value))) 495 | (kill-new value) 496 | (message "Copied worksheet output.")))) 497 | 498 | (defun lsp-metals-analyze-stacktrace () 499 | "Convert provided stacktrace in the region to a format with links." 500 | (interactive) 501 | (when (and (use-region-p) default-directory) 502 | (with-lsp-workspace (lsp-find-workspace 'metals default-directory) 503 | (let ((stacktrace (buffer-substring (region-beginning) (region-end)))) 504 | (lsp-send-execute-command "metals.analyze-stacktrace" (vector stacktrace)))))) 505 | 506 | (defun lsp-metals-super-method-hierarchy () 507 | "Calculate inheritance hierarchy of a class that should contain given method." 508 | (interactive) 509 | (lsp-send-execute-command 510 | "super-method-hierarchy" 511 | (lsp--text-document-position-params))) 512 | 513 | (defun lsp-metals-goto-super-method () 514 | "Jumps to super method/field definition of a symbol under cursor." 515 | (interactive) 516 | (lsp-send-execute-command 517 | "goto-super-method" 518 | (lsp--text-document-position-params))) 519 | 520 | (defun lsp-metals--generate-decode-file-buffer-name (uri) 521 | "Generate DecodeFile buffer name for the given URI." 522 | (format "*%s*" (file-name-nondirectory uri))) 523 | 524 | (defun lsp-metals--decode (uri) 525 | "View the decoded representation of the given URI." 526 | (when-let* ((command-result (lsp-send-execute-command "metals.file-decode" uri)) 527 | (value (lsp-get command-result :value))) 528 | (pop-to-buffer (lsp-metals--generate-decode-file-buffer-name uri)) 529 | (setq-local show-trailing-whitespace nil) 530 | (setq-local buffer-read-only nil) 531 | (erase-buffer) 532 | (insert value) 533 | (goto-char (point-min)) 534 | (when (string-suffix-p "javap" uri) 535 | (require 'cc-mode) 536 | (java-mode) 537 | (insert "// ")) 538 | (view-mode 1) 539 | (setq view-exit-action 'kill-buffer))) 540 | 541 | (defun lsp-metals-decode-file (format-id) 542 | "View the decoded representation of the given FORMAT-ID for the current buffer. 543 | 544 | When run as a command, prompt for the format id to use from 545 | `lsp-metals--all-format-ids'. See URL 546 | `https://scalameta.org/metals/docs/integrations/new-editor/#decode-file' 547 | for more information on the metals \"files-decode\" command." 548 | (interactive (list (completing-read "format: " lsp-metals--all-format-ids))) 549 | (lsp-metals--decode (format "metalsDecode:%s.%s" (lsp--buffer-uri) format-id))) 550 | 551 | (defun lsp-metals-view-javap () 552 | "View javap for a class in the current file." 553 | (interactive) 554 | (lsp-metals-decode-file lsp-metals--javap-format-id)) 555 | 556 | (defun lsp-metals-view-javap-verbose () 557 | "View javap verbose a class in the for current file." 558 | (interactive) 559 | (lsp-metals-decode-file lsp-metals--javap-verbose-format-id)) 560 | 561 | (defun lsp-metals-view-semanticdb-compact () 562 | "View semanticdb compact for current file." 563 | (interactive) 564 | (lsp-metals-decode-file lsp-metals--semanticdb-compact-format-id)) 565 | 566 | (defun lsp-metals-view-semanticdb-detailed () 567 | "View semanticdb detailed for current file." 568 | (interactive) 569 | (lsp-metals-decode-file lsp-metals--semanticdb-detailed-format-id)) 570 | 571 | (defun lsp-metals-view-tasty-decoded () 572 | "View tasty decoded for current file." 573 | (interactive) 574 | (lsp-metals-decode-file lsp-metals--tasty-decoded-format-id)) 575 | 576 | (defun lsp-metals-run-scalafix () 577 | "Run scalafix rules for the current buffer, requires metals >= v0.11.7." 578 | (interactive) 579 | (lsp-send-execute-command "scalafix-run" (lsp--text-document-position-params))) 580 | 581 | (defun lsp-metals--browse-url (url &rest _) 582 | "Handle `command:' matals URLs." 583 | (when-let* ((workspace (lsp-find-workspace 'metals default-directory)) 584 | (decoded (url-unhex-string url)) 585 | ((string-match "command:\\(.*\\)\\?\\(.*\\)" decoded)) 586 | (command (match-string 1 decoded)) 587 | (arguments (lsp--read-json (match-string 2 decoded)))) 588 | (lsp-metals--execute-client-command workspace (lsp-make-execute-command-params :command command :arguments? arguments)))) 589 | 590 | (defun lsp-metals--render-html (html) 591 | "Render the Metals HTML in the current buffer." 592 | (require 'shr) 593 | (setq-local show-trailing-whitespace nil) 594 | (setq-local buffer-read-only nil) 595 | (setq-local browse-url-handlers '(("\\`command:" . lsp-metals--browse-url))) 596 | (erase-buffer) 597 | (insert html) 598 | (shr-render-region (point-min) (point-max)) 599 | (goto-char (point-min)) 600 | (view-mode 1) 601 | (setq view-exit-action 'kill-buffer)) 602 | 603 | (defun lsp-metals--generate-doctor-buffer-name (workspace) 604 | "Generate doctor buffer name for the WORKSPACE." 605 | (format "*Metals Doctor: %s*" (process-id (lsp--workspace-cmd-proc workspace)))) 606 | 607 | (defun lsp-metals--doctor-run (workspace html) 608 | "Focus on a window displaying troubleshooting help from the Metals doctor. 609 | HTML is the help contents. 610 | WORKSPACE is the workspace the client command was received from." 611 | (pop-to-buffer (lsp-metals--generate-doctor-buffer-name workspace)) 612 | (lsp-metals--render-html html)) 613 | 614 | (defun lsp-metals--doctor-reload (workspace html) 615 | "Reload the HTML contents of an open Doctor window, if any. 616 | Should be ignored if there is no open doctor window. 617 | WORKSPACE is the workspace the client command was received from." 618 | (when-let ((buffer (get-buffer (lsp-metals--generate-doctor-buffer-name workspace)))) 619 | (with-current-buffer buffer 620 | (lsp-metals--render-html html)))) 621 | 622 | (defun lsp-metals--goto-location (workspace location &optional _) 623 | "Move the cursor focus to the provided LOCATION. 624 | WORKSPACE is the workspace the client command was received from." 625 | (let ((uri (url-unhex-string (lsp--location-uri location)))) 626 | (if (string-prefix-p "metalsDecode:" uri) 627 | (with-lsp-workspace workspace (lsp-metals--decode uri)) 628 | (let ((xrefs (lsp--locations-to-xref-items (list location)))) 629 | (if (boundp 'xref-show-definitions-function) 630 | (with-no-warnings 631 | (funcall xref-show-definitions-function 632 | (-const xrefs) 633 | `((window . ,(selected-window))))) 634 | (xref--show-xrefs xrefs nil)))))) 635 | 636 | (defun lsp-metals--echo-command (workspace command) 637 | "A client COMMAND that should be forwarded back to the Metals server. 638 | WORKSPACE is the workspace the client command was received from." 639 | (with-lsp-workspace workspace 640 | (lsp-send-execute-command command))) 641 | 642 | (defun lsp-metals-bsp-switch () 643 | "Interactively switch between BSP servers." 644 | (interactive) 645 | (lsp-send-execute-command "bsp-switch" ())) 646 | 647 | (defun lsp-metals-generate-bsp-config () 648 | "Generate a Scala BSP Config based on the current BSP server." 649 | (interactive) 650 | (lsp-send-execute-command "generate-bsp-config" ())) 651 | 652 | (defun lsp-metals-zip-reports () 653 | "Create a zip from incognito and bloop reports." 654 | (interactive) 655 | (lsp-send-execute-command "zip-reports" ())) 656 | 657 | (defun lsp-metals-reset-workspace () 658 | "Clean metals cache and restart build server." 659 | (interactive) 660 | (lsp-send-execute-command "reset-workspace" ())) 661 | 662 | (defun lsp-metals-open-server-log () 663 | "Open a buffer with the metals log for the current workspace." 664 | (interactive) 665 | (if-let ((root (lsp-workspace-root))) 666 | (if-let* ((log-file (f-join (file-name-as-directory root) ".metals" "metals.log")) 667 | ((f-exists-p log-file))) 668 | (progn (find-file log-file) (goto-char (point-max))) 669 | (user-error "%s does not exist, are you in the right directory?" log-file)) 670 | (user-error "No LSP workspace is in effect"))) 671 | 672 | (lsp-defun lsp-metals--publish-decorations (workspace (&PublishDecorationsParams :uri :options)) 673 | "Handle the metals/publishDecorations extension notification. 674 | WORKSPACE is the workspace the notification was received from." 675 | (with-lsp-workspace workspace 676 | (let* ((file (lsp--uri-to-path uri)) 677 | (buffer (find-buffer-visiting file))) 678 | (when buffer 679 | (with-current-buffer buffer 680 | (lsp--remove-overlays 'metals-decoration) 681 | (mapc #'lsp-metals--make-overlay options)))))) 682 | 683 | (lsp-defun lsp-metals--make-overlay ((&DecorationOptions :range :render-options :hover-message?)) 684 | "Create overlay from metals decoration." 685 | (let* ((region (lsp--range-to-region range)) 686 | (ov (make-overlay (car region) (cdr region) nil t t))) 687 | (-when-let* (((&ThemableDecorationInstanceRenderOption :after?) render-options) 688 | ((&ThemableDecorationAttachmentRenderOptions :content-text?) after?) 689 | (text (if hover-message? 690 | (propertize content-text? 'help-echo (lsp--render-element hover-message?)) 691 | content-text?))) 692 | (overlay-put ov 'after-string (propertize text 'cursor t 'font-lock-face 'lsp-metals-face-overlay))) 693 | (overlay-put ov 'metals-decoration t))) 694 | 695 | (defun lsp-metals--logs-toggle (_workspace) 696 | "Toggle focus on the logs reported by the server via `window/logMessage'." 697 | (switch-to-buffer (get-buffer-create "*lsp-log*"))) 698 | 699 | (defun lsp-metals--diagnostics-focus (_workspace) 700 | "Focus on the window that lists all published diagnostics." 701 | (lsp-treemacs-errors-list)) 702 | 703 | (defun lsp-metals--show-stacktrace (_workspace html) 704 | "Display stacktrace in a new buffer. 705 | HTML is the stacktrace contents." 706 | (pop-to-buffer (generate-new-buffer "*Metals Stacktrace*")) 707 | (lsp-metals--render-html html)) 708 | 709 | (defun lsp-metals--reset-choice (workspace &optional choice?) 710 | "Reset a decision you made about different settings. 711 | WORKSPACE is the workspace the notification was received from. 712 | CHOICE is the decision to reset." 713 | (with-lsp-workspace workspace (lsp-send-execute-command "reset-choice" choice?))) 714 | 715 | (lsp-defun lsp-metals--execute-client-command (workspace (&ExecuteCommandParams :command :arguments?)) 716 | "Handle the metals/executeClientCommand extension notification. 717 | WORKSPACE is the workspace the notification was received from." 718 | (when-let ((command (pcase (string-remove-prefix "metals." command) 719 | (`"metals-doctor-run" #'lsp-metals--doctor-run) 720 | (`"metals-doctor-reload" #'lsp-metals--doctor-reload) 721 | (`"metals-logs-toggle" #'lsp-metals--logs-toggle) 722 | (`"metals-diagnostics-focus" #'lsp-metals--diagnostics-focus) 723 | (`"metals-goto-location" #'lsp-metals--goto-location) 724 | (`"metals-echo-command" #'lsp-metals--echo-command) 725 | (`"metals-model-refresh" #'lsp-metals--model-refresh) 726 | (`"metals-show-stacktrace" #'lsp-metals--show-stacktrace) 727 | (`"reset-choice" #'lsp-metals--reset-choice) 728 | (c (ignore (lsp-warn "Unknown metals client command: %s" c)))))) 729 | (apply command (append (list workspace) arguments? nil)))) 730 | 731 | (defvar lsp-metals--current-buffer nil 732 | "Current buffer used to send `metals/didFocusTextDocument' notification.") 733 | 734 | (defun lsp-metals--workspaces () 735 | "Get the list of all metals workspaces." 736 | (--filter 737 | (eq (lsp--client-server-id (lsp--workspace-client it)) 'metals) 738 | (lsp--session-workspaces (lsp-session)))) 739 | 740 | (defun lsp-metals--did-focus () 741 | "Send `metals/didFocusTextDocument' on buffer switch." 742 | (unless (eq lsp-metals--current-buffer (current-buffer)) 743 | (setq lsp-metals--current-buffer (current-buffer)) 744 | (lsp-notify "metals/didFocusTextDocument" (lsp--buffer-uri)))) 745 | 746 | 747 | (defun lsp-metals-populate-config (conf) 748 | "Prepare CONF for debug session." 749 | (if (and (plist-get conf :debugServer) 750 | (plist-get conf :name)) 751 | conf 752 | (-let (((&DebugSession :name :uri) 753 | (lsp-send-execute-command 754 | "debug-adapter-start" 755 | (vector (list :data conf 756 | :dataKind (cond 757 | ((equal "attach" (plist-get conf :request)) 758 | "scala-attach-remote") 759 | ((plist-get conf :dataKind)) 760 | (t "scala-main-class")) 761 | :targets (or 762 | (plist-get conf :targets) 763 | (vector `(:uri ,(concat 764 | (lsp--path-to-uri (or (lsp-workspace-root) 765 | (error "The debug provide can be called under project root"))) 766 | "?id=" 767 | (or 768 | (plist-get conf :buildTarget) 769 | "root")))))))))) 770 | (-> conf 771 | (dap--put-if-absent :name name) 772 | (dap--put-if-absent :request "launch") 773 | (dap--put-if-absent :host (or "localhost" 774 | (plist-get conf :hostName))) 775 | (dap--put-if-absent :debugServer 776 | (-> uri 777 | (split-string ":") 778 | cl-third 779 | string-to-number)))))) 780 | 781 | (dap-register-debug-provider "scala" #'lsp-metals-populate-config) 782 | 783 | (dap-register-debug-template 784 | "Scala Main Class" 785 | '(:type "scala" :class "" :name "Scala Main Class" :arguments [] :jvmOptions [] :environmentVariables [])) 786 | 787 | (dap-register-debug-template 788 | "Scala Attach" 789 | '(:type "scala" :request "attach" :name "Scala Attach" :hostName "localhost" :port 0)) 790 | 791 | 792 | (lsp-defun lsp-metals--debug-start (no-debug (&Command :arguments?)) 793 | "Start debug session. 794 | If NO-DEBUG is true launch the program without enabling debugging. 795 | PARAMS are the action params." 796 | ;; make sure the arguments are plist 797 | (-let (((&DebugSession :name :uri) (lsp-send-execute-command 798 | "debug-adapter-start" 799 | arguments?))) 800 | (dap-debug 801 | (list :debugServer (-> uri 802 | (split-string ":") 803 | cl-third 804 | string-to-number) 805 | :type "scala" 806 | :name name 807 | :host "localhost" 808 | :request "launch" 809 | :noDebug no-debug)))) 810 | 811 | (defun lsp-metals--model-refresh (workspace) 812 | "Handle `metals-model-refresh' notification refreshing lenses. 813 | WORKSPACE is the workspace the notification was received from." 814 | (->> workspace 815 | (lsp--workspace-buffers) 816 | (mapc (lambda (buffer) 817 | (with-current-buffer buffer 818 | (when (bound-and-true-p lsp-lens-mode) 819 | (lsp-lens--schedule-refresh t))))))) 820 | 821 | (defun lsp-metals--status-string-keymap (workspace command?) 822 | "Keymap for `metals/status' notification. 823 | WORKSPACE is the workspace we received notification from. 824 | COMMAND is the client command to execute." 825 | (when command? 826 | (-doto (make-sparse-keymap) 827 | (define-key [mode-line mouse-1] 828 | (lambda () 829 | (interactive) 830 | (lsp-metals--execute-client-command workspace (lsp-make-execute-command-params :command command?))))))) 831 | 832 | (lsp-defun lsp-metals--status-string (workspace (&MetalsStatusParams :text :hide? :tooltip? :command?)) 833 | "Handle `metals/status' notification. 834 | WORKSPACE is the workspace we received notification from." 835 | (if (or hide? (s-blank-str? text)) 836 | (lsp-workspace-status nil workspace) 837 | (lsp-workspace-status (propertize text 838 | 'help-echo tooltip? 839 | 'local-map (lsp-metals--status-string-keymap workspace command?)) 840 | workspace))) 841 | 842 | (lsp-defun lsp-metals--quick-pick (_workspace (&MetalsQuickPickParams :items :place-holder?)) 843 | "Provide a string value by picking from given options." 844 | (let* ((choices (--map (-let* (((&MetalsQuickPickItem :id :label :description?) it)) 845 | (cons label (cons id description?))) 846 | items))) 847 | (if choices 848 | (let ((completion-extra-properties 849 | `(:annotation-function (lambda (c) 850 | (-when-let (description (cdr (cdr (assoc c ',choices)))) 851 | (concat " " description)))))) 852 | (list :itemId (car (cdr (assoc (completing-read (concat place-holder? ": ") choices nil t) choices)))))))) 853 | 854 | (lsp-defun lsp-metals--input-box (_workspace (&MetalsInputBoxParams :prompt)) 855 | "Provide a string value for a given prompt." 856 | (list :value (read-from-minibuffer (concat prompt ": ")))) 857 | 858 | (lsp-register-client 859 | (make-lsp-client :new-connection (lsp-stdio-connection 'lsp-metals--server-command) 860 | :major-modes '(scala-mode scala-ts-mode) 861 | :priority -1 862 | :multi-root lsp-metals-multi-root 863 | :initialization-options '((decorationProvider . t) 864 | (inlineDecorationProvider . t) 865 | (didFocusProvider . t) 866 | (executeClientCommandProvider . t) 867 | (doctorProvider . "html") 868 | (statusBarProvider . "on") 869 | (debuggingProvider . t) 870 | (treeViewProvider . t) 871 | (quickPickProvider . t) 872 | (inputBoxProvider . t) 873 | (commandInHtmlFormat . "vscode")) 874 | :notification-handlers (ht ("metals/executeClientCommand" #'lsp-metals--execute-client-command) 875 | ("metals/publishDecorations" #'lsp-metals--publish-decorations) 876 | ("metals/treeViewDidChange" #'lsp-metals-treeview--did-change) 877 | ("metals-model-refresh" #'lsp-metals--model-refresh) 878 | ("metals/status" #'lsp-metals--status-string)) 879 | :request-handlers (ht ("metals/quickPick" #'lsp-metals--quick-pick) 880 | ("metals/inputBox" #'lsp-metals--input-box)) 881 | :action-handlers (ht ("metals-debug-session-start" (-partial #'lsp-metals--debug-start :json-false)) 882 | ("metals-run-session-start" (-partial #'lsp-metals--debug-start t))) 883 | :server-id 'metals 884 | :initialized-fn (lambda (workspace) 885 | (with-lsp-workspace workspace 886 | (lsp--set-configuration 887 | (lsp-configuration-section "metals")))) 888 | :after-open-fn (lambda () 889 | (add-hook 'lsp-on-idle-hook #'lsp-metals--did-focus nil t)) 890 | :completion-in-comments? t 891 | :download-server-fn #'lsp-metals--download-server)) 892 | 893 | (defmacro lsp-metals--create-bool-toggle (name config var) 894 | "Create a toggle for lsp-metal config. 895 | NAME is a user-facing name used for the interactive command. CONFIG is the LSP 896 | configuration name. VAR is the variable holding the value of the configuration." 897 | (let ((func-name (intern (format "lsp-metals-toggle-%s" name)))) 898 | `(defun ,func-name () 899 | ,(format "Toggle LSP metals %s config" name) 900 | (interactive) 901 | (setq ,var (not ,var)) 902 | (lsp-register-custom-settings '((,(format "metals.%s" config) ,var t))) 903 | (with-lsp-workspaces (lsp-metals--workspaces) 904 | (lsp--set-configuration (lsp-configuration-section "metals"))) 905 | (let ((status (if ,var "on" "off"))) 906 | (lsp--info "Turned %s %s" status ,name))))) 907 | 908 | (lsp-metals--create-bool-toggle "show-super-method-lenses" "super-method-lenses-enabled" lsp-metals-super-method-lenses-enabled) 909 | (lsp-metals--create-bool-toggle "enable-semantic-highlighting" "enable-semantic-highlighting" lsp-metals-enable-semantic-highlighting) 910 | (lsp-metals--create-bool-toggle "inlay-hints-enable-inferred-types" "inlay-hints.inferredTypes.enable" lsp-metals-inlay-hints-enable-inferred-types) 911 | (lsp-metals--create-bool-toggle "inlay-hints-enable-implicit-conversions" "inlay-hints.implicitConversions.enable" lsp-metals-inlay-hints-enable-implicit-conversions) 912 | (lsp-metals--create-bool-toggle "inlay-hints-enable-implicit-arguments" "inlay-hints.implicitArguments.enable" lsp-metals-inlay-hints-enable-implicit-arguments) 913 | (lsp-metals--create-bool-toggle "inlay-hints-enable-type-parameters" "inlay-hints.typeParameters.enable" lsp-metals-inlay-hints-enable-type-parameters) 914 | (lsp-metals--create-bool-toggle "inlay-hints-enable-hints-in-pattern-match" "inlay-hints.hintsInPatternMatch.enable" lsp-metals-inlay-hints-enable-hints-in-pattern-match) 915 | 916 | (provide 'lsp-metals) 917 | ;;; lsp-metals.el ends here 918 | 919 | ;; Local Variables: 920 | ;; End: 921 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: LSP Metals 2 | site_url: https://emacs-lsp.github.io/lsp-metals 3 | 4 | nav: 5 | - Home: README.md 6 | - Changelog: CHANGELOG.md 7 | - LSP Mode: 8 | - LSP Mode: https://emacs-lsp.github.io/lsp-mode 9 | 10 | extra_css: 11 | - stylesheets/extra.css 12 | 13 | theme: 14 | name: material 15 | logo: images/logo.png 16 | favicon: images/logo.png 17 | icon: 18 | repo: fontawesome/brands/github 19 | features: 20 | - tabs 21 | 22 | extra: 23 | social: 24 | - icon: fontawesome/brands/github-alt 25 | link: https://github.com/emacs-lsp 26 | - icon: fontawesome/brands/twitter 27 | link: https://twitter.com/yonchovski 28 | - icon: fontawesome/brands/gitter 29 | link: https://gitter.im/emacs-lsp/lsp-mode 30 | 31 | repo_name: emacs-lsp/lsp-metals 32 | repo_url: https://github.com/emacs-lsp/lsp-metals 33 | 34 | markdown_extensions: 35 | - pymdownx.superfences 36 | - pymdownx.emoji: 37 | emoji_index: !!python/name:materialx.emoji.twemoji 38 | emoji_generator: !!python/name:materialx.emoji.to_svg 39 | - codehilite 40 | - toc: 41 | permalink: '#' 42 | 43 | plugins: 44 | - search 45 | - awesome-pages 46 | - git-revision-date-localized 47 | 48 | google_analytics: 49 | - UA-165661257-4 50 | - auto 51 | --------------------------------------------------------------------------------