diff --git a/Cargo.lock b/Cargo.lock index 226d14e2..bb9aa5bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" version = "0.7.20" @@ -92,6 +107,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -129,6 +159,31 @@ version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + [[package]] name = "cast" version = "0.3.0" @@ -472,6 +527,102 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures 0.1.31", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -493,6 +644,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "globset" version = "0.4.10" @@ -576,6 +733,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -619,9 +785,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "linux-raw-sys" @@ -635,12 +801,28 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "memchr" version = "2.5.0" @@ -656,6 +838,26 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + [[package]] name = "mockall" version = "0.11.4" @@ -708,6 +910,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -726,12 +937,47 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets", +] + [[package]] name = "path-clean" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd" +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "plotters" version = "0.3.5" @@ -848,6 +1094,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -888,6 +1143,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" version = "0.37.27" @@ -945,6 +1206,19 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b929ea725591083cbca8b8ea178ed6efc918eccd40b784e199ce88967104199" +dependencies = [ + "anyhow", + "byteorder", + "bytes 0.4.12", + "serde", + "thiserror", +] + [[package]] name = "serde_cbor" version = "0.11.2" @@ -990,6 +1264,40 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "strsim" version = "0.10.0" @@ -1116,6 +1424,52 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +dependencies = [ + "backtrace", + "bytes 1.5.0", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes 1.5.0", + "futures-core", + "futures-io", + "futures-sink", + "log", + "pin-project-lite", + "slab", + "tokio", +] + [[package]] name = "toml" version = "0.5.11" @@ -1147,7 +1501,9 @@ dependencies = [ "serde_json", "sha-1", "tempfile", + "tokio", "toml", + "watchman_client", "which", ] @@ -1260,6 +1616,24 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "watchman_client" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839fea2d85719bb69089290d7970bba2131f544448db8f990ea75813c30775ca" +dependencies = [ + "anyhow", + "bytes 1.5.0", + "futures 0.3.29", + "maplit", + "serde", + "serde_bser", + "thiserror", + "tokio", + "tokio-util", + "winapi", +] + [[package]] name = "web-sys" version = "0.3.64" diff --git a/Cargo.toml b/Cargo.toml index fb6f441e..1bd51e49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,8 @@ sha-1 = "0.9.2" tempfile = "3.2.0" toml = "0.5" which = "4.0.2" +watchman_client = "0.8.0" +tokio = "1.35" [dev-dependencies] criterion = "0.3" diff --git a/src/command/format.rs b/src/command/format.rs index 16b7e97d..340b4e77 100644 --- a/src/command/format.rs +++ b/src/command/format.rs @@ -1,10 +1,13 @@ use crate::engine::run_treefmt; use anyhow::anyhow; use directories::ProjectDirs; -use log::debug; +use log::{debug, warn}; use std::path::{Path, PathBuf}; +use tokio; +use watchman_client::prelude::Connector; -pub fn format_cmd( +#[tokio::main] +pub async fn format_cmd( tree_root: &Option, work_dir: &Path, config_file: &Path, @@ -49,6 +52,25 @@ pub fn format_cmd( paths ); + let client = match Connector::new().connect().await { + Err(e) => { + warn!( + "watchman is not available (err = {:?}), falling back on stat(2)", + e + ); + None + } + Ok(c) => { + // This is important. Subprocess wastes ~20ms. + if !std::env::var("WATCHMAN_SOCK").is_ok() { + warn!( + "Environment variable `WATCHMAN_SOCK' is not set, falling back on subprocess" + ); + }; + Some(c) + } + }; + // Finally run the main formatter logic from the engine. run_treefmt( &tree_root, @@ -62,7 +84,9 @@ pub fn format_cmd( fail_on_change, allow_missing_formatter, selected_formatters, - )?; + &client.as_ref(), + ) + .await?; Ok(()) } diff --git a/src/engine.rs b/src/engine.rs index 311f7ca0..62f96973 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,7 @@ //! The main formatting engine logic is in this module. use crate::config::Root; -use crate::{config, eval_cache::CacheManifest, formatter::FormatterName}; +use crate::{config, eval_cache::CacheManifest, formatter::FormatterName, Deserialize}; use crate::{expand_path, formatter::Formatter, get_meta, get_path_meta, FileMeta}; use anyhow::anyhow; use ignore::{Walk, WalkBuilder}; @@ -12,6 +12,7 @@ use std::io::{self, Write}; use std::iter::Iterator; use std::path::{Path, PathBuf}; use std::{collections::BTreeMap, time::Instant}; +use watchman_client::prelude::*; /// Controls how the information is displayed at the end of a run pub enum DisplayType { @@ -21,9 +22,17 @@ pub enum DisplayType { Long, } +watchman_client::query_result_type! { + struct WatchmanResponse { + name: NameField, + mtime: MTimeField, + size: SizeField, + } +} + /// Run the treefmt #[allow(clippy::too_many_arguments)] -pub fn run_treefmt( +pub async fn run_treefmt( tree_root: &Path, work_dir: &Path, cache_dir: &Path, @@ -35,6 +44,7 @@ pub fn run_treefmt( fail_on_change: bool, allow_missing_formatter: bool, selected_formatters: &Option>, + watchman: &Option<&Client>, ) -> anyhow::Result<()> { assert!(tree_root.is_absolute()); assert!(work_dir.is_absolute()); @@ -89,11 +99,130 @@ pub fn run_treefmt( // Insert the new formatter configs cache.update_formatters(formatters.clone()); } + stats.timed_debug("update formatters"); + let mut resolved_root: Option = None; + + let matches = match watchman { + None => { + let walker = build_walker(paths, hidden); + + let matches = collect_matches_from_walker(walker, &formatters, &mut stats); + stats.timed_debug("tree walk"); + matches + } + Some(watchman) => { + let root = watchman + .resolve_root(CanonicalPath::with_canonicalized_path( + tree_root.to_path_buf(), + )) + .await + .map_err(anyhow::Error::new)?; + stats.timed_debug("resolve root"); + + let mut matches: Vec = vec![]; + + for fmt in formatters.values() { + let mut includes: Vec = vec![]; + let mut excludes: Vec = vec![]; + + // As far as "watchman" concerned, "*.c" matches only $ROOT/foo.c, but not + // $ROOT/src.foo.c + for pattern in fmt.includes_str.iter() { + let mut s = pattern.to_string(); + s.insert_str(0, "**/"); + + includes.push(Expr::Match(MatchTerm { + glob: s, + include_dot_files: hidden, + no_escape: false, + wholename: true, + })); + } + for pattern in fmt.excludes_str.iter() { + let mut s = pattern.to_string(); + s.insert_str(0, "**/"); + + excludes.push(Expr::Match(MatchTerm { + glob: s, + include_dot_files: hidden, + no_escape: false, + wholename: true, + })); + } + if excludes.len() > 0 { + matches.push(Expr::All(vec![ + Expr::Any(includes), + Expr::Not(Box::new(Expr::Any(excludes))), + ])); + } else { + matches.push(Expr::Any(includes)); + } + } + + let since = if no_cache { + ClockSpec::null() + } else { + match cache.clock.clone() { + // FIXME: Avoid .clone() + Some(since) => since, + _ => ClockSpec::null(), + } + }; - let walker = build_walker(paths, hidden); + let query = QueryRequestCommon { + expression: Some(Expr::All(vec![ + Expr::Exists, + Expr::FileType(FileType::Regular), + Expr::Since(SinceTerm::ObservedClock(since)), + Expr::Any(matches), + ])), + ..Default::default() + }; - let matches = collect_matches_from_walker(walker, &formatters, &mut stats); - stats.timed_debug("tree walk"); + let resp: QueryResult = watchman + .query(&root, query) + .await + .map_err(anyhow::Error::new)?; + resolved_root = Some(root); + let files = resp.files.unwrap_or_default(); + stats.timed_debug(&format!("watchman query (returned {} files)", files.len())); + let mut matches: BTreeMap> = + Default::default(); + + // Now the data returned by "watchman" must be massaged into the shape used by + // stat(2)-based branch of code. List of fields returned by "watchman" are files that + // (1) are changed and (2) match some glob of some formatter, but I have to figure + // myself to which formatter they belong. Also, "watchman" has no concept of exclusion + // globs, so that also must be filtered out. + // + // In addition, "watchman" returns list of files modified without respect to + // "paths" parameter, so that also must be post-processed. + for r in files.iter() { + let pathbuf = expand_path(&r.name.clone().into_inner(), tree_root); // FIXME: Get rid of clone(). + match formatters.values().find(|fmt| fmt.is_match(&pathbuf)) { + // Watchman is not aware of exclusion globs. + None => {} + Some(fmt) => { + let pathbuf = expand_path(&pathbuf, tree_root); + // File changed is somewhere under "paths". + if paths.iter().any(|path| pathbuf.starts_with(path)) { + let meta = FileMeta { + size: r.size.clone().into_inner(), + mtime: r.mtime.clone().into_inner(), + }; + matches + .entry(fmt.name.clone()) + .or_default() + .insert(pathbuf, meta); + } + } + } + } + stats.timed_debug("post-process watchman list"); + matches + } + }; + stats.timed_debug("finish walking"); // Filter out all of the paths that were already in the cache let matches = if !no_cache { @@ -153,8 +282,21 @@ pub fn run_treefmt( stats.timed_debug("format"); if !no_cache { - // Record the new matches in the cache - cache.add_results(new_matches.clone()); + match watchman { + None => cache.add_results(new_matches.clone()), + // We only need "since" value in cache. + Some(watchman) => { + cache.clock = Some( + watchman + .clock(&resolved_root.unwrap(), Default::default()) + .await?, + ); + } + } + + if !watchman.is_some() { + cache.add_results(new_matches.clone()); + }; // And write to disk cache.write(cache_dir, treefmt_toml); stats.timed_debug("write cache"); diff --git a/src/eval_cache.rs b/src/eval_cache.rs index 4ca64495..57dd9f08 100644 --- a/src/eval_cache.rs +++ b/src/eval_cache.rs @@ -12,6 +12,7 @@ use std::collections::BTreeMap; use std::fs::{create_dir_all, read_to_string, File}; use std::io::Write; use std::path::{Path, PathBuf}; +use watchman_client::pdu::ClockSpec; /// Metadata about the formatter #[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] @@ -37,6 +38,8 @@ pub struct FormatterInfo { #[derive(Debug, Default, Deserialize, Serialize)] /// RootManifest pub struct CacheManifest { + /// Timestamp of last full-tree formatting. + pub clock: Option, /// Map of all the formatter infos pub formatters: BTreeMap, /// Map of all the formatted paths @@ -46,6 +49,7 @@ pub struct CacheManifest { impl Clone for CacheManifest { fn clone(&self) -> Self { Self { + clock: self.clock.clone(), formatters: self.formatters.clone(), matches: self.matches.clone(), } diff --git a/src/formatter.rs b/src/formatter.rs index 4b695106..4799b3f8 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -88,8 +88,18 @@ pub struct Formatter { pub work_dir: PathBuf, /// File or Folder that is included to be formatted pub includes: GlobSet, + /// The string representation of glob patterns that was used + /// to build `includes` member to be used to query `watchman(1). + /// + /// There is no interface to decompile `GlobSet`. + pub includes_str: Vec, /// File or Folder that is excluded to be formatted pub excludes: GlobSet, + /// The string representation of glob patterns that was used + /// to build `excludes` member to be used to query `watchman(1). + /// + /// There is no interface to decompile `GlobSet`. + pub excludes_str: Vec, } impl Formatter { @@ -191,7 +201,10 @@ impl Formatter { options: cfg.options.clone(), work_dir, includes, + // TODO: Avoid clone(). + includes_str: cfg.includes.clone(), excludes, + excludes_str: cfg.excludes.clone(), }) } }