Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking Issue: Language Server Selection #10906

Open
4 of 5 tasks
maxdeviant opened this issue Apr 23, 2024 · 20 comments
Open
4 of 5 tasks

Tracking Issue: Language Server Selection #10906

maxdeviant opened this issue Apr 23, 2024 · 20 comments
Assignees
Labels
ignore top-ranking issues [ignored label] language server An umbrella label for all language servers tracking Issue that tracks a group of related issues

Comments

@maxdeviant
Copy link
Member

maxdeviant commented Apr 23, 2024

This is a tracking issue for adding support for selecting which language servers should run for a given language.

Problem

Today, language servers in Zed are always run when installed and a buffer is opened with a language that has one or more registered language servers.

This can create issues with language servers conflicting with one another when multiple language servers are running for the same language.

There are also cases where a user may want to use an existing language server—provided by Zed or by an extension—with a language that is not registered for use with that language server.

Finally, there are cases where a user may want to disable the use of a particular language server entirely if they do not want to use it.

User Stories

  • User A wants to use the Biome language server instead of the built-in TypeScript and ESLint language servers.
  • User B wants to choose between two different language servers available for Elixir.
  • User C wants to use the toml extension for TOML syntax highlighting, but does not want to use the taplo language server.
  • User D wants to use the Tailwind language server with ASP.NET Razor pages (*.cshtml).

Proposed Solution

To provide Zed users with the ability to customize which language servers are run, we propose a new language_servers field under a given language's settings:

{
  "languages": {
    "TypeScript": {
      "language_servers": ["biome", "!typescript-language-server", "..."]
    }
  }
}

The language_servers field would be an array consisting of strings corresponding to the IDs of language servers.

In addition to specific language server IDs, the following additional syntax would be supported:

  • "!<language-server-id>" - Language server IDs may optionally be prefixed with a ! to remove the specified language server from consideration.
    • For example, "!typescript-language-server" would disable the typescript-language-server for the given language.
  • "..." - A placeholder to refer to the rest of the registered language servers for a given language.
    • This list would not contain any language servers that have already been referenced, e.g., if the set of all available language servers is ["a", "b", "c"] and the language_servers field contains ["!a", "..."] then "..." would expand to just ["b", "c"].

The introduction of the language_servers setting would supersede the following partial solutions we currently have in place to address these issues in a non-general fashion:

  • The elixir.lsp setting
    • This is currently used to choose between language servers to use with Elixir.
  • The deno.enable setting
    • This is currently used to toggle between using Deno over the built-in typescript-language-server and eslint.

User Stories

User A wants to use the Biome language server instead of the built-in TypeScript and ESLint language servers.

User A would add the following to their settings to use the Biome language server for their .ts and .tsx files:

{
  "languages": {
    "TypeScript": {
      "language_servers": ["biome", "!typescript-language-server", "!eslint", "..."]
    },
    "TSX": {
      "language_servers": ["biome", "!typescript-language-server", "!eslint", "..."]
    }
  }
}

User B wants to choose between two different language servers available for Elixir.

Zed currently offers two language servers for Elixir: elixir-ls and next-ls. elixir-ls is currently used by default.

If User B wants to prefer next-ls over elixir-ls they can do:

{
  "languages": {
    "Elixir": {
      "language_servers": ["next-ls", "..."]
    }
  }
}

This would give next-ls precedence over elixir-ls due to being first in the list. If User B wants to disable elixir-ls entirely (to prevent it from running at all), they could do:

{
  "languages": {
    "Elixir": {
      "language_servers": ["elixir-ls", "!next-ls", "..."]
    }
  }
}

User C wants to use the toml extension for TOML syntax highlighting, but does not want to use the taplo language server.

User C would add the following to their language settings to disable the taplo language server for TOML files:

{
  "languages": {
    "TOML": {
      "language_servers": ["!taplo", "..."]
    }
  }
}

User D wants to use the Tailwind language server with ASP.NET Razor pages (*.cshtml).

User D would add the following to their language settings to enable Zed's built-in Tailwind language server for their Razor files:

{
  "languages": {
    "Razor": {
      "language_servers": ["...", "tailwindcss-language-server"]
    }
  }
}

Steps

Related Issues

Biome

Deno

Elixir

Ruby

