From 2ec2036c2f739894cd80c2f26f7f6471a93906ea Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 26 Sep 2023 18:32:55 +0300 Subject: [PATCH] Invoke remote Prettier commands --- crates/prettier/src/prettier.rs | 289 ++++++++++++++++++-------------- crates/project/src/project.rs | 9 +- crates/rpc/proto/zed.proto | 9 +- 3 files changed, 172 insertions(+), 135 deletions(-) diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 147262baed..2df96224b6 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -9,6 +9,7 @@ use fs::Fs; use gpui::{AsyncAppContext, ModelHandle}; use language::language_settings::language_settings; use language::{Buffer, BundledFormatter, Diff}; +use lsp::request::Request; use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId}; use node_runtime::NodeRuntime; use serde::{Deserialize, Serialize}; @@ -216,148 +217,176 @@ impl Prettier { })) } + pub async fn invoke( + &self, + buffer: &ModelHandle, + buffer_path: Option, + method: &str, + cx: &AsyncAppContext, + ) -> anyhow::Result> { + match method { + Format::METHOD => self + .format(buffer, buffer_path, cx) + .await + .context("invoke method") + .map(Some), + ClearCache::METHOD => { + self.clear_cache().await.context("invoke method")?; + Ok(None) + } + unknown => anyhow::bail!("Unknown method {unknown}"), + } + } + pub async fn format( &self, buffer: &ModelHandle, buffer_path: Option, cx: &AsyncAppContext, ) -> anyhow::Result { - let params = buffer.read_with(cx, |buffer, cx| { - let buffer_language = buffer.language(); - let parsers_with_plugins = buffer_language - .into_iter() - .flat_map(|language| { - language - .lsp_adapters() - .iter() - .flat_map(|adapter| adapter.enabled_formatters()) - .filter_map(|formatter| match formatter { - BundledFormatter::Prettier { - parser_name, - plugin_names, - } => Some((parser_name, plugin_names)), + match self { + Self::Local(local) => { + let params = buffer.read_with(cx, |buffer, cx| { + let buffer_language = buffer.language(); + let parsers_with_plugins = buffer_language + .into_iter() + .flat_map(|language| { + language + .lsp_adapters() + .iter() + .flat_map(|adapter| adapter.enabled_formatters()) + .filter_map(|formatter| match formatter { + BundledFormatter::Prettier { + parser_name, + plugin_names, + } => Some((parser_name, plugin_names)), + }) }) - }) - .fold( - HashMap::default(), - |mut parsers_with_plugins, (parser_name, plugins)| { - match parser_name { - Some(parser_name) => parsers_with_plugins - .entry(parser_name) - .or_insert_with(HashSet::default) - .extend(plugins), - None => parsers_with_plugins.values_mut().for_each(|existing_plugins| { - existing_plugins.extend(plugins.iter()); - }), - } - parsers_with_plugins - }, - ); + .fold( + HashMap::default(), + |mut parsers_with_plugins, (parser_name, plugins)| { + match parser_name { + Some(parser_name) => parsers_with_plugins + .entry(parser_name) + .or_insert_with(HashSet::default) + .extend(plugins), + None => parsers_with_plugins.values_mut().for_each(|existing_plugins| { + existing_plugins.extend(plugins.iter()); + }), + } + parsers_with_plugins + }, + ); - let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len()); - if parsers_with_plugins.len() > 1 { - log::warn!("Found multiple parsers with plugins {parsers_with_plugins:?}, will select only one: {selected_parser_with_plugins:?}"); + let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len()); + if parsers_with_plugins.len() > 1 { + log::warn!("Found multiple parsers with plugins {parsers_with_plugins:?}, will select only one: {selected_parser_with_plugins:?}"); + } + + let prettier_node_modules = self.prettier_dir().join("node_modules"); + anyhow::ensure!(prettier_node_modules.is_dir(), "Prettier node_modules dir does not exist: {prettier_node_modules:?}"); + let plugin_name_into_path = |plugin_name: &str| { + let prettier_plugin_dir = prettier_node_modules.join(plugin_name); + for possible_plugin_path in [ + prettier_plugin_dir.join("dist").join("index.mjs"), + prettier_plugin_dir.join("index.mjs"), + prettier_plugin_dir.join("plugin.js"), + prettier_plugin_dir.join("index.js"), + prettier_plugin_dir, + ] { + if possible_plugin_path.is_file() { + return Some(possible_plugin_path); + } + } + None + }; + let (parser, located_plugins) = match selected_parser_with_plugins { + Some((parser, plugins)) => { + // Tailwind plugin requires being added last + // https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins + let mut add_tailwind_back = false; + + let mut plugins = plugins.into_iter().filter(|&&plugin_name| { + if plugin_name == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME { + add_tailwind_back = true; + false + } else { + true + } + }).map(|plugin_name| (plugin_name, plugin_name_into_path(plugin_name))).collect::>(); + if add_tailwind_back { + plugins.push((&TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME, plugin_name_into_path(TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME))); + } + (Some(parser.to_string()), plugins) + }, + None => (None, Vec::new()), + }; + + let prettier_options = if self.is_default() { + let language_settings = language_settings(buffer_language, buffer.file(), cx); + let mut options = language_settings.prettier.clone(); + if !options.contains_key("tabWidth") { + options.insert( + "tabWidth".to_string(), + serde_json::Value::Number(serde_json::Number::from( + language_settings.tab_size.get(), + )), + ); + } + if !options.contains_key("printWidth") { + options.insert( + "printWidth".to_string(), + serde_json::Value::Number(serde_json::Number::from( + language_settings.preferred_line_length, + )), + ); + } + Some(options) + } else { + None + }; + + let plugins = located_plugins.into_iter().filter_map(|(plugin_name, located_plugin_path)| { + match located_plugin_path { + Some(path) => Some(path), + None => { + log::error!("Have not found plugin path for {plugin_name:?} inside {prettier_node_modules:?}"); + None}, + } + }).collect(); + log::debug!("Formatting file {:?} with prettier, plugins :{plugins:?}, options: {prettier_options:?}", buffer.file().map(|f| f.full_path(cx))); + + anyhow::Ok(FormatParams { + text: buffer.text(), + options: FormatOptions { + parser, + plugins, + path: buffer_path, + prettier_options, + }, + }) + }).context("prettier params calculation")?; + let response = local + .server + .request::(params) + .await + .context("prettier format request")?; + let diff_task = buffer.read_with(cx, |buffer, cx| buffer.diff(response.text, cx)); + Ok(diff_task.await) } - - let prettier_node_modules = self.prettier_dir().join("node_modules"); - anyhow::ensure!(prettier_node_modules.is_dir(), "Prettier node_modules dir does not exist: {prettier_node_modules:?}"); - let plugin_name_into_path = |plugin_name: &str| { - let prettier_plugin_dir = prettier_node_modules.join(plugin_name); - for possible_plugin_path in [ - prettier_plugin_dir.join("dist").join("index.mjs"), - prettier_plugin_dir.join("index.mjs"), - prettier_plugin_dir.join("plugin.js"), - prettier_plugin_dir.join("index.js"), - prettier_plugin_dir, - ] { - if possible_plugin_path.is_file() { - return Some(possible_plugin_path); - } - } - None - }; - let (parser, located_plugins) = match selected_parser_with_plugins { - Some((parser, plugins)) => { - // Tailwind plugin requires being added last - // https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins - let mut add_tailwind_back = false; - - let mut plugins = plugins.into_iter().filter(|&&plugin_name| { - if plugin_name == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME { - add_tailwind_back = true; - false - } else { - true - } - }).map(|plugin_name| (plugin_name, plugin_name_into_path(plugin_name))).collect::>(); - if add_tailwind_back { - plugins.push((&TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME, plugin_name_into_path(TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME))); - } - (Some(parser.to_string()), plugins) - }, - None => (None, Vec::new()), - }; - - let prettier_options = if self.is_default() { - let language_settings = language_settings(buffer_language, buffer.file(), cx); - let mut options = language_settings.prettier.clone(); - if !options.contains_key("tabWidth") { - options.insert( - "tabWidth".to_string(), - serde_json::Value::Number(serde_json::Number::from( - language_settings.tab_size.get(), - )), - ); - } - if !options.contains_key("printWidth") { - options.insert( - "printWidth".to_string(), - serde_json::Value::Number(serde_json::Number::from( - language_settings.preferred_line_length, - )), - ); - } - Some(options) - } else { - None - }; - - let plugins = located_plugins.into_iter().filter_map(|(plugin_name, located_plugin_path)| { - match located_plugin_path { - Some(path) => Some(path), - None => { - log::error!("Have not found plugin path for {plugin_name:?} inside {prettier_node_modules:?}"); - None}, - } - }).collect(); - log::debug!("Formatting file {:?} with prettier, plugins :{plugins:?}, options: {prettier_options:?}", buffer.file().map(|f| f.full_path(cx))); - - anyhow::Ok(FormatParams { - text: buffer.text(), - options: FormatOptions { - parser, - plugins, - path: buffer_path, - prettier_options, - }, - }) - }).context("prettier params calculation")?; - let response = self - .server() - .expect("TODO kb split into local and remote") - .request::(params) - .await - .context("prettier format request")?; - let diff_task = buffer.read_with(cx, |buffer, cx| buffer.diff(response.text, cx)); - Ok(diff_task.await) + Self::Remote(remote) => todo!("TODO kb"), + } } pub async fn clear_cache(&self) -> anyhow::Result<()> { - self.server() - .expect("TODO kb split into local and remote") - .request::(()) - .await - .context("prettier clear cache") + match self { + Self::Local(local) => local + .server + .request::(()) + .await + .context("prettier clear cache"), + Self::Remote(remote) => todo!("TODO kb"), + } } pub fn server(&self) -> Option<&Arc> { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 3e85e70728..f3a92ad00f 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -8353,10 +8353,13 @@ impl Project { }); let diff = prettier - .format(&buffer, buffer_path, &cx) + .invoke(&buffer, buffer_path, &envelope.payload.method, &cx) .await - .context("handle buffer formatting")?; - todo!("TODO kb serialize diff") + .with_context(|| format!("prettier invoke method {}", &envelope.payload.method))?; + + Ok(proto::InvokePrettierForBufferResponse { + diff: todo!("TODO kb serialize diff"), + }) } fn prettier_instance_for_buffer( diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index 64fd8c620d..0622fcef90 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -1578,9 +1578,14 @@ message InvokePrettierForBuffer { uint64 buffer_id = 3; optional uint64 worktree_id = 4; string method = 5; - optional string command_parameters = 6; } message InvokePrettierForBufferResponse { - optional string diff = 1; + optional Diff diff = 1; +} + +message Diff { + VectorClockEntry version = 1; + string line_ending = 2; + string edits = 3; }