Invoke remote Prettier commands

Kirill Bulatov created

Change summary

crates/prettier/src/prettier.rs | 285 +++++++++++++++++++---------------
crates/project/src/project.rs   |   9 
crates/rpc/proto/zed.proto      |   9 
3 files changed, 170 insertions(+), 133 deletions(-)

Detailed changes

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>,
+        buffer_path: Option<PathBuf>,
+        method: &str,
+        cx: &AsyncAppContext,
+    ) -> anyhow::Result<Option<Diff>> {
+        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>,
         buffer_path: Option<PathBuf>,
         cx: &AsyncAppContext,
     ) -> anyhow::Result<Diff> {
-        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());
-                            }),
+                        .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 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);
+                            }
                         }
-                        parsers_with_plugins
-                    },
-                );
+                        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::<Vec<_>>();
+                            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 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_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 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
+                    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},
                         }
-                    }).map(|plugin_name| (plugin_name, plugin_name_into_path(plugin_name))).collect::<Vec<_>>();
-                    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::<Format>(params)
-            .await
-            .context("prettier format request")?;
-        let diff_task = buffer.read_with(cx, |buffer, cx| buffer.diff(response.text, cx));
-        Ok(diff_task.await)
+                    }).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::<Format>(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::<ClearCache>(())
-            .await
-            .context("prettier clear cache")
+        match self {
+            Self::Local(local) => local
+                .server
+                .request::<ClearCache>(())
+                .await
+                .context("prettier clear cache"),
+            Self::Remote(remote) => todo!("TODO kb"),
+        }
     }
 
     pub fn server(&self) -> Option<&Arc<LanguageServer>> {

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(

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