Various improvements #169

Merged
schmelczer merged 78 commits from asch/saturday into main 2025-11-30 15:24:52 +00:00
Showing only changes of commit c5ee8e1cd7 - Show all commits

Fix dotfile handling

Andras Schmelczer 2025-11-22 21:02:30 +00:00

View file

@ -9,9 +9,16 @@ pub fn dedup_paths(path: &str) -> impl Iterator<Item = String> {
directory.push('/'); directory.push('/');
} }
// Handle dotfiles: ".gitignore" should have no extension, ".config.json" should split as ".config" + ".json"
let is_simple_dotfile = file_name.starts_with('.') && file_name.matches('.').count() == 1;
let (stem, extension) = if is_simple_dotfile {
(file_name.clone(), String::new())
} else {
// Regular file or dotfile with extension
let name_parts = file_name.rsplitn(2, '.').collect::<Vec<_>>(); let name_parts = file_name.rsplitn(2, '.').collect::<Vec<_>>();
let mut reverse_parts = name_parts.into_iter().rev(); let mut reverse_parts = name_parts.into_iter().rev();
let (stem, extension) = match (reverse_parts.next(), reverse_parts.next()) { match (reverse_parts.next(), reverse_parts.next()) {
(Some(stem), maybe_extension) => ( (Some(stem), maybe_extension) => (
copilot-pull-request-reviewer[bot] commented 2025-11-30 15:03:02 +00:00 (Migrated from github.com)

The comment states '.config.json should split as .config + .json' but the code treats it as a complex dotfile (not simple). Consider clarifying the comment to explain that simple dotfiles have exactly one dot (e.g., '.gitignore') while complex dotfiles like '.config.json' have multiple dots and are handled by the regular path splitting logic.

    // Handle dotfiles:
    //   - Simple dotfiles (exactly one dot, e.g., ".gitignore") are treated as having no extension.
    //   - Complex dotfiles (multiple dots, e.g., ".config.json") are handled by the regular extension splitting logic.
The comment states '.config.json should split as .config + .json' but the code treats it as a complex dotfile (not simple). Consider clarifying the comment to explain that simple dotfiles have exactly one dot (e.g., '.gitignore') while complex dotfiles like '.config.json' have multiple dots and are handled by the regular path splitting logic. ```suggestion // Handle dotfiles: // - Simple dotfiles (exactly one dot, e.g., ".gitignore") are treated as having no extension. // - Complex dotfiles (multiple dots, e.g., ".config.json") are handled by the regular extension splitting logic. ```
stem.to_owned(), stem.to_owned(),
maybe_extension maybe_extension
@ -19,6 +26,7 @@ pub fn dedup_paths(path: &str) -> impl Iterator<Item = String> {
.unwrap_or_default(), .unwrap_or_default(),
), ),
_ => unreachable!("Path must have at least one part"), _ => unreachable!("Path must have at least one part"),
}
}; };
let regex = Regex::new(r" \((\d+)\)$").unwrap(); let regex = Regex::new(r" \((\d+)\)$").unwrap();
@ -85,4 +93,58 @@ mod test {
Some("my/path.with.dots/file (6)".to_owned()) Some("my/path.with.dots/file (6)".to_owned())
); );
} }
#[test]
fn test_regex_capturing_group() {
// Single digit in parentheses
let mut deduped = dedup_paths("document (5).md");
assert_eq!(deduped.next(), Some("document (5).md".to_owned()));
assert_eq!(deduped.next(), Some("document (6).md".to_owned()));
assert_eq!(deduped.next(), Some("document (7).md".to_owned()));
// Multi-digit number
let mut deduped = dedup_paths("report (123).pdf");
assert_eq!(deduped.next(), Some("report (123).pdf".to_owned()));
assert_eq!(deduped.next(), Some("report (124).pdf".to_owned()));
assert_eq!(deduped.next(), Some("report (125).pdf".to_owned()));
// Number without extension
let mut deduped = dedup_paths("folder (99)");
assert_eq!(deduped.next(), Some("folder (99)".to_owned()));
assert_eq!(deduped.next(), Some("folder (100)".to_owned()));
assert_eq!(deduped.next(), Some("folder (101)".to_owned()));
}
#[test]
fn test_dedup_dotfiles() {
// Simple dotfile (no extension)
let mut deduped = dedup_paths(".gitignore");
assert_eq!(deduped.next(), Some(".gitignore".to_owned()));
assert_eq!(deduped.next(), Some(".gitignore (1)".to_owned()));
assert_eq!(deduped.next(), Some(".gitignore (2)".to_owned()));
// Dotfile with extension
let mut deduped = dedup_paths(".config.json");
assert_eq!(deduped.next(), Some(".config.json".to_owned()));
assert_eq!(deduped.next(), Some(".config (1).json".to_owned()));
assert_eq!(deduped.next(), Some(".config (2).json".to_owned()));
// Dotfile with number
let mut deduped = dedup_paths(".gitignore (5)");
assert_eq!(deduped.next(), Some(".gitignore (5)".to_owned()));
assert_eq!(deduped.next(), Some(".gitignore (6)".to_owned()));
assert_eq!(deduped.next(), Some(".gitignore (7)".to_owned()));
// Dotfile with extension and number
let mut deduped = dedup_paths(".config (3).json");
assert_eq!(deduped.next(), Some(".config (3).json".to_owned()));
assert_eq!(deduped.next(), Some(".config (4).json".to_owned()));
assert_eq!(deduped.next(), Some(".config (5).json".to_owned()));
// Dotfile in subdirectory
let mut deduped = dedup_paths("my/path/.gitignore");
assert_eq!(deduped.next(), Some("my/path/.gitignore".to_owned()));
assert_eq!(deduped.next(), Some("my/path/.gitignore (1)".to_owned()));
assert_eq!(deduped.next(), Some("my/path/.gitignore (2)".to_owned()));
}
} }