diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 9b1b9c1e9d..ecd4376477 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -9,7 +9,7 @@ pub struct Prettier { _private: (), } -type NodeRuntime = (); +pub struct NodeRuntime; impl Prettier { // This was taken from the prettier-vscode extension. diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 42b0a13c51..d24fe17380 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -50,7 +50,7 @@ use lsp::{ }; use lsp_command::*; use postage::watch; -use prettier::Prettier; +use prettier::{NodeRuntime, Prettier}; use project_settings::{LspSettings, ProjectSettings}; use rand::prelude::*; use search::SearchQuery; @@ -153,7 +153,10 @@ pub struct Project { copilot_lsp_subscription: Option, copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, - prettier_instances: HashMap<(WorktreeId, PathBuf), Shared>>>>, + prettier_instances: HashMap< + (Option, PathBuf), + Shared, Arc>>>, + >, } struct DelayedDebounced { @@ -3953,7 +3956,7 @@ impl Project { push_to_history: bool, trigger: FormatTrigger, cx: &mut ModelContext, - ) -> Task> { + ) -> Task> { if self.is_local() { let mut buffers_with_paths_and_servers = buffers .into_iter() @@ -4082,15 +4085,26 @@ impl Project { } } (Formatter::Auto, FormatOnSave::On | FormatOnSave::Off) => { - if let Some(prettier) = this.update(&mut cx, |project, _| { - project.prettier_instance_for_buffer(buffer) - }) { - format_operation = Some(FormatOperation::Prettier( - prettier - .format(buffer) + if let Some(prettier_task) = this + .update(&mut cx, |project, cx| { + project.prettier_instance_for_buffer(buffer, cx) + }) { + match prettier_task .await - .context("autoformatting via prettier")?, - )); + .await + { + Ok(prettier) => { + format_operation = Some(FormatOperation::Prettier( + prettier + .format(buffer) + .await + .context("formatting via prettier")?, + )); + } + Err(e) => anyhow::bail!( + "Failed to create prettier instance for buffer during autoformatting: {e:#}" + ), + } } else if let Some((language_server, buffer_abs_path)) = language_server.as_ref().zip(buffer_abs_path.as_ref()) { @@ -4109,16 +4123,27 @@ impl Project { } } (Formatter::Prettier { .. }, FormatOnSave::On | FormatOnSave::Off) => { - if let Some(prettier) = this.update(&mut cx, |project, _| { - project.prettier_instance_for_buffer(buffer) - }) { - format_operation = Some(FormatOperation::Prettier( - prettier - .format(buffer) + if let Some(prettier_task) = this + .update(&mut cx, |project, cx| { + project.prettier_instance_for_buffer(buffer, cx) + }) { + match prettier_task .await - .context("formatting via prettier")?, - )); - } + .await + { + Ok(prettier) => { + format_operation = Some(FormatOperation::Prettier( + prettier + .format(buffer) + .await + .context("formatting via prettier")?, + )); + } + Err(e) => anyhow::bail!( + "Failed to create prettier instance for buffer during formatting: {e:#}" + ), + } + } } }; @@ -8157,9 +8182,53 @@ impl Project { } } - fn prettier_instance_for_buffer(&self, buffer: &ModelHandle) -> Option { - // TODO kb - None + fn prettier_instance_for_buffer( + &mut self, + buffer: &ModelHandle, + cx: &mut ModelContext, + ) -> Option, Arc>>>>> { + let buffer_file = File::from_dyn(buffer.read(cx).file()); + let buffer_path = buffer_file.map(|file| Arc::clone(file.path())); + let worktree_id = buffer_file.map(|file| file.worktree_id(cx)); + + // TODO kb return None if config opted out of prettier + + let task = cx.spawn(|this, mut cx| async move { + let fs = this.update(&mut cx, |project, _| Arc::clone(&project.fs)); + // TODO kb can we have a cache for this instead? + let prettier_path = Prettier::locate(buffer_path.as_deref(), fs).await; + if let Some(existing_prettier) = this.update(&mut cx, |project, _| { + project + .prettier_instances + .get(&(worktree_id, prettier_path.clone())) + .cloned() + }) { + return existing_prettier; + } + + let task_node_runtime = Arc::new(NodeRuntime); + let task_prettier_path = prettier_path.clone(); + let new_prettier_task = cx + .background() + .spawn(async move { + Ok(Arc::new( + Prettier::start(&task_prettier_path, task_node_runtime) + .await + .with_context(|| { + format!("starting new prettier for path {task_prettier_path:?}") + })?, + )) + .map_err(Arc::new) + }) + .shared(); + this.update(&mut cx, |project, _| { + project + .prettier_instances + .insert((worktree_id, prettier_path), new_prettier_task.clone()); + }); + new_prettier_task + }); + Some(task) } }