diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index a3763810e1a262e43daa8c1810d96cc8b10a0ffa..b33416228e0d81fedb5174e100b9aaae187b300a 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -158,7 +158,7 @@ impl LocalLspStore { async fn format_locally( lsp_store: WeakModel, - mut buffers_with_paths: Vec<(Model, Option)>, + mut buffers: Vec, push_to_history: bool, trigger: FormatTrigger, mut cx: AsyncAppContext, @@ -167,22 +167,22 @@ impl LocalLspStore { // same buffer. lsp_store.update(&mut cx, |this, cx| { let this = this.as_local_mut().unwrap(); - buffers_with_paths.retain(|(buffer, _)| { + buffers.retain(|buffer| { this.buffers_being_formatted - .insert(buffer.read(cx).remote_id()) + .insert(buffer.handle.read(cx).remote_id()) }); })?; let _cleanup = defer({ let this = lsp_store.clone(); let mut cx = cx.clone(); - let buffers = &buffers_with_paths; + let buffers = &buffers; move || { this.update(&mut cx, |this, cx| { let this = this.as_local_mut().unwrap(); - for (buffer, _) in buffers { + for buffer in buffers { this.buffers_being_formatted - .remove(&buffer.read(cx).remote_id()); + .remove(&buffer.handle.read(cx).remote_id()); } }) .ok(); @@ -190,10 +190,10 @@ impl LocalLspStore { }); let mut project_transaction = ProjectTransaction::default(); - for (buffer, buffer_abs_path) in &buffers_with_paths { + for buffer in &buffers { let (primary_adapter_and_server, adapters_and_servers) = lsp_store.update(&mut cx, |lsp_store, cx| { - let buffer = buffer.read(cx); + let buffer = buffer.handle.read(cx); let adapters_and_servers = lsp_store .language_servers_for_buffer(buffer, cx) @@ -207,7 +207,7 @@ impl LocalLspStore { (primary_adapter, adapters_and_servers) })?; - let settings = buffer.update(&mut cx, |buffer, cx| { + let settings = buffer.handle.update(&mut cx, |buffer, cx| { language_settings(buffer.language(), buffer.file(), cx).clone() })?; @@ -218,13 +218,14 @@ impl LocalLspStore { let trailing_whitespace_diff = if remove_trailing_whitespace { Some( buffer + .handle .update(&mut cx, |b, cx| b.remove_trailing_whitespace(cx))? .await, ) } else { None }; - let whitespace_transaction_id = buffer.update(&mut cx, |buffer, cx| { + let whitespace_transaction_id = buffer.handle.update(&mut cx, |buffer, cx| { buffer.finalize_last_transaction(); buffer.start_transaction(); if let Some(diff) = trailing_whitespace_diff { @@ -246,7 +247,7 @@ impl LocalLspStore { &lsp_store, &adapters_and_servers, code_actions, - buffer, + &buffer.handle, push_to_history, &mut project_transaction, &mut cx, @@ -261,9 +262,9 @@ impl LocalLspStore { primary_adapter_and_server.map(|(_adapter, server)| server.clone()); let server_and_buffer = primary_language_server .as_ref() - .zip(buffer_abs_path.as_ref()); + .zip(buffer.abs_path.as_ref()); - let prettier_settings = buffer.read_with(&cx, |buffer, cx| { + let prettier_settings = buffer.handle.read_with(&cx, |buffer, cx| { language_settings(buffer.language(), buffer.file(), cx) .prettier .clone() @@ -288,7 +289,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -302,7 +302,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -325,7 +324,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -351,7 +349,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -379,7 +376,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -393,7 +389,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -418,7 +413,6 @@ impl LocalLspStore { server_and_buffer, lsp_store.clone(), buffer, - buffer_abs_path, &settings, &adapters_and_servers, push_to_history, @@ -438,7 +432,7 @@ impl LocalLspStore { } } - buffer.update(&mut cx, |b, cx| { + buffer.handle.update(&mut cx, |b, cx| { // If the buffer had its whitespace formatted and was edited while the language-specific // formatting was being computed, avoid applying the language-specific formatting, because // it can't be grouped with the whitespace formatting in the undo history. @@ -467,7 +461,7 @@ impl LocalLspStore { if let Some(transaction_id) = whitespace_transaction_id { b.group_until_transaction(transaction_id); - } else if let Some(transaction) = project_transaction.0.get(buffer) { + } else if let Some(transaction) = project_transaction.0.get(&buffer.handle) { b.group_until_transaction(transaction.id) } } @@ -476,7 +470,9 @@ impl LocalLspStore { if !push_to_history { b.forget_transaction(transaction.id); } - project_transaction.0.insert(buffer.clone(), transaction); + project_transaction + .0 + .insert(buffer.handle.clone(), transaction); } })?; } @@ -489,8 +485,7 @@ impl LocalLspStore { formatter: &Formatter, primary_server_and_buffer: Option<(&Arc, &PathBuf)>, lsp_store: WeakModel, - buffer: &Model, - buffer_abs_path: &Option, + buffer: &FormattableBuffer, settings: &LanguageSettings, adapters_and_servers: &[(Arc, Arc)], push_to_history: bool, @@ -514,7 +509,7 @@ impl LocalLspStore { Some(FormatOperation::Lsp( LspStore::format_via_lsp( &lsp_store, - buffer, + &buffer.handle, buffer_abs_path, language_server, settings, @@ -531,27 +526,20 @@ impl LocalLspStore { let prettier = lsp_store.update(cx, |lsp_store, _cx| { lsp_store.prettier_store().unwrap().downgrade() })?; - prettier_store::format_with_prettier(&prettier, buffer, cx) + prettier_store::format_with_prettier(&prettier, &buffer.handle, cx) .await .transpose() .ok() .flatten() } Formatter::External { command, arguments } => { - let buffer_abs_path = buffer_abs_path.as_ref().map(|path| path.as_path()); - Self::format_via_external_command( - buffer, - buffer_abs_path, - command, - arguments.as_deref(), - cx, - ) - .await - .context(format!( - "failed to format via external command {:?}", - command - ))? - .map(FormatOperation::External) + Self::format_via_external_command(buffer, command, arguments.as_deref(), cx) + .await + .context(format!( + "failed to format via external command {:?}", + command + ))? + .map(FormatOperation::External) } Formatter::CodeActions(code_actions) => { let code_actions = deserialize_code_actions(code_actions); @@ -560,7 +548,7 @@ impl LocalLspStore { &lsp_store, adapters_and_servers, code_actions, - buffer, + &buffer.handle, push_to_history, transaction, cx, @@ -574,13 +562,12 @@ impl LocalLspStore { } async fn format_via_external_command( - buffer: &Model, - buffer_abs_path: Option<&Path>, + buffer: &FormattableBuffer, command: &str, arguments: Option<&[String]>, cx: &mut AsyncAppContext, ) -> Result> { - let working_dir_path = buffer.update(cx, |buffer, cx| { + let working_dir_path = buffer.handle.update(cx, |buffer, cx| { let file = File::from_dyn(buffer.file())?; let worktree = file.worktree.read(cx); let mut worktree_path = worktree.abs_path().to_path_buf(); @@ -597,13 +584,17 @@ impl LocalLspStore { child.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0); } + if let Some(buffer_env) = buffer.env.as_ref() { + child.envs(buffer_env); + } + if let Some(working_dir_path) = working_dir_path { child.current_dir(working_dir_path); } if let Some(arguments) = arguments { child.args(arguments.iter().map(|arg| { - if let Some(buffer_abs_path) = buffer_abs_path { + if let Some(buffer_abs_path) = buffer.abs_path.as_ref() { arg.replace("{buffer_path}", &buffer_abs_path.to_string_lossy()) } else { arg.replace("{buffer_path}", "Untitled") @@ -621,7 +612,9 @@ impl LocalLspStore { .stdin .as_mut() .ok_or_else(|| anyhow!("failed to acquire stdin"))?; - let text = buffer.update(cx, |buffer, _| buffer.as_rope().clone())?; + let text = buffer + .handle + .update(cx, |buffer, _| buffer.as_rope().clone())?; for chunk in text.chunks() { stdin.write_all(chunk.as_bytes()).await?; } @@ -640,12 +633,19 @@ impl LocalLspStore { let stdout = String::from_utf8(output.stdout)?; Ok(Some( buffer + .handle .update(cx, |buffer, cx| buffer.diff(stdout, cx))? .await, )) } } +pub struct FormattableBuffer { + handle: Model, + abs_path: Option, + env: Option>, +} + pub struct RemoteLspStore { upstream_client: AnyProtoClient, upstream_project_id: u64, @@ -5028,6 +5028,28 @@ impl LspStore { .and_then(|local| local.last_formatting_failure.as_deref()) } + pub fn environment_for_buffer( + &self, + buffer: &Model, + cx: &mut ModelContext, + ) -> Shared>>> { + let worktree_id = buffer.read(cx).file().map(|file| file.worktree_id(cx)); + let worktree_abs_path = worktree_id.and_then(|worktree_id| { + self.worktree_store + .read(cx) + .worktree_for_id(worktree_id, cx) + .map(|entry| entry.read(cx).abs_path().clone()) + }); + + if let Some(environment) = &self.as_local().map(|local| local.environment.clone()) { + environment.update(cx, |env, cx| { + env.get_environment(worktree_id, worktree_abs_path, cx) + }) + } else { + Task::ready(None).shared() + } + } + pub fn format( &mut self, buffers: HashSet>, @@ -5042,14 +5064,31 @@ impl LspStore { let buffer = buffer_handle.read(cx); let buffer_abs_path = File::from_dyn(buffer.file()) .and_then(|file| file.as_local().map(|f| f.abs_path(cx))); + (buffer_handle, buffer_abs_path) }) .collect::>(); cx.spawn(move |lsp_store, mut cx| async move { + let mut formattable_buffers = Vec::with_capacity(buffers_with_paths.len()); + + for (handle, abs_path) in buffers_with_paths { + let env = lsp_store + .update(&mut cx, |lsp_store, cx| { + lsp_store.environment_for_buffer(&handle, cx) + })? + .await; + + formattable_buffers.push(FormattableBuffer { + handle, + abs_path, + env, + }); + } + let result = LocalLspStore::format_locally( lsp_store.clone(), - buffers_with_paths, + formattable_buffers, push_to_history, trigger, cx.clone(),