diff --git a/pkl-html-highlighter/Cargo.lock b/pkl-html-highlighter/Cargo.lock index 1f2a549d48..e6cf05a6ae 100644 --- a/pkl-html-highlighter/Cargo.lock +++ b/pkl-html-highlighter/Cargo.lock @@ -59,14 +59,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "cc" -version = "1.0.83" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" [[package]] name = "clap" @@ -120,12 +123,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "libc" -version = "0.2.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" - [[package]] name = "memchr" version = "2.7.1" @@ -136,6 +133,7 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" name = "pkl_html_highlighter" version = "0.6.0" dependencies = [ + "anyhow", "clap", "tree-sitter-highlight", "tree-sitter-pkl", @@ -161,9 +159,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -173,9 +171,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -207,18 +205,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", diff --git a/pkl-html-highlighter/Cargo.toml b/pkl-html-highlighter/Cargo.toml index 2a9948fd59..10ceaaa5f3 100644 --- a/pkl-html-highlighter/Cargo.toml +++ b/pkl-html-highlighter/Cargo.toml @@ -8,6 +8,7 @@ authors = ["pkl-oss@group.apple.com"] tree-sitter-highlight = "0.20" tree-sitter-pkl = { git = "ssh://git@github.com/apple/tree-sitter-pkl.git", tag = "0.16.0" } clap = { version = "4.4.2", features = ["derive"] } +anyhow = "1.0.40" [net] git-fetch-with-cli = true diff --git a/pkl-html-highlighter/src/main.rs b/pkl-html-highlighter/src/main.rs index 22318ae7af..f03e6741ee 100644 --- a/pkl-html-highlighter/src/main.rs +++ b/pkl-html-highlighter/src/main.rs @@ -13,12 +13,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // ===----------------------------------------------------------------------===// -use std::process::exit; -use std::{fs, io}; -use std::io::Read; +use anyhow::{Context, Result}; use clap::Parser; +use std::fs; +use std::io::{self, Read}; +use std::process::exit; use tree_sitter_highlight::{HighlightConfiguration, Highlighter, HtmlRenderer}; -use tree_sitter_pkl; #[derive(Parser)] struct CliArgs { @@ -57,72 +57,111 @@ static QUERY_NAMES: [&str; 26] = [ "title.function.invoke", "params", "comment", - "doctag" + "doctag", ]; -fn highlight_expr(contents: String, highlights_query: String) -> String { - let highlighted = highlight(format!("expr = {}", contents), highlights_query, false); - let stripped = highlighted.strip_prefix("expr = ").unwrap(); - return String::from(stripped); +fn highlight_expr(contents: &str, highlights_query: &str) -> Result { + let highlighted = highlight(&format!("expr = {}", contents), highlights_query, false)?; + let stripped = highlighted + .strip_prefix( + "expr = ", + ) + .context("Failed to strip prefix from highlighted output")?; + Ok(String::from(stripped)) } -pub fn highlight(contents: String, highlights_query: String, is_expr: bool) -> String { +pub fn highlight(contents: &str, highlights_query: &str, is_expr: bool) -> Result { // highlight each line as if it were an expression. if is_expr { return highlight_expr(contents, highlights_query); } + let mut pkl_config = HighlightConfiguration::new( tree_sitter_pkl::language(), - highlights_query.as_str(), + highlights_query, tree_sitter_pkl::INJECTIONS_QUERY, tree_sitter_pkl::LOCALS_QUERY, - ).unwrap(); + )?; + let attrs = &QUERY_NAMES.map(|it| { let classes = it - .split(".") + .split('.') .enumerate() // The first scope is prefixed with hljs-. // Subscopes receive N number of underscores. // https://highlightjs.readthedocs.io/en/stable/css-classes-reference.html#a-note-on-scopes-with-sub-scopes - .map(|(idx, it)| - if idx == 0 { - format!("hljs-{}", it) - } else { - format!("{}{}", it, "_".repeat(idx)) - } - ) + .map(|(idx, it)| { + if idx == 0 { + format!("hljs-{}", it) + } else { + format!("{}{}", it, "_".repeat(idx)) + } + }) .collect::>() .join(" "); format!("class=\"{}\"", classes) }); pkl_config.configure(&QUERY_NAMES); - let mut highlighter = Highlighter::new(); - let events = highlighter.highlight( - &pkl_config, - contents.as_bytes(), - None, - |_| None - ).unwrap(); + let events = highlighter.highlight(&pkl_config, contents.as_bytes(), None, |_| None)?; + let mut renderer = HtmlRenderer::new(); - renderer.render( - events, - contents.as_bytes(), - &move |it| &attrs[it.0].as_bytes() - ).unwrap(); - renderer.lines().collect::>().join("") + renderer.render(events, contents.as_bytes(), &move |it| { + attrs[it.0].as_bytes() + })?; + Ok(renderer.lines().collect::>().join("")) } -fn main() { +fn run() -> Result<()> { let args: CliArgs = CliArgs::parse(); - if args.queries.is_none() { - print!("Missing required argument: --queries "); - exit(1) - } - let queries = fs::read_to_string(args.queries.unwrap().as_str()).unwrap(); + let queries_path = args + .queries + .ok_or_else(|| anyhow::anyhow!("Missing required argument: --queries "))?; + let queries = fs::read_to_string(&queries_path) + .with_context(|| format!("Failed to read queries from path: {}", queries_path))?; + let mut buf = String::new(); - io::stdin().read_to_string(&mut buf).expect("Failed to read stdin!"); - let result = highlight(buf, queries, args.expr); - print!("{}", result) + io::stdin() + .read_to_string(&mut buf) + .context("Failed to read from stdin")?; + let result = highlight(&buf, &queries, args.expr)?; + print!("{}", result); + Ok(()) +} + +fn main() { + if let Err(err) = run() { + eprintln!("Error: {:#}", err); + exit(1); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + static TEST_HIGHLIGHT_QUERY: &str = "(source_file) @keyword"; + + #[test] + fn test_highlight_expr() { + let expr = "let x = 42"; + let result = highlight_expr(expr, TEST_HIGHLIGHT_QUERY).unwrap(); + assert!(result.contains("hljs-operator")); + } + + #[test] + fn test_highlight_document() { + let doc = "let x = 42"; + let result = highlight(doc, TEST_HIGHLIGHT_QUERY, false).unwrap(); + assert!(result.contains("hljs-operator")); + } + + #[test] + fn test_highlight_fails_on_invalid_query() { + let doc = "let x = 42"; + let invalid_query = "(invalid_query) @unknown"; + let result = highlight(doc, invalid_query, false); + assert!(result.is_err()); + } }