mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-10 05:37:29 +03:00
Merge pull request #560 from zed-industries/login-shell-env
Populate environment from shell
This commit is contained in:
commit
479c0dd391
@ -179,19 +179,26 @@ pub struct LanguageRegistry {
|
||||
language_server_download_dir: Option<Arc<Path>>,
|
||||
lsp_binary_statuses_tx: async_broadcast::Sender<(Arc<Language>, LanguageServerBinaryStatus)>,
|
||||
lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc<Language>, LanguageServerBinaryStatus)>,
|
||||
login_shell_env_loaded: Shared<Task<()>>,
|
||||
}
|
||||
|
||||
impl LanguageRegistry {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(login_shell_env_loaded: Task<()>) -> Self {
|
||||
let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16);
|
||||
Self {
|
||||
language_server_download_dir: None,
|
||||
languages: Default::default(),
|
||||
lsp_binary_statuses_tx,
|
||||
lsp_binary_statuses_rx,
|
||||
login_shell_env_loaded: login_shell_env_loaded.shared(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test() -> Self {
|
||||
Self::new(Task::ready(()))
|
||||
}
|
||||
|
||||
pub fn add(&self, language: Arc<Language>) {
|
||||
self.languages.write().push(language.clone());
|
||||
}
|
||||
@ -234,7 +241,7 @@ impl LanguageRegistry {
|
||||
|
||||
pub fn start_language_server(
|
||||
&self,
|
||||
language: &Arc<Language>,
|
||||
language: Arc<Language>,
|
||||
root_path: Arc<Path>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
cx: &mut MutableAppContext,
|
||||
@ -273,28 +280,28 @@ impl LanguageRegistry {
|
||||
|
||||
let adapter = language.adapter.clone()?;
|
||||
let background = cx.background().clone();
|
||||
let server_binary_path = {
|
||||
Some(
|
||||
language
|
||||
.lsp_binary_path
|
||||
.lock()
|
||||
.get_or_insert_with(|| {
|
||||
get_server_binary_path(
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
http_client,
|
||||
download_dir,
|
||||
self.lsp_binary_statuses_tx.clone(),
|
||||
)
|
||||
.map_err(Arc::new)
|
||||
.boxed()
|
||||
.shared()
|
||||
})
|
||||
.clone()
|
||||
.map_err(|e| anyhow!(e)),
|
||||
)
|
||||
}?;
|
||||
let lsp_binary_statuses = self.lsp_binary_statuses_tx.clone();
|
||||
let login_shell_env_loaded = self.login_shell_env_loaded.clone();
|
||||
Some(cx.background().spawn(async move {
|
||||
login_shell_env_loaded.await;
|
||||
let server_binary_path = language
|
||||
.lsp_binary_path
|
||||
.lock()
|
||||
.get_or_insert_with(|| {
|
||||
get_server_binary_path(
|
||||
adapter.clone(),
|
||||
language.clone(),
|
||||
http_client,
|
||||
download_dir,
|
||||
lsp_binary_statuses,
|
||||
)
|
||||
.map_err(Arc::new)
|
||||
.boxed()
|
||||
.shared()
|
||||
})
|
||||
.clone()
|
||||
.map_err(|e| anyhow!(e));
|
||||
|
||||
let server_binary_path = server_binary_path.await?;
|
||||
let server_args = adapter.server_args();
|
||||
let server = lsp::LanguageServer::new(
|
||||
|
@ -25,7 +25,7 @@ fn init_logger() {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_select_language() {
|
||||
let registry = LanguageRegistry::new();
|
||||
let registry = LanguageRegistry::test();
|
||||
registry.add(Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
|
@ -403,7 +403,7 @@ impl Project {
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test(fs: Arc<dyn Fs>, cx: &mut gpui::TestAppContext) -> ModelHandle<Project> {
|
||||
let languages = Arc::new(LanguageRegistry::new());
|
||||
let languages = Arc::new(LanguageRegistry::test());
|
||||
let http_client = client::test::FakeHttpClient::with_404_response();
|
||||
let client = client::Client::new(http_client.clone());
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
@ -1017,7 +1017,7 @@ impl Project {
|
||||
.entry(key.clone())
|
||||
.or_insert_with(|| {
|
||||
let language_server = self.languages.start_language_server(
|
||||
&language,
|
||||
language.clone(),
|
||||
worktree_path,
|
||||
self.client.http_client(),
|
||||
cx,
|
||||
|
@ -1065,7 +1065,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_share_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
cx_a.foreground().forbid_parking();
|
||||
|
||||
@ -1197,7 +1197,7 @@ mod tests {
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_unshare_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
cx_a.foreground().forbid_parking();
|
||||
|
||||
@ -1297,7 +1297,7 @@ mod tests {
|
||||
cx_b: &mut TestAppContext,
|
||||
cx_c: &mut TestAppContext,
|
||||
) {
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
cx_a.foreground().forbid_parking();
|
||||
|
||||
@ -1471,7 +1471,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_buffer_conflict_after_save(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Connect to a server as 2 clients.
|
||||
@ -1553,7 +1553,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_buffer_reloading(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Connect to a server as 2 clients.
|
||||
@ -1635,7 +1635,7 @@ mod tests {
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Connect to a server as 2 clients.
|
||||
@ -1714,7 +1714,7 @@ mod tests {
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Connect to a server as 2 clients.
|
||||
@ -1786,7 +1786,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_leaving_project(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Connect to a server as 2 clients.
|
||||
@ -1884,7 +1884,7 @@ mod tests {
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Set up a fake language server.
|
||||
@ -2103,7 +2103,7 @@ mod tests {
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Set up a fake language server.
|
||||
@ -2310,7 +2310,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_formatting_buffer(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Set up a fake language server.
|
||||
@ -2409,7 +2409,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_definition(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
fs.insert_tree(
|
||||
"/root-1",
|
||||
@ -2544,7 +2544,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_references(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
fs.insert_tree(
|
||||
"/root-1",
|
||||
@ -2675,7 +2675,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_project_search(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
fs.insert_tree(
|
||||
"/root-1",
|
||||
@ -2782,7 +2782,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_document_highlights(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
fs.insert_tree(
|
||||
"/root-1",
|
||||
@ -2918,7 +2918,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_project_symbols(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
fs.insert_tree(
|
||||
"/code",
|
||||
@ -3053,7 +3053,7 @@ mod tests {
|
||||
mut rng: StdRng,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
@ -3157,7 +3157,7 @@ mod tests {
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
let mut path_openers_b = Vec::new();
|
||||
cx_b.update(|cx| editor::init(cx, &mut path_openers_b));
|
||||
@ -3393,7 +3393,7 @@ mod tests {
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut TestAppContext) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let mut lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
let mut path_openers_b = Vec::new();
|
||||
cx_b.update(|cx| editor::init(cx, &mut path_openers_b));
|
||||
@ -4024,7 +4024,7 @@ mod tests {
|
||||
cx_c: &mut TestAppContext,
|
||||
) {
|
||||
cx_a.foreground().forbid_parking();
|
||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let fs = FakeFs::new(cx_a.background());
|
||||
|
||||
// Connect to a server as 3 clients.
|
||||
@ -4171,7 +4171,7 @@ mod tests {
|
||||
|
||||
let rng = Arc::new(Mutex::new(rng));
|
||||
|
||||
let guest_lang_registry = Arc::new(LanguageRegistry::new());
|
||||
let guest_lang_registry = Arc::new(LanguageRegistry::test());
|
||||
let (language_server_config, _fake_language_servers) = LanguageServerConfig::fake();
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
@ -4202,7 +4202,7 @@ mod tests {
|
||||
Project::local(
|
||||
host.client.clone(),
|
||||
host.user_store.clone(),
|
||||
Arc::new(LanguageRegistry::new()),
|
||||
Arc::new(LanguageRegistry::test()),
|
||||
fs.clone(),
|
||||
cx,
|
||||
)
|
||||
|
@ -505,7 +505,7 @@ impl WorkspaceParams {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test(cx: &mut MutableAppContext) -> Self {
|
||||
let fs = project::FakeFs::new(cx.background().clone());
|
||||
let languages = Arc::new(LanguageRegistry::new());
|
||||
let languages = Arc::new(LanguageRegistry::test());
|
||||
let http_client = client::test::FakeHttpClient::new(|_| async move {
|
||||
Ok(client::http::ServerResponse::new(404))
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ use anyhow::{anyhow, Context, Result};
|
||||
use async_compression::futures::bufread::GzipDecoder;
|
||||
use client::http::{self, HttpClient, Method};
|
||||
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||
use gpui::Task;
|
||||
pub use language::*;
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
@ -531,8 +532,8 @@ impl LspAdapter for JsonLspAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_language_registry() -> LanguageRegistry {
|
||||
let mut languages = LanguageRegistry::new();
|
||||
pub fn build_language_registry(login_shell_env_loaded: Task<()>) -> LanguageRegistry {
|
||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||
languages.set_language_server_download_dir(
|
||||
dirs::home_dir()
|
||||
.expect("failed to determine home directory")
|
||||
|
@ -1,14 +1,17 @@
|
||||
// Allow binary to be called Zed for a nice application menu when running executable direcly
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use client::{self, http, ChannelList, UserStore};
|
||||
use fs::OpenOptions;
|
||||
use gpui::{App, AssetSource};
|
||||
use gpui::{App, AssetSource, Task};
|
||||
use log::LevelFilter;
|
||||
use parking_lot::Mutex;
|
||||
use simplelog::SimpleLogger;
|
||||
use std::{fs, path::PathBuf, sync::Arc};
|
||||
use smol::process::Command;
|
||||
use std::{env, fs, path::PathBuf, sync::Arc};
|
||||
use theme::{ThemeRegistry, DEFAULT_THEME_NAME};
|
||||
use util::ResultExt;
|
||||
use workspace::{self, settings, AppState, OpenNew, OpenParams, OpenPaths, Settings};
|
||||
use zed::{
|
||||
self, assets::Assets, build_window_options, build_workspace, fs::RealFs, language, menus,
|
||||
@ -39,7 +42,16 @@ fn main() {
|
||||
},
|
||||
);
|
||||
let (settings_tx, settings) = postage::watch::channel_with(settings);
|
||||
let languages = Arc::new(language::build_language_registry());
|
||||
|
||||
let login_shell_env_loaded = if stdout_is_a_pty() {
|
||||
Task::ready(())
|
||||
} else {
|
||||
app.background().spawn(async {
|
||||
load_login_shell_environment().await.log_err();
|
||||
})
|
||||
};
|
||||
|
||||
let languages = Arc::new(language::build_language_registry(login_shell_env_loaded));
|
||||
languages.set_theme(&settings.borrow().theme.editor.syntax);
|
||||
|
||||
app.run(move |cx| {
|
||||
@ -127,12 +139,47 @@ fn init_logger() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_login_shell_environment() -> Result<()> {
|
||||
let marker = "ZED_LOGIN_SHELL_START";
|
||||
let shell = env::var("SHELL").context(
|
||||
"SHELL environment variable is not assigned so we can't source login environment variables",
|
||||
)?;
|
||||
let output = Command::new(&shell)
|
||||
.args(["-lic", &format!("echo {marker} && /usr/bin/env")])
|
||||
.output()
|
||||
.await
|
||||
.context("failed to spawn login shell to source login environment variables")?;
|
||||
if !output.status.success() {
|
||||
Err(anyhow!("login shell exited with error"))?;
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
if let Some(env_output_start) = stdout.find(marker) {
|
||||
let env_output = &stdout[env_output_start + marker.len()..];
|
||||
for line in env_output.lines() {
|
||||
if let Some(separator_index) = line.find('=') {
|
||||
let key = &line[..separator_index];
|
||||
let value = &line[separator_index + 1..];
|
||||
env::set_var(key, value);
|
||||
}
|
||||
}
|
||||
log::info!(
|
||||
"set environment variables from shell:{}, path:{}",
|
||||
shell,
|
||||
env::var("PATH").unwrap_or_default(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stdout_is_a_pty() -> bool {
|
||||
unsafe { libc::isatty(libc::STDOUT_FILENO as i32) != 0 }
|
||||
}
|
||||
|
||||
fn collect_path_args() -> Vec<PathBuf> {
|
||||
std::env::args()
|
||||
env::args()
|
||||
.skip(1)
|
||||
.filter_map(|arg| match fs::canonicalize(arg) {
|
||||
Ok(path) => Some(path),
|
||||
|
@ -25,7 +25,7 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
|
||||
let http = FakeHttpClient::with_404_response();
|
||||
let client = Client::new(http.clone());
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||
let languages = LanguageRegistry::new();
|
||||
let languages = LanguageRegistry::test();
|
||||
languages.add(Arc::new(language::Language::new(
|
||||
language::LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
|
Loading…
Reference in New Issue
Block a user