QCodeSitter 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. QCodeSitter/__init__.py +38 -0
  2. QCodeSitter/behaviors/__init__.py +115 -0
  3. QCodeSitter/behaviors/auto_bracket.py +141 -0
  4. QCodeSitter/behaviors/code_folding.py +530 -0
  5. QCodeSitter/behaviors/comment_toggle.py +71 -0
  6. QCodeSitter/behaviors/highlight_matching_brackets.py +181 -0
  7. QCodeSitter/behaviors/highlight_matching_selection.py +59 -0
  8. QCodeSitter/behaviors/line_numbers.py +49 -0
  9. QCodeSitter/behaviors/multi_cursor_paint.py +131 -0
  10. QCodeSitter/behaviors/overscroll.py +104 -0
  11. QCodeSitter/behaviors/smart_indent.py +191 -0
  12. QCodeSitter/behaviors/syntax_highlighting.py +181 -0
  13. QCodeSitter/behaviors/tab_completion/__init__.py +257 -0
  14. QCodeSitter/behaviors/tab_completion/popup.py +123 -0
  15. QCodeSitter/behaviors/tab_completion/providers.py +66 -0
  16. QCodeSitter/behaviors/tab_completion/types.py +55 -0
  17. QCodeSitter/code_editor.py +540 -0
  18. QCodeSitter/cursors/__init__.py +8 -0
  19. QCodeSitter/cursors/clipboard.py +51 -0
  20. QCodeSitter/cursors/iterator.py +141 -0
  21. QCodeSitter/cursors/line_edits.py +128 -0
  22. QCodeSitter/cursors/multi.py +240 -0
  23. QCodeSitter/cursors/state.py +55 -0
  24. QCodeSitter/cursors/tracked.py +97 -0
  25. QCodeSitter/defaults.py +93 -0
  26. QCodeSitter/editing_keys.py +150 -0
  27. QCodeSitter/editor_options.py +246 -0
  28. QCodeSitter/gui_colors.py +51 -0
  29. QCodeSitter/gutters.py +141 -0
  30. QCodeSitter/integrations/__init__.py +1 -0
  31. QCodeSitter/integrations/preditor_interface.py +390 -0
  32. QCodeSitter/languages/__init__.py +99 -0
  33. QCodeSitter/languages/base.py +60 -0
  34. QCodeSitter/languages/python.py +146 -0
  35. QCodeSitter/languages/python_highlights.py +444 -0
  36. QCodeSitter/py.typed +0 -0
  37. QCodeSitter/queries/__init__.py +73 -0
  38. QCodeSitter/queries/predicates.py +172 -0
  39. QCodeSitter/syntax_specs.py +65 -0
  40. QCodeSitter/themes.py +271 -0
  41. QCodeSitter/tracked_document.py +107 -0
  42. QCodeSitter/tree_manager.py +180 -0
  43. QCodeSitter/utils.py +84 -0
  44. qcodesitter-0.1.0.dist-info/METADATA +255 -0
  45. qcodesitter-0.1.0.dist-info/RECORD +49 -0
  46. qcodesitter-0.1.0.dist-info/WHEEL +5 -0
  47. qcodesitter-0.1.0.dist-info/entry_points.txt +2 -0
  48. qcodesitter-0.1.0.dist-info/licenses/LICENSE.txt +7 -0
  49. qcodesitter-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,146 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Collection
