mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 02:47:34 +03:00
Invoke remote Prettier commands
This commit is contained in:
parent
faf1d38a6d
commit
2ec2036c2f
@ -9,6 +9,7 @@ use fs::Fs;
|
|||||||
use gpui::{AsyncAppContext, ModelHandle};
|
use gpui::{AsyncAppContext, ModelHandle};
|
||||||
use language::language_settings::language_settings;
|
use language::language_settings::language_settings;
|
||||||
use language::{Buffer, BundledFormatter, Diff};
|
use language::{Buffer, BundledFormatter, Diff};
|
||||||
|
use lsp::request::Request;
|
||||||
use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId};
|
use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId};
|
||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use serde::{Deserialize, Serialize};
|
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(
|
pub async fn format(
|
||||||
&self,
|
&self,
|
||||||
buffer: &ModelHandle<Buffer>,
|
buffer: &ModelHandle<Buffer>,
|
||||||
buffer_path: Option<PathBuf>,
|
buffer_path: Option<PathBuf>,
|
||||||
cx: &AsyncAppContext,
|
cx: &AsyncAppContext,
|
||||||
) -> anyhow::Result<Diff> {
|
) -> anyhow::Result<Diff> {
|
||||||
let params = buffer.read_with(cx, |buffer, cx| {
|
match self {
|
||||||
let buffer_language = buffer.language();
|
Self::Local(local) => {
|
||||||
let parsers_with_plugins = buffer_language
|
let params = buffer.read_with(cx, |buffer, cx| {
|
||||||
.into_iter()
|
let buffer_language = buffer.language();
|
||||||
.flat_map(|language| {
|
let parsers_with_plugins = buffer_language
|
||||||
language
|
.into_iter()
|
||||||
.lsp_adapters()
|
.flat_map(|language| {
|
||||||
.iter()
|
language
|
||||||
.flat_map(|adapter| adapter.enabled_formatters())
|
.lsp_adapters()
|
||||||
.filter_map(|formatter| match formatter {
|
.iter()
|
||||||
BundledFormatter::Prettier {
|
.flat_map(|adapter| adapter.enabled_formatters())
|
||||||
parser_name,
|
.filter_map(|formatter| match formatter {
|
||||||
plugin_names,
|
BundledFormatter::Prettier {
|
||||||
} => Some((parser_name, plugin_names)),
|
parser_name,
|
||||||
|
plugin_names,
|
||||||
|
} => Some((parser_name, plugin_names)),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
.fold(
|
||||||
.fold(
|
HashMap::default(),
|
||||||
HashMap::default(),
|
|mut parsers_with_plugins, (parser_name, plugins)| {
|
||||||
|mut parsers_with_plugins, (parser_name, plugins)| {
|
match parser_name {
|
||||||
match parser_name {
|
Some(parser_name) => parsers_with_plugins
|
||||||
Some(parser_name) => parsers_with_plugins
|
.entry(parser_name)
|
||||||
.entry(parser_name)
|
.or_insert_with(HashSet::default)
|
||||||
.or_insert_with(HashSet::default)
|
.extend(plugins),
|
||||||
.extend(plugins),
|
None => parsers_with_plugins.values_mut().for_each(|existing_plugins| {
|
||||||
None => parsers_with_plugins.values_mut().for_each(|existing_plugins| {
|
existing_plugins.extend(plugins.iter());
|
||||||
existing_plugins.extend(plugins.iter());
|
}),
|
||||||
}),
|
}
|
||||||
}
|
parsers_with_plugins
|
||||||
parsers_with_plugins
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len());
|
let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len());
|
||||||
if parsers_with_plugins.len() > 1 {
|
if parsers_with_plugins.len() > 1 {
|
||||||
log::warn!("Found multiple parsers with plugins {parsers_with_plugins:?}, will select only one: {selected_parser_with_plugins:?}");
|
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::<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 = 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"),
|
||||||
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::<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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn clear_cache(&self) -> anyhow::Result<()> {
|
pub async fn clear_cache(&self) -> anyhow::Result<()> {
|
||||||
self.server()
|
match self {
|
||||||
.expect("TODO kb split into local and remote")
|
Self::Local(local) => local
|
||||||
.request::<ClearCache>(())
|
.server
|
||||||
.await
|
.request::<ClearCache>(())
|
||||||
.context("prettier clear cache")
|
.await
|
||||||
|
.context("prettier clear cache"),
|
||||||
|
Self::Remote(remote) => todo!("TODO kb"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server(&self) -> Option<&Arc<LanguageServer>> {
|
pub fn server(&self) -> Option<&Arc<LanguageServer>> {
|
||||||
|
@ -8353,10 +8353,13 @@ impl Project {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let diff = prettier
|
let diff = prettier
|
||||||
.format(&buffer, buffer_path, &cx)
|
.invoke(&buffer, buffer_path, &envelope.payload.method, &cx)
|
||||||
.await
|
.await
|
||||||
.context("handle buffer formatting")?;
|
.with_context(|| format!("prettier invoke method {}", &envelope.payload.method))?;
|
||||||
todo!("TODO kb serialize diff")
|
|
||||||
|
Ok(proto::InvokePrettierForBufferResponse {
|
||||||
|
diff: todo!("TODO kb serialize diff"),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prettier_instance_for_buffer(
|
fn prettier_instance_for_buffer(
|
||||||
|
@ -1578,9 +1578,14 @@ message InvokePrettierForBuffer {
|
|||||||
uint64 buffer_id = 3;
|
uint64 buffer_id = 3;
|
||||||
optional uint64 worktree_id = 4;
|
optional uint64 worktree_id = 4;
|
||||||
string method = 5;
|
string method = 5;
|
||||||
optional string command_parameters = 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message InvokePrettierForBufferResponse {
|
message InvokePrettierForBufferResponse {
|
||||||
optional string diff = 1;
|
optional Diff diff = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Diff {
|
||||||
|
VectorClockEntry version = 1;
|
||||||
|
string line_ending = 2;
|
||||||
|
string edits = 3;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user