1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//! Compile query metadata and filter matches with custom predicates.
use crate::{
Error,
backend::{Backend, Match},
};
mod predicate;
pub use predicate::Predicate;
/// A compiled query and its metadata in the shared format.
pub struct Query<Q> {
/// Runtime-specific compiled query.
pub raw: Q,
/// Capture names indexed by [`crate::backend::Capture::index`].
pub capture_names: Vec<String>,
/// Pattern metadata indexed by [`Match::pattern`].
pub patterns: Vec<Pattern>,
/// Metadata required for each capture across all query patterns.
pub metadata: Vec<Metadata>,
}
/// Node metadata needed by a query capture.
#[derive(Clone, Copy, Default)]
pub struct Metadata {
/// Maximum number of ancestor names to collect. Zero disables collection.
pub ancestors: usize,
/// Whether to collect ancestor depth without relying on ancestor names.
pub depth: bool,
/// Whether the immediate parent ID is needed for direct rainbow scopes.
pub parent: bool,
/// Whether to collect immediate children to exclude from injected content.
pub children: bool,
}
/// Properties and custom predicates for one query pattern.
pub struct Pattern {
/// Property settings in runtime order.
pub properties: Vec<Property>,
/// Custom predicates applied after runtime matching.
pub predicates: Vec<Predicate>,
/// Required local-binding state, or `None` for either state.
pub local: Option<bool>,
/// Compiled highlight priority, defaulting to 100.
pub priority: i32,
offsets: bool,
}
impl Pattern {
/// Compile custom predicates and retain normalized pattern metadata.
///
/// # Errors
///
/// Returns [`Error::Query`] for unsupported or malformed custom predicates.
pub fn new(
properties: Vec<Property>,
predicates: Vec<(String, Vec<Argument>)>,
local: Option<bool>,
) -> Result<Self, Error> {
let predicates = predicates
.into_iter()
.map(|(operator, arguments)| Predicate::compile(&operator, arguments))
.collect::<Result<Vec<_>, _>>()?;
let priority = properties
.iter()
.find(|property| property.key == "priority")
.and_then(|property| property.value.as_deref())
.and_then(|value| value.parse().ok())
.unwrap_or(100);
let offsets = predicates
.iter()
.any(|predicate| matches!(predicate, Predicate::Offset { .. }));
Ok(Self {
properties,
predicates,
local,
priority,
offsets,
})
}
/// Return the first property matching the key.
pub fn property(&self, key: &str) -> Option<&Property> {
self.properties.iter().find(|property| property.key == key)
}
/// Return the first matching property's value, if present.
pub fn value(&self, key: &str) -> Option<&str> {
self.property(key)
.and_then(|property| property.value.as_deref())
}
}
/// A query property in the shared format.
pub struct Property {
/// Property name, such as `injection.language`.
pub key: String,
/// Optional literal property value.
pub value: Option<String>,
/// Optional capture index associated with the property.
pub capture: Option<u32>,
}
/// A custom-predicate argument from a compiled query.
#[derive(Debug)]
pub enum Argument {
/// A capture index in the query.
Capture(u32),
/// A literal string argument.
String(String),
}
impl<Q> Query<Q> {
/// Store a compiled query and determine the node metadata each capture needs.
pub fn new(raw: Q, capture_names: Vec<String>, patterns: Vec<Pattern>) -> Self {
let mut metadata = vec![Metadata::default(); capture_names.len()];
for (name, metadata) in capture_names.iter().zip(&mut metadata) {
metadata.depth = matches!(name.as_str(), "local.scope" | "rainbow.scope");
metadata.parent = name == "rainbow.bracket";
metadata.children = name == "injection.content"
&& patterns
.iter()
.any(|pattern| pattern.property("injection.include-children").is_none());
}
for pattern in &patterns {
for predicate in &pattern.predicates {
if let Predicate::Ancestor {
capture, parent, ..
} = predicate
{
let required = if *parent { 1 } else { usize::MAX };
metadata[*capture as usize].ancestors =
metadata[*capture as usize].ancestors.max(required);
}
}
}
Self {
raw,
capture_names,
patterns,
metadata,
}
}
/// Run a query and filter its matches. Empty queries skip runtime execution.
///
/// # Errors
///
/// Returns runtime, predicate, or invalid-range errors.
pub fn matches<B: Backend<Query = Q>>(
&self,
backend: &mut B,
tree: &B::Tree,
source: &str,
) -> Result<Vec<Match>, Error> {
if self.patterns.is_empty() {
return Ok(Vec::new());
}
self.filtered(backend.matches(self, tree, source)?, source)
}
/// Validate captures, filter custom predicates, and apply capture offsets.
///
/// Reuses the input vector and preserves the order of accepted matches.
/// Input indices must refer to this query.
///
/// # Errors
///
/// Returns [`Error::InvalidRange`] for invalid source boundaries or offsets.
///
/// # Panics
///
/// Panics if a match references a pattern outside [`Self::patterns`].
pub fn filtered(&self, mut matches: Vec<Match>, source: &str) -> Result<Vec<Match>, Error> {
let mut accepted = 0;
for index in 0..matches.len() {
let matched = &mut matches[index];
for capture in &matched.captures {
if source.get(capture.node.range.clone()).is_none() {
return Err(Error::InvalidRange(capture.node.range.clone()));
}
}
let pattern = &self.patterns[matched.pattern];
if !pattern
.predicates
.iter()
.all(|predicate| predicate.accepts(matched, source))
{
continue;
}
if pattern.offsets {
for predicate in &pattern.predicates {
predicate.apply(matched, source)?;
}
for capture in &matched.captures {
if source.get(capture.node.range.clone()).is_none() {
return Err(Error::InvalidRange(capture.node.range.clone()));
}
}
}
matches.swap(accepted, index);
accepted += 1;
}
matches.truncate(accepted);
Ok(matches)
}
}