From ca2979122b99d7cd700d75f83af48f9917a49d12 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 0fa99c28f4..ff90ef9bde 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() }