4
+ from typing import TYPE_CHECKING
5
+
6
+ from ..utils import leading_whitespace
7
+ from .base import IndentRules, LanguageSpec
8
+ from .python_highlights import HIGHLIGHT_QUERY
9
+
10
+ if TYPE_CHECKING:
11
+ from tree_sitter import Node
12
+
13
+ # Statements whose body starts after a colon
14
+ _BLOCK_TYPES = frozenset(
15
+ {
16
+ "if_statement",
17
+ "for_statement",
18
+ "while_statement",
19
+ "function_definition",
20
+ "class_definition",
21
+ "with_statement",
22
+ "try_statement",
23
+ "except_clause",
24
+ "finally_clause",
25
+ "elif_clause",
26
+ "else_clause",
27
+ "match_statement",
28
+ "case_clause",
29
+ }
30
+ )
31
+ _COLLECTION_TYPES = frozenset(
32
+ {"list", "dictionary", "set", "tuple", "argument_list", "parameters"}
33
+ )
34
+ # Statements that end their block, so the next line dedents
35
+ _TERMINAL_TYPES = frozenset(
36
+ {
37
+ "return_statement",
38
+ "break_statement",
39
+ "continue_statement",
40
+ "pass_statement",
41
+ "raise_statement",
42
+ }
43
+ )
44
+ _OPEN_BRACKETS = frozenset("([{")
45
+ _CLOSE_BRACKETS = frozenset(")]}")
46
+
47
+
48
+ def _child_of_type(node: Node, types: Collection[str]) -> Node | None:
49
+ for child in node.children:
50
+ if child.type in types:
51
+ return child
52
+ return None
53
+
54
+
55
+ def _opens_block(node: Node, row: int) -> bool:
56
+ """Whether this node starts an indented block or open bracket on the row"""
57
+ if node.type in _BLOCK_TYPES:
58
+ # The colon can be followed by a comment, like "def foo(): # comment"
59
+ colon = _child_of_type(node, (":",))
60
+ return colon is not None and colon.start_point.row == row
61
+ if node.type in _OPEN_BRACKETS:
62
+ # Incomplete code can leave a bare bracket in an ERROR node
63
+ return node.start_point.row == row
64
+ if node.type in _COLLECTION_TYPES:
65
+ opening = _child_of_type(node, _OPEN_BRACKETS)
66
+ closing = _child_of_type(node, _CLOSE_BRACKETS)
67
+ if opening is None or opening.start_point.row != row:
68
+ return False
69
+ return closing is None or closing.start_point.row != row
70
+ return False
71
+
72
+
73
+ class PythonIndentRules(IndentRules):
74
+ def should_indent_after(self, row: int, col: int) -> bool:
75
+ node = self.node_at(row, col)
76
+ while node is not None:
77
+ if _opens_block(node, row):
78
+ return True
79
+ node = node.parent
80
+ return False
81
+
82
+ def should_dedent_after(self, row: int, col: int) -> bool:
83
+ node = self.node_at(row, col)
84
+ if node is None:
85
+ return False
86
+ if self._closes_earlier_bracket(row, col):
87
+ return True
88
+ while node is not None:
89
+ if node.type in _TERMINAL_TYPES:
90
+ return True
91
+ node = node.parent
92
+ return False
93
+
94
+ def _closes_earlier_bracket(self, row: int, col: int) -> bool:
95
+ """Whether the line starts with a bracket, after the column, that closes
96
+ a bracket opened on an earlier line
97
+ """
98
+ line_text = self.document.findBlockByNumber(row).text()
99
+ # The indent is all whitespace, so its length is also its UTF-16 length
100
+ bracket_col = len(leading_whitespace(line_text))
101
+ if line_text[bracket_col : bracket_col + 1] not in _CLOSE_BRACKETS:
102
+ return False
103
+ # Enter after the bracket keeps the indent of the closing line
104
+ if col >= bracket_col:
105
+ return False
106
+ bracket = self.node_at(row, bracket_col)
107
+ if bracket is None or bracket.parent is None:
108
+ return False
109
+ opening = _child_of_type(bracket.parent, _OPEN_BRACKETS)
110
+ return opening is not None and opening.start_point.row < row
111
+
112
+
113
+ IDENTIFIER_QUERY = """
114
+ (function_definition name: (identifier) @function)
115
+ (class_definition name: (identifier) @class)
116
+ (assignment left: (identifier) @variable)
117
+ (parameters (identifier) @parameter)
118
+ (import_statement name: (dotted_name (identifier) @import))
119
+ (import_from_statement name: (dotted_name (identifier) @import))
120
+ (aliased_import name: (dotted_name (identifier) @import))
121
+ (aliased_import alias: (identifier) @import)
122
+ """
123
+
124
+ PYTHON = LanguageSpec(
125
+ name="python",
126
+ comment_prefix="# ",
127
+ highlight_query=HIGHLIGHT_QUERY,
128
+ identifier_query=IDENTIFIER_QUERY,
129
+ fold_hide_last_line=frozenset(
130
+ {
131
+ "class_definition",
132
+ "for_statement",
133
+ "function_definition",
134
+ "if_statement",
135
+ "match_statement",
136
+ "try_statement",
137
+ "while_statement",
138
+ "with_statement",
139
+ }
140
+ ),
141
+ fold_keep_last_line=frozenset({"argument_list", "dictionary", "list", "tuple"}),
142
+ indent_rules=PythonIndentRules,
143
+ no_completion_types=frozenset({"string", "comment", "string_start", "string_end"}),
144
+ string_type="string",
145
+ string_delimiter_types=("string_start", "string_end"),
146
+ )
@@ -0,0 +1,444 @@
1
+ HIGHLIGHT_QUERY = """
2
+ ; From tree-sitter-python licensed under MIT License
3
+ ; Copyright (c) 2016 Max Brunsfeld
4
+ ; Variables
5
+ (identifier) @variable
6
+
7
+ ; Reset highlighting in f-string interpolations
8
+ (interpolation) @none
9
+
10
+ ; Identifier naming conventions
11
+ ((identifier) @type
12
+ (#match? @type "^[A-Z].*[a-z]"))
13
+
14
+ ((identifier) @constant
15
+ (#match? @constant "^[A-Z][A-Z_0-9]*$"))
16
+
17
+ ((identifier) @constant.builtin
18
+ (#match? @constant.builtin "^__[a-zA-Z0-9_]*__$"))
19
+
20
+ ((identifier) @constant.builtin
21
+ (#any-of? @constant.builtin
22
+ ; https://docs.python.org/3/library/constants.html
23
+ "NotImplemented" "Ellipsis" "quit" "exit" "copyright" "credits" "license"))
24
+
25
+ "_" @character.special ; match wildcard
26
+
27
+ ((assignment
28
+ left: (identifier) @type.definition
29
+ (type
30
+ (identifier) @_annotation))
31
+ (#eq? @_annotation "TypeAlias"))
32
+
33
+ ((assignment
34
+ left: (identifier) @type.definition
35
+ right: (call
36
+ function: (identifier) @_func))
37
+ (#any-of? @_func "TypeVar" "NewType"))
38
+
39
+ ; Function definitions
40
+ (function_definition
41
+ name: (identifier) @function)
42
+
43
+ (type
44
+ (identifier) @type)
45
+
46
+ (type
47
+ (subscript
48
+ (identifier) @type)) ; type subscript: Tuple[int]
49
+
50
+ ((call
51
+ function: (identifier) @_isinstance
52
+ arguments: (argument_list
53
+ (_)
54
+ (identifier) @type))
55
+ (#eq? @_isinstance "isinstance"))
56
+
57
+ ; Literals
58
+ (none) @constant.builtin
59
+
60
+ [
61
+ (true)
62
+ (false)
63
+ ] @boolean
64
+
65
+ (integer) @number
66
+
67
+ (float) @number.float
68
+
69
+ (comment) @comment @spell
70
+
71
+ ((module
72
+ .
73
+ (comment) @keyword.directive @nospell)
74
+ (#match? @keyword.directive "^#!/"))
75
+
76
+ (string) @string
77
+
78
+ [
79
+ (escape_sequence)
80
+ (escape_interpolation)
81
+ ] @string.escape
82
+
83
+ ; doc-strings
84
+ (expression_statement
85
+ (string
86
+ (string_content) @spell) @string.documentation)
87
+
88
+ ; Tokens
89
+ [
90
+ "-"
91
+ "-="
92
+ ":="
93
+ "!="
94
+ "*"
95
+ "**"
96
+ "**="
97
+ "*="
98
+ "/"
99
+ "//"
100
+ "//="
101
+ "/="
102
+ "&"
103
+ "&="
104
+ "%"
105
+ "%="
106
+ "^"
107
+ "^="
108
+ "+"
109
+ "+="
110
+ "<"
111
+ "<<"
112
+ "<<="
113
+ "<="
114
+ "<>"
115
+ "="
116
+ "=="
117
+ ">"
118
+ ">="
119
+ ">>"
120
+ ">>="
121
+ "@"
122
+ "@="
123
+ "|"
124
+ "|="
125
+ "~"
126
+ "->"
127
+ ] @operator
128
+
129
+ ; Keywords
130
+ [
131
+ "and"
132
+ "in"
133
+ "is"
134
+ "not"
135
+ "or"
136
+ "is not"
137
+ "not in"
138
+ "del"
139
+ ] @keyword.operator
140
+
141
+ [
142
+ "def"
143
+ "lambda"
144
+ ] @keyword.function
145
+
146
+ [
147
+ "assert"
148
+ "exec"
149
+ "global"
150
+ "nonlocal"
151
+ "pass"
152
+ "print"
153
+ "with"
154
+ "as"
155
+ ] @keyword
156
+
157
+ [
158
+ "type"
159
+ "class"
160
+ ] @keyword.type
161
+
162
+ [
163
+ "async"
164
+ "await"
165
+ ] @keyword.coroutine
166
+
167
+ [
168
+ "return"
169
+ "yield"
170
+ ] @keyword.return
171
+
172
+ (yield
173
+ "from" @keyword.return)
174
+
175
+ (future_import_statement
176
+ "from" @keyword.import
177
+ "__future__" @module.builtin)
178
+
179
+ (import_from_statement
180
+ "from" @keyword.import)
181
+
182
+ "import" @keyword.import
183
+
184
+ (aliased_import
185
+ "as" @keyword.import)
186
+
187
+ (wildcard_import
188
+ "*" @character.special)
189
+
190
+ (import_statement
191
+ name: (dotted_name
192
+ (identifier) @module))
193
+
194
+ (import_statement
195
+ name: (aliased_import
196
+ name: (dotted_name
197
+ (identifier) @module)
198
+ alias: (identifier) @module))
199
+
200
+ (import_from_statement
201
+ module_name: (dotted_name
202
+ (identifier) @module))
203
+
204
+ (import_from_statement
205
+ module_name: (relative_import
206
+ (dotted_name
207
+ (identifier) @module)))
208
+
209
+ [
210
+ "if"
211
+ "elif"
212
+ "else"
213
+ "match"
214
+ "case"
215
+ ] @keyword.conditional
216
+
217
+ [
218
+ "for"
219
+ "while"
220
+ "break"
221
+ "continue"
222
+ ] @keyword.repeat
223
+
224
+ [
225
+ "try"
226
+ "except"
227
+ "raise"
228
+ "finally"
229
+ ] @keyword.exception
230
+
231
+ (raise_statement
232
+ "from" @keyword.exception)
233
+
234
+ (try_statement
235
+ (else_clause
236
+ "else" @keyword.exception))
237
+
238
+ [
239
+ "("
240
+ ")"
241
+ "["
242
+ "]"
243
+ "{"
244
+ "}"
245
+ ] @punctuation.bracket
246
+
247
+ (interpolation
248
+ "{" @punctuation.special
249
+ "}" @punctuation.special)
250
+
251
+ (type_conversion) @function.macro
252
+
253
+ [
254
+ ","
255
+ "."
256
+ ":"
257
+ ";"
258
+ (ellipsis)
259
+ ] @punctuation.delimiter
260
+
261
+ ((identifier) @type.builtin
262
+ (#any-of? @type.builtin
263
+ ; https://docs.python.org/3/library/exceptions.html
264
+ "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError"
265
+ "AttributeError" "EOFError" "FloatingPointError" "GeneratorExit" "ImportError"
266
+ "ModuleNotFoundError" "IndexError" "KeyError" "KeyboardInterrupt" "MemoryError" "NameError"
267
+ "NotImplementedError" "OSError" "OverflowError" "RecursionError" "ReferenceError" "RuntimeError"
268
+ "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" "SystemError"
269
+ "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError"
270
+ "UnicodeDecodeError" "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError"
271
+ "IOError" "WindowsError" "BlockingIOError" "ChildProcessError" "ConnectionError"
272
+ "BrokenPipeError" "ConnectionAbortedError" "ConnectionRefusedError" "ConnectionResetError"
273
+ "FileExistsError" "FileNotFoundError" "InterruptedError" "IsADirectoryError"
274
+ "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning"
275
+ "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning"
276
+ "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning"
277
+ ; https://docs.python.org/3/library/stdtypes.html
278
+ "bool" "int" "float" "complex" "list" "tuple" "range" "str" "bytes" "bytearray" "memoryview"
279
+ "set" "frozenset" "dict" "type" "object"))
280
+
281
+ ; Normal parameters
282
+ (parameters
283
+ (identifier) @variable.parameter)
284
+
285
+ ; Lambda parameters
286
+ (lambda_parameters
287
+ (identifier) @variable.parameter)
288
+
289
+ (lambda_parameters
290
+ (tuple_pattern
291
+ (identifier) @variable.parameter))
292
+
293
+ ; Default parameters
294
+ (keyword_argument
295
+ name: (identifier) @variable.parameter)
296
+
297
+ ; Naming parameters on call-site
298
+ (default_parameter
299
+ name: (identifier) @variable.parameter)
300
+
301
+ (typed_parameter
302
+ (identifier) @variable.parameter)
303
+
304
+ (typed_default_parameter
305
+ name: (identifier) @variable.parameter)
306
+
307
+ ; Variadic parameters *args, **kwargs
308
+ (parameters
309
+ (list_splat_pattern ; *args
310
+ (identifier) @variable.parameter))
311
+
312
+ (parameters
313
+ (dictionary_splat_pattern ; **kwargs
314
+ (identifier) @variable.parameter))
315
+
316
+ ; Typed variadic parameters
317
+ (parameters
318
+ (typed_parameter
319
+ (list_splat_pattern ; *args: type
320
+ (identifier) @variable.parameter)))
321
+
322
+ (parameters
323
+ (typed_parameter
324
+ (dictionary_splat_pattern ; *kwargs: type
325
+ (identifier) @variable.parameter)))
326
+
327
+ ; Lambda parameters
328
+ (lambda_parameters
329
+ (list_splat_pattern
330
+ (identifier) @variable.parameter))
331
+
332
+ (lambda_parameters
333
+ (dictionary_splat_pattern
334
+ (identifier) @variable.parameter))
335
+
336
+ ((identifier) @variable.builtin
337
+ (#eq? @variable.builtin "self"))
338
+
339
+ ((identifier) @variable.builtin
340
+ (#eq? @variable.builtin "cls"))
341
+
342
+ ; After @type.builtin bacause builtins (such as `type`) are valid as attribute name
343
+ ((attribute
344
+ attribute: (identifier) @variable.member)
345
+ (#match? @variable.member "^[%l_].*$"))
346
+
347
+ ; Class definitions
348
+ (class_definition
349
+ name: (identifier) @type)
350
+
351
+ (class_definition
352
+ body: (block
353
+ (function_definition
354
+ name: (identifier) @function.method)))
355
+
356
+ (class_definition
357
+ superclasses: (argument_list
358
+ (identifier) @type))
359
+
360
+ ((class_definition
361
+ body: (block
362
+ (expression_statement
363
+ (assignment
364
+ left: (identifier) @variable.member))))
365
+ (#match? @variable.member "^[%l_].*$"))
366
+
367
+ ((class_definition
368
+ body: (block
369
+ (expression_statement
370
+ (assignment
371
+ left: (_
372
+ (identifier) @variable.member)))))
373
+ (#match? @variable.member "^[%l_].*$"))
374
+
375
+ ((class_definition
376
+ (block
377
+ (function_definition
378
+ name: (identifier) @constructor)))
379
+ (#any-of? @constructor "__new__" "__init__"))
380
+
381
+ ; Function calls
382
+ (call
383
+ function: (identifier) @function.call)
384
+
385
+ (call
386
+ function: (attribute
387
+ attribute: (identifier) @function.method.call))
388
+
389
+ ((call
390
+ function: (identifier) @constructor)
391
+ (#match? @constructor "^%u"))
392
+
393
+ ((call
394
+ function: (attribute
395
+ attribute: (identifier) @constructor))
396
+ (#match? @constructor "^%u"))
397
+
398
+ ; Builtin functions
399
+ ((call
400
+ function: (identifier) @function.builtin)
401
+ (#any-of? @function.builtin
402
+ "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr"
403
+ "classmethod" "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec"
404
+ "filter" "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id"
405
+ "input" "int" "isinstance" "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview"
406
+ "min" "next" "object" "oct" "open" "ord" "pow" "print" "property" "range" "repr" "reversed"
407
+ "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type"
408
+ "vars" "zip" "__import__"))
409
+
410
+ ; Regex from the `re` module
411
+ (call
412
+ function: (attribute
413
+ object: (identifier) @_re)
414
+ arguments: (argument_list
415
+ .
416
+ (string
417
+ (string_content) @string.regexp))
418
+ (#eq? @_re "re"))
419
+
420
+ ; Decorators
421
+ ((decorator
422
+ "@" @attribute)
423
+ (#set! priority 101))
424
+
425
+ (decorator
426
+ (identifier) @attribute)
427
+
428
+ (decorator
429
+ (attribute
430
+ attribute: (identifier) @attribute))
431
+
432
+ (decorator
433
+ (call
434
+ (identifier) @attribute))
435
+
436
+ (decorator
437
+ (call
438
+ (attribute
439
+ attribute: (identifier) @attribute)))
440
+
441
+ ((decorator
442
+ (identifier) @attribute.builtin)
443
+ (#any-of? @attribute.builtin "classmethod" "property" "staticmethod"))
444
+ """
QCodeSitter/py.typed ADDED
File without changes
@@ -0,0 +1,73 @@
1
+ """Compiling and running tree-sitter queries
2
+
3
+ Every query is compiled with `compile_query` and run with `query_byte_range`.
4
+ Tree-sitter can't evaluate text predicates like #eq? on our UTF-16 trees, so
5
+ compile_query renames them and query_byte_range evaluates them against the
6
+ document text. predicates.py has the details
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import functools
12
+ import warnings
13
+ from collections.abc import Callable
14
+ from typing import Literal
15
+
16
+ from tree_sitter import Language, Node, Query, QueryCursor, QueryError
17
+
18
+ from .predicates import QueryPredicates, prepare_query_source
19
+
20
+ # A predicate argument is (value, "capture" or "string")
21
+ PredicateArgs = list[tuple[str, Literal["capture", "string"]]]
22
+ # Capture name -> the nodes it captured
23
+ Captures = dict[str, list[Node]]
24
+
25
+
26
+ @functools.lru_cache(maxsize=64)
27
+ def compile_query(language: Language, source: str) -> Query | None:
28
+ """Compile a query once, and share it between every editor and behavior
29
+
30
+ A query with an error warns once, and gives None so the behavior that
31
+ uses it does nothing, like an invalid #match? pattern matches nothing
32
+ """
33
+ try:
34
+ return Query(language, prepare_query_source(source))
35
+ except QueryError as err:
36
+ warnings.warn(
37
+ f"Invalid tree-sitter query for {language.name}: {err}", stacklevel=2
38
+ )
39
+ return None
40
+
41
+
42
+ def query_byte_range(
43
+ query: Query,
44
+ root: Node,
45
+ start_byte: int,
46
+ end_byte: int,
47
+ get_text: Callable[[Node], str] | None = None,
48
+ ) -> Captures:
49
+ """Run a query over a byte range of a tree, returning nodes by capture name
50
+
51
+ A byte range that ends before it starts warns once and returns no
52
+ captures.
53
+
54
+ Args:
55
+ get_text: Returns a node's source text, to evaluate the query's text
56
+ predicates with. Without it, the text predicates match everything.
57
+ Errors it raises propagate
58
+ """
59
+ cursor = QueryCursor(query)
60
+ try:
61
+ cursor.set_byte_range(start_byte, end_byte)
62
+ except ValueError:
63
+ # Tree-sitter rejects a range that ends before it starts
64
+ warnings.warn(
65
+ f"Skipped a tree-sitter query over the reversed byte range "
66
+ f"{start_byte}..{end_byte}",
67
+ stacklevel=2,
68
+ )
69
+ return {}
70
+ if get_text is None:
71
+ # Tree-sitter doesn't accept predicate=None
72
+ return cursor.captures(root)
73
+ return cursor.captures(root, QueryPredicates(get_text))