Prepare prettier file lookup code infra
This commit is contained in:
parent
92f23e626e
commit
a8dfa01362
6 changed files with 165 additions and 8 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -5523,6 +5523,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"fs",
|
"fs",
|
||||||
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
"language",
|
"language",
|
||||||
]
|
]
|
||||||
|
|
|
@ -199,7 +199,8 @@
|
||||||
// "arguments": ["--stdin-filepath", "{buffer_path}"]
|
// "arguments": ["--stdin-filepath", "{buffer_path}"]
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
"formatter": "language_server",
|
// TODO kb description
|
||||||
|
"formatter": "auto",
|
||||||
// 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:
|
||||||
//
|
//
|
||||||
|
|
|
@ -85,7 +85,7 @@ pub struct RemoveOptions {
|
||||||
pub ignore_if_not_exists: bool,
|
pub ignore_if_not_exists: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
pub inode: u64,
|
pub inode: u64,
|
||||||
pub mtime: SystemTime,
|
pub mtime: SystemTime,
|
||||||
|
@ -229,11 +229,12 @@ impl Fs for RealFs {
|
||||||
} else {
|
} else {
|
||||||
symlink_metadata
|
symlink_metadata
|
||||||
};
|
};
|
||||||
|
let file_type_metadata = metadata.file_type();
|
||||||
Ok(Some(Metadata {
|
Ok(Some(Metadata {
|
||||||
inode: metadata.ino(),
|
inode: metadata.ino(),
|
||||||
mtime: metadata.modified().unwrap(),
|
mtime: metadata.modified().unwrap(),
|
||||||
is_symlink,
|
is_symlink,
|
||||||
is_dir: metadata.file_type().is_dir(),
|
is_dir: file_type_metadata.is_dir(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ gpui = { path = "../gpui" }
|
||||||
fs = { path = "../fs" }
|
fs = { path = "../fs" }
|
||||||
|
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
language = { path = "../language", features = ["test-support"] }
|
language = { path = "../language", features = ["test-support"] }
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
pub use std::path::{Path, PathBuf};
|
pub use std::path::{Path, PathBuf};
|
||||||
pub use std::sync::Arc;
|
pub use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::ModelHandle;
|
use gpui::ModelHandle;
|
||||||
use language::{Buffer, Diff};
|
use language::{Buffer, Diff};
|
||||||
|
@ -11,6 +13,12 @@ pub struct Prettier {
|
||||||
|
|
||||||
pub struct NodeRuntime;
|
pub struct NodeRuntime;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LocateStart {
|
||||||
|
pub worktree_root_path: Arc<Path>,
|
||||||
|
pub starting_path: Arc<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Prettier {
|
impl Prettier {
|
||||||
// This was taken from the prettier-vscode extension.
|
// This was taken from the prettier-vscode extension.
|
||||||
pub const CONFIG_FILE_NAMES: &'static [&'static str] = &[
|
pub const CONFIG_FILE_NAMES: &'static [&'static str] = &[
|
||||||
|
@ -28,8 +36,107 @@ impl Prettier {
|
||||||
".editorconfig",
|
".editorconfig",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub async fn locate(starting_path: Option<&Path>, fs: Arc<dyn Fs>) -> PathBuf {
|
pub async fn locate(
|
||||||
todo!()
|
starting_path: Option<LocateStart>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
) -> anyhow::Result<PathBuf> {
|
||||||
|
let paths_to_check = match starting_path {
|
||||||
|
Some(starting_path) => {
|
||||||
|
let worktree_root = starting_path
|
||||||
|
.worktree_root_path
|
||||||
|
.components()
|
||||||
|
.into_iter()
|
||||||
|
.take_while(|path_component| {
|
||||||
|
path_component.as_os_str().to_str() != Some("node_modules")
|
||||||
|
})
|
||||||
|
.collect::<PathBuf>();
|
||||||
|
|
||||||
|
if worktree_root != starting_path.worktree_root_path.as_ref() {
|
||||||
|
vec![worktree_root]
|
||||||
|
} else {
|
||||||
|
let (worktree_root_metadata, start_path_metadata) = if starting_path
|
||||||
|
.starting_path
|
||||||
|
.as_ref()
|
||||||
|
== Path::new("")
|
||||||
|
{
|
||||||
|
let worktree_root_data =
|
||||||
|
fs.metadata(&worktree_root).await.with_context(|| {
|
||||||
|
format!(
|
||||||
|
"FS metadata fetch for worktree root path {worktree_root:?}",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
(worktree_root_data.unwrap_or_else(|| {
|
||||||
|
panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}")
|
||||||
|
}), None)
|
||||||
|
} else {
|
||||||
|
let full_starting_path = worktree_root.join(&starting_path.starting_path);
|
||||||
|
let (worktree_root_data, start_path_data) = futures::try_join!(
|
||||||
|
fs.metadata(&worktree_root),
|
||||||
|
fs.metadata(&full_starting_path),
|
||||||
|
)
|
||||||
|
.with_context(|| {
|
||||||
|
format!("FS metadata fetch for starting path {full_starting_path:?}",)
|
||||||
|
})?;
|
||||||
|
(
|
||||||
|
worktree_root_data.unwrap_or_else(|| {
|
||||||
|
panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}")
|
||||||
|
}),
|
||||||
|
start_path_data,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
match start_path_metadata {
|
||||||
|
Some(start_path_metadata) => {
|
||||||
|
anyhow::ensure!(worktree_root_metadata.is_dir,
|
||||||
|
"For non-empty start path, worktree root {starting_path:?} should be a directory");
|
||||||
|
anyhow::ensure!(
|
||||||
|
!start_path_metadata.is_dir,
|
||||||
|
"For non-empty start path, it should not be a directory {starting_path:?}"
|
||||||
|
);
|
||||||
|
anyhow::ensure!(
|
||||||
|
!start_path_metadata.is_symlink,
|
||||||
|
"For non-empty start path, it should not be a symlink {starting_path:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let file_to_format = starting_path.starting_path.as_ref();
|
||||||
|
let mut paths_to_check = VecDeque::from(vec![worktree_root.clone()]);
|
||||||
|
let mut current_path = worktree_root;
|
||||||
|
for path_component in file_to_format.components().into_iter() {
|
||||||
|
current_path = current_path.join(path_component);
|
||||||
|
paths_to_check.push_front(current_path.clone());
|
||||||
|
if path_component.as_os_str().to_str() == Some("node_modules") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paths_to_check.pop_front(); // last one is the file itself or node_modules, skip it
|
||||||
|
Vec::from(paths_to_check)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
anyhow::ensure!(
|
||||||
|
!worktree_root_metadata.is_dir,
|
||||||
|
"For empty start path, worktree root should not be a directory {starting_path:?}"
|
||||||
|
);
|
||||||
|
anyhow::ensure!(
|
||||||
|
!worktree_root_metadata.is_symlink,
|
||||||
|
"For empty start path, worktree root should not be a symlink {starting_path:?}"
|
||||||
|
);
|
||||||
|
worktree_root
|
||||||
|
.parent()
|
||||||
|
.map(|path| vec![path.to_path_buf()])
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if dbg!(paths_to_check).is_empty() {
|
||||||
|
// TODO kb return the default prettier, how, without state?
|
||||||
|
} else {
|
||||||
|
// TODO kb now check all paths to check for prettier
|
||||||
|
}
|
||||||
|
Ok(PathBuf::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(prettier_path: &Path, node: Arc<NodeRuntime>) -> anyhow::Result<Self> {
|
pub async fn start(prettier_path: &Path, node: Arc<NodeRuntime>) -> anyhow::Result<Self> {
|
||||||
|
|
|
@ -50,7 +50,7 @@ use lsp::{
|
||||||
};
|
};
|
||||||
use lsp_command::*;
|
use lsp_command::*;
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use prettier::{NodeRuntime, Prettier};
|
use prettier::{LocateStart, NodeRuntime, Prettier};
|
||||||
use project_settings::{LspSettings, ProjectSettings};
|
use project_settings::{LspSettings, ProjectSettings};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use search::SearchQuery;
|
use search::SearchQuery;
|
||||||
|
@ -8189,14 +8189,61 @@ impl Project {
|
||||||
) -> Option<Task<Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>>> {
|
) -> Option<Task<Shared<Task<Result<Arc<Prettier>, Arc<anyhow::Error>>>>>> {
|
||||||
let buffer_file = File::from_dyn(buffer.read(cx).file());
|
let buffer_file = File::from_dyn(buffer.read(cx).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
|
||||||
|
.as_ref()
|
||||||
|
.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
|
// 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 = Prettier::locate(buffer_path.as_deref(), fs).await;
|
let prettier_path = match cx
|
||||||
|
.background()
|
||||||
|
.spawn(Prettier::locate(
|
||||||
|
worktree_path
|
||||||
|
.zip(buffer_path)
|
||||||
|
.map(|(worktree_root_path, starting_path)| LocateStart {
|
||||||
|
worktree_root_path,
|
||||||
|
starting_path,
|
||||||
|
}),
|
||||||
|
fs,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(path) => path,
|
||||||
|
Err(e) => {
|
||||||
|
return Task::Ready(Some(Result::Err(Arc::new(
|
||||||
|
e.context("determining prettier path for worktree {worktree_path:?}"),
|
||||||
|
))))
|
||||||
|
.shared();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue