From de1c462ba684b74158bc3479d8dfd2d6e750f08d Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Sat, 24 Feb 2024 13:52:50 +0900 Subject: [PATCH] gitignore: make objects chain be more Arc friendly This partially reverts changes in a9f489ccdf17 "Switch to ignore crate for gitignore handling." Since child ignore object no longer needs to access the root to resolve the prefix path, it's simpler to store a matcher per node. --- lib/src/gitignore.rs | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/src/gitignore.rs b/lib/src/gitignore.rs index 63904846d3..527e093876 100644 --- a/lib/src/gitignore.rs +++ b/lib/src/gitignore.rs @@ -16,7 +16,7 @@ use std::path::PathBuf; use std::sync::Arc; -use std::{fs, io}; +use std::{fs, io, iter}; use ignore::gitignore; use thiserror::Error; @@ -39,13 +39,15 @@ pub enum GitIgnoreError { /// Models the effective contents of multiple .gitignore files. #[derive(Debug)] pub struct GitIgnoreFile { - matchers: Vec, + parent: Option>, + matcher: gitignore::Gitignore, } impl GitIgnoreFile { pub fn empty() -> Arc { Arc::new(GitIgnoreFile { - matchers: Default::default(), + parent: None, + matcher: gitignore::Gitignore::empty(), }) } @@ -72,10 +74,12 @@ impl GitIgnoreFile { builder.add_line(None, line)?; } let matcher = builder.build()?; - let mut matchers = self.matchers.clone(); - matchers.push(matcher); - - Ok(Arc::new(GitIgnoreFile { matchers })) + let parent = if self.matcher.is_empty() { + self.parent.clone() // omit the empty root + } else { + Some(self.clone()) + }; + Ok(Arc::new(GitIgnoreFile { parent, matcher })) } /// Concatenates new `.gitignore` file at the `prefix` directory. @@ -99,18 +103,17 @@ impl GitIgnoreFile { } fn matches_helper(&self, path: &str, is_dir: bool) -> bool { - self.matchers - .iter() - .rev() - .find_map(|matcher| - // TODO: the documentation warns that - // `matched_path_or_any_parents` is slower than `matched`; - // ideally, we would switch to that. - match matcher.matched_path_or_any_parents(path, is_dir) { - ignore::Match::None => None, - ignore::Match::Ignore(_) => Some(true), - ignore::Match::Whitelist(_) => Some(false), - }) + iter::successors(Some(self), |file| file.parent.as_deref()) + .find_map(|file| { + // TODO: the documentation warns that + // `matched_path_or_any_parents` is slower than `matched`; + // ideally, we would switch to that. + match file.matcher.matched_path_or_any_parents(path, is_dir) { + ignore::Match::None => None, + ignore::Match::Ignore(_) => Some(true), + ignore::Match::Whitelist(_) => Some(false), + } + }) .unwrap_or_default() }