Detailed changes
@@ -19,8 +19,8 @@ use gpui::{
use indoc::indoc;
use language::{
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
- BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry,
- Override, Point,
+ BracketPairConfig, BundledFormatter, FakeLspAdapter, LanguageConfig, LanguageConfigOverride,
+ LanguageRegistry, Override, Point,
};
use parking_lot::Mutex;
use project::project_settings::{LspSettings, ProjectSettings};
@@ -7815,6 +7815,68 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui:
});
}
+#[gpui::test]
+async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
+ init_test(cx, |settings| {
+ settings.defaults.formatter = Some(language_settings::Formatter::Prettier)
+ });
+
+ let mut language = Language::new(
+ LanguageConfig {
+ name: "Rust".into(),
+ path_suffixes: vec!["rs".to_string()],
+ ..Default::default()
+ },
+ Some(tree_sitter_rust::language()),
+ );
+
+ let _ = language
+ .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+ enabled_formatters: vec![BundledFormatter::Prettier {
+ parser_name: Some("test_parser"),
+ plugin_names: vec!["test_plugin"],
+ }],
+ ..Default::default()
+ }))
+ .await;
+
+ let fs = FakeFs::new(cx.background());
+ fs.insert_file("/file.rs", Default::default()).await;
+
+ // TODO kb have to specify some test node runtime
+ let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
+ project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+ let buffer = project
+ .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+ .await
+ .unwrap();
+
+ let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
+ let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx);
+ editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+
+ let format = editor.update(cx, |editor, cx| {
+ editor.perform_format(project.clone(), FormatTrigger::Manual, cx)
+ });
+ format.await.unwrap();
+ assert_eq!(
+ editor.read_with(cx, |editor, cx| editor.text(cx)),
+ "one, two\nthree\n"
+ );
+
+ editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
+ let format = editor.update(cx, |editor, cx| {
+ editor.perform_format(project, FormatTrigger::Manual, cx)
+ });
+ cx.foreground().advance_clock(super::FORMAT_TIMEOUT);
+ cx.foreground().start_waiting();
+ format.await.unwrap();
+ assert_eq!(
+ editor.read_with(cx, |editor, cx| editor.text(cx)),
+ "one\ntwo\nthree\n"
+ );
+}
+
fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
let point = DisplayPoint::new(row as u32, column as u32);
point..point
@@ -498,6 +498,7 @@ pub struct FakeLspAdapter {
pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>,
pub disk_based_diagnostics_progress_token: Option<String>,
pub disk_based_diagnostics_sources: Vec<String>,
+ pub enabled_formatters: Vec<BundledFormatter>,
}
#[derive(Clone, Debug, Default)]
@@ -1760,6 +1761,7 @@ impl Default for FakeLspAdapter {
disk_based_diagnostics_progress_token: None,
initialization_options: None,
disk_based_diagnostics_sources: Vec::new(),
+ enabled_formatters: Vec::new(),
}
}
}
@@ -1816,6 +1818,10 @@ impl LspAdapter for Arc<FakeLspAdapter> {
async fn initialization_options(&self) -> Option<Value> {
self.initialization_options.clone()
}
+
+ fn enabled_formatters(&self) -> Vec<BundledFormatter> {
+ self.enabled_formatters.clone()
+ }
}
fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option<u32>)]) {
@@ -230,19 +230,32 @@ impl FakeNodeRuntime {
#[async_trait::async_trait]
impl NodeRuntime for FakeNodeRuntime {
- async fn binary_path(&self) -> Result<PathBuf> {
- unreachable!()
+ async fn binary_path(&self) -> anyhow::Result<PathBuf> {
+ // TODO kb move away into a separate type + a Project's setter (for test code)
+ Ok(PathBuf::from("prettier_fake_node"))
}
async fn run_npm_subcommand(&self, _: Option<&Path>, _: &str, _: &[&str]) -> Result<Output> {
unreachable!()
}
- async fn npm_package_latest_version(&self, _: &str) -> Result<String> {
- unreachable!()
+ async fn npm_package_latest_version(&self, name: &str) -> anyhow::Result<String> {
+ if name == "prettier" {
+ Ok("0.0.1".to_string())
+ } else {
+ unreachable!()
+ }
}
- async fn npm_install_packages(&self, _: &Path, _: &[(&str, &str)]) -> Result<()> {
- unreachable!()
+ async fn npm_install_packages(
+ &self,
+ _: &Path,
+ packages: &[(&str, &str)],
+ ) -> anyhow::Result<()> {
+ if packages == [("prettier", "0.0.1")] {
+ Ok(())
+ } else {
+ unreachable!()
+ }
}
}
@@ -6,6 +6,9 @@ edition = "2021"
[lib]
path = "src/prettier.rs"
+[features]
+test-support = []
+
[dependencies]
client = { path = "../client" }
collections = { path = "../collections"}
@@ -11,7 +11,7 @@ use language::language_settings::language_settings;
use language::proto::deserialize_diff;
use language::{Buffer, BundledFormatter, Diff};
use lsp::request::Request;
-use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId};
+use lsp::{LanguageServer, LanguageServerId};
use node_runtime::NodeRuntime;
use serde::{Deserialize, Serialize};
use util::paths::DEFAULT_PRETTIER_DIR;
@@ -19,6 +19,8 @@ use util::paths::DEFAULT_PRETTIER_DIR;
pub enum Prettier {
Local(Local),
Remote(Remote),
+ #[cfg(any(test, feature = "test-support"))]
+ Test(TestPrettier),
}
pub struct Local {
@@ -35,6 +37,13 @@ pub struct Remote {
client: Arc<Client>,
}
+#[cfg(any(test, feature = "test-support"))]
+pub struct TestPrettier {
+ worktree_id: Option<usize>,
+ prettier_dir: PathBuf,
+ default: bool,
+}
+
#[derive(Debug)]
pub struct LocateStart {
pub worktree_root_path: Arc<Path>,
@@ -180,6 +189,22 @@ impl Prettier {
}
}
+ #[cfg(any(test, feature = "test-support"))]
+ pub async fn start(
+ worktree_id: Option<usize>,
+ _: LanguageServerId,
+ prettier_dir: PathBuf,
+ _: Arc<dyn NodeRuntime>,
+ _: AsyncAppContext,
+ ) -> anyhow::Result<Self> {
+ Ok(Self::Test(TestPrettier {
+ worktree_id,
+ default: prettier_dir == DEFAULT_PRETTIER_DIR.as_path(),
+ prettier_dir,
+ }))
+ }
+
+ #[cfg(not(any(test, feature = "test-support")))]
pub async fn start(
worktree_id: Option<usize>,
server_id: LanguageServerId,
@@ -400,6 +425,12 @@ impl Prettier {
.diff
.map(deserialize_diff)
.context("missing diff after prettier diff invocation"),
+ Self::Test(_) => Ok(buffer
+ .read_with(cx, |buffer, cx| {
+ let formatted_text = buffer.text() + "\nformatted by test prettier";
+ buffer.diff(formatted_text, cx)
+ })
+ .await),
}
}
@@ -427,34 +458,39 @@ impl Prettier {
)
})
.context("prettier invoke clear cache"),
+ Self::Test(_) => Ok(()),
}
}
pub fn server(&self) -> Option<&Arc<LanguageServer>> {
match self {
- Prettier::Local(local) => Some(&local.server),
- Prettier::Remote(_) => None,
+ Self::Local(local) => Some(&local.server),
+ Self::Remote(_) => None,
+ Self::Test(_) => None,
}
}
pub fn is_default(&self) -> bool {
match self {
- Prettier::Local(local) => local.default,
- Prettier::Remote(_) => false,
+ Self::Local(local) => local.default,
+ Self::Remote(_) => false,
+ Self::Test(test_prettier) => test_prettier.default,
}
}
pub fn prettier_dir(&self) -> &Path {
match self {
- Prettier::Local(local) => &local.prettier_dir,
- Prettier::Remote(remote) => &remote.prettier_dir,
+ Self::Local(local) => &local.prettier_dir,
+ Self::Remote(remote) => &remote.prettier_dir,
+ Self::Test(test_prettier) => &test_prettier.prettier_dir,
}
}
pub fn worktree_id(&self) -> Option<usize> {
match self {
- Prettier::Local(local) => local.worktree_id,
- Prettier::Remote(remote) => remote.worktree_id,
+ Self::Local(local) => local.worktree_id,
+ Self::Remote(remote) => remote.worktree_id,
+ Self::Test(test_prettier) => test_prettier.worktree_id,
}
}
}
@@ -15,6 +15,7 @@ test-support = [
"language/test-support",
"settings/test-support",
"text/test-support",
+ "prettier/test-support",
]
[dependencies]
@@ -75,6 +76,7 @@ gpui = { path = "../gpui", features = ["test-support"] }
language = { path = "../language", features = ["test-support"] }
lsp = { path = "../lsp", features = ["test-support"] }
settings = { path = "../settings", features = ["test-support"] }
+prettier = { path = "../prettier", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
rpc = { path = "../rpc", features = ["test-support"] }
git2.workspace = true