Skip to content

lue-bird/elm-language-server-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Small LSP language server for elm. To use, install rust and

cargo +nightly install --git https://github.com/lue-bird/elm-language-server-rs

Then point your editor to elm-language-server-rs, see also specific setups.

Works with elm-format and elm-test, install them if you haven't already.

You can also set their paths in the language server settings:

  • elm-language-server-rs.elmPath: string: compiler executable, default "elm"
  • elm-language-server-rs.elmTestPath: string: test runner executable, default "elm-test"
  • elm-language-server-rs.elmFormatPath: "builtin" | string: formatter executable, default "elm-format". "builtin" is a fast rust formatter that is mostly but not fully compatible

goals

  • fast (e.g. rename must be instant no matter the project size)
  • reliable (e.g. work well with code that only partially parses)
  • getting to know the language server protocol :)

not planned

  • support for elm version <= 0.18 or inline GLSL blocks
  • type inference
  • directly integrating elm-test running
  • codelens, workspace symbols, code folding, linked editing
  • elm.json help

not sure (Please give me feedback on this)

  • add support for elm-review (depends on how easy it is, which I assume it isn't)
  • show all module exposes when hovering (..) (only if I have time and there is interest)
  • add code actions like "expose (including variants)", "inline", "inline all uses" (leaning towards no as it is fairly complicated, though it is very useful for sure)
  • show function parameter names (leaning towards no, as they are often confusing if they are curried, reveal non-exposed variant patterns, have more parameters than the type suggests, are undescriptive etc)
  • currently, an exposed member will still be suggested even when a local module-declared reference/local binding with the same name exists. Likewise, a local module-declared reference will still be suggested even when a local binding with the same name exists. (somewhat easily fixable but I don't really see the harm in directly showing this shadowing in your face)
  • add support for using elm-dev as the compiler to speed up compile times and retrieve type info (I assume integrating it is hard and to me it seems kind of bloated with MCP bs etc, currently leaning towards no)
  • your idea 👀

known limitations

  • It is possible that an elm module belongs to multiple projects when source directory paths overlap between projects. This throws a wrench in pretty much all existing code (likely internal document source desync and a more limited lsp feature range in one of the containing projects). This situation is, I assume, fixable by special-casing their storage and handling but it would require a lot of work

editor setups

feel free to contribute, as I only use vscodium

vscode-like

pre-built

  1. download https://github.com/lue-bird/elm-language-server-rs/blob/main/vscode/elm-language-server-rs-0.0.1.vsix
  2. open the command bar at the top and select: >Extensions: Install from VSIX

build from source

  1. clone this repo
  2. open vscode/
  3. run npm run package to create the .vsix
  4. open the command bar at the top and select: >Extensions: Install from VSIX

server only

There is no built-in language server bridge as far as I know but you can install an extension like vscode-generic-lsp-proxy that will work for any language server. Then add a .vscode/lsp-proxy.json like

[
  {
    "languageId": "elm",
    "command": "elm-language-server-rs",
    "fileExtensions": [
      ".json",
      ".elm"
    ]
  }
]

helix

write to ~/.config/helix/languages.toml:

[language-server.elm-language-server-rs]
command = "elm-language-server-rs"
[[language]]
name = "elm"
scope = "source.elm"
injection-regex = "elm"
roots = ["elm.json"]
file-types = ["elm", "json"]
comment-token = "--"
block-comment-tokens = { start = "{-", end = "-}" }
indent = { tab-width = 4, unit = "    " }
language-servers = [ "elm-language-server-rs" ]
auto-format = true

setup for developing

Rebuild the project with

cargo build

Then point your editor to the created ???/target/debug/elm-language-server-rs.

log of failed optimizations

  • switching to mimalloc, ~>25% faster (really nice) at the cost of 25% more memory consumption. Might be worth for some people but I'm already worried about our memory footprint!
  • declarations.shrink_to_fit(); saves around 0.6% of memory at the cost of a bit of speed
  • upgrading lto to "thin" to "fat" both improve runtime speed by ~13% compared to the default (and reduce binary size) but increase build time by about 30% (default to thin) and 15% (thin to fat). As this prolongs installation and prevents people from quickly trying it, the default is kept. If this language server get distributed as a binary or people end up using this language server a lot, this "thin" might become a reasonable trade-off.

optimizations to try

  • reparse incrementally (somewhat easy to implement but somehow it's for me at least pretty much fast enough already without? More data points welcome)
  • switch to position_encoding: Some(lsp_types::PositionEncodingKind::UTF8). This makes source edits and parsing easier and faster at the cost of compatibility with lsp clients below version 3.17.0. Is that acceptable? (leaning towards yes). Also validate if elm --report region column is UTF-8 or UTF-16 (seems to be UTF-16 strangely)
  • if memory consumptions turns out to be a problem, stop storing the source in memory and request full file content on each change (potentially only for dependencies). This adds complexity and is slower so only if necessary.
  • in syntax tree, use separate range type for single-line tokens like keywords, symbols, names etc to save on memory consumption
  • switch most syntax tree Box<str>s to https://docs.rs/smallstr/0.3.1/smallstr/ to for example speed up collecting references (e.g. for rename)
  • in syntax tree, use Box<[]> instead of Vec for common nodes like call arguments
  • on init, read modules in parallel, not just projects, to even out difference in project size (seems not worth using threads, maybe something more lightweight?)

About

language server for elm, in rust

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published