diff --git a/README.md b/README.md index 9c7e8011..2d89b3b8 100644 --- a/README.md +++ b/README.md @@ -816,6 +816,41 @@ which can be caught as: cat sensitive.go | srgn --go-query '(field_declaration name: (field_identifier) @name tag: (raw_string_literal) @tag (#match? @name "[tT]oken") (#not-eq? @tag "`json:\"-\"`"))' --fail-any # will fail ``` +###### Ignoring parts of matches + +Occassionally, parts of a match need to be ignored, for example when no suitable +tree-sitter node type is available. For example, say we'd like to replace the `error` +with `wrong` inside the string of the macro body: + +```rust wrong.rs +fn wrong() { + let wrong = "wrong"; + error!("This went error"); +} +``` + +Let's assume there's a node type for matching *entire* macros (`macro_invocation`) and +one to match macro *names* (`((macro_invocation macro: (identifier) @name))`), but +*none* to match macro *contents* (this is wrong, tree-sitter offers this in the form of +`token_tree`, but let's imagine...). To match just `"This went error"`, the entire macro +would need to be matched, with the name part ignored. Any capture name containing +`IGNORE` will provide just that: + +```bash +cat wrong.rs | srgn --rust-query '((macro_invocation macro: (identifier) @IGNORE_name) @macro)' 'error' 'wrong' +``` + +```rust output-wrong.rs +fn wrong() { + let wrong = "wrong"; + error!("This went wrong"); +} +``` + +If it weren't ignored, the result would read `wrong!("This went wrong");`. + +###### Further reading + These matching expressions are a mouthful. A couple resources exist for getting started with your own queries: diff --git a/src/scoping/langs/mod.rs b/src/scoping/langs/mod.rs index 455c9051..7d6e08fd 100644 --- a/src/scoping/langs/mod.rs +++ b/src/scoping/langs/mod.rs @@ -131,11 +131,22 @@ pub trait LanguageScoper: Scoper { let ranges = run(query); - let has_ignore = query.capture_names().iter().any(|name| name == IGNORE); + let is_ignored = |name: &str| name.contains(IGNORE); + let has_ignored_captures = query.capture_names().iter().any(|name| is_ignored(name)); - if has_ignore { + if has_ignored_captures { let ignored_ranges = { - disable_all_captures_except(IGNORE, query); + let acknowledged_captures = query + .capture_names() + .iter() + .filter(|name| !is_ignored(name)) + .cloned() + .collect::>(); + + for name in acknowledged_captures { + trace!("Disabling capture for: {:?}", name); + query.disable_capture(&name); + } debug!("Query has captures to ignore: running additional query"); run(query) @@ -150,12 +161,3 @@ pub trait LanguageScoper: Scoper { } } } - -fn disable_all_captures_except(capture_name: &str, query: &mut TSQuery) { - let capture_names = query.capture_names().to_owned(); - for name in capture_names { - if name != capture_name { - query.disable_capture(&name); - } - } -}