From 1fb976ab8843488ae0b7fe7ae3818bb8bff95bf8 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Sat, 11 Nov 2023 11:43:28 +0100 Subject: [PATCH] Support `--respect-ignores` (#765) * Support `--respect-ignores` * Update readme and changelog * Add tests * Update changelog --- CHANGELOG.md | 5 ++ Cargo.lock | 214 +++++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 1 + README.md | 4 +- src/cli/main.rs | 144 ++++++++++++++++++++++++++++---- src/cli/opt.rs | 4 + 6 files changed, 344 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef0ebac3..1c432d27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added flag `--respect-ignores`. By default, files explicitly passed to stylua (e.g. `stylua foo.lua`) will always be formatted, regardless of whether the file is ignored. Enabling this flag will consider `.styluaignore` or glob matches before formatting the file. + - Note: for backwards compatibility reasons, formatting via stdin always respects ignores. This behaviour will change in the next major release + ## [0.18.2] - 2023-09-10 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 77006ab4..ba6276dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,12 +38,27 @@ dependencies = [ "anstyle", "bstr", "doc-comment", - "predicates", + "predicates 2.1.1", "predicates-core", "predicates-tree", "wait-timeout", ] +[[package]] +name = "assert_fs" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f070617a68e5c2ed5d06ee8dd620ee18fb72b99f6c094bed34cf8ab07c875b48" +dependencies = [ + "anstyle", + "doc-comment", + "globwalk", + "predicates 3.0.3", + "predicates-core", + "predicates-tree", + "tempfile", +] + [[package]] name = "atty" version = "0.2.14" @@ -73,6 +88,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bstr" version = "1.6.2" @@ -102,6 +123,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -142,7 +172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "indexmap 1.7.0", "lazy_static", @@ -175,7 +205,7 @@ dependencies = [ "lazy_static", "libc", "unicode-width", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -322,6 +352,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fnv" version = "1.0.7" @@ -369,6 +426,17 @@ dependencies = [ "regex", ] +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "half" version = "1.7.1" @@ -501,9 +569,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linked-hash-map" @@ -511,6 +579,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + [[package]] name = "log" version = "0.4.20" @@ -650,6 +724,18 @@ dependencies = [ "predicates-core", ] +[[package]] +name = "predicates" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +dependencies = [ + "anstyle", + "difflib", + "itertools", + "predicates-core", +] + [[package]] name = "predicates-core" version = "1.0.6" @@ -733,6 +819,15 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "regex" version = "1.10.2" @@ -783,6 +878,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rustversion" version = "1.0.9" @@ -920,6 +1028,7 @@ version = "0.18.2" dependencies = [ "anyhow", "assert_cmd", + "assert_fs", "cfg-if", "clap", "console", @@ -967,6 +1076,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -1222,7 +1344,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -1231,13 +1362,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1246,42 +1392,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "winnow" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index 5f62e188..5988d3d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ wasm-bindgen = { version = "0.2.81", optional = true } criterion = "0.4.0" insta = { version = "1.34.0", features = ["glob"] } assert_cmd = "2.0.12" +assert_fs = "1.0.13" [[bench]] name = "date" diff --git a/README.md b/README.md index e1575e51..8b233bde 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,9 @@ stylua -g '*.lua' -g '!*.spec.lua' -- . # format all Lua files except test files Note, if you are using the glob argument, it can take in multiple strings, so `--` is required to break between the glob pattern and the files to format. -Glob Filtering is only used for directory searching - passing a file directly (e.g. `stylua foo.txt`) will override the glob. +By default, glob filtering (and `.styluaignore` files) are only applied for directory traversal and searching. +Files passed directly (e.g. `stylua foo.txt`) will override the glob / ignore and always be formatted. +To disable this behaviour, pass the `--respect-ignores` flag (`stylua --respect-ignores foo.txt`). ### Filtering using `.styluaignore` diff --git a/src/cli/main.rs b/src/cli/main.rs index 4554f04d..4c19a428 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -221,7 +221,13 @@ fn get_ignore( directory: &Path, search_parent_directories: bool, ) -> Result { - let file_path = find_ignore_file_path(directory.to_path_buf(), search_parent_directories); + let file_path = find_ignore_file_path(directory.to_path_buf(), search_parent_directories) + .or_else(|| { + std::env::current_dir() + .ok() + .and_then(|cwd| find_ignore_file_path(cwd, false)) + }); + if let Some(file_path) = file_path { let (ignore, err) = Gitignore::new(file_path); if let Some(err) = err { @@ -234,6 +240,31 @@ fn get_ignore( } } +/// Whether the provided path was explicitly provided to the tool +fn is_explicitly_provided(opt: &opt::Opt, path: &Path) -> bool { + opt.files.iter().any(|p| path == *p) +} + +/// By default, files explicitly passed to the command line will be formatted regardless of whether +/// they are present in .styluaignore / not glob matched. If `--respect-ignores` is provided, +/// then we enforce .styluaignore / glob matching on explicitly passed paths. +fn should_respect_ignores(opt: &opt::Opt, path: &Path) -> bool { + !is_explicitly_provided(opt, path) || opt.respect_ignores +} + +fn path_is_stylua_ignored(path: &Path, search_parent_directories: bool) -> Result { + let ignore = get_ignore( + path.parent().expect("cannot get parent directory"), + search_parent_directories, + ) + .context("failed to parse ignore file")?; + + Ok(matches!( + ignore.matched(path, false), + ignore::Match::Ignore(_) + )) +} + fn format(opt: opt::Opt) -> Result { if opt.files.is_empty() { bail!("no files provided"); @@ -400,15 +431,7 @@ fn format(opt: opt::Opt) -> Result { let opt = opt.clone(); let should_skip_format = match &opt.stdin_filepath { - Some(filepath) => { - let ignore = get_ignore( - filepath.parent().expect("cannot get parent directory"), - opt.search_parent_directories, - ) - .context("failed to parse ignore file")?; - - matches!(ignore.matched(filepath, false), ignore::Match::Ignore(_)) - } + Some(path) => path_is_stylua_ignored(path, opt.search_parent_directories)?, None => false, }; @@ -468,8 +491,8 @@ fn format(opt: opt::Opt) -> Result { if path.is_file() { // If the user didn't provide a glob pattern, we should match against our default one - // We should ignore the glob check if the path provided was explicitly given to the CLI - if use_default_glob && !opt.files.iter().any(|p| path == *p) { + if use_default_glob && should_respect_ignores(opt.as_ref(), path.as_path()) + { lazy_static::lazy_static! { static ref DEFAULT_GLOB: globset::GlobSet = { let mut builder = globset::GlobSetBuilder::new(); @@ -484,6 +507,15 @@ fn format(opt: opt::Opt) -> Result { } } + // If `--respect-ignores` was given and this is an explicit file path, + // we should check .styluaignore + if is_explicitly_provided(opt.as_ref(), &path) + && should_respect_ignores(opt.as_ref(), &path) + && path_is_stylua_ignored(&path, opt.search_parent_directories)? + { + continue; + } + let tx = tx.clone(); pool.execute(move || { #[cfg(not(feature = "editorconfig"))] @@ -632,10 +664,27 @@ fn main() { #[cfg(test)] mod tests { use assert_cmd::Command; + use assert_fs::prelude::*; + + macro_rules! construct_tree { + ({ $($file_name:literal:$file_contents:literal,)+ }) => {{ + let cwd = assert_fs::TempDir::new().unwrap(); + + $( + cwd.child($file_name).write_str($file_contents).unwrap(); + )+ + + cwd + }}; + } + + fn create_stylua() -> Command { + Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap() + } #[test] fn test_no_files_provided() { - let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); + let mut cmd = create_stylua(); cmd.assert() .failure() .code(2) @@ -644,11 +693,78 @@ mod tests { #[test] fn test_format_stdin() { - let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); + let mut cmd = create_stylua(); cmd.arg("-") .write_stdin("local x = 1") .assert() .success() .stdout("local x = 1\n"); } + + #[test] + fn test_format_file() { + let cwd = construct_tree!({ + "foo.lua": "local x = 1", + }); + + let mut cmd = create_stylua(); + cmd.current_dir(cwd.path()).arg(".").assert().success(); + + cwd.child("foo.lua").assert("local x = 1\n"); + + cwd.close().unwrap(); + } + + #[test] + fn test_stylua_ignore() { + let cwd = construct_tree!({ + ".styluaignore": "ignored/", + "foo.lua": "local x = 1", + "ignored/bar.lua": "local x = 1", + }); + + let mut cmd = create_stylua(); + cmd.current_dir(cwd.path()).arg(".").assert().success(); + + cwd.child("foo.lua").assert("local x = 1\n"); + cwd.child("ignored/bar.lua").assert("local x = 1"); + + cwd.close().unwrap(); + } + + #[test] + fn explicitly_provided_files_dont_check_ignores() { + let cwd = construct_tree!({ + ".styluaignore": "foo.lua", + "foo.lua": "local x = 1", + }); + + let mut cmd = create_stylua(); + cmd.current_dir(cwd.path()) + .arg("foo.lua") + .assert() + .success(); + + cwd.child("foo.lua").assert("local x = 1\n"); + + cwd.close().unwrap(); + } + + #[test] + fn test_respect_ignores() { + let cwd = construct_tree!({ + ".styluaignore": "foo.lua", + "foo.lua": "local x = 1", + }); + + let mut cmd = create_stylua(); + cmd.current_dir(cwd.path()) + .args(["--respect-ignores", "foo.lua"]) + .assert() + .success(); + + cwd.child("foo.lua").assert("local x = 1"); + + cwd.close().unwrap(); + } } diff --git a/src/cli/opt.rs b/src/cli/opt.rs index e37fe5ae..6a75f470 100644 --- a/src/cli/opt.rs +++ b/src/cli/opt.rs @@ -101,6 +101,10 @@ pub struct Opt { #[cfg(feature = "editorconfig")] #[structopt(long)] pub no_editorconfig: bool, + + /// Respect .styluaignore and glob matching for file paths provided directly to the tool + #[structopt(long)] + pub respect_ignores: bool, } #[derive(ArgEnum, Clone, Copy, Debug, PartialEq, Eq)]