88import java .lang .foreign .SegmentAllocator ;
99import java .util .*;
1010import java .util .function .BiPredicate ;
11- import java .util .function .Consumer ;
1211import java .util .function .Supplier ;
1312import java .util .regex .Pattern ;
1413import java .util .regex .PatternSyntaxException ;
1514import java .util .stream .Stream ;
16- import java . util . stream . StreamSupport ;
15+
1716import org .jspecify .annotations .NullMarked ;
1817import org .jspecify .annotations .Nullable ;
1918
2625@ NullMarked
2726public final class Query implements AutoCloseable {
2827 private final MemorySegment query ;
29- private final MemorySegment cursor ;
28+ private final QueryCursorOptions cursorOptions = new QueryCursorOptions () ;
3029 private final Arena arena ;
3130 private final Language language ;
3231 private final String source ;
@@ -86,7 +85,6 @@ public final class Query implements AutoCloseable {
8685 this .language = language ;
8786 this .source = source ;
8887 this .query = query .reinterpret (arena , TreeSitter ::ts_query_delete );
89- cursor = ts_query_cursor_new ().reinterpret (arena , TreeSitter ::ts_query_cursor_delete );
9088
9189 var captureCount = ts_query_capture_count (this .query );
9290 captureNames = new ArrayList <>(captureCount );
@@ -263,6 +261,10 @@ private static boolean invalidPredicateChar(char c) {
263261 return !(Character .isLetterOrDigit (c ) || c == '_' || c == '-' || c == '.' || c == '?' || c == '!' );
264262 }
265263
264+ MemorySegment self (){
265+ return query ;
266+ }
267+
266268 /** Get the number of patterns in the query. */
267269 public @ Unsigned int getPatternCount () {
268270 return ts_query_pattern_count (query );
@@ -273,13 +275,21 @@ private static boolean invalidPredicateChar(char c) {
273275 return ts_query_capture_count (query );
274276 }
275277
278+ public List <List <QueryPredicate >> getPredicates (){
279+ return predicates .stream ().map (Collections ::unmodifiableList ).toList ();
280+ }
281+
282+ public List <String > getCaptureNames (){
283+ return Collections .unmodifiableList (captureNames );
284+ }
285+
276286 /**
277287 * Get the maximum number of in-progress matches.
278288 *
279289 * @apiNote Defaults to {@code -1} (unlimited).
280290 */
281291 public @ Unsigned int getMatchLimit () {
282- return ts_query_cursor_match_limit ( cursor );
292+ return cursorOptions . getMatchLimit ( );
283293 }
284294
285295 /**
@@ -288,10 +298,7 @@ private static boolean invalidPredicateChar(char c) {
288298 * @throws IllegalArgumentException If {@code matchLimit == 0}.
289299 */
290300 public Query setMatchLimit (@ Unsigned int matchLimit ) throws IllegalArgumentException {
291- if (matchLimit == 0 ) {
292- throw new IllegalArgumentException ("The match limit cannot equal 0" );
293- }
294- ts_query_cursor_set_match_limit (cursor , matchLimit );
301+ cursorOptions .setMatchLimit (matchLimit );
295302 return this ;
296303 }
297304
@@ -303,7 +310,7 @@ public Query setMatchLimit(@Unsigned int matchLimit) throws IllegalArgumentExcep
303310 * @since 0.23.1
304311 */
305312 public @ Unsigned long getTimeoutMicros () {
306- return ts_query_cursor_timeout_micros ( cursor );
313+ return cursorOptions . getTimeoutMicros ( );
307314 }
308315
309316 /**
@@ -313,7 +320,7 @@ public Query setMatchLimit(@Unsigned int matchLimit) throws IllegalArgumentExcep
313320 * @since 0.23.1
314321 */
315322 public Query setTimeoutMicros (@ Unsigned long timeoutMicros ) {
316- ts_query_cursor_set_timeout_micros ( cursor , timeoutMicros );
323+ cursorOptions . setTimeoutMicros ( timeoutMicros );
317324 return this ;
318325 }
319326
@@ -324,32 +331,24 @@ public Query setTimeoutMicros(@Unsigned long timeoutMicros) {
324331 * <br>Note that if a pattern includes many children, then they will still be checked.
325332 */
326333 public Query setMaxStartDepth (@ Unsigned int maxStartDepth ) {
327- ts_query_cursor_set_max_start_depth ( cursor , maxStartDepth );
334+ cursorOptions . setMaxStartDepth ( maxStartDepth );
328335 return this ;
329336 }
330337
331338 /** Set the range of bytes in which the query will be executed. */
332339 public Query setByteRange (@ Unsigned int startByte , @ Unsigned int endByte ) {
333- ts_query_cursor_set_byte_range (cursor , startByte , endByte );
340+ cursorOptions .setStartByte (startByte );
341+ cursorOptions .setEndByte (endByte );
334342 return this ;
335343 }
336344
337345 /** Set the range of points in which the query will be executed. */
338346 public Query setPointRange (Point startPoint , Point endPoint ) {
339- try (var alloc = Arena .ofConfined ()) {
340- MemorySegment start = startPoint .into (alloc ), end = endPoint .into (alloc );
341- ts_query_cursor_set_point_range (cursor , start , end );
342- }
347+ cursorOptions .setStartPoint (startPoint );
348+ cursorOptions .setEndPoint (endPoint );
343349 return this ;
344350 }
345351
346- /**
347- * Check if the query exceeded its maximum number of
348- * in-progress matches during its last execution.
349- */
350- public boolean didExceedMatchLimit () {
351- return ts_query_cursor_did_exceed_match_limit (cursor );
352- }
353352
354353 /**
355354 * Disable a certain pattern within a query.
@@ -478,6 +477,16 @@ public Map<String, Optional<String>> getPatternAssertions(@Unsigned int index, b
478477 return Collections .unmodifiableMap (assertions .get (index ));
479478 }
480479
480+
481+ public QueryCursor execute (Node node ){
482+ return new QueryCursor (this , node , cursorOptions );
483+ }
484+
485+ public QueryCursor execute (Node node , QueryCursorOptions options ){
486+ return new QueryCursor (this , node , options );
487+ }
488+
489+
481490 /**
482491 * Iterate over all the matches in the order that they were found. The lifetime of the native memory of the returned
483492 * matches is bound to the lifetime of this query object.
@@ -521,10 +530,9 @@ public Stream<QueryMatch> findMatches(Node node, @Nullable BiPredicate<QueryPred
521530 * @param allocator The allocator that is used to allocate the native memory of the returned matches.
522531 */
523532 public Stream <QueryMatch > findMatches (Node node , @ Nullable BiPredicate <QueryPredicate , QueryMatch > predicate , SegmentAllocator allocator ) {
524- try ( var alloc = Arena . ofConfined ()) {
525- ts_query_cursor_exec ( cursor , query , node . copy ( alloc ) );
533+ try ( QueryCursor cursor = this . execute ( node )) {
534+ return cursor . stream ( allocator , predicate );
526535 }
527- return StreamSupport .stream (new MatchesIterator (node .getTree (), predicate , allocator ), false );
528536 }
529537
530538 @ Override
@@ -537,12 +545,6 @@ public String toString() {
537545 return "Query{language=%s, source=%s}" .formatted (language , source );
538546 }
539547
540- private boolean matches (@ Nullable BiPredicate <QueryPredicate , QueryMatch > predicate , QueryMatch match ) {
541- return predicates .get (match .patternIndex ()).stream ().allMatch (p -> {
542- if (p .getClass () != QueryPredicate .class ) return p .test (match );
543- return predicate == null || predicate .test (p , match );
544- });
545- }
546548
547549 private void checkIndex (@ Unsigned int index ) throws IndexOutOfBoundsException {
548550 if (Integer .compareUnsigned (index , getPatternCount ()) >= 0 ) {
@@ -551,40 +553,5 @@ private void checkIndex(@Unsigned int index) throws IndexOutOfBoundsException {
551553 }
552554 }
553555
554- private final class MatchesIterator extends Spliterators .AbstractSpliterator <QueryMatch > {
555- private final @ Nullable BiPredicate <QueryPredicate , QueryMatch > predicate ;
556- private final Tree tree ;
557- private final SegmentAllocator allocator ;
558556
559- public MatchesIterator (Tree tree , @ Nullable BiPredicate <QueryPredicate , QueryMatch > predicate , SegmentAllocator allocator ) {
560- super (Long .MAX_VALUE , Spliterator .IMMUTABLE | Spliterator .NONNULL );
561- this .predicate = predicate ;
562- this .tree = tree ;
563- this .allocator = allocator ;
564- }
565-
566- @ Override
567- public boolean tryAdvance (Consumer <? super QueryMatch > action ) {
568- var hasNoText = tree .getText () == null ;
569- MemorySegment match = arena .allocate (TSQueryMatch .layout ());
570- while (ts_query_cursor_next_match (cursor , match )) {
571- var count = Short .toUnsignedInt (TSQueryMatch .capture_count (match ));
572- var matchCaptures = TSQueryMatch .captures (match );
573- var captureList = new ArrayList <QueryCapture >(count );
574- for (int i = 0 ; i < count ; ++i ) {
575- var capture = TSQueryCapture .asSlice (matchCaptures , i );
576- var name = captureNames .get (TSQueryCapture .index (capture ));
577- var node = TSNode .allocate (allocator ).copyFrom (TSQueryCapture .node (capture ));
578- captureList .add (new QueryCapture (name , new Node (node , tree )));
579- }
580- var patternIndex = TSQueryMatch .pattern_index (match );
581- var result = new QueryMatch (patternIndex , captureList );
582- if (hasNoText || matches (predicate , result )) {
583- action .accept (result );
584- return true ;
585- }
586- }
587- return false ;
588- }
589- }
590557}
0 commit comments