Skip to content

Commit 18089b7

Browse files
committed
Put tests into their own separate directory
1 parent 3c53e2d commit 18089b7

13 files changed

+319
-34
lines changed

bin/test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ else
1818
--directory "$PROJECT_ROOT/core" \
1919
--directory "$PROJECT_ROOT/lisp" \
2020
--directory "$PROJECT_ROOT/langs" \
21+
--directory "$PROJECT_ROOT/tests" \
2122
-l ert \
2223
-l tree-sitter-tests.el \
2324
-f ert-run-tests-batch-and-exit

bin/test.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ if ($args[0] -eq "watch") {
2929
--directory "$project_root\core" `
3030
--directory "$project_root\lisp" `
3131
--directory "$project_root\langs" `
32+
--directory "$project_root\tests" `
3233
-l ert `
3334
-l tree-sitter-tests.el `
3435
-f ert-run-tests-batch-and-exit

tests/.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
data/* -linguist-detectable
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

tests/data/narrowing.bash

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
ls -lah

tests/data/query.rs

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
use std::cell::RefCell;
2+
3+
use emacs::{defun, Env, Error, GlobalRef, IntoLisp, Result, Value, Vector};
4+
use tree_sitter::{Node, QueryCursor, QueryErrorKind};
5+
6+
use crate::{
7+
types::{BytePos, Point},
8+
lang::Language,
9+
node::RNode,
10+
error,
11+
};
12+
13+
fn vec_to_vector<'e, T: IntoLisp<'e>>(env: &'e Env, vec: Vec<T>) -> Result<Vector<'e>> {
14+
let vector = env.make_vector(vec.len(), ())?;
15+
for (i, v) in vec.into_iter().enumerate() {
16+
vector.set(i, v)?;
17+
}
18+
Ok(vector)
19+
}
20+
21+
// -------------------------------------------------------------------------------------------------
22+
// Query
23+
24+
struct Query {
25+
pub(crate) raw: tree_sitter::Query,
26+
pub(crate) capture_tags: Vec<GlobalRef>,
27+
}
28+
29+
impl_pred!(query_p, &RefCell<Query>);
30+
31+
/// Create a new query from a SOURCE containing one or more S-expression patterns.
32+
///
33+
/// The query is associated with LANGUAGE, and can only be run on syntax nodes
34+
/// parsed with LANGUAGE.
35+
///
36+
/// TAG-ASSIGNER is a function that is called to determine how captures are tagged
37+
/// in query results. It should take a capture name defined in SOURCE's patterns
38+
/// (e.g. "function.builtin"), and return a tag value. If the return value is nil,
39+
/// the associated capture name is disabled.
40+
#[defun(user_ptr)]
41+
fn _make_query(language: Language, source: String, tag_assigner: Value) -> Result<Query> {
42+
let mut raw = tree_sitter::Query::new(language.into(), &source).or_else(|err| {
43+
let symbol = match err.kind {
44+
QueryErrorKind::Syntax => error::tsc_query_invalid_syntax,
45+
QueryErrorKind::NodeType => error::tsc_query_invalid_node_type,
46+
QueryErrorKind::Field => error::tsc_query_invalid_field,
47+
QueryErrorKind::Capture => error::tsc_query_invalid_capture,
48+
QueryErrorKind::Predicate => error::tsc_query_invalid_predicate,
49+
QueryErrorKind::Structure => error::tsc_query_invalid_structure,
50+
};
51+
let byte_pos: BytePos = err.offset.into();
52+
let point: Point = tree_sitter::Point { row: err.row, column: err.column }.into();
53+
// TODO: Character position?
54+
// TODO: Convert named node types and field names to symbols and keywords?
55+
tag_assigner.env.signal(symbol, (err.message, point, byte_pos))
56+
})?;
57+
let capture_names = raw.capture_names().to_vec();
58+
let mut capture_tags = vec![];
59+
for name in &capture_names {
60+
let value = tag_assigner.call((name, ))?;
61+
if !value.is_not_nil() {
62+
raw.disable_capture(name);
63+
}
64+
capture_tags.push(value.make_global_ref())
65+
}
66+
Ok(Query { raw, capture_tags })
67+
}
68+
69+
macro_rules! defun_query_methods {
70+
($($(#[$meta:meta])* $($lisp_name:literal)? fn $name:ident $( ( $( $param:ident : $type:ty ),* ) )? -> $rtype:ty $(; $into:ident)? )*) => {
71+
$(
72+
#[defun$((name = $lisp_name))?]
73+
$(#[$meta])*
74+
fn $name(query: &Query, $( $( $param : $type ),* )? ) -> Result<$rtype> {
75+
Ok(query.raw.$name( $( $( $param ),* )? )$(.$into())?)
76+
}
77+
)*
78+
};
79+
}
80+
81+
defun_query_methods! {
82+
/// Return the byte position where the NTH pattern starts in QUERY's source.
83+
"-query-start-byte-for-pattern" fn start_byte_for_pattern(nth: usize) -> BytePos; into
84+
85+
/// Return the number of patterns in QUERY.
86+
"query-count-patterns" fn pattern_count -> usize
87+
}
88+
89+
/// Return the names of the captures used in QUERY.
90+
#[defun]
91+
fn _query_capture_names(query: Value) -> Result<Vector> {
92+
let env = query.env;
93+
let query = query.into_ref::<Query>()?;
94+
let names = query.raw.capture_names();
95+
let vec = env.make_vector(names.len(), ())?;
96+
for (i, name) in names.iter().enumerate() {
97+
vec.set(i, name)?;
98+
}
99+
Ok(vec)
100+
}
101+
102+
/// Return all of QUERY's available capture tags.
103+
/// See `tsc-make-query' for an explanation of capture tagging.
104+
#[defun(mod_in_name = true)]
105+
fn capture_tags<'e>(env: &'e Env, query: &Query) -> Result<Vector<'e>> {
106+
let symbols = env.make_vector(query.capture_tags.len(), ())?;
107+
for (i, symbol) in query.capture_tags.iter().enumerate() {
108+
symbols.set(i, symbol)?;
109+
}
110+
Ok(symbols)
111+
}
112+
113+
/// Disable a certain capture within QUERY, by specifying its NAME.
114+
///
115+
/// This prevents the capture from being returned in matches, and also avoids any
116+
/// resource usage associated with recording the capture.
117+
#[defun]
118+
fn _disable_capture(query: &mut Query, name: String) -> Result<()> {
119+
query.raw.disable_capture(&name);
120+
Ok(())
121+
}
122+
123+
// -------------------------------------------------------------------------------------------------
124+
// QueryCursor
125+
126+
impl_pred!(query_cursor_p, &RefCell<QueryCursor>);
127+
128+
/// Create a new cursor for executing a given query.
129+
///
130+
/// The cursor stores the state that is needed to iteratively search for matches.
131+
#[defun(user_ptr)]
132+
fn make_query_cursor() -> Result<QueryCursor> {
133+
Ok(QueryCursor::new())
134+
}
135+
136+
fn text_callback<'e>(
137+
text_function: Value<'e>,
138+
error: &'e RefCell<Option<Error>>,
139+
) -> impl FnMut(Node<'e>) -> String + 'e {
140+
move |child| {
141+
let beg: BytePos = child.start_byte().into();
142+
let end: BytePos = child.end_byte().into();
143+
text_function.call((beg, end)).and_then(|v| v.into_rust()).unwrap_or_else(|e| {
144+
error.borrow_mut().replace(e);
145+
"".to_owned()
146+
})
147+
}
148+
}
149+
150+
#[defun]
151+
fn _query_cursor_matches<'e>(
152+
cursor: &mut QueryCursor,
153+
query: &Query,
154+
node: &RNode,
155+
text_function: Value<'e>,
156+
) -> Result<Vector<'e>> {
157+
let raw = &query.raw;
158+
let error = RefCell::new(None);
159+
let matches = cursor.matches(
160+
raw,
161+
node.borrow().clone(),
162+
text_callback(text_function, &error),
163+
);
164+
let mut vec = vec![];
165+
let env = text_function.env;
166+
for m in matches {
167+
if let Some(error) = error.borrow_mut().take() {
168+
return Err(error);
169+
}
170+
let captures = env.make_vector(m.captures.len(), ())?;
171+
for (ci, c) in m.captures.iter().enumerate() {
172+
let captured_node = node.map(|_| c.node);
173+
let capture = env.cons(
174+
&query.capture_tags[c.index as usize],
175+
captured_node
176+
)?;
177+
captures.set(ci, capture)?;
178+
}
179+
let _match = env.cons(m.pattern_index, captures)?;
180+
vec.push(_match);
181+
}
182+
vec_to_vector(env, vec)
183+
}
184+
185+
// TODO: Make _query_cursor_captures accept a `capture_type` instead, e.g. node type, byte range.
186+
#[defun]
187+
fn _query_cursor_captures_1<'e>(
188+
cursor: &mut QueryCursor,
189+
query: Value<'e>,
190+
node: &RNode,
191+
text_function: Value<'e>,
192+
) -> Result<Vector<'e>> {
193+
let query = query.into_rust::<&RefCell<Query>>()?.borrow();
194+
let raw = &query.raw;
195+
let error = RefCell::new(None);
196+
let captures = cursor.captures(
197+
raw,
198+
node.borrow().clone(),
199+
text_callback(text_function, &error),
200+
);
201+
let mut vec = vec![];
202+
let env = text_function.env;
203+
for (m, capture_index) in captures {
204+
if let Some(error) = error.borrow_mut().take() {
205+
return Err(error);
206+
}
207+
let c = m.captures[capture_index];
208+
let beg: BytePos = c.node.start_byte().into();
209+
let end: BytePos = c.node.end_byte().into();
210+
let capture = env.cons(
211+
&query.capture_tags[c.index as usize],
212+
env.cons(beg, end)?,
213+
)?;
214+
vec.push((m.pattern_index, capture));
215+
}
216+
// Prioritize captures from earlier patterns.
217+
vec.sort_unstable_by_key(|(i, _)| *i);
218+
let vector = env.make_vector(vec.len(), ())?;
219+
for (i, (_, v)) in vec.into_iter().enumerate() {
220+
vector.set(i, v)?;
221+
}
222+
Ok(vector)
223+
}
224+
225+
#[defun]
226+
fn _query_cursor_captures<'e>(
227+
cursor: &mut QueryCursor,
228+
query: Value<'e>,
229+
node: &RNode,
230+
text_function: Value<'e>,
231+
) -> Result<Vector<'e>> {
232+
let query = query.into_rust::<&RefCell<Query>>()?.borrow();
233+
let raw = &query.raw;
234+
let error = RefCell::new(None);
235+
let captures = cursor.captures(
236+
raw,
237+
node.borrow().clone(),
238+
text_callback(text_function, &error),
239+
);
240+
let mut vec = vec![];
241+
let env = text_function.env;
242+
for (m, capture_index) in captures {
243+
if let Some(error) = error.borrow_mut().take() {
244+
return Err(error);
245+
}
246+
let c = m.captures[capture_index];
247+
let captured_node = node.map(|_| c.node);
248+
let capture = env.cons(
249+
&query.capture_tags[c.index as usize],
250+
captured_node
251+
)?;
252+
vec.push(capture);
253+
}
254+
255+
// XXX
256+
let vector = env.make_vector(vec.len(), ())?;
257+
for (i, v) in vec.into_iter().enumerate() {
258+
vector.set(i, v)?;
259+
}
260+
Ok(vector)
261+
}
262+
263+
/// Limit CURSOR's query executions to the range of byte positions, from BEG to END.
264+
#[defun]
265+
fn _query_cursor_set_byte_range(cursor: &mut QueryCursor, beg: BytePos, end: BytePos) -> Result<()> {
266+
cursor.set_byte_range(beg.into(), end.into());
267+
Ok(())
268+
}
269+
270+
/// Limit CURSOR's query executions to the point range, from BEG to END.
271+
///
272+
/// A "point" in this context is a (LINE-NUMBER . BYTE-COLUMN) pair. See
273+
/// `tsc-parse-chunks' for a more detailed explanation.
274+
#[defun]
275+
fn _query_cursor_set_point_range(cursor: &mut QueryCursor, beg: Point, end: Point) -> Result<()> {
276+
cursor.set_point_range(beg.into(), end.into());
277+
Ok(())
278+
}

0 commit comments

Comments
 (0)