
Несколько недель назад я занимался хаком языковых серверов в Zed, пытаясь заставить Zed определять, когда заданный бинарник языкового сервера, например gopls, уже присутствует в $PATH. Если так, вместо загрузки нового бинарника Zed должен использовать его.
Трудность: часто $PATH динамически изменяется такими инструментами, как direnv, asdf, mise и другими, которые позволяют в данной папке установить определённый $PATH. Почему эти инструменты так делают? Потому что это даёт возможность, скажем, в начале $PATH добавить ./my_custom_binaries, пока вы находитесь в my-cool-project. Поэтому нельзя просто использовать $PATH, связанный с процессом Zed, нужен $PATH, как он есть, когда выполняется cd в каталог проекта.
Легко, подумал я. Просто запусти $SHELL, выполни cd в проект, чтобы запустить direnv и всё такое, запусти env, сохрани окружение, выбери $PATH, найди в нём бинарники. И это было легко. Вот часть кода, та часть, которая запускает $SHELL, cd и получает env:
fn load_shell_environment(dir: &Path) -> Result<HashMap<String, String>> {
// Получает $SHELL
let shell = std::env::var("SHELL")?;
// Конструирует команду, которую хочется выполнить в $SHELL
let command = format!("cd {:?}; /usr/bin/env -0;", dir);
// Запускает $SHELL как интерактивную оболочку (чтобы использовались файлы пользователя rc).
// и выполняет `command`:
let output = std::process::Command::new(&shell)
.args(["-i", "-c", &command])
.output()?;
// [... проверка кода выхода, получение stdout, превращение stdout в HashMap и т. д. ...]
}
За исключением одного: после запуска экземпляра Zed в терминале, который выполнял эту функцию, я больше не мог убить Zed, нажав Ctrl-C.