Populate environment from shell

Co-Authored-By: Keith Simmons <keith@zed.dev>
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Max Brunsfeld 2022-03-07 16:39:50 -08:00
parent bb6ab837cf
commit 5cc5fa2f93
8 changed files with 111 additions and 56 deletions

View File

@ -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,9 +280,11 @@ impl LanguageRegistry {
let adapter = language.adapter.clone()?;
let background = cx.background().clone();
let server_binary_path = {
Some(
language
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(|| {
@ -284,17 +293,15 @@ impl LanguageRegistry {
language.clone(),
http_client,
download_dir,
self.lsp_binary_statuses_tx.clone(),
lsp_binary_statuses,
)
.map_err(Arc::new)
.boxed()
.shared()
})
.clone()
.map_err(|e| anyhow!(e)),
)
}?;
Some(cx.background().spawn(async move {
.map_err(|e| anyhow!(e));
let server_binary_path = server_binary_path.await?;
let server_args = adapter.server_args();
let server = lsp::LanguageServer::new(

View File

@ -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(),

View File

@ -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,

View File

@ -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,
)

View File

@ -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))
});

View File

@ -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")

View File

@ -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),

View File

@ -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(),