Add .prettierignore support (#21297)
Closes #11115 **Context**: Consider a monorepo setup like this: the root has Prettier installed, but the individual monorepos do not. In this case, only one Prettier instance is used, with its installation located at the root. The monorepos also use this same instance for formatting. However, monorepo can have its own `.prettierignore` file, which will take precedence over the `.prettierignore` file at the root level (if one exists) for files in that monorepo. <img src="https://github.com/user-attachments/assets/742f16ac-11ad-4d2f-a5a2-696e47a617b9" alt="prettier" width="200px" /> **Implementation**: From the context above, we should keep ignore dir decoupled from the Prettier instance. This means that even if the project has only one Prettier installation (and thus a single Prettier instance), there can still be multiple `.prettierignore` in play. This approach also allows us to respect `.prettierignore` even when the project does not have Prettier installed locally and instead relies on the editor’s Prettier instance. **Tests**: 1. No Prettier in project, using editor Prettier: Ensures `.prettierignore` is respected even without a local Prettier installation. 2. Monorepo with root Prettier and child `.prettierignore`: Confirms that the child project’s ignore file is correctly used. 3. Monorepo with root and child `.prettierignore` files: Verifies the child ignore file takes precedence over the root’s. Release Notes: - Added `.prettierignore` support to the Prettier integration.
This commit is contained in:
parent
8dd1c23b92
commit
6a37307302
3 changed files with 344 additions and 4 deletions
|
@ -36,6 +36,7 @@ pub struct PrettierStore {
|
|||
worktree_store: Model<WorktreeStore>,
|
||||
default_prettier: DefaultPrettier,
|
||||
prettiers_per_worktree: HashMap<WorktreeId, HashSet<Option<PathBuf>>>,
|
||||
prettier_ignores_per_worktree: HashMap<WorktreeId, HashSet<PathBuf>>,
|
||||
prettier_instances: HashMap<PathBuf, PrettierInstance>,
|
||||
}
|
||||
|
||||
|
@ -65,11 +66,13 @@ impl PrettierStore {
|
|||
worktree_store,
|
||||
default_prettier: DefaultPrettier::default(),
|
||||
prettiers_per_worktree: HashMap::default(),
|
||||
prettier_ignores_per_worktree: HashMap::default(),
|
||||
prettier_instances: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_worktree(&mut self, id_to_remove: WorktreeId, cx: &mut ModelContext<Self>) {
|
||||
self.prettier_ignores_per_worktree.remove(&id_to_remove);
|
||||
let mut prettier_instances_to_clean = FuturesUnordered::new();
|
||||
if let Some(prettier_paths) = self.prettiers_per_worktree.remove(&id_to_remove) {
|
||||
for path in prettier_paths.iter().flatten() {
|
||||
|
@ -211,6 +214,65 @@ impl PrettierStore {
|
|||
}
|
||||
}
|
||||
|
||||
fn prettier_ignore_for_buffer(
|
||||
&mut self,
|
||||
buffer: &Model<Buffer>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Option<PathBuf>> {
|
||||
let buffer = buffer.read(cx);
|
||||
let buffer_file = buffer.file();
|
||||
if buffer.language().is_none() {
|
||||
return Task::ready(None);
|
||||
}
|
||||
match File::from_dyn(buffer_file).map(|file| (file.worktree_id(cx), file.abs_path(cx))) {
|
||||
Some((worktree_id, buffer_path)) => {
|
||||
let fs = Arc::clone(&self.fs);
|
||||
let prettier_ignores = self
|
||||
.prettier_ignores_per_worktree
|
||||
.get(&worktree_id)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
cx.spawn(|lsp_store, mut cx| async move {
|
||||
match cx
|
||||
.background_executor()
|
||||
.spawn(async move {
|
||||
Prettier::locate_prettier_ignore(
|
||||
fs.as_ref(),
|
||||
&prettier_ignores,
|
||||
&buffer_path,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(ControlFlow::Break(())) => None,
|
||||
Ok(ControlFlow::Continue(None)) => None,
|
||||
Ok(ControlFlow::Continue(Some(ignore_dir))) => {
|
||||
log::debug!("Found prettier ignore in {ignore_dir:?}");
|
||||
lsp_store
|
||||
.update(&mut cx, |store, _| {
|
||||
store
|
||||
.prettier_ignores_per_worktree
|
||||
.entry(worktree_id)
|
||||
.or_default()
|
||||
.insert(ignore_dir.clone());
|
||||
})
|
||||
.ok();
|
||||
Some(ignore_dir)
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Failed to determine prettier ignore path for buffer: {e:#}"
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
None => Task::ready(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn start_prettier(
|
||||
node: NodeRuntime,
|
||||
prettier_dir: PathBuf,
|
||||
|
@ -654,6 +716,13 @@ pub(super) async fn format_with_prettier(
|
|||
.ok()?
|
||||
.await;
|
||||
|
||||
let ignore_dir = prettier_store
|
||||
.update(cx, |prettier_store, cx| {
|
||||
prettier_store.prettier_ignore_for_buffer(buffer, cx)
|
||||
})
|
||||
.ok()?
|
||||
.await;
|
||||
|
||||
let (prettier_path, prettier_task) = prettier_instance?;
|
||||
|
||||
let prettier_description = match prettier_path.as_ref() {
|
||||
|
@ -671,7 +740,7 @@ pub(super) async fn format_with_prettier(
|
|||
.flatten();
|
||||
|
||||
let format_result = prettier
|
||||
.format(buffer, buffer_path, cx)
|
||||
.format(buffer, buffer_path, ignore_dir, cx)
|
||||
.await
|
||||
.map(crate::lsp_store::FormatOperation::Prettier)
|
||||
.with_context(|| format!("{} failed to format buffer", prettier_description));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue