Skip to content

Commit 5926b4a

Browse files
feat!: separate QueryCursor class
1 parent e3e6c90 commit 5926b4a

File tree

18 files changed

+1251
-1001
lines changed

18 files changed

+1251
-1001
lines changed

.github/detekt.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
complexity:
2+
CyclomaticComplexMethod:
3+
ignoreFunction:
4+
- "io.github.treesitter.ktreesitter.Query.Companion.init"
25
TooManyFunctions:
36
active: false
47
LongParameterList:

ktreesitter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ add_library(ktreesitter SHARED
3232
./src/jni/node.c
3333
./src/jni/parser.c
3434
./src/jni/query.c
35+
./src/jni/query_cursor.c
3536
./src/jni/tree.c
3637
./src/jni/tree_cursor.c
3738
./src/jni/module.c

ktreesitter/src/androidMain/kotlin/io/github/treesitter/ktreesitter/Query.kt

Lines changed: 35 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@ actual class Query @Throws(QueryError::class) actual constructor(
1818
private val language: Language,
1919
private val source: String
2020
) : AutoCloseable {
21-
private val self: Long = init(language.self, source)
21+
internal val self: Long = init(language.self, source)
2222

23-
private val cursor: Long = cursor()
24-
25-
private val captureNames: MutableList<String>
26-
27-
private val predicates: List<MutableList<QueryPredicate>>
23+
internal val predicates: List<MutableList<QueryPredicate>>
2824

2925
private val settingList: List<MutableMap<String, String?>>
3026

@@ -37,21 +33,36 @@ actual class Query @Throws(QueryError::class) actual constructor(
3733

3834
/** The number of captures in the query. */
3935
@get:JvmName("getCaptureCount")
36+
@Deprecated("captureCount is deprecated.", ReplaceWith("captureNames.size"))
4037
actual val captureCount: UInt
4138
get() = nativeCaptureCount().toUInt()
4239

40+
/**
41+
* The capture names used in the query.
42+
*
43+
* @since 0.25.0
44+
*/
45+
actual val captureNames: List<String>
46+
47+
/**
48+
* The string literals used in the query.
49+
*
50+
* @since 0.25.0
51+
*/
52+
actual val stringValues: List<String>
53+
4354
init {
44-
RefCleaner(this, CleanAction(self, cursor))
55+
RefCleaner(this, CleanAction(self))
4556

4657
predicates = List(nativePatternCount()) { mutableListOf() }
4758
settingList = List(nativePatternCount()) { mutableMapOf() }
48-
assertionList = List(nativeCaptureCount()) { mutableMapOf() }
49-
captureNames = MutableList(nativeCaptureCount()) {
59+
assertionList = List(nativePatternCount()) { mutableMapOf() }
60+
captureNames = List(nativeCaptureCount()) {
5061
checkNotNull(captureNameForId(it)) {
5162
"Failed to get capture name at index $it"
5263
}
5364
}
54-
val stringValues = List(stringCount()) {
65+
stringValues = List(stringCount()) {
5566
checkNotNull(stringValueForId(it)) {
5667
"Failed to get string value at index $it"
5768
}
@@ -268,134 +279,15 @@ actual class Query @Throws(QueryError::class) actual constructor(
268279
}
269280

270281
/**
271-
* The maximum duration in microseconds that query
272-
* execution should be allowed to take before halting.
273-
*
274-
* Default: `0`
275-
*
276-
* @since 0.23.0
277-
*/
278-
@get:JvmName("getTimeoutMicros")
279-
@set:JvmName("setTimeoutMicros")
280-
actual var timeoutMicros: ULong
281-
@FastNative external get
282-
283-
@FastNative external set
284-
285-
/**
286-
* The maximum number of in-progress matches.
287-
*
288-
* Default: `UInt.MAX_VALUE`
289-
*
290-
* @throws [IllegalArgumentException] If the match limit is set to `0`.
291-
*/
292-
@get:JvmName("getMatchLimit")
293-
@set:JvmName("setMatchLimit")
294-
actual var matchLimit: UInt
295-
@FastNative external get
296-
297-
@FastNative external set
298-
299-
/**
300-
* The maximum start depth for the query.
301-
*
302-
* This prevents cursors from exploring children nodes at a certain depth.
303-
* Note that if a pattern includes many children, then they will still be checked.
304-
*
305-
* Default: `UInt.MAX_VALUE`
306-
*/
307-
@get:JvmName("getMaxStartDepth")
308-
@set:JvmName("setMaxStartDepth")
309-
actual var maxStartDepth: UInt = UInt.MAX_VALUE
310-
@FastNative external set
311-
312-
/**
313-
* The range of bytes in which the query will be executed.
314-
*
315-
* Default: `UInt.MIN_VALUE..UInt.MAX_VALUE`
316-
*/
317-
actual var byteRange: UIntRange = UInt.MIN_VALUE..UInt.MAX_VALUE
318-
set(value) {
319-
nativeSetByteRange(value.first.toInt(), value.last.toInt())
320-
field = value
321-
}
322-
323-
/**
324-
* The range of points in which the query will be executed.
325-
*
326-
* Default: `Point.MIN..Point.MAX`
327-
*/
328-
actual var pointRange: ClosedRange<Point> = Point.MIN..Point.MAX
329-
set(value) {
330-
nativeSetPointRange(value.start, value.endInclusive)
331-
field = value
332-
}
333-
334-
/**
335-
* Check if the query exceeded its maximum number of
336-
* in-progress matches during its last execution.
337-
*/
338-
@get:JvmName("didExceedMatchLimit")
339-
actual val didExceedMatchLimit: Boolean
340-
@FastNative external get
341-
342-
/**
343-
* Iterate over all the matches in the order that they were found.
344-
*
345-
* #### Example
346-
*
347-
* ```kotlin
348-
* query.matches(tree.rootNode) {
349-
* if (name != "ieq?") return@matches true
350-
* val node = it[(args[0] as QueryPredicateArg.Capture).value].first()
351-
* val value = (args[1] as QueryPredicateArg.Literal).value
352-
* value.equals(node.text()?.toString(), ignoreCase = true)
353-
* }
354-
* ```
355-
*
356-
* @param node The node that the query will run on.
357-
* @param predicate A function that handles custom predicates.
358-
*/
359-
@JvmOverloads
360-
actual fun matches(
361-
node: Node,
362-
predicate: QueryPredicate.(QueryMatch) -> Boolean
363-
): Sequence<QueryMatch> {
364-
exec(node)
365-
return sequence {
366-
var match = nextMatch(node.tree)
367-
while (match != null) {
368-
val result = match.check(node.tree, predicate)
369-
if (result != null) yield(result)
370-
match = nextMatch(node.tree)
371-
}
372-
}
373-
}
374-
375-
/**
376-
* Iterate over all the individual captures in the order that they appear.
282+
* Execute the query on the given [Node].
377283
*
378-
* This is useful if you don't care about _which_ pattern matched.
379-
*
380-
* @param node The node that the query will run on.
381-
* @param predicate A function that handles custom predicates.
284+
* @since 0.25.0
382285
*/
286+
@FastNative
383287
@JvmOverloads
384-
actual fun captures(
385-
node: Node,
386-
predicate: QueryPredicate.(QueryMatch) -> Boolean
387-
): Sequence<Pair<UInt, QueryMatch>> {
388-
exec(node)
389-
return sequence {
390-
var capture = nextCapture(node.tree)
391-
while (capture != null) {
392-
val index = capture.first
393-
val match = capture.second.check(node.tree, predicate)
394-
if (match != null) yield(index to match)
395-
capture = nextCapture(node.tree)
396-
}
397-
}
398-
}
288+
@JvmName("exec")
289+
actual operator fun invoke(node: Node, progressCallback: QueryProgressCallback?) =
290+
QueryCursor(this, node, progressCallback)
399291

400292
/**
401293
* Get the property settings for the given pattern index.
@@ -410,7 +302,7 @@ actual class Query @Throws(QueryError::class) actual constructor(
410302
@Throws(IndexOutOfBoundsException::class)
411303
actual fun settings(index: UInt): Map<String, String?> {
412304
if (index >= patternCount)
413-
throw IndexOutOfBoundsException("Pattern index $index is out of bounds")
305+
throw IndexOutOfBoundsException("Index $index exceeds count $patternCount")
414306
return settingList[index]
415307
}
416308

@@ -429,7 +321,7 @@ actual class Query @Throws(QueryError::class) actual constructor(
429321
@Throws(IndexOutOfBoundsException::class)
430322
actual fun assertions(index: UInt): Map<String, Pair<String?, Boolean>> {
431323
if (index >= patternCount)
432-
throw IndexOutOfBoundsException("Pattern index $index is out of bounds")
324+
throw IndexOutOfBoundsException("Index $index exceeds count $patternCount")
433325
return assertionList[index]
434326
}
435327

@@ -453,15 +345,9 @@ actual class Query @Throws(QueryError::class) actual constructor(
453345
* This prevents the capture from being returned in matches,
454346
* and also avoids most resource usage associated with recording
455347
* the capture. Currently, there is no way to undo this.
456-
*
457-
* @throws [NoSuchElementException] If the capture does not exist.
458348
*/
459-
@Throws(NoSuchElementException::class)
460-
actual fun disableCapture(name: String) {
461-
if (!captureNames.remove(name))
462-
throw NoSuchElementException("Capture @$name does not exist")
463-
nativeDisableCapture(name)
464-
}
349+
@FastNative
350+
actual external fun disableCapture(name: String)
465351

466352
/**
467353
* Get the byte offset where the given pattern starts in the query's source.
@@ -517,7 +403,6 @@ actual class Query @Throws(QueryError::class) actual constructor(
517403
* Check if a pattern is guaranteed to match
518404
* once a given byte offset is reached.
519405
*/
520-
@FastNative
521406
@JvmName("isPatternGuaranteedAtStep")
522407
@Throws(IndexOutOfBoundsException::class)
523408
actual fun isPatternGuaranteedAtStep(offset: UInt): Boolean {
@@ -528,7 +413,7 @@ actual class Query @Throws(QueryError::class) actual constructor(
528413

529414
override fun toString() = "Query(language=$language, source=$source)"
530415

531-
override fun close() = delete(self, cursor)
416+
override fun close() = delete(self)
532417

533418
@FastNative
534419
private external fun nativePatternCount(): Int
@@ -539,44 +424,17 @@ actual class Query @Throws(QueryError::class) actual constructor(
539424
@FastNative
540425
private external fun stringCount(): Int
541426

542-
@FastNative
543-
private external fun exec(node: Node)
544-
545-
private external fun nextMatch(tree: Tree): QueryMatch?
546-
547-
private external fun nextCapture(tree: Tree): Pair<UInt, QueryMatch>?
548-
549427
@FastNative
550428
private external fun captureNameForId(index: Int): String?
551429

552430
@FastNative
553431
private external fun stringValueForId(index: Int): String?
554432

555-
@FastNative
556-
private external fun nativeSetByteRange(start: Int, end: Int)
557-
558-
@FastNative
559-
private external fun nativeSetPointRange(start: Point, end: Point)
560-
561-
@FastNative
562-
private external fun nativeDisableCapture(name: String)
563-
564433
@FastNative
565434
private external fun nativeIsPatternGuaranteedAtStep(index: Int): Boolean
566435

567436
private external fun predicatesForPattern(index: Int): List<IntArray>?
568437

569-
private inline fun QueryMatch.check(
570-
tree: Tree,
571-
predicate: QueryPredicate.(QueryMatch) -> Boolean
572-
): QueryMatch? {
573-
if (tree.text() == null) return this
574-
val result = predicates[patternIndex].all {
575-
if (it !is QueryPredicate.Generic) it(this) else predicate(it, this)
576-
}
577-
return if (result) this else null
578-
}
579-
580438
@Suppress("NOTHING_TO_INLINE")
581439
private inline operator fun <T> List<T>.get(index: UInt) = get(index.toInt())
582440

@@ -586,8 +444,8 @@ actual class Query @Throws(QueryError::class) actual constructor(
586444
private inline val IntArray.type: Int
587445
inline get() = get(1)
588446

589-
private class CleanAction(private val query: Long, private val cursor: Long) : Runnable {
590-
override fun run() = delete(query, cursor)
447+
private class CleanAction(private val ptr: Long) : Runnable {
448+
override fun run() = delete(ptr)
591449
}
592450

593451
@Suppress("ConstPropertyName")
@@ -604,10 +462,6 @@ actual class Query @Throws(QueryError::class) actual constructor(
604462

605463
@JvmStatic
606464
@CriticalNative
607-
private external fun cursor(): Long
608-
609-
@JvmStatic
610-
@CriticalNative
611-
private external fun delete(query: Long, cursor: Long)
465+
private external fun delete(self: Long)
612466
}
613467
}

0 commit comments

Comments
 (0)