├── .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 | [](https://melpa.org/#/lsp-metals)
2 | [](https://stable.melpa.org/#/lsp-metals)
3 |
4 |
5 | # lsp-metals
6 |
7 | [](https://github.com/emacs-lsp/lsp-metals/actions)
8 | [](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 |
--------------------------------------------------------------------------------