@maxdeviant maxdeviant added language server An umbrella label for all language servers tracking Issue that tracks a group of related issues labels Apr 23, 2024
@maxdeviant maxdeviant self-assigned this Apr 23, 2024
maxdeviant added a commit that referenced this issue Apr 23, 2024
… run (#10911)

This PR adds a new `language_servers` setting underneath the language
settings.


This setting controls which of the available language servers for a
given language will run.

The `language_servers` setting is an array of strings. Each item in the
array must be either:

- A language server ID (e.g., `"rust-analyzer"`,
`"typescript-language-server"`, `"eslint"`, etc.) denoting a language
server that should be enabled.
- A language server ID prefixed with a `!` (e.g., `"!rust-analyzer"`,
`"!typescript-language-server"`, `"!eslint"`, etc.) denoting a language
server that should be disabled.
- A `"..."` placeholder, which will be replaced by the remaining
available language servers that haven't already been mentioned in the
array.

For example, to enable the Biome language server in place of the default
TypeScript language server, you would add the following to your
settings:

```json
{
  "languages": {
    "TypeScript": {
      "language_servers": ["biome", "!typescript-language-server", "..."]
    }
  }
}
```

More details can be found in #10906.

Release Notes:

- Added `language_servers` setting to language settings for customizing
which language server(s) run for a given language.
@syphar
Copy link

syphar commented Apr 24, 2024

@maxdeviant I have a LSP usecase and would like to know if this should work at the moment, of after the language server selection feature.

I want to add ruff-lsp for formatting & linting, and some code actions (#4539). But I also want to keep pyright for the rest.

Would this work? I would expect diagnostics & code actions to come from both language servers

@failable
Copy link

Tried to use a different Python LSP, but this does not work

  "languages": {
    "Python": {
      "language_servers": ["pylsp", "!pyright"]
    }
  }

@drcongo
Copy link

drcongo commented Apr 25, 2024

I tried killing pyright without luck too.

"languages": {
  "Python": {
    "language_servers": ["ruff-lsp", "pylsp", "!pyright"]
  }
}

@syphar
Copy link

syphar commented Apr 25, 2024

I tried killing pyright without luck too.

"languages": {
  "Python": {
    "language_servers": ["ruff-lsp", "pylsp", "!pyright"]
  }
}

my understanding is that before lsp selection, each lsp has to be provided by the zed-core or an extension. And I don't see any ruff-lsp or pylsp extensions yet.

For python I could also imagine the python extension (when it's extracted) can select the LSP based on a setting?

@jansol
Copy link
Contributor

jansol commented Apr 25, 2024

my understanding is that before lsp selection, each lsp has to be provided by the zed-core or an extension. And I don't see any ruff-lsp or pylsp extensions yet.

Correct, that is the case for now.

It would be nice to be able to register arbitrary language servers simply by creating an entry for them under "lsp" and providing a path to a local binary. And then add them to the list of servers to be used for individual languages.

@cholwell
Copy link

Would be nice to be able to see somewhere in the ui the running language servers

@syphar
Copy link

syphar commented Apr 25, 2024

It would be nice to be able to register arbitrary language servers simply by creating an entry for them under "lsp" and providing a path to a local binary. And then add them to the list of servers to be used for individual languages.

Looking at the extension API there also has to be some custom conversion logic for every LSP, so not sure if we can get around writing an extension for each of them.

Still, especially with python, having multiple active servers is not unusual / needed. Or using pylsp, which supports extensions

@maxdeviant
Copy link
Member Author

my understanding is that before lsp selection, each lsp has to be provided by the zed-core or an extension. And I don't see any ruff-lsp or pylsp extensions yet.

Correct, that is the case for now.

It would be nice to be able to register arbitrary language servers simply by creating an entry for them under "lsp" and providing a path to a local binary. And then add them to the list of servers to be used for individual languages.

I don't think we're going to support this (at least, there are no plans to as part of this work).

As @syphar notes:

Looking at the extension API there also has to be some custom conversion logic for every LSP, so not sure if we can get around writing an extension for each of them.

Right now the intent is that any language servers should be provided by extensions.

@maxdeviant
Copy link
Member Author

Would be nice to be able to see somewhere in the ui the running language servers

You can see a list of the currently running language servers in debug: open language server logs:

Screenshot 2024-04-25 at 11 15 06 AM

@syphar
Copy link

syphar commented Apr 25, 2024

when having multiple LSP it would be cool to be able to configure which to use for which use-case:

  • for formatting it's probably enough to select one
  • for diagnostics I would like to have multiple sources
  • same for code actions

not for my current use-case, but generally I could also imagine having multiple servers for autocompletions.

@vitallium
Copy link
Contributor

@maxdeviant Hi, is there a plan to extract the solargraph (LSP) for Ruby? There is an issue regarding adding support for the Ruby LSP. I was considering writing an extension for it, but upon finding this issue, I thought it would be better to ask before. As far as I understand, extracting the solargraph LSP could serve as a good starting point for integrating Ruby LSP into the same extension, similar to what was done for the Elixir language in #10948. So, Is it okay if I start working on extracting the solargraph gem into an extension as the starting point?

@maxdeviant
Copy link
Member Author

@maxdeviant Hi, is there a plan to extract the solargraph (LSP) for Ruby? There is an issue regarding adding support for the Ruby LSP. I was considering writing an extension for it, but upon finding this issue, I thought it would be better to ask before. As far as I understand, extracting the solargraph LSP could serve as a good starting point for integrating Ruby LSP into the same extension, similar to what was done for the Elixir language in #10948. So, Is it okay if I start working on extracting the solargraph gem into an extension as the starting point?

If you'd like to start on extracting the Ruby support into an extension (we can keep it in the extensions/ directory in the Zed repo, for now), that would be great!

@luckydye
Copy link
Contributor

luckydye commented May 2, 2024

Maybe related: #11288

@d1y d1y mentioned this issue May 4, 2024
1 task
@vitallium
Copy link
Contributor

@maxdeviant

@maxdeviant Hi, is there a plan to extract the solargraph (LSP) for Ruby? There is an issue regarding adding support for the Ruby LSP. I was considering writing an extension for it, but upon finding this issue, I thought it would be better to ask before. As far as I understand, extracting the solargraph LSP could serve as a good starting point for integrating Ruby LSP into the same extension, similar to what was done for the Elixir language in #10948. So, Is it okay if I start working on extracting the solargraph gem into an extension as the starting point?

If you'd like to start on extracting the Ruby support into an extension (we can keep it in the extensions/ directory in the Zed repo, for now), that would be great!

Awesome, I created a pull request here #11360. Thanks!

@trickster
Copy link

ruff-lsp will be superseded by ruff server for Python LSP implementation. Is there a plan to support that as well?

@jansol
Copy link
Contributor

jansol commented Jun 4, 2024

ruff-lsp will be superseded by ruff server for Python LSP implementation. Is there a plan to support that as well?

That just means that the way to launch the LSP changes, should be easy to account for. And given that ruff is not supported at all yet the new way should probably be the one that is implemented (first, or even only)

@JosephTLyons JosephTLyons added ignore top-ranking issues [ignored label] and removed meta labels Jun 5, 2024
@tashi21
Copy link

tashi21 commented Jun 14, 2024

Is there any way for me to see what are the valid language server ids available for me to use currently?

@tobico
Copy link

tobico commented Sep 3, 2024

Another use case: it would be very helpful to be able to select which language server to use on a per-file or per-folder basis.

I have a number of projects which use both Deno and Typescript within different folders of the same repo, and I need the correct language server to be used for each file.

@injust
Copy link
Contributor

injust commented Sep 18, 2024

This syntax is really confusing to me. I understand that this handles some advanced scenarios, but it seems like you've introduced a tri-state for something that is conventionally a boolean.

What's the difference between ["foo", "bar"] and ["foo", "bar", "!baz"]?

Does "..." mean "any language server that is installed but not present in the array becomes implicitly enabled"?

@achimnol
Copy link

achimnol commented Dec 20, 2024

I keep observing pyright (supplementary) and pylsp (supplementary) are fighting with each other whenever I restart Zed. One of them are randomly used while my workspace lsp is always chosen as pylsp -- the result is that I have two LSPs (supplementary one and configured one) running concurrently, which is completely unnecessary, even I have disabled pyright using the "!" syntax.

My project settings say:

{
  "languages": {
    "Python": {
      "language_servers": ["!pyright", "!pyflakes", "pylsp"],
      // ...
    },
  },
  // ...
}

Sometimes:
image
Sometimes:
image

I have to repeat restarting Zed until I get the second. I'd like to know how to disable the supplementary one.
I also suspect that having two LSPs may be a potential root cause of #21076.

Related:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ignore top-ranking issues [ignored label] language server An umbrella label for all language servers tracking Issue that tracks a group of related issues
Projects
None yet
Development

No branches or pull requests