Несколько недель назад я занимался хаком языковых серверов в 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
.