From 6871383709b6a82e1f9754fc48080ee3e23e30a7 Mon Sep 17 00:00:00 2001 From: Will Clark Date: Thu, 24 Oct 2024 23:20:21 +0200 Subject: [PATCH] feat: add --gituntracked option (#96) This will allow ignoring files which are untracked by git. Use in conjunction with --gitignore for best effect. --- README.md | 1 + src/cli.rs | 13 ++++++++++++ src/lib.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++ tests/end_to_end.rs | 2 ++ 4 files changed, 68 insertions(+) diff --git a/README.md b/README.md index 3707d36..51a0545 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ The following arguments are available: | `--version` | `-V` | Print current version of mlc | | `--ignore-path` | `-p` | Comma separated list of directories or files which shall be ignored. For example | | `--gitignore` | `-g` | Ignore all files currently ignored by git (requires `git` binary to be available on $PATH). | +| `--gituntracked` | `-u` | Ignore all files currently untracked by git (requires `git` binary to be available on $PATH). | | `--ignore-links` | `-i` | Comma separated list of links which shall be ignored. Use simple `?` and `*` wildcards. For example `--ignore-links "http*://crates.io*"` will skip all links to the crates.io website. See the [used lib](https://github.com/becheran/wildmatch) for more information. | | `--markup-types` | `-t` | Comma separated list list of markup types which shall be checked [possible values: md, html] | | `--root-dir` | `-r` | All links to the file system starting with a slash on linux or backslash on windows will use another virtual root dir. For example the link in a file `[link](/dir/other/file.md)` checked with the cli arg `--root-dir /env/another/dir` will let *mlc* check the existence of `/env/another/dir/dir/other/file.md`. | diff --git a/src/cli.rs b/src/cli.rs index 37b9649..0b2af3a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -105,6 +105,15 @@ pub fn parse_args() -> Config { .action(ArgAction::SetTrue) .required(false), ) + .arg( + Arg::new("gituntracked") + .long("gituntracked") + .short('u') + .value_name("GITUNTRACKED") + .help("Ignore all files untracked by git") + .action(ArgAction::SetTrue) + .required(false), + ) .get_matches(); let default_dir = format!(".{}", &MAIN_SEPARATOR); @@ -175,6 +184,10 @@ pub fn parse_args() -> Config { opt.gitignore = Some(true); } + if matches.get_flag("gituntracked") { + opt.gituntracked = Some(true); + } + if let Some(root_dir) = matches.get_one::("root-dir") { let root_path = Path::new( &root_dir diff --git a/src/lib.rs b/src/lib.rs index 20d9c52..5e049e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,8 @@ pub struct OptionalConfig { pub root_dir: Option, #[serde(rename(deserialize = "gitignore"))] pub gitignore: Option, + #[serde(rename(deserialize = "gituntracked"))] + pub gituntracked: Option, pub throttle: Option, } @@ -92,6 +94,7 @@ Offline: {} MatchExt: {} RootDir: {} Gitignore: {} +Gituntracked: {} IgnoreLinks: {} IgnorePath: {:?} Throttle: {} ms", @@ -103,6 +106,7 @@ Throttle: {} ms", self.optional.match_file_extension.unwrap_or_default(), root_dir_str, self.optional.gitignore.unwrap_or_default(), + self.optional.gituntracked.unwrap_or_default(), ignore_str.join(","), ignore_path_str, self.optional.throttle.unwrap_or(0) @@ -157,7 +161,30 @@ fn find_git_ignored_files() -> Option> { None } } +fn find_git_untracked_files() -> Option> { + let output = Command::new("git") + .arg("ls-files") + .arg("--others") + .arg("--exclude-standard") + .output() + .expect("Failed to execute 'git' command"); + if output.status.success() { + let ignored_files = String::from_utf8(output.stdout) + .expect("Invalid UTF-8 sequence") + .lines() + .filter(|line| line.ends_with(".md") || line.ends_with(".html")) + .filter_map(|line| fs::canonicalize(Path::new(line.trim())).ok()) + .collect::>(); + Some(ignored_files) + } else { + eprintln!( + "git ls-files command failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + None + } +} fn print_helper( link: &MarkupLink, @@ -213,6 +240,16 @@ pub async fn run(config: &Config) -> Result<(), ()> { let is_gitignore_enabled = gitignored_files.is_some(); + let gituntracked_files: Option> = if config.optional.gituntracked.is_some() { + let files = find_git_untracked_files(); + debug!("Found gituntracked files: {:?}", files); + files + } else { + None + }; + + let is_gituntracked_enabled = gituntracked_files.is_some(); + for link in &links { let canonical_link_source = match fs::canonicalize(&link.source) { Ok(path) => path, @@ -237,6 +274,21 @@ pub async fn run(config: &Config) -> Result<(), ()> { } } + if is_gituntracked_enabled { + if let Some(ref gif) = gituntracked_files { + if gif.iter().any(|path| path == &canonical_link_source) { + print_helper( + link, + &"Skip".green(), + "Ignore link because it is untracked by git.", + false, + ); + skipped += 1; + continue; + } + } + } + if ignore_links.iter().any(|m| m.matches(&link.target)) { print_helper( link, diff --git a/tests/end_to_end.rs b/tests/end_to_end.rs index 67675c5..95add0d 100644 --- a/tests/end_to_end.rs +++ b/tests/end_to_end.rs @@ -25,6 +25,7 @@ async fn end_to_end() { ]), root_dir: None, gitignore: None, + gituntracked: None, }, }; if let Err(e) = mlc::run(&config).await { @@ -48,6 +49,7 @@ async fn end_to_end_different_root() { throttle: None, root_dir: Some(test_files), gitignore: None, + gituntracked: None, }, }; if let Err(e) = mlc::run(&config).await {