Skip to content

Commit

Permalink
improved tests and documentation of QueryCursor
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmeral committed Jan 10, 2025
1 parent 5eae036 commit 4a190d8
Show file tree
Hide file tree
Showing 6 changed files with 444 additions and 157 deletions.
69 changes: 34 additions & 35 deletions src/main/java/io/github/treesitter/jtreesitter/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Stream;

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

Expand All @@ -25,7 +24,7 @@
@NullMarked
public final class Query implements AutoCloseable {
private final MemorySegment query;
private final QueryCursorOptions cursorOptions = new QueryCursorOptions();
private final QueryCursorConfig cursorConfig = new QueryCursorConfig();
private final Arena arena;
private final Language language;
private final String source;
Expand Down Expand Up @@ -261,7 +260,7 @@ private static boolean invalidPredicateChar(char c) {
return !(Character.isLetterOrDigit(c) || c == '_' || c == '-' || c == '.' || c == '?' || c == '!');
}

MemorySegment self(){
MemorySegment self() {
return query;
}

Expand All @@ -275,30 +274,30 @@ MemorySegment self(){
return ts_query_capture_count(query);
}

public List<List<QueryPredicate>> getPredicates(){
public List<List<QueryPredicate>> getPredicates() {
return predicates.stream().map(Collections::unmodifiableList).toList();
}

public List<String> getCaptureNames(){
public List<String> getCaptureNames() {
return Collections.unmodifiableList(captureNames);
}

/**
* Get the maximum number of in-progress matches.
* Get the maximum number of in-progress matches of the default {@link QueryCursorConfig}
*
* @apiNote Defaults to {@code -1} (unlimited).
*/
public @Unsigned int getMatchLimit() {
return cursorOptions.getMatchLimit();
return cursorConfig.getMatchLimit();
}

/**
* Get the maximum number of in-progress matches.
* Set the maximum number of in-progress matches of the default {@link QueryCursorConfig}
*
* @throws IllegalArgumentException If {@code matchLimit == 0}.
*/
public Query setMatchLimit(@Unsigned int matchLimit) throws IllegalArgumentException {
cursorOptions.setMatchLimit(matchLimit);
cursorConfig.setMatchLimit(matchLimit);
return this;
}

Expand All @@ -308,19 +307,23 @@ public Query setMatchLimit(@Unsigned int matchLimit) throws IllegalArgumentExcep
*
* @apiNote Defaults to {@code 0} (unlimited).
* @since 0.23.1
* @deprecated
*/
@Deprecated(forRemoval = true)
public @Unsigned long getTimeoutMicros() {
return cursorOptions.getTimeoutMicros();
return cursorConfig.getTimeoutMicros();
}

/**
* Set the maximum duration in microseconds that query
* execution should be allowed to take before halting.
*
* @since 0.23.1
* @deprecated
*/
@Deprecated(forRemoval = true)
public Query setTimeoutMicros(@Unsigned long timeoutMicros) {
cursorOptions.setTimeoutMicros(timeoutMicros);
cursorConfig.setTimeoutMicros(timeoutMicros);
return this;
}

Expand All @@ -331,25 +334,22 @@ public Query setTimeoutMicros(@Unsigned long timeoutMicros) {
* <br>Note that if a pattern includes many children, then they will still be checked.
*/
public Query setMaxStartDepth(@Unsigned int maxStartDepth) {
cursorOptions.setMaxStartDepth(maxStartDepth);
cursorConfig.setMaxStartDepth(maxStartDepth);
return this;
}

/** Set the range of bytes in which the query will be executed. */
public Query setByteRange(@Unsigned int startByte, @Unsigned int endByte) {
cursorOptions.setStartByte(startByte);
cursorOptions.setEndByte(endByte);
cursorConfig.setByteRange(startByte, endByte);
return this;
}

/** Set the range of points in which the query will be executed. */
public Query setPointRange(Point startPoint, Point endPoint) {
cursorOptions.setStartPoint(startPoint);
cursorOptions.setEndPoint(endPoint);
cursorConfig.setPointRange(startPoint, endPoint);
return this;
}


/**
* Disable a certain pattern within a query.
*
Expand Down Expand Up @@ -477,14 +477,13 @@ public Map<String, Optional<String>> getPatternAssertions(@Unsigned int index, b
return Collections.unmodifiableMap(assertions.get(index));
}


/**
* Execute the query on a given node.
* Execute the query on a given node with the default {@link QueryCursorConfig}.
* @param node The node that the query will run on.
* @return A cursor that can be used to iterate over the matches.
*/
public QueryCursor execute(Node node){
return new QueryCursor(this, node, cursorOptions);
public QueryCursor execute(Node node) {
return new QueryCursor(this, node, cursorConfig);
}

/**
Expand All @@ -493,22 +492,22 @@ public QueryCursor execute(Node node){
* @param options The options that will be used for this query.
* @return A cursor that can be used to iterate over the matches.
*/
public QueryCursor execute(Node node, QueryCursorOptions options){
public QueryCursor execute(Node node, QueryCursorConfig options) {
return new QueryCursor(this, node, options);
}


/**
* Iterate over all the matches in the order that they were found. The lifetime of the native memory of the returned
* matches is bound to the lifetime of this query object.
*
* @param node The node that the query will run on.
* @implNote The stream is not created lazily such that there is no open {@link QueryCursor} instance left behind.
* For creating a lazy stream use {@link #execute(Node)} and {@link QueryCursor#matchStream()}.
*/
public Stream<QueryMatch> findMatches(Node node) {
return findMatches(node, null, arena);
return findMatches(node, arena, null);
}


/**
* Iterate over all the matches in the order that they were found. The lifetime of the native memory of the returned
* matches is bound to the lifetime of this query object.
Expand All @@ -526,26 +525,29 @@ public Stream<QueryMatch> findMatches(Node node) {
*
* @param node The node that the query will run on.
* @param predicate A function that handles custom predicates.
* @implNote The stream is not created lazily such that there is no open {@link QueryCursor} instance left behind. For creating a lazy stream use {@link #execute(Node)} and {@link QueryCursor#stream(BiPredicate)}.
* @implNote The stream is not created lazily such that there is no open {@link QueryCursor} instance left behind.
* For creating a lazy stream use {@link #execute(Node)} and {@link QueryCursor#matchStream(BiPredicate)}.
*/
public Stream<QueryMatch> findMatches(Node node, @Nullable BiPredicate<QueryPredicate, QueryMatch> predicate) {
return findMatches(node, predicate, arena);
return findMatches(node, arena, predicate);
}


/**
* Like {@link #findMatches(Node, BiPredicate)} but the native memory of the returned matches is created using the
* given allocator.
*
* @param node The node that the query will run on.
* @param predicate A function that handles custom predicates.
* @param allocator The allocator that is used to allocate the native memory of the returned matches.
* @implNote The stream is not created lazily such that there is no open {@link QueryCursor} instance left behind.
* For creating a lazy stream use {@link #execute(Node)} and {@link QueryCursor#matchStream(SegmentAllocator, BiPredicate)}.
*/
public Stream<QueryMatch> findMatches(Node node, @Nullable BiPredicate<QueryPredicate, QueryMatch> predicate, SegmentAllocator allocator) {
try(QueryCursor cursor = this.execute(node)){
public Stream<QueryMatch> findMatches(
Node node, SegmentAllocator allocator, @Nullable BiPredicate<QueryPredicate, QueryMatch> predicate) {
try (QueryCursor cursor = this.execute(node)) {
// make sure to load the entire stream into memory before closing the cursor.
// Otherwise, we call for nextMatch after closing the cursor which leads to undefined behavior.
return cursor.stream(allocator, predicate).toList().stream();
// Otherwise, we call for nextMatch after closing the cursor which leads to an exception.
return cursor.matchStream(allocator, predicate).toList().stream();
}
}

Expand All @@ -559,13 +561,10 @@ public String toString() {
return "Query{language=%s, source=%s}".formatted(language, source);
}


private void checkIndex(@Unsigned int index) throws IndexOutOfBoundsException {
if (Integer.compareUnsigned(index, getPatternCount()) >= 0) {
throw new IndexOutOfBoundsException(
"Pattern index %s is out of bounds".formatted(Integer.toUnsignedString(index)));
}
}


}
Loading

0 comments on commit 4a190d8

Please sign in to comment.