Skip to content

Commit 234b585

Browse files
committed
Reimplement treesitter query capture filters for syntax highlighting
1 parent 36dac88 commit 234b585

File tree

1 file changed

+72
-66
lines changed

1 file changed

+72
-66
lines changed

src/text/syntax_map.nim

Lines changed: 72 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import std/[options, strformat, tables, algorithm]
22
import nimsumtree/[rope, sumtree, buffer, clock]
33
import scripting_api except DocumentEditor, TextDocumentEditor, AstDocumentEditor
44
from scripting_api as api import nil
5-
import misc/[custom_async, custom_unicode, util, regex, timer, rope_utils, arena, array_view]
5+
import misc/[custom_async, custom_unicode, util, regex, timer, rope_utils, arena, array_view, array_table]
66
import text/diff, text/[custom_treesitter, treesitter_type_conv]
77
from language/lsp_types import nil
88
import theme
@@ -88,6 +88,7 @@ type
8888
parenColors: seq[Color]
8989
depthOffset: int
9090
currentNode: TSNode
91+
regexCache: ArrayTable[cstring, Regex]
9192

9293
func high*(_: typedesc[Point]): Point = Point(row: uint32.high, column: uint32.high)
9394

@@ -448,7 +449,9 @@ func contentString(self: var StyledChunkIterator, selection: Range[Point], byteR
448449
if selection.a >= currentChunk.point and selection.b <= currentChunk.endPoint:
449450
let startIndex = selection.a.column - currentChunk.point.column
450451
let endIndex = selection.b.column - currentChunk.point.column
451-
return $currentChunk[startIndex.int...endIndex.int]
452+
result = newStringOfCap(endIndex.int - startIndex.int)
453+
for c in currentChunk.data.toOpenArray(startIndex.int, endIndex.int - 1):
454+
result.add c
452455
else:
453456
result = newStringOfCap(min(selection.b.column.int - selection.a.column.int, maxLen))
454457
for slice in self.chunks.rope.iterateChunks(byteRange):
@@ -475,17 +478,13 @@ proc addHighlight(highlights: var seq[Highlight], nextHighlight: sink Highlight)
475478
elif highlights.len == 0 or nextHighlight != highlights[^1]:
476479
highlights.add nextHighlight
477480

478-
var regexes = initTable[string, Regex]()
479-
480481
proc depth(n: TSNode): int =
481482
var n = n
482483
while not n.parent.isNull:
483484
inc result
484485
n = n.parent
485486

486487
proc next*(self: var StyledChunkIterator): Option[StyledChunk] =
487-
var regexes = ({.gcsafe.}: regexes.addr)
488-
489488
if self.atEnd:
490489
return
491490

@@ -537,73 +536,80 @@ proc next*(self: var StyledChunkIterator): Option[StyledChunk] =
537536
let predicates = self.highlighter.get.query.predicatesForPattern(match.pattern, self.arena)
538537
for capture in match.captures:
539538
let node = capture.node
540-
# let byteRange = node.startByte...node.endByte
539+
let byteRange = node.startByte...node.endByte
541540
let nodeRange = node.startPoint.toCursor.toPoint...node.endPoint.toCursor.toPoint
542541
if nodeRange.b <= currentChunk.point or nodeRange.a >= currentChunk.endPoint:
543542
continue
544543

545544
var matches = true
546-
if nodeRange.a.row != nodeRange.b.row:
547-
matches = false
548-
549-
for predicate in predicates:
550-
if not matches:
551-
break
552-
553-
for operand in predicate.operands:
554-
if operand.name != capture.name:
555-
matches = false
545+
if nodeRange.a.row == nodeRange.b.row:
546+
for predicate in predicates:
547+
if not matches:
556548
break
557549

558-
case predicate.operator
559-
# of "match?":
560-
# if not regexes[].contains(operand.`type`):
561-
# try:
562-
# regexes[][operand.`type`] = re(operand.`type`)
563-
# except RegexError:
564-
# matches = false
565-
# break
566-
# let regex {.cursor.} = regexes[][operand.`type`]
567-
568-
# let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
569-
# if nodeText.matchLen(regex, 0) != nodeText.len:
570-
# matches = false
571-
# break
572-
573-
# of "not-match?":
574-
# if not regexes[].contains(operand.`type`):
575-
# try:
576-
# regexes[][operand.`type`] = re(operand.`type`)
577-
# except RegexError:
578-
# matches = false
579-
# break
580-
# let regex {.cursor.} = regexes[][operand.`type`]
581-
582-
# let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
583-
# if nodeText.matchLen(regex, 0) == nodeText.len:
584-
# matches = false
585-
# break
586-
587-
# of "eq?":
588-
# # @todo: second arg can be capture aswell
589-
# let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
590-
# if nodeText != operand.`type`:
591-
# matches = false
592-
# break
593-
594-
# of "not-eq?":
595-
# # @todo: second arg can be capture aswell
596-
# let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
597-
# if nodeText == operand.`type`:
598-
# matches = false
599-
# break
600-
601-
# of "any-of?":
602-
# # todo
603-
# log(lvlError, fmt"Unknown predicate '{predicate.name}'")
604-
605-
else:
606-
discard
550+
for operand in predicate.operands:
551+
if operand.name != capture.name:
552+
matches = false
553+
break
554+
555+
case predicate.operator
556+
of "match?":
557+
let cachedRegex = self.regexCache.tryGet(operand.`type`)
558+
var regex: Regex
559+
if cachedRegex.isSome:
560+
regex = cachedRegex.get
561+
else:
562+
try:
563+
regex = re($operand.`type`)
564+
self.regexCache[operand.`type`] = regex
565+
except RegexError:
566+
matches = false
567+
break
568+
569+
let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
570+
if nodeText.matchLen(regex, 0) != nodeText.len:
571+
matches = false
572+
break
573+
574+
of "not-match?":
575+
let cachedRegex = self.regexCache.tryGet(operand.`type`)
576+
var regex: Regex
577+
if cachedRegex.isSome:
578+
regex = cachedRegex.get
579+
else:
580+
try:
581+
regex = re($operand.`type`)
582+
self.regexCache[operand.`type`] = regex
583+
except RegexError:
584+
matches = false
585+
break
586+
587+
let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
588+
if nodeText.matchLen(regex, 0) == nodeText.len:
589+
matches = false
590+
break
591+
592+
of "eq?":
593+
# @todo: second arg can be capture aswell
594+
let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
595+
if nodeText != operand.`type`:
596+
matches = false
597+
break
598+
599+
of "not-eq?":
600+
# @todo: second arg can be capture aswell
601+
let nodeText = self.contentString(nodeRange, byteRange, maxPredicateCheckLen)
602+
if nodeText == operand.`type`:
603+
matches = false
604+
break
605+
606+
# of "any-of?":
607+
# # todo
608+
# # echo &"Unknown predicate '{predicate.name}'"
609+
# discard
610+
611+
else:
612+
discard
607613

608614
if not matches:
609615
continue

0 commit comments

Comments
 (0)