Slightly better prettier settings and discovery

This commit is contained in:
Kirill Bulatov 2023-09-08 15:41:23 +03:00
parent ce6b31d938
commit 4f956d71e2
8 changed files with 52 additions and 52 deletions

1
Cargo.lock generated
View file

@ -5531,6 +5531,7 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"util",
] ]
[[package]] [[package]]

View file

@ -201,6 +201,8 @@
// } // }
// TODO kb description // TODO kb description
"formatter": "auto", "formatter": "auto",
// TODO kb description + better settings
"prettier": true,
// How to soft-wrap long lines of text. This setting can take // How to soft-wrap long lines of text. This setting can take
// three values: // three values:
// //

View file

@ -15,6 +15,7 @@ use fs::FakeFs;
use futures::{channel::oneshot, StreamExt as _}; use futures::{channel::oneshot, StreamExt as _};
use gpui::{executor::Deterministic, ModelHandle, Task, TestAppContext, WindowHandle}; use gpui::{executor::Deterministic, ModelHandle, Task, TestAppContext, WindowHandle};
use language::LanguageRegistry; use language::LanguageRegistry;
use node_runtime::FakeNodeRuntime;
use parking_lot::Mutex; use parking_lot::Mutex;
use project::{Project, WorktreeId}; use project::{Project, WorktreeId};
use rpc::RECEIVE_TIMEOUT; use rpc::RECEIVE_TIMEOUT;
@ -218,6 +219,7 @@ impl TestServer {
build_window_options: |_, _, _| Default::default(), build_window_options: |_, _, _| Default::default(),
initialize_workspace: |_, _, _, _| Task::ready(Ok(())), initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
background_actions: || &[], background_actions: || &[],
node_runtime: FakeNodeRuntime::new(),
}); });
cx.update(|cx| { cx.update(|cx| {
@ -569,6 +571,7 @@ impl TestClient {
self.client().clone(), self.client().clone(),
self.app_state.user_store.clone(), self.app_state.user_store.clone(),
self.app_state.languages.clone(), self.app_state.languages.clone(),
self.app_state.node_runtime.clone(),
self.app_state.fs.clone(), self.app_state.fs.clone(),
cx, cx,
) )

View file

@ -47,6 +47,7 @@ pub struct LanguageSettings {
pub show_wrap_guides: bool, pub show_wrap_guides: bool,
pub wrap_guides: Vec<usize>, pub wrap_guides: Vec<usize>,
pub format_on_save: FormatOnSave, pub format_on_save: FormatOnSave,
pub prettier: bool,
pub remove_trailing_whitespace_on_save: bool, pub remove_trailing_whitespace_on_save: bool,
pub ensure_final_newline_on_save: bool, pub ensure_final_newline_on_save: bool,
pub formatter: Formatter, pub formatter: Formatter,
@ -92,6 +93,8 @@ pub struct LanguageSettingsContent {
#[serde(default)] #[serde(default)]
pub format_on_save: Option<FormatOnSave>, pub format_on_save: Option<FormatOnSave>,
#[serde(default)] #[serde(default)]
pub prettier: Option<bool>,
#[serde(default)]
pub remove_trailing_whitespace_on_save: Option<bool>, pub remove_trailing_whitespace_on_save: Option<bool>,
#[serde(default)] #[serde(default)]
pub ensure_final_newline_on_save: Option<bool>, pub ensure_final_newline_on_save: Option<bool>,
@ -398,6 +401,7 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent
); );
merge(&mut settings.formatter, src.formatter.clone()); merge(&mut settings.formatter, src.formatter.clone());
merge(&mut settings.format_on_save, src.format_on_save.clone()); merge(&mut settings.format_on_save, src.format_on_save.clone());
merge(&mut settings.prettier, src.prettier);
merge( merge(
&mut settings.remove_trailing_whitespace_on_save, &mut settings.remove_trailing_whitespace_on_save,
src.remove_trailing_whitespace_on_save, src.remove_trailing_whitespace_on_save,

View file

@ -11,6 +11,7 @@ language = { path = "../language" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
fs = { path = "../fs" } fs = { path = "../fs" }
node_runtime = { path = "../node_runtime"} node_runtime = { path = "../node_runtime"}
util = { path = "../util" }
serde.workspace = true serde.workspace = true
serde_derive.workspace = true serde_derive.workspace = true

View file

@ -130,20 +130,21 @@ impl Prettier {
None => Vec::new(), None => Vec::new(),
}; };
match find_closest_prettier_path(paths_to_check, fs.as_ref()) match find_closest_prettier_dir(paths_to_check, fs.as_ref())
.await .await
.with_context(|| format!("Finding prettier starting with {starting_path:?}"))? .with_context(|| format!("finding prettier starting with {starting_path:?}"))?
{ {
Some(prettier_path) => Ok(prettier_path), Some(prettier_dir) => Ok(prettier_dir),
None => { None => Ok(util::paths::DEFAULT_PRETTIER_DIR.to_path_buf()),
// TODO kb return the default prettier, how, without state?
Ok(PathBuf::new())
}
} }
} }
pub async fn start(prettier_path: &Path, node: Arc<dyn NodeRuntime>) -> anyhow::Result<Self> { pub async fn start(prettier_dir: &Path, node: Arc<dyn NodeRuntime>) -> anyhow::Result<Self> {
todo!() anyhow::ensure!(
prettier_dir.is_dir(),
"Prettier dir {prettier_dir:?} is not a directory"
);
anyhow::bail!("TODO kb: start prettier server in {prettier_dir:?}")
} }
pub async fn format(&self, buffer: &ModelHandle<Buffer>) -> anyhow::Result<Diff> { pub async fn format(&self, buffer: &ModelHandle<Buffer>) -> anyhow::Result<Diff> {
@ -156,14 +157,14 @@ impl Prettier {
} }
const PRETTIER_PACKAGE_NAME: &str = "prettier"; const PRETTIER_PACKAGE_NAME: &str = "prettier";
async fn find_closest_prettier_path( async fn find_closest_prettier_dir(
paths_to_check: Vec<PathBuf>, paths_to_check: Vec<PathBuf>,
fs: &dyn Fs, fs: &dyn Fs,
) -> anyhow::Result<Option<PathBuf>> { ) -> anyhow::Result<Option<PathBuf>> {
for path in paths_to_check { for path in paths_to_check {
let possible_package_json = path.join("package.json"); let possible_package_json = path.join("package.json");
if let Some(package_json_metadata) = fs if let Some(package_json_metadata) = fs
.metadata(&path) .metadata(&possible_package_json)
.await .await
.with_context(|| format!("Fetching metadata for {possible_package_json:?}"))? .with_context(|| format!("Fetching metadata for {possible_package_json:?}"))?
{ {
@ -192,7 +193,7 @@ async fn find_closest_prettier_path(
let possible_node_modules_location = path.join("node_modules").join(PRETTIER_PACKAGE_NAME); let possible_node_modules_location = path.join("node_modules").join(PRETTIER_PACKAGE_NAME);
if let Some(node_modules_location_metadata) = fs if let Some(node_modules_location_metadata) = fs
.metadata(&path) .metadata(&possible_node_modules_location)
.await .await
.with_context(|| format!("fetching metadata for {possible_node_modules_location:?}"))? .with_context(|| format!("fetching metadata for {possible_node_modules_location:?}"))?
{ {
@ -203,3 +204,10 @@ async fn find_closest_prettier_path(
} }
Ok(None) Ok(None)
} }
async fn prepare_default_prettier(
fs: Arc<dyn Fs>,
node: Arc<dyn NodeRuntime>,
) -> anyhow::Result<PathBuf> {
todo!("TODO kb need to call per language that supports it, and have to use extra packages sometimes")
}

View file

@ -154,7 +154,7 @@ pub struct Project {
copilot_lsp_subscription: Option<gpui::Subscription>, copilot_lsp_subscription: Option<gpui::Subscription>,
copilot_log_subscription: Option<lsp::Subscription>, copilot_log_subscription: Option<lsp::Subscription>,
current_lsp_settings: HashMap<Arc<str>, LspSettings>, current_lsp_settings: HashMap<Arc<str>, LspSettings>,
node_runtime: Option<Arc<dyn NodeRuntime>>, node: Option<Arc<dyn NodeRuntime>>,
prettier_instances: HashMap< prettier_instances: HashMap<
(Option<WorktreeId>, PathBuf), (Option<WorktreeId>, PathBuf),
Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>, Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>,
@ -612,7 +612,7 @@ impl Project {
pub fn local( pub fn local(
client: Arc<Client>, client: Arc<Client>,
node_runtime: Arc<dyn NodeRuntime>, node: Arc<dyn NodeRuntime>,
user_store: ModelHandle<UserStore>, user_store: ModelHandle<UserStore>,
languages: Arc<LanguageRegistry>, languages: Arc<LanguageRegistry>,
fs: Arc<dyn Fs>, fs: Arc<dyn Fs>,
@ -668,7 +668,7 @@ impl Project {
copilot_lsp_subscription, copilot_lsp_subscription,
copilot_log_subscription: None, copilot_log_subscription: None,
current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(), current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(),
node_runtime: Some(node_runtime), node: Some(node),
prettier_instances: HashMap::default(), prettier_instances: HashMap::default(),
} }
}) })
@ -767,7 +767,7 @@ impl Project {
copilot_lsp_subscription, copilot_lsp_subscription,
copilot_log_subscription: None, copilot_log_subscription: None,
current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(), current_lsp_settings: settings::get::<ProjectSettings>(cx).lsp.clone(),
node_runtime: None, node: None,
prettier_instances: HashMap::default(), prettier_instances: HashMap::default(),
}; };
for worktree in worktrees { for worktree in worktrees {
@ -802,8 +802,6 @@ impl Project {
root_paths: impl IntoIterator<Item = &Path>, root_paths: impl IntoIterator<Item = &Path>,
cx: &mut gpui::TestAppContext, cx: &mut gpui::TestAppContext,
) -> ModelHandle<Project> { ) -> ModelHandle<Project> {
use node_runtime::FakeNodeRuntime;
let mut languages = LanguageRegistry::test(); let mut languages = LanguageRegistry::test();
languages.set_executor(cx.background()); languages.set_executor(cx.background());
let http_client = util::http::FakeHttpClient::with_404_response(); let http_client = util::http::FakeHttpClient::with_404_response();
@ -812,7 +810,7 @@ impl Project {
let project = cx.update(|cx| { let project = cx.update(|cx| {
Project::local( Project::local(
client, client,
FakeNodeRuntime::new(), node_runtime::FakeNodeRuntime::new(),
user_store, user_store,
Arc::new(languages), Arc::new(languages),
fs, fs,
@ -8202,43 +8200,25 @@ impl Project {
buffer: &ModelHandle<Buffer>, buffer: &ModelHandle<Buffer>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Option<Task<Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>>> { ) -> Option<Task<Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>>> {
let node_runtime = Arc::clone(self.node_runtime.as_ref()?); let buffer = buffer.read(cx);
let buffer_file = File::from_dyn(buffer.read(cx).file()); let buffer_file = buffer.file();
let language_settings = language_settings(buffer.language(), buffer_file, cx).clone();
if !language_settings.prettier {
return None;
}
let node = Arc::clone(self.node.as_ref()?);
let buffer_file = File::from_dyn(buffer_file);
let buffer_path = buffer_file.map(|file| Arc::clone(file.path())); let buffer_path = buffer_file.map(|file| Arc::clone(file.path()));
let worktree_path = buffer_file let worktree_path = buffer_file
.as_ref() .as_ref()
.map(|file| file.worktree.read(cx).abs_path()); .map(|file| file.worktree.read(cx).abs_path());
let worktree_id = buffer_file.map(|file| file.worktree_id(cx)); let worktree_id = buffer_file.map(|file| file.worktree_id(cx));
// TODO kb return None if config opted out of prettier
if true {
let fs = Arc::clone(&self.fs);
let buffer_path = buffer_path.clone();
let worktree_path = worktree_path.clone();
cx.spawn(|_, _| async move {
let prettier_path = Prettier::locate(
worktree_path
.zip(buffer_path)
.map(|(worktree_root_path, starting_path)| {
dbg!(LocateStart {
worktree_root_path,
starting_path,
})
}),
fs,
)
.await
.unwrap();
dbg!(prettier_path);
})
.detach();
return None;
}
let task = cx.spawn(|this, mut cx| async move { let task = cx.spawn(|this, mut cx| async move {
let fs = this.update(&mut cx, |project, _| Arc::clone(&project.fs)); let fs = this.update(&mut cx, |project, _| Arc::clone(&project.fs));
// TODO kb can we have a cache for this instead? // TODO kb can we have a cache for this instead?
let prettier_path = match cx let prettier_dir = match cx
.background() .background()
.spawn(Prettier::locate( .spawn(Prettier::locate(
worktree_path worktree_path
@ -8263,21 +8243,21 @@ impl Project {
if let Some(existing_prettier) = this.update(&mut cx, |project, _| { if let Some(existing_prettier) = this.update(&mut cx, |project, _| {
project project
.prettier_instances .prettier_instances
.get(&(worktree_id, prettier_path.clone())) .get(&(worktree_id, prettier_dir.clone()))
.cloned() .cloned()
}) { }) {
return existing_prettier; return existing_prettier;
} }
let task_prettier_path = prettier_path.clone(); let task_prettier_dir = prettier_dir.clone();
let new_prettier_task = cx let new_prettier_task = cx
.background() .background()
.spawn(async move { .spawn(async move {
Ok(Arc::new( Ok(Arc::new(
Prettier::start(&task_prettier_path, node_runtime) Prettier::start(&task_prettier_dir, node)
.await .await
.with_context(|| { .with_context(|| {
format!("starting new prettier for path {task_prettier_path:?}") format!("starting new prettier for path {task_prettier_dir:?}")
})?, })?,
)) ))
.map_err(Arc::new) .map_err(Arc::new)
@ -8286,7 +8266,7 @@ impl Project {
this.update(&mut cx, |project, _| { this.update(&mut cx, |project, _| {
project project
.prettier_instances .prettier_instances
.insert((worktree_id, prettier_path), new_prettier_task.clone()); .insert((worktree_id, prettier_dir), new_prettier_task.clone());
}); });
new_prettier_task new_prettier_task
}); });

View file

@ -11,6 +11,7 @@ lazy_static::lazy_static! {
pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed"); pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages"); pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
pub static ref COPILOT_DIR: PathBuf = HOME.join("Library/Application Support/Zed/copilot"); pub static ref COPILOT_DIR: PathBuf = HOME.join("Library/Application Support/Zed/copilot");
pub static ref DEFAULT_PRETTIER_DIR: PathBuf = HOME.join("Library/Application Support/Zed/prettier");
pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db"); pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db");
pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json"); pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json");
pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json"); pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json");