mirror of https://github.com/helix-editor/helix
getting somewhere
parent
29ffc20290
commit
f7686d7af2
@ -1,66 +0,0 @@
|
|||||||
use std::ptr::{self, NonNull};
|
|
||||||
|
|
||||||
use regex_cursor::Cursor;
|
|
||||||
|
|
||||||
use crate::tree_sitter::query::Query;
|
|
||||||
use crate::tree_sitter::syntax_tree_node::SyntaxTreeNodeRaw;
|
|
||||||
|
|
||||||
enum QueryCursorData {}
|
|
||||||
|
|
||||||
pub struct QueryCaptures<'a> {
|
|
||||||
query: &'a Query,
|
|
||||||
query_cursor: &'a mut QueryCursorData,
|
|
||||||
text_cursor: regex_cursor::RopeyCursor<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: Cursor> QueryCaptures<'_, C> {
|
|
||||||
fn next(&mut self) {
|
|
||||||
let mut query_match = TSQueryMatch {
|
|
||||||
id: 0,
|
|
||||||
pattern_index: 0,
|
|
||||||
capture_count: 0,
|
|
||||||
captures: ptr::null(),
|
|
||||||
};
|
|
||||||
let mut capture_idx = 0;
|
|
||||||
loop {
|
|
||||||
let success = unsafe {
|
|
||||||
ts_query_cursor_next_capture(
|
|
||||||
&mut self.query_cursor,
|
|
||||||
&mut query_match,
|
|
||||||
&mut capture_idx,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if !success {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut input = regex_cursor::Input::new(self.text_cursor.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TSQueryCapture {
|
|
||||||
node: SyntaxTreeNodeRaw,
|
|
||||||
index: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TSQueryMatch {
|
|
||||||
id: u32,
|
|
||||||
pattern_index: u16,
|
|
||||||
capture_count: u16,
|
|
||||||
captures: *const TSQueryCapture,
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
/// Advance to the next capture of the currently running query.
|
|
||||||
/// If there is a capture, write its match to `*match` and its index within
|
|
||||||
/// the matche's capture list to `*capture_index`. Otherwise, return `false`.
|
|
||||||
fn ts_query_cursor_next_capture(
|
|
||||||
self_: &mut QueryCursorData,
|
|
||||||
match_: &mut TSQueryMatch,
|
|
||||||
capture_index: &mut u32,
|
|
||||||
) -> bool;
|
|
||||||
}
|
|
@ -0,0 +1,319 @@
|
|||||||
|
use core::slice;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::replace;
|
||||||
|
use std::ops::Range;
|
||||||
|
use std::ptr::{self, NonNull};
|
||||||
|
|
||||||
|
use crate::tree_sitter::query::{Capture, Pattern, Query, QueryData};
|
||||||
|
use crate::tree_sitter::syntax_tree_node::SyntaxTreeNodeRaw;
|
||||||
|
use crate::tree_sitter::{SyntaxTree, SyntaxTreeNode, TsInput};
|
||||||
|
|
||||||
|
enum QueryCursorData {}
|
||||||
|
|
||||||
|
pub struct QueryCursor<'a, 'tree, I: TsInput> {
|
||||||
|
query: &'a Query,
|
||||||
|
ptr: *mut QueryCursorData,
|
||||||
|
tree: PhantomData<&'tree SyntaxTree>,
|
||||||
|
input: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tree, I: TsInput> QueryCursor<'_, 'tree, I> {
|
||||||
|
pub fn next_match(&mut self) -> Option<QueryMatch<'_, 'tree>> {
|
||||||
|
let mut query_match = TSQueryMatch {
|
||||||
|
id: 0,
|
||||||
|
pattern_index: 0,
|
||||||
|
capture_count: 0,
|
||||||
|
captures: ptr::null(),
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
let success = unsafe { ts_query_cursor_next_match(self.ptr, &mut query_match) };
|
||||||
|
if !success {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let matched_nodes = unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
query_match.captures.cast(),
|
||||||
|
query_match.capture_count as usize,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let satisfies_predicates = self
|
||||||
|
.query
|
||||||
|
.pattern_text_predicates(query_match.pattern_index)
|
||||||
|
.iter()
|
||||||
|
.all(|predicate| predicate.satsified(&mut self.input, matched_nodes, self.query));
|
||||||
|
if satisfies_predicates {
|
||||||
|
let res = QueryMatch {
|
||||||
|
id: query_match.id,
|
||||||
|
pattern: Pattern(query_match.pattern_index as u32),
|
||||||
|
matched_nodes,
|
||||||
|
query_cursor: unsafe { &mut *self.ptr },
|
||||||
|
_tree: PhantomData,
|
||||||
|
};
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_matched_node(&mut self) -> Option<(QueryMatch<'_, 'tree>, MatchedNodeIdx)> {
|
||||||
|
let mut query_match = TSQueryMatch {
|
||||||
|
id: 0,
|
||||||
|
pattern_index: 0,
|
||||||
|
capture_count: 0,
|
||||||
|
captures: ptr::null(),
|
||||||
|
};
|
||||||
|
let mut capture_idx = 0;
|
||||||
|
loop {
|
||||||
|
let success = unsafe {
|
||||||
|
ts_query_cursor_next_capture(self.ptr, &mut query_match, &mut capture_idx)
|
||||||
|
};
|
||||||
|
if !success {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let matched_nodes = unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
query_match.captures.cast(),
|
||||||
|
query_match.capture_count as usize,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let satisfies_predicates = self
|
||||||
|
.query
|
||||||
|
.pattern_text_predicates(query_match.pattern_index)
|
||||||
|
.iter()
|
||||||
|
.all(|predicate| predicate.satsified(&mut self.input, matched_nodes, self.query));
|
||||||
|
if satisfies_predicates {
|
||||||
|
let res = QueryMatch {
|
||||||
|
id: query_match.id,
|
||||||
|
pattern: Pattern(query_match.pattern_index as u32),
|
||||||
|
matched_nodes,
|
||||||
|
query_cursor: unsafe { &mut *self.ptr },
|
||||||
|
_tree: PhantomData,
|
||||||
|
};
|
||||||
|
return Some((res, capture_idx));
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
ts_query_cursor_remove_match(self.ptr, query_match.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_byte_range(&mut self, range: Range<usize>) {
|
||||||
|
unsafe {
|
||||||
|
ts_query_cursor_set_byte_range(self.ptr, range.start as u32, range.end as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reuse(mut self) -> InactiveQueryCursor {
|
||||||
|
let ptr = replace(&mut self.ptr, ptr::null_mut());
|
||||||
|
InactiveQueryCursor {
|
||||||
|
ptr: unsafe { NonNull::new_unchecked(ptr) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: TsInput> Drop for QueryCursor<'_, '_, I> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// we allow moving the cursor data out so we need the null check here
|
||||||
|
// would be cleaner with a subtype but doesn't really matter at the end of the day
|
||||||
|
if !self.ptr.is_null() {
|
||||||
|
unsafe { ts_query_cursor_delete(self.ptr) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A query cursor that is not actively associated with a query
|
||||||
|
pub struct InactiveQueryCursor {
|
||||||
|
ptr: NonNull<QueryCursorData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InactiveQueryCursor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
InactiveQueryCursor {
|
||||||
|
ptr: unsafe { NonNull::new_unchecked(ts_query_cursor_new()) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the maximum number of in-progress matches for this cursor.
|
||||||
|
#[doc(alias = "ts_query_cursor_match_limit")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn match_limit(&self) -> u32 {
|
||||||
|
unsafe { ts_query_cursor_match_limit(self.ptr.as_ptr()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the maximum number of in-progress matches for this cursor. The
|
||||||
|
/// limit must be > 0 and <= 65536.
|
||||||
|
#[doc(alias = "ts_query_cursor_set_match_limit")]
|
||||||
|
pub fn set_match_limit(&mut self, limit: u32) {
|
||||||
|
unsafe {
|
||||||
|
ts_query_cursor_set_match_limit(self.ptr.as_ptr(), limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if, on its last execution, this cursor exceeded its maximum number
|
||||||
|
/// of in-progress matches.
|
||||||
|
#[doc(alias = "ts_query_cursor_did_exceed_match_limit")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn did_exceed_match_limit(&self) -> bool {
|
||||||
|
unsafe { ts_query_cursor_did_exceed_match_limit(self.ptr.as_ptr()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_byte_range(&mut self, range: Range<usize>) {
|
||||||
|
unsafe {
|
||||||
|
ts_query_cursor_set_byte_range(self.ptr.as_ptr(), range.start as u32, range.end as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_query<'a, 'tree, I: TsInput>(
|
||||||
|
self,
|
||||||
|
query: &'a Query,
|
||||||
|
node: &SyntaxTreeNode<'tree>,
|
||||||
|
input: I,
|
||||||
|
) -> QueryCursor<'a, 'tree, I> {
|
||||||
|
let ptr = self.ptr.as_ptr();
|
||||||
|
unsafe { ts_query_cursor_exec(self.ptr.as_ptr(), query.raw.as_ref(), node.as_raw()) };
|
||||||
|
QueryCursor {
|
||||||
|
query,
|
||||||
|
ptr,
|
||||||
|
tree: PhantomData,
|
||||||
|
input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for InactiveQueryCursor {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { ts_query_cursor_delete(self.ptr.as_ptr()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type MatchedNodeIdx = u32;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MatchedNode<'tree> {
|
||||||
|
pub syntax_node: SyntaxTreeNode<'tree>,
|
||||||
|
pub capture: Capture,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct QueryMatch<'cursor, 'tree> {
|
||||||
|
id: u32,
|
||||||
|
pattern: Pattern,
|
||||||
|
matched_nodes: &'cursor [MatchedNode<'tree>],
|
||||||
|
query_cursor: &'cursor mut QueryCursorData,
|
||||||
|
_tree: PhantomData<&'tree super::SyntaxTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tree> QueryMatch<'_, 'tree> {
|
||||||
|
pub fn matched_nodes(&self) -> impl Iterator<Item = &MatchedNode<'tree>> {
|
||||||
|
self.matched_nodes.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matched_node(&self, i: MatchedNodeIdx) -> &MatchedNode {
|
||||||
|
&self.matched_nodes[i as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn id(&self) -> u32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub const fn pattern(&self) -> Pattern {
|
||||||
|
self.pattern
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "ts_query_cursor_remove_match")]
|
||||||
|
/// removes this match from the cursor so that further captures
|
||||||
|
/// from its cursor so that future captures that belong to this match
|
||||||
|
/// are no longer returned by capture iterators
|
||||||
|
pub fn remove(self) {
|
||||||
|
unsafe {
|
||||||
|
ts_query_cursor_remove_match(self.query_cursor, self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TSQueryCapture {
|
||||||
|
node: SyntaxTreeNodeRaw,
|
||||||
|
index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TSQueryMatch {
|
||||||
|
id: u32,
|
||||||
|
pattern_index: u16,
|
||||||
|
capture_count: u16,
|
||||||
|
captures: *const TSQueryCapture,
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
/// Advance to the next capture of the currently running query.
|
||||||
|
/// If there is a capture, write its match to `*match` and its index within
|
||||||
|
/// the matche's capture list to `*capture_index`. Otherwise, return `false`.
|
||||||
|
fn ts_query_cursor_next_capture(
|
||||||
|
self_: *mut QueryCursorData,
|
||||||
|
match_: &mut TSQueryMatch,
|
||||||
|
capture_index: &mut u32,
|
||||||
|
) -> bool;
|
||||||
|
|
||||||
|
/// Advance to the next match of the currently running query.
|
||||||
|
///
|
||||||
|
/// If there is a match, write it to `*match` and return `true`.
|
||||||
|
/// Otherwise, return `false`.
|
||||||
|
pub fn ts_query_cursor_next_match(
|
||||||
|
self_: *mut QueryCursorData,
|
||||||
|
match_: &mut TSQueryMatch,
|
||||||
|
) -> bool;
|
||||||
|
fn ts_query_cursor_remove_match(self_: *mut QueryCursorData, match_id: u32);
|
||||||
|
/// Delete a query cursor, freeing all of the memory that it used
|
||||||
|
pub fn ts_query_cursor_delete(self_: *mut QueryCursorData);
|
||||||
|
/// Create a new cursor for executing a given query.
|
||||||
|
/// The cursor stores the state that is needed to iteratively search
|
||||||
|
/// for matches. To use the query cursor, first call [`ts_query_cursor_exec`]
|
||||||
|
/// to start running a given query on a given syntax node. Then, there are
|
||||||
|
/// two options for consuming the results of the query:
|
||||||
|
/// 1. Repeatedly call [`ts_query_cursor_next_match`] to iterate over all of the
|
||||||
|
/// *matches* in the order that they were found. Each match contains the
|
||||||
|
/// index of the pattern that matched, and an array of captures. Because
|
||||||
|
/// multiple patterns can match the same set of nodes, one match may contain
|
||||||
|
/// captures that appear *before* some of the captures from a previous match.
|
||||||
|
/// 2. Repeatedly call [`ts_query_cursor_next_capture`] to iterate over all of the
|
||||||
|
/// individual *captures* in the order that they appear. This is useful if
|
||||||
|
/// don't care about which pattern matched, and just want a single ordered
|
||||||
|
/// sequence of captures.
|
||||||
|
/// If you don't care about consuming all of the results, you can stop calling
|
||||||
|
/// [`ts_query_cursor_next_match`] or [`ts_query_cursor_next_capture`] at any point.
|
||||||
|
/// You can then start executing another query on another node by calling
|
||||||
|
/// [`ts_query_cursor_exec`] again."]
|
||||||
|
pub fn ts_query_cursor_new() -> *mut QueryCursorData;
|
||||||
|
|
||||||
|
/// Start running a given query on a given node.
|
||||||
|
pub fn ts_query_cursor_exec(
|
||||||
|
self_: *mut QueryCursorData,
|
||||||
|
query: &QueryData,
|
||||||
|
node: SyntaxTreeNodeRaw,
|
||||||
|
);
|
||||||
|
/// Manage the maximum number of in-progress matches allowed by this query
|
||||||
|
/// cursor.
|
||||||
|
///
|
||||||
|
/// Query cursors have an optional maximum capacity for storing lists of
|
||||||
|
/// in-progress captures. If this capacity is exceeded, then the
|
||||||
|
/// earliest-starting match will silently be dropped to make room for further
|
||||||
|
/// matches. This maximum capacity is optional — by default, query cursors allow
|
||||||
|
/// any number of pending matches, dynamically allocating new space for them as
|
||||||
|
/// needed as the query is executed.
|
||||||
|
pub fn ts_query_cursor_did_exceed_match_limit(self_: *const QueryCursorData) -> bool;
|
||||||
|
pub fn ts_query_cursor_match_limit(self_: *const QueryCursorData) -> u32;
|
||||||
|
pub fn ts_query_cursor_set_match_limit(self_: *mut QueryCursorData, limit: u32);
|
||||||
|
/// Set the range of bytes or (row, column) positions in which the query
|
||||||
|
/// will be executed.
|
||||||
|
pub fn ts_query_cursor_set_byte_range(
|
||||||
|
self_: *mut QueryCursorData,
|
||||||
|
start_byte: u32,
|
||||||
|
end_byte: u32,
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
pub struct QueryMatch {}
|
@ -1,38 +1,54 @@
|
|||||||
|
use std::ops;
|
||||||
|
|
||||||
use regex_cursor::{Cursor, RopeyCursor};
|
use regex_cursor::{Cursor, RopeyCursor};
|
||||||
use ropey::RopeSlice;
|
use ropey::RopeSlice;
|
||||||
|
|
||||||
use crate::tree_sitter::parser::{IntoParserInput, ParserInput};
|
use crate::tree_sitter::{IntoTsInput, TsInput};
|
||||||
|
|
||||||
pub struct RopeParserInput<'a> {
|
pub struct RopeTsInput<'a> {
|
||||||
src: RopeSlice<'a>,
|
src: RopeSlice<'a>,
|
||||||
cursor: regex_cursor::RopeyCursor<'a>,
|
cursor: regex_cursor::RopeyCursor<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoParserInput for RopeSlice<'a> {
|
impl<'a> RopeTsInput<'a> {
|
||||||
type ParserInput = RopeParserInput<'a>;
|
pub fn new(src: RopeSlice<'a>) -> Self {
|
||||||
|
RopeTsInput {
|
||||||
|
src,
|
||||||
|
cursor: regex_cursor::RopeyCursor::new(src),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn into_parser_input(self) -> Self::ParserInput {
|
impl<'a> IntoTsInput for RopeSlice<'a> {
|
||||||
RopeParserInput {
|
type TsInput = RopeTsInput<'a>;
|
||||||
|
|
||||||
|
fn into_ts_input(self) -> Self::TsInput {
|
||||||
|
RopeTsInput {
|
||||||
src: self,
|
src: self,
|
||||||
cursor: RopeyCursor::new(self),
|
cursor: RopeyCursor::new(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserInput for RopeParserInput<'_> {
|
impl<'a> TsInput for RopeTsInput<'a> {
|
||||||
fn read(&mut self, offset: usize) -> &[u8] {
|
type Cursor = RopeyCursor<'a>;
|
||||||
|
fn cursor_at(&mut self, offset: usize) -> &mut RopeyCursor<'a> {
|
||||||
// this cursor is optimized for contigous reads which are by far the most common during parsing
|
// this cursor is optimized for contigous reads which are by far the most common during parsing
|
||||||
// very far jumps (like injections at the other end of the document) are handelde
|
// very far jumps (like injections at the other end of the document) are handelde
|
||||||
// by restarting a new cursor (new chunks iterator)
|
// by starting a new cursor (new chunks iterator)
|
||||||
if offset < self.cursor.offset() && self.cursor.offset() - offset > 4906 {
|
if offset < self.cursor.offset() || self.cursor.offset() - offset > 4906 {
|
||||||
self.cursor = regex_cursor::RopeyCursor::at(self.src, offset);
|
self.cursor = regex_cursor::RopeyCursor::at(self.src, offset);
|
||||||
} else {
|
} else {
|
||||||
while self.cursor.offset() + self.cursor.chunk().len() >= offset {
|
while self.cursor.offset() + self.cursor.chunk().len() >= offset {
|
||||||
if !self.cursor.advance() {
|
if !self.cursor.advance() {
|
||||||
return &[];
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.cursor.chunk()
|
&mut self.cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq(&mut self, range1: ops::Range<usize>, range2: ops::Range<usize>) -> bool {
|
||||||
|
self.src.byte_slice(range1) == self.src.byte_slice(range2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue