-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
linux: Fix "Failed to start language server" errors when starting Zed from .desktop file #22335
linux: Fix "Failed to start language server" errors when starting Zed from .desktop file #22335
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much simpler, thank you.
… from .desktop file (zed-industries#22335) Closes zed-industries#21406 Context: A few weeks ago on Linux, we resolved an [issue](zed-industries#20070) where users could not open more than one file from the file explorer. This was fixed by replacing `zed-editor` (zed binary in the code) with `zed` (cli binary in the code) in the `.desktop` file. The reason for this change was that using the cli to open files is more convenient - it determines weather to spawn a new Zed instance or use an existing one, if we use main binary instead it would throw error `Zed is already running`. You can read the complete PR here: [linux: Fix file not opening from file explorer](zed-industries#21137). While this fix resolved the original issue, it introduced a new one. Problem: When the cli binary is used, it assumes it is always being invoked from a terminal and relies on `std::env::vars()` to retrieve the environment variables needed to spawn Zed. These env vars are then passed to the worktree, and eventually, languages use the `PATH` from this env to find binaries. This leads to the "Failed to start language server" error when the `.desktop` entry is used on Linux. Solution: When the `zed-editor` binary is used, it uses some clever Unix-specific logic to retrieve the default shell (`load_shell_from_passwd`) and then fetch the env vars from that shell (`load_login_shell_environment`). This same logic should be used in the cli binary when it is invoked via a `.desktop` entry rather than from a terminal. Approach: I moved these two functions mentioned above to a utils file and reused them in cli binary to fetch env vars only on Linux when it is not run from a terminal. This provides missing paths, and fix the issue. It is also possible to handle this in the `zed-editor` binary by modifying the logic in `handle_cli_connection`, where `CliRequest::Open` is processed. There we can discard incoming env, and use our logic. But discarding incoming envs felt weird, and I thought it's better to handle this at source. Release Notes: - Fixed `Failed to start language server` errors when starting from dekstop entry on Linux
… from .desktop file (zed-industries#22335) Closes zed-industries#21406 Context: A few weeks ago on Linux, we resolved an [issue](zed-industries#20070) where users could not open more than one file from the file explorer. This was fixed by replacing `zed-editor` (zed binary in the code) with `zed` (cli binary in the code) in the `.desktop` file. The reason for this change was that using the cli to open files is more convenient - it determines weather to spawn a new Zed instance or use an existing one, if we use main binary instead it would throw error `Zed is already running`. You can read the complete PR here: [linux: Fix file not opening from file explorer](zed-industries#21137). While this fix resolved the original issue, it introduced a new one. Problem: When the cli binary is used, it assumes it is always being invoked from a terminal and relies on `std::env::vars()` to retrieve the environment variables needed to spawn Zed. These env vars are then passed to the worktree, and eventually, languages use the `PATH` from this env to find binaries. This leads to the "Failed to start language server" error when the `.desktop` entry is used on Linux. Solution: When the `zed-editor` binary is used, it uses some clever Unix-specific logic to retrieve the default shell (`load_shell_from_passwd`) and then fetch the env vars from that shell (`load_login_shell_environment`). This same logic should be used in the cli binary when it is invoked via a `.desktop` entry rather than from a terminal. Approach: I moved these two functions mentioned above to a utils file and reused them in cli binary to fetch env vars only on Linux when it is not run from a terminal. This provides missing paths, and fix the issue. It is also possible to handle this in the `zed-editor` binary by modifying the logic in `handle_cli_connection`, where `CliRequest::Open` is processed. There we can discard incoming env, and use our logic. But discarding incoming envs felt weird, and I thought it's better to handle this at source. Release Notes: - Fixed `Failed to start language server` errors when starting from dekstop entry on Linux
Hey! Found this commit after #9786 (comment) and I'm wondering whether we couldn't get away with a shorter solution. Here's how the CLI/main-binary env handling works, essentially: the CLI passes an environment to the So what I'm thinking here is that instead of loading the login-shell env in the CLI and then passing it on, why not pass This, basically: diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs
index 32c5bbb21b..4cb47c1f24 100644
--- a/crates/cli/src/main.rs
+++ b/crates/cli/src/main.rs
@@ -167,16 +167,15 @@ fn main() -> Result<()> {
None
};
+ let mut env = Some(std::env::vars().collect::<HashMap<_, _>>());
+
// On Linux, desktop entry uses `cli` to spawn `zed`, so we need to load env vars from the shell
// since it doesn't inherit env vars from the terminal.
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
if !std::io::stdout().is_terminal() {
- load_shell_from_passwd().log_err();
- load_login_shell_environment().log_err();
+ env = None;
}
- let env = Some(std::env::vars().collect::<HashMap<_, _>>());
-
let exit_status = Arc::new(Mutex::new(None));
let mut paths = vec![];
let mut urls = vec![]; Then we don't need to whole env-loading logic shared. What do you think @SomeoneToIgnore @0xtimsb? |
Sounds great too, I'm somewhat limited in this field and do not see all possible ways to get it working. |
Hi @mrnugget, I think what you're saying makes sense. I somehow missed the code below while working on the fix and assumed the only way to pass the right envs to the LSP was through the CLI. I didn’t realize there was already a fallback setup if /// Returns the project environment, if possible.
/// If the project was opened from the CLI, then the inherited CLI environment is returned.
/// If it wasn't opened from the CLI, and a worktree is given, then a shell is spawned in
/// the worktree's path, to get environment variables as if the user has `cd`'d into
/// the worktrees path.
pub(crate) fn get_environment( |
If you want, that'd be neat! I think that comment ~could stay the same, if we add the note that "opened from the CLI" can mean that the Zed CLI binary was used, but not on the CLI. |
On it. |
… (direnv) (#22803) Closes #18908 This PR started as a cleanup of redundant logic for setting up envs when Zed is launched as a desktop entry on Linux. More on this can be read [here](#22335 (comment)). The TLDR is that desktop entries on Linux sometimes might not have the correct envs (as they don't `cwd` into your project directory). To address this, we initially tried to fix it by loading the default shell and its env vars. However, a better solution, as recommended by @mrnugget, is to pass `env` as `None`. Internally, if `env` is `None`, it falls back to the project's working dir envs. This removes the need to manually load the envs and is cleaner. Additionally, it also fixes an issue with Zed not loading project-specific envs because now we are actually doing so (albeit unintentionally?). I don't have macOS to test, but I believe this is not an issue on macOS since it uses the Zed binary instead of the CLI, which essentially sets the CLI `env` to `None` automatically. Before: Here, I have `/home/tims/go/bin` set up in `.envrc`, which only loads in that project directory. When launching Zed via the CLI in the project directory, notice `/home/tims/go/bin` is in the `PATH`. As a result, we use the user-installed `gopls` server. ```sh [INFO] attempting to start language server "gopls", path: "/home/tims/temp/go-proj", id: 1 [INFO] using project environment variables from CLI. PATH="/home/tims/go/bin:/usr/local/go/bin" [INFO] found user-installed language server for gopls. path: "/home/tims/go/bin/gopls", arguments: ["-mode=stdio"] [INFO] starting language server process. binary path: "/home/tims/go/bin/gopls", working directory: "/home/tims/temp/go-proj", args: ["-mode=stdio"] ``` However, when using the desktop entry and attempting to load envs from the default shell, notice `/home/tims/go/bin` is no longer there since it's not in the project directory. Zed cannot find the user-installed language server and starts downloading its own `gopls`. ```sh [INFO] attempting to start language server "gopls", path: "/home/tims/temp/go-proj", id: 1 [INFO] using project environment variables from CLI. PATH="/usr/local/go/bin" [INFO] fetching latest version of language server "gopls" [INFO] downloading language server "gopls" [INFO] starting language server process. binary path: "/home/tims/.local/share/zed/languages/gopls/gopls_0.17.1_go_1.23.4", working directory: "/home/tims/temp/go-proj", args: ["-mode=stdio"] ``` After: When using the desktop entry, we pass the CLI env as `None`. For the language server, it falls back to the project directory envs. Result, Zed finds the user-installed language server. ```sh [INFO] attempting to start language server "gopls", path: "/home/tims/temp/go-proj", id: 1 [INFO] using project environment variables shell launched in "/home/tims/temp/go-proj". PATH="/home/tims/go/bin:/usr/local/go/bin" [INFO] found user-installed language server for gopls. path: "/home/tims/go/bin/gopls", arguments: ["-mode=stdio"] [INFO] starting language server process. binary path: "/home/tims/go/bin/gopls", working directory: "/home/tims/temp/go-proj", args: ["-mode=stdio"] ``` Release Notes: - Fixed issue with project-specific env not being found via .envrc (direnv) on Linux
Closes #21406
Context:
A few weeks ago on Linux, we resolved an issue where users could not open more than one file from the file explorer. This was fixed by replacing
zed-editor
(zed binary in the code) withzed
(cli binary in the code) in the.desktop
file. The reason for this change was that using the cli to open files is more convenient - it determines weather to spawn a new Zed instance or use an existing one, if we use main binary instead it would throw errorZed is already running
.You can read the complete PR here: linux: Fix file not opening from file explorer.
While this fix resolved the original issue, it introduced a new one.
Problem:
When the cli binary is used, it assumes it is always being invoked from a terminal and relies on
std::env::vars()
to retrieve the environment variables needed to spawn Zed. These env vars are then passed to the worktree, and eventually, languages use thePATH
from this env to find binaries. This leads to the "Failed to start language server" error when the.desktop
entry is used on Linux.Solution:
When the
zed-editor
binary is used, it uses some clever Unix-specific logic to retrieve the default shell (load_shell_from_passwd
) and then fetch the env vars from that shell (load_login_shell_environment
). This same logic should be used in the cli binary when it is invoked via a.desktop
entry rather than from a terminal.Approach:
I moved these two functions mentioned above to a utils file and reused them in cli binary to fetch env vars only on Linux when it is not run from a terminal. This provides missing paths, and fix the issue.
It is also possible to handle this in the
zed-editor
binary by modifying the logic inhandle_cli_connection
, whereCliRequest::Open
is processed. There we can discard incoming env, and use our logic. But discarding incoming envs felt weird, and I thought it's better to handle this at source.Release Notes:
Failed to start language server
errors when starting from dekstop entry on Linux