From 16be945cddb2652fac5520b0fe6bd90824673466 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 3 Jan 2025 04:15:21 -0500 Subject: [PATCH] feat(cli): rework tags to use new input handler Co-authored-by: Will Lillis --- cli/src/main.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++---- cli/src/tags.rs | 134 ++++++++++++++++++++------------------------- 2 files changed, 187 insertions(+), 88 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 398027d8..99deecae 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -420,11 +420,18 @@ struct Tags { long = "paths", help = "The path to a file with paths to source file(s)" )] - pub paths_file: Option, + pub paths_file: Option, #[arg(num_args = 1.., help = "The source file(s) to use")] - pub paths: Option>, + pub paths: Option>, #[arg(long, help = "The path to an alternative config.json file")] pub config_path: Option, + #[arg( + long, + short = 'n', + help = "Generate tags from the contents of a specific test" + )] + #[clap(conflicts_with = "paths", conflicts_with = "paths_file")] + pub test_number: Option, } #[derive(Args)] @@ -1434,15 +1441,127 @@ impl Tags { let config = Config::load(self.config_path)?; let loader_config = config.get()?; loader.find_all_languages(&loader_config)?; - let paths = collect_paths(self.paths_file.as_deref(), self.paths)?; - tags::generate_tags( - &loader, - &config.get()?, - self.scope.as_deref(), - &paths, - self.quiet, - self.time, + + let cancellation_flag = util::cancel_on_signal(); + + let (mut language, mut language_configuration) = (None, None); + if let Some(scope) = self.scope.as_deref() { + if let Some((lang, lang_config)) = loader.language_configuration_for_scope(scope)? { + language = Some(lang); + language_configuration = Some(lang_config); + }; + if language.is_none() { + return Err(anyhow!("Unknown scope '{scope}'")); + } + } + + let options = TagsOptions { + scope: self.scope, + quiet: self.quiet, + print_time: self.time, + cancellation_flag: cancellation_flag.clone(), + }; + + let input = get_input( + self.paths_file.as_deref(), + self.paths, + self.test_number, + &cancellation_flag, )?; + match input { + CliInput::Paths(paths) => { + let indent = paths.len() > 1; + for path in paths { + let (language, language_config) = + match (language.clone(), language_configuration) { + (Some(l), Some(lc)) => (l, lc), + _ => { + if let Some((lang, lang_config)) = + loader.language_configuration_for_file_name(&path)? + { + (lang, lang_config) + } else { + eprintln!( + "{}", + util::lang_not_found_for_path(&path, &loader_config) + ); + continue; + } + } + }; + + if let Some(tags_config) = language_config.tags_config(language)? { + tags::generate_tags( + &path, + &path.display().to_string(), + tags_config, + indent, + &options, + )?; + } else { + eprintln!("No tags config found for path {}", path.display()); + } + } + } + + CliInput::Test { + name, + contents, + languages: language_names, + } => { + let path = get_tmp_source_file(&contents)?; + + let languages = loader.languages_at_path(current_dir)?; + let language = languages + .iter() + .find(|(_, n)| language_names.contains(&Box::from(n.as_str()))) + .or_else(|| languages.first()) + .map(|(l, _)| l.clone()) + .ok_or_else(|| anyhow!("No language found in current path"))?; + let language_config = loader + .get_language_configuration_in_current_path() + .ok_or_else(|| anyhow!("No language configuration found in current path"))?; + + if let Some(tags_config) = language_config.tags_config(language)? { + tags::generate_tags(&path, &name, tags_config, false, &options)?; + } else { + eprintln!("No tags config found for test {name}"); + } + fs::remove_file(path)?; + } + + CliInput::Stdin(contents) => { + // Place user input and tags output on separate lines + println!(); + + let path = get_tmp_source_file(&contents)?; + + let (language, language_config) = + if let (Some(l), Some(lc)) = (language.clone(), language_configuration) { + (l, lc) + } else { + let languages = loader.languages_at_path(current_dir)?; + let language = languages + .first() + .map(|(l, _)| l.clone()) + .ok_or_else(|| anyhow!("No language found in current path"))?; + let language_configuration = loader + .get_language_configuration_in_current_path() + .ok_or_else(|| { + anyhow!("No language configuration found in current path") + })?; + (language, language_configuration) + }; + + if let Some(tags_config) = language_config.tags_config(language)? { + tags::generate_tags(&path, "stdin", tags_config, false, &options)?; + } else { + eprintln!("No tags config found for path {}", current_dir.display()); + } + fs::remove_file(path)?; + } + } + Ok(()) } } @@ -1565,7 +1684,7 @@ fn run() -> Result<()> { Commands::Fuzz(fuzz_options) => fuzz_options.run(loader, ¤t_dir)?, Commands::Query(query_options) => query_options.run(loader, ¤t_dir)?, Commands::Highlight(highlight_options) => highlight_options.run(loader, ¤t_dir)?, - Commands::Tags(tags_options) => tags_options.run(loader)?, + Commands::Tags(tags_options) => tags_options.run(loader, ¤t_dir)?, Commands::Playground(playground_options) => playground_options.run(¤t_dir)?, Commands::DumpLanguages(dump_options) => dump_options.run(loader)?, Commands::Complete(complete_options) => complete_options.run(&mut cli), diff --git a/cli/src/tags.rs b/cli/src/tags.rs index 4e2058c7..2d205c04 100644 --- a/cli/src/tags.rs +++ b/cli/src/tags.rs @@ -3,95 +3,75 @@ use std::{ io::{self, Write}, path::Path, str, + sync::{atomic::AtomicUsize, Arc}, time::Instant, }; -use anyhow::{anyhow, Result}; -use tree_sitter_loader::{Config, Loader}; -use tree_sitter_tags::TagsContext; +use anyhow::Result; +use tree_sitter_tags::{TagsConfiguration, TagsContext}; -use super::util; +pub struct TagsOptions { + pub scope: Option, + pub quiet: bool, + pub print_time: bool, + pub cancellation_flag: Arc, +} pub fn generate_tags( - loader: &Loader, - loader_config: &Config, - scope: Option<&str>, - paths: &[String], - quiet: bool, - time: bool, + path: &Path, + name: &str, + config: &TagsConfiguration, + indent: bool, + opts: &TagsOptions, ) -> Result<()> { - let mut lang = None; - if let Some(scope) = scope { - lang = loader.language_configuration_for_scope(scope)?; - if lang.is_none() { - return Err(anyhow!("Unknown scope '{scope}'")); - } - } - let mut context = TagsContext::new(); - let cancellation_flag = util::cancel_on_signal(); let stdout = io::stdout(); let mut stdout = stdout.lock(); - for path in paths { - let path = Path::new(&path); - let (language, language_config) = match lang.clone() { - Some(v) => v, - None => { - if let Some(v) = loader.language_configuration_for_file_name(path)? { - v - } else { - eprintln!("{}", util::lang_not_found_for_path(path, loader_config)); - continue; - } - } - }; - - if let Some(tags_config) = language_config.tags_config(language)? { - let indent = if paths.len() > 1 { - if !quiet { - writeln!(&mut stdout, "{}", path.to_string_lossy())?; - } - "\t" - } else { - "" - }; - - let source = fs::read(path)?; - let t0 = Instant::now(); - for tag in context - .generate_tags(tags_config, &source, Some(&cancellation_flag))? - .0 - { - let tag = tag?; - if !quiet { - write!( - &mut stdout, - "{indent}{:<10}\t | {:<8}\t{} {} - {} `{}`", - str::from_utf8(&source[tag.name_range]).unwrap_or(""), - &tags_config.syntax_type_name(tag.syntax_type_id), - if tag.is_definition { "def" } else { "ref" }, - tag.span.start, - tag.span.end, - str::from_utf8(&source[tag.line_range]).unwrap_or(""), - )?; - if let Some(docs) = tag.docs { - if docs.len() > 120 { - write!(&mut stdout, "\t{:?}...", docs.get(0..120).unwrap_or(""))?; - } else { - write!(&mut stdout, "\t{:?}", &docs)?; - } - } - writeln!(&mut stdout)?; - } - } - - if time { - writeln!(&mut stdout, "{indent}time: {}ms", t0.elapsed().as_millis(),)?; - } - } else { - eprintln!("No tags config found for path {path:?}"); + let indent_str = if indent { + if !opts.quiet { + writeln!(&mut stdout, "{name}")?; } + "\t" + } else { + "" + }; + + let source = fs::read(path)?; + let start = Instant::now(); + for tag in context + .generate_tags(config, &source, Some(&opts.cancellation_flag))? + .0 + { + let tag = tag?; + if !opts.quiet { + write!( + &mut stdout, + "{indent_str}{:<10}\t | {:<8}\t{} {} - {} `{}`", + str::from_utf8(&source[tag.name_range]).unwrap_or(""), + &config.syntax_type_name(tag.syntax_type_id), + if tag.is_definition { "def" } else { "ref" }, + tag.span.start, + tag.span.end, + str::from_utf8(&source[tag.line_range]).unwrap_or(""), + )?; + if let Some(docs) = tag.docs { + if docs.len() > 120 { + write!(&mut stdout, "\t{:?}...", docs.get(0..120).unwrap_or(""))?; + } else { + write!(&mut stdout, "\t{:?}", &docs)?; + } + } + writeln!(&mut stdout)?; + } + } + + if opts.print_time { + writeln!( + &mut stdout, + "{indent_str}time: {}ms", + start.elapsed().as_millis(), + )?; } Ok(())