diff --git a/Cargo.lock b/Cargo.lock index 9147240..fe8f689 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,16 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "bstr" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "builtin" version = "0.0.0" @@ -436,6 +446,19 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + [[package]] name = "half" version = "1.8.2" @@ -507,6 +530,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.3", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -702,6 +741,7 @@ dependencies = [ "async-lsp", "codespan-reporting", "ide", + "ignore", "log", "lsp-types", "macro_rules_attribute", diff --git a/crates/nil/Cargo.toml b/crates/nil/Cargo.toml index 64a231f..200bfe7 100644 --- a/crates/nil/Cargo.toml +++ b/crates/nil/Cargo.toml @@ -11,6 +11,7 @@ argh = "0.1.10" async-lsp = { version = "0.2.0", features = ["tokio"] } codespan-reporting = "0.11.1" ide = { path = "../ide" } +ignore = "0.4.22" log = "0.4.17" lsp-types = "0.95.0" macro_rules_attribute = "0.2.0" diff --git a/crates/nil/src/main.rs b/crates/nil/src/main.rs index 3c0a7b8..8547ce4 100644 --- a/crates/nil/src/main.rs +++ b/crates/nil/src/main.rs @@ -1,4 +1,3 @@ -use anyhow::anyhow; use anyhow::{Context, Result}; use argh::FromArgs; use codespan_reporting::diagnostic::Severity; @@ -9,6 +8,8 @@ use ide::FileId; use ide::FileSet; use ide::SourceRoot; use ide::VfsPath; +use ignore::types::TypesBuilder; +use ignore::WalkBuilder; use std::io::IsTerminal; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -83,6 +84,8 @@ fn main() { return; } + setup_logger(); + if let Some(subcommand) = args.subcommand { return match subcommand { Subcommand::Diagnostics(args) => main_diagnostics(args), @@ -91,8 +94,6 @@ fn main() { }; } - setup_logger(); - if !args.stdio && (io::stdin().is_terminal() || io::stdout().is_terminal()) { // TODO: Make this a hard error. eprintln!( @@ -136,29 +137,56 @@ fn main_diagnostics(args: DiagnosticsArgs) { } } -fn diagnostics_for_files(paths: Vec) -> Result> { +fn diagnostics_for_files(mut roots: Vec) -> Result> { use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; - if paths.is_empty() { - return Err(anyhow!("No files given")); + let mut walk = if roots.is_empty() { + WalkBuilder::new(".") + } else { + WalkBuilder::new(roots.pop().expect("non_empty vec has an item")) + }; + + walk.types({ + let mut builder = TypesBuilder::new(); + builder + .add("nix", "*.nix") + .expect("parsing glob '*.nix' will never fail"); + builder.select("nix"); + builder.build().expect("building types will never fail") + }); + + for root in roots { + walk.add(root); } + let walk = walk.build(); + let mut sources = Vec::new(); let mut file_set = FileSet::default(); let mut change = Change::default(); - for (id, path) in paths.iter().enumerate() { - let file = FileId(id as u32); - - let src = if path.as_os_str() == "-" { - io::read_to_string(io::stdin().lock()).context("Failed to read from stdin")? - } else { - fs::read_to_string(path).context("Failed to read file")? + for (id, entry) in walk.enumerate() { + let entry = match entry { + Ok(entry) => entry, + Err(err) => { + tracing::error!("{err}"); + continue; + } }; - let src: Arc = src.into(); - sources.push((file, path, src.clone())); + let file = FileId(id as u32); + let src = if entry.is_stdin() { + io::read_to_string(io::stdin().lock()).context("Failed to read from stdin")? + } else { + if entry.path().is_dir() { + continue; + } + fs::read_to_string(entry.path()).context("Failed to read file")? + }; - change.change_file(file, src); - file_set.insert(file, VfsPath::new(path)); + let path = entry.into_path(); + let src: Arc = src.into(); + change.change_file(file, src.clone()); + file_set.insert(file, VfsPath::new(&path)); + sources.push((file, path, src)); } change.set_roots(vec![SourceRoot::new_local(file_set, None)]); @@ -172,7 +200,7 @@ fn diagnostics_for_files(paths: Vec) -> Result> { let mut max_severity = None; for (id, path, src) in sources { let diagnostics = snapshot.diagnostics(id).expect("No cancellation"); - emit_diagnostics(path, &src, &mut writer, &mut diagnostics.iter().cloned())?; + emit_diagnostics(&path, &src, &mut writer, &mut diagnostics.iter().cloned())?; max_severity = std::cmp::max( max_severity,