-
Notifications
You must be signed in to change notification settings - Fork 2
Crate-level API changes for tree-sitter-lint #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: run-context
Are you sure you want to change the base?
Changes from 30 commits
b2c06c0
551e376
3d4682c
422058d
2118d3c
54b39ed
0602f3c
b05884b
d341747
1930ccf
3c2a567
5fe38b3
5a3ede1
0ad0481
c0ae26e
c73c3da
d8f21b0
3a039da
debbbc0
30a1c71
c0bf4ca
2eba2c5
e471b0d
672ac41
c5f3fa2
c371ae9
53e844f
57495f8
31959d1
2061904
6daf28a
6bb81e1
325d3e0
355ea2b
2e842eb
3b1230c
c5a8c85
d250c7c
c5b2e88
7626af8
526d110
d93d175
db220fe
7543d9d
045b98d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,19 @@ | ||
| use clap::Parser; | ||
| use tree_sitter_grep::{run_with_callback, Args}; | ||
| use tree_sitter_lint_tree_sitter_grep::{run_with_callback, Args}; | ||
|
|
||
| fn main() { | ||
| let args = Args::parse_from(["tree_sitter_grep", "-q", "(function_item) @f"]); | ||
| run_with_callback(args, |node, file_contents, path| { | ||
| run_with_callback(args, |query_match, file_contents, path| { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As of now I updated all of the crate-level "callback"-oriented entry points to use the match-oriented (vs single-capture-oriented) querying/callback style Not sure where it should actually "land", for the moment mostly just driving based on the |
||
| println!( | ||
| "Found match in {path:?}: {}", | ||
| std::str::from_utf8(&file_contents[node.byte_range()]).unwrap(), | ||
| std::str::from_utf8( | ||
| &file_contents[query_match | ||
| .nodes_for_capture_index(0) | ||
| .next() | ||
| .unwrap() | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this is a little boilerplate-y I'm guessing the argument for in fact only exposing match-oriented crate-level callback API's is that I believe you can then basically "wrap" those (with a little match -> individual query captures boilerplate) to achieve the same idea/behavior as a single-capture-oriented style |
||
| .byte_range()] | ||
| ) | ||
| .unwrap(), | ||
| ); | ||
| }) | ||
| .unwrap(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,4 +3,5 @@ format_macro_bodies = true | |
| format_macro_matchers = true | ||
| group_imports = "StdExternalCrate" | ||
| imports_granularity = "Crate" | ||
| wrap_comments = true | ||
| edition = "2021" | ||
| # wrap_comments = true | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The thing of |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,16 @@ | ||
| use std::{ | ||
| collections::HashMap, | ||
| fs, | ||
| path::{Path, PathBuf}, | ||
| sync::{Arc, Mutex}, | ||
| }; | ||
|
|
||
| use clap::{ArgGroup, Parser}; | ||
| use derive_builder::Builder; | ||
| use ignore::{types::Types, WalkBuilder, WalkParallel}; | ||
| use rayon::iter::IterBridge; | ||
| use termcolor::BufferWriter; | ||
| use tree_sitter::Query; | ||
|
|
||
| use crate::{ | ||
| language::SupportedLanguage, | ||
|
|
@@ -23,7 +26,8 @@ use crate::{ | |
|
|
||
| const ALL_NODES_QUERY: &str = "(_) @node"; | ||
|
|
||
| #[derive(Parser)] | ||
| #[derive(Builder, Clone, Default, Parser)] | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exposed an Presumably there should be some additional validation eg that some of the invariants that |
||
| #[builder(default, setter(strip_option, into))] | ||
| #[clap(group( | ||
| ArgGroup::new("query_or_filter") | ||
| .multiple(true) | ||
|
|
@@ -37,13 +41,16 @@ pub struct Args { | |
| /// | ||
| /// This conflicts with the --query option. | ||
| #[arg(short = 'Q', long = "query-file", conflicts_with = "query_text")] | ||
| pub path_to_query_file: Option<PathBuf>, | ||
| path_to_query_file: Option<PathBuf>, | ||
|
|
||
| /// The source text of a tree-sitter query. | ||
| /// | ||
| /// This conflicts with the --query-file option. | ||
| #[arg(short, long = "query", conflicts_with = "path_to_query_file")] | ||
| pub query_text: Option<String>, | ||
| query_text: Option<String>, | ||
|
|
||
| #[clap(skip)] | ||
| query_per_language: Option<QueryPerLanguage>, | ||
|
|
||
| /// The name of the tree-sitter query capture (without leading "@") whose | ||
| /// matching nodes will be output. | ||
|
|
@@ -174,7 +181,11 @@ impl Args { | |
| } | ||
|
|
||
| pub(crate) fn get_project_file_walker_types(&self) -> Types { | ||
| get_project_file_walker_types(self.language) | ||
| get_project_file_walker_types(self.language.map(|language| vec![language]).or_else(|| { | ||
| self.query_per_language | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is basically asserting that if you pass Some of this is probably a little bit conceptually unresolved but that seems more or less like the right idea |
||
| .as_ref() | ||
| .map(|query_per_language| query_per_language.keys().cloned().collect()) | ||
| })) | ||
| } | ||
|
|
||
| pub(crate) fn get_project_file_walker(&self) -> WalkParallel { | ||
|
|
@@ -199,18 +210,83 @@ impl Args { | |
| Ok(get_loaded_filter(self.filter.as_deref(), self.filter_arg.as_deref())?.map(Arc::new)) | ||
| } | ||
|
|
||
| pub(crate) fn get_loaded_query_text(&self) -> Result<String, Error> { | ||
| pub(crate) fn get_loaded_query_text_per_language( | ||
| &self, | ||
| ) -> Result<QueryOrQueryTextPerLanguage, Error> { | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may seem like a weird "half-step" (toward The reason is that we do want to go ahead and fail fast if eg we got passed a path to a query file that we can't read But eg for the case of auto-language (and not having passed |
||
| Ok( | ||
| match (self.path_to_query_file.as_ref(), self.query_text.as_ref()) { | ||
| (Some(path_to_query_file), None) => fs::read_to_string(path_to_query_file) | ||
| match ( | ||
| self.path_to_query_file.as_ref(), | ||
| self.query_text.as_ref(), | ||
| self.query_per_language.as_ref(), | ||
| ) { | ||
| (Some(path_to_query_file), None, None) => fs::read_to_string(path_to_query_file) | ||
| .map_err(|source| Error::QueryFileReadError { | ||
| source, | ||
| path_to_query_file: path_to_query_file.clone(), | ||
| })?, | ||
| (None, Some(query_text)) => query_text.clone(), | ||
| (None, None) => ALL_NODES_QUERY.to_owned(), | ||
| })? | ||
| .into(), | ||
| (None, Some(query_text), None) => query_text.clone().into(), | ||
| (None, None, Some(query_per_language)) => query_per_language.clone().into(), | ||
| (None, None, None) => ALL_NODES_QUERY.to_owned().into(), | ||
| _ => unreachable!(), | ||
| }, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| impl ArgsBuilder { | ||
| pub fn maybe_language(&mut self, language: Option<SupportedLanguage>) -> &mut Self { | ||
| self.language = Some(language); | ||
| self | ||
| } | ||
| } | ||
|
|
||
| pub type QueryPerLanguage = HashMap<SupportedLanguage, Arc<Query>>; | ||
|
|
||
| pub enum QueryOrQueryTextPerLanguage { | ||
| SingleQueryText(String), | ||
| PerLanguage(QueryPerLanguage), | ||
| } | ||
|
|
||
| impl QueryOrQueryTextPerLanguage { | ||
| pub fn get_query_or_query_text_for_language( | ||
| &self, | ||
| language: SupportedLanguage, | ||
| ) -> QueryOrQueryText { | ||
| match self { | ||
| QueryOrQueryTextPerLanguage::SingleQueryText(query_text) => (&**query_text).into(), | ||
| QueryOrQueryTextPerLanguage::PerLanguage(per_language) => { | ||
| per_language.get(&language).unwrap().clone().into() | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl From<String> for QueryOrQueryTextPerLanguage { | ||
| fn from(value: String) -> Self { | ||
| Self::SingleQueryText(value) | ||
| } | ||
| } | ||
|
|
||
| impl From<QueryPerLanguage> for QueryOrQueryTextPerLanguage { | ||
| fn from(value: QueryPerLanguage) -> Self { | ||
| Self::PerLanguage(value) | ||
| } | ||
| } | ||
|
|
||
| pub enum QueryOrQueryText<'a> { | ||
| QueryText(&'a str), | ||
| Query(Arc<Query>), | ||
| } | ||
|
|
||
| impl<'a> From<&'a str> for QueryOrQueryText<'a> { | ||
| fn from(value: &'a str) -> Self { | ||
| Self::QueryText(value) | ||
| } | ||
| } | ||
|
|
||
| impl<'a> From<Arc<Query>> for QueryOrQueryText<'a> { | ||
| fn from(value: Arc<Query>) -> Self { | ||
| Self::Query(value) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| use std::{ | ||
| collections::HashMap, | ||
| collections::{HashMap, HashSet}, | ||
| ops::{Deref, Index}, | ||
| }; | ||
|
|
||
|
|
@@ -50,6 +50,10 @@ impl SupportedLanguage { | |
| pub fn name_for_ignore_select(&self) -> &'static str { | ||
| SUPPORTED_LANGUAGE_NAMES_FOR_IGNORE_SELECT[*self] | ||
| } | ||
|
|
||
| pub fn comment_kinds(&self) -> &'static HashSet<&'static str> { | ||
| &SUPPORTED_LANGUAGE_COMMENT_KINDS[*self] | ||
| } | ||
| } | ||
|
|
||
| static SUPPORTED_LANGUAGE_LANGUAGES: Lazy<BySupportedLanguage<Language>> = Lazy::new(|| { | ||
|
|
@@ -117,3 +121,31 @@ pub static ALL_SUPPORTED_LANGUAGES_BY_NAME_FOR_IGNORE_SELECT: Lazy< | |
| }) | ||
| .collect() | ||
| }); | ||
|
|
||
| static SUPPORTED_LANGUAGE_COMMENT_KINDS: Lazy<BySupportedLanguage<HashSet<&'static str>>> = | ||
| Lazy::new(|| { | ||
| by_supported_language!( | ||
| Rust => ["line_comment", "block_comment"].into(), | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of the ESLint helper methods are "comment-aware" and so "baking in" some comment-awareness at the I have no idea if these are right but can go through and add the correct "kinds" here for the different languages at some point |
||
| Typescript => ["comment"].into(), | ||
| Javascript => ["comment"].into(), | ||
| Swift => ["comment"].into(), | ||
| ObjectiveC => ["comment"].into(), | ||
| Toml => ["comment"].into(), | ||
| Python => ["comment"].into(), | ||
| Ruby => ["comment"].into(), | ||
| C => ["comment"].into(), | ||
| Cpp => ["comment"].into(), | ||
| Go => ["comment"].into(), | ||
| Java => ["comment"].into(), | ||
| CSharp => ["comment"].into(), | ||
| Kotlin => ["comment"].into(), | ||
| Elisp => ["comment"].into(), | ||
| Elm => ["comment"].into(), | ||
| Dockerfile => ["comment"].into(), | ||
| Html => ["comment"].into(), | ||
| TreeSitterQuery => ["comment"].into(), | ||
| Json => ["comment"].into(), | ||
| Css => ["comment"].into(), | ||
| Lua => ["comment"].into(), | ||
| ) | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per helixbass/tree-sitter-lint#4, updating this to allow publishing that crate while this hasn't "landed"