diff --git a/Cargo.lock b/Cargo.lock index 87f90e9..374a9c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,7 +641,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.16", ] [[package]] @@ -663,7 +663,20 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.13", ] [[package]] @@ -710,7 +723,7 @@ dependencies = [ "serde", "shlex", "snapbox", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] @@ -806,6 +819,7 @@ dependencies = [ "unic-char-range", "unic-ucd-block", "unic-ucd-name", + "walkdir", ] [[package]] @@ -989,3 +1003,12 @@ checksum = "557404e450152cd6795bb558bca69e43c585055f4606e3bcae5894fc6dac9ba0" dependencies = [ "memchr", ] + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index fb43529..58ac136 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ unic-ucd-block = "0.9.0" unic-char-range = "0.9.0" toml = "0.8.14" serde = { version = "1.0.203", features = ["derive"] } +walkdir = "2.5.0" [dev-dependencies] trycmd = "0.15.5" diff --git a/README.md b/README.md index 04c9512..d6b1bde 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ # Unicop +## Usage + +``` +unicop [FILES]... +``` + +Where `[FILES]...` is a list of files or directory to check, default: `.`. + ## Example ```console diff --git a/src/main.rs b/src/main.rs index 73fde85..69e8e59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,33 +1,41 @@ use std::env; use std::fs; +use std::path::Path; use miette::{miette, LabeledSpan, NamedSource, Severity}; use unic_ucd_name::Name; -mod config; -mod rules; - fn main() { - for arg in env::args().skip(1) { - check_file(&arg); + let mut args: Vec = env::args().skip(1).collect(); + if args.is_empty() { + args = vec![String::from(".")] + } + for arg in args.iter() { + for entry in walkdir::WalkDir::new(arg) { + match entry { + Err(err) => eprintln!("{:}", err), + Ok(entry) if entry.file_type().is_file() => check_file(entry.path()), + Ok(_) => {} + } + } } } -fn check_file(arg: &str) { - let src = fs::read_to_string(arg).unwrap(); - let nsrc = NamedSource::new(arg, src.clone()); +fn check_file(path: &Path) { + let Some(lang) = detect_language(path) else { + return; + }; + let filename = path.display().to_string(); + let src = fs::read_to_string(path).unwrap(); + let nsrc = NamedSource::new(&filename, src.clone()); let mut parser = tree_sitter::Parser::new(); - parser - .set_language(&tree_sitter_javascript::language()) - .expect("Error loading JavaScript grammar"); - // parser - // .set_language(&tree_sitter_python::language()) - // .expect("Error loading Python grammar"); + parser.set_language(&lang).expect("Error loading grammar"); let tree = parser.parse(&src, None).unwrap(); if tree.root_node().has_error() { println!( "{:?}", - miette!(severity = Severity::Warning, "{}: parse error", arg).with_source_code(nsrc) + miette!(severity = Severity::Warning, "{}: parse error", filename) + .with_source_code(nsrc) ); } for (off, ch) in src.char_indices() { @@ -52,7 +60,23 @@ fn check_file(arg: &str) { chname, node.kind() ) - .with_source_code(NamedSource::new(arg, src.clone())); + .with_source_code(NamedSource::new(&filename, src.clone())); println!("{:?}", report); } } + +// Tree-sitter grammars include some configurations to help decide whether the language applies to +// a given file. +// Unfortunately, neither the language-detection algorithm nor the configurations are included in +// the Rust crates. So for now we have a simplified language-detection with hard-coded +// configurations. +// See https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-detection +fn detect_language(path: &Path) -> Option { + match path.extension()?.to_str()? { + // https://github.com/tree-sitter/tree-sitter-javascript/blob/master/package.json + "js" | "mjs" | "cjs" | "jsx" => Some(tree_sitter_javascript::language()), + // https://github.com/tree-sitter/tree-sitter-python/blob/master/package.json + "py" => Some(tree_sitter_python::language()), + _ => None, + } +}