@kerebron/tree-sitter 0.4.27 → 0.4.29

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 (107) hide show
  1. package/esm/deno-tree-sitter/main/extended/base_node.d.ts +48 -0
  2. package/esm/deno-tree-sitter/main/extended/base_node.d.ts.map +1 -0
  3. package/esm/deno-tree-sitter/main/extended/base_node.js +155 -0
  4. package/esm/deno-tree-sitter/main/extended/base_node.js.map +1 -0
  5. package/esm/deno-tree-sitter/main/extended/node_extended.d.ts +237 -0
  6. package/esm/deno-tree-sitter/main/extended/node_extended.d.ts.map +1 -0
  7. package/esm/deno-tree-sitter/main/extended/node_extended.js +573 -0
  8. package/esm/deno-tree-sitter/main/extended/node_extended.js.map +1 -0
  9. package/esm/deno-tree-sitter/main/extended/parser.d.ts +37 -0
  10. package/esm/deno-tree-sitter/main/extended/parser.d.ts.map +1 -0
  11. package/esm/deno-tree-sitter/main/extended/parser.js +87 -0
  12. package/esm/deno-tree-sitter/main/extended/parser.js.map +1 -0
  13. package/esm/deno-tree-sitter/main/extended/soft_node.d.ts +15 -0
  14. package/esm/deno-tree-sitter/main/extended/soft_node.d.ts.map +1 -0
  15. package/esm/deno-tree-sitter/main/extended/soft_node.js +28 -0
  16. package/esm/deno-tree-sitter/main/extended/soft_node.js.map +1 -0
  17. package/esm/deno-tree-sitter/main/extended/soft_text_node.d.ts +10 -0
  18. package/esm/deno-tree-sitter/main/extended/soft_text_node.d.ts.map +1 -0
  19. package/esm/deno-tree-sitter/main/extended/soft_text_node.js +11 -0
  20. package/esm/deno-tree-sitter/main/extended/soft_text_node.js.map +1 -0
  21. package/esm/deno-tree-sitter/main/extended/whitespace_node.d.ts +10 -0
  22. package/esm/deno-tree-sitter/main/extended/whitespace_node.d.ts.map +1 -0
  23. package/esm/deno-tree-sitter/main/extended/whitespace_node.js +11 -0
  24. package/esm/deno-tree-sitter/main/extended/whitespace_node.js.map +1 -0
  25. package/esm/deno-tree-sitter/main/extras/misc.d.ts +2 -0
  26. package/esm/deno-tree-sitter/main/extras/misc.d.ts.map +1 -0
  27. package/esm/deno-tree-sitter/main/extras/misc.js +14 -0
  28. package/esm/deno-tree-sitter/main/extras/misc.js.map +1 -0
  29. package/esm/deno-tree-sitter/main/tree_sitter/bindings.d.ts +14 -0
  30. package/esm/deno-tree-sitter/main/tree_sitter/bindings.d.ts.map +1 -0
  31. package/esm/deno-tree-sitter/main/tree_sitter/bindings.js +22 -0
  32. package/esm/deno-tree-sitter/main/tree_sitter/bindings.js.map +1 -0
  33. package/esm/deno-tree-sitter/main/tree_sitter/constants.d.ts +60 -0
  34. package/esm/deno-tree-sitter/main/tree_sitter/constants.d.ts.map +1 -0
  35. package/esm/deno-tree-sitter/main/tree_sitter/constants.js +67 -0
  36. package/esm/deno-tree-sitter/main/tree_sitter/constants.js.map +1 -0
  37. package/esm/deno-tree-sitter/main/tree_sitter/language.d.ts +137 -0
  38. package/esm/deno-tree-sitter/main/tree_sitter/language.d.ts.map +1 -0
  39. package/esm/deno-tree-sitter/main/tree_sitter/language.js +265 -0
  40. package/esm/deno-tree-sitter/main/tree_sitter/language.js.map +1 -0
  41. package/esm/deno-tree-sitter/main/tree_sitter/lookahead_iterator.d.ts +41 -0
  42. package/esm/deno-tree-sitter/main/tree_sitter/lookahead_iterator.d.ts.map +1 -0
  43. package/esm/deno-tree-sitter/main/tree_sitter/lookahead_iterator.js +66 -0
  44. package/esm/deno-tree-sitter/main/tree_sitter/lookahead_iterator.js.map +1 -0
  45. package/esm/deno-tree-sitter/main/tree_sitter/marshal.d.ts +85 -0
  46. package/esm/deno-tree-sitter/main/tree_sitter/marshal.d.ts.map +1 -0
  47. package/esm/deno-tree-sitter/main/tree_sitter/marshal.js +174 -0
  48. package/esm/deno-tree-sitter/main/tree_sitter/marshal.js.map +1 -0
  49. package/esm/deno-tree-sitter/main/tree_sitter/node.d.ts +260 -0
  50. package/esm/deno-tree-sitter/main/tree_sitter/node.d.ts.map +1 -0
  51. package/esm/deno-tree-sitter/main/tree_sitter/node.js +558 -0
  52. package/esm/deno-tree-sitter/main/tree_sitter/node.js.map +1 -0
  53. package/esm/deno-tree-sitter/main/tree_sitter/parser.d.ts +124 -0
  54. package/esm/deno-tree-sitter/main/tree_sitter/parser.d.ts.map +1 -0
  55. package/esm/deno-tree-sitter/main/tree_sitter/parser.js +253 -0
  56. package/esm/deno-tree-sitter/main/tree_sitter/parser.js.map +1 -0
  57. package/esm/deno-tree-sitter/main/tree_sitter/query.d.ts +134 -0
  58. package/esm/deno-tree-sitter/main/tree_sitter/query.d.ts.map +1 -0
  59. package/esm/deno-tree-sitter/main/tree_sitter/query.js +621 -0
  60. package/esm/deno-tree-sitter/main/tree_sitter/query.js.map +1 -0
  61. package/esm/deno-tree-sitter/main/tree_sitter/tree.d.ts +49 -0
  62. package/esm/deno-tree-sitter/main/tree_sitter/tree.d.ts.map +1 -0
  63. package/esm/deno-tree-sitter/main/tree_sitter/tree.js +131 -0
  64. package/esm/deno-tree-sitter/main/tree_sitter/tree.js.map +1 -0
  65. package/esm/deno-tree-sitter/main/tree_sitter/tree_cursor.d.ts +165 -0
  66. package/esm/deno-tree-sitter/main/tree_sitter/tree_cursor.d.ts.map +1 -0
  67. package/esm/deno-tree-sitter/main/tree_sitter/tree_cursor.js +281 -0
  68. package/esm/deno-tree-sitter/main/tree_sitter/tree_cursor.js.map +1 -0
  69. package/esm/deno-tree-sitter/main/tree_sitter_wasm.d.ts +3 -0
  70. package/esm/deno-tree-sitter/main/tree_sitter_wasm.d.ts.map +1 -0
  71. package/esm/deno-tree-sitter/main/tree_sitter_wasm.js +0 -0
  72. package/esm/deno-tree-sitter/main/tree_sitter_wasm.js.map +1 -0
  73. package/esm/deno-tree-sitter/main/wasm_loader.d.ts +29 -0
  74. package/esm/deno-tree-sitter/main/wasm_loader.d.ts.map +1 -0
  75. package/esm/deno-tree-sitter/main/wasm_loader.js +1703 -0
  76. package/esm/deno-tree-sitter/main/wasm_loader.js.map +1 -0
  77. package/esm/deno-tree-sitter/main/wasm_loader_with_defaults.d.ts +3 -0
  78. package/esm/deno-tree-sitter/main/wasm_loader_with_defaults.d.ts.map +1 -0
  79. package/esm/deno-tree-sitter/main/wasm_loader_with_defaults.js +9 -0
  80. package/esm/deno-tree-sitter/main/wasm_loader_with_defaults.js.map +1 -0
  81. package/esm/mod.d.ts +7 -0
  82. package/esm/mod.d.ts.map +1 -0
  83. package/esm/mod.js +8 -0
  84. package/esm/mod.js.map +1 -0
  85. package/esm/package.json +3 -0
  86. package/package.json +5 -2
  87. package/src/deno-tree-sitter/main/extended/base_node.js +174 -0
  88. package/src/deno-tree-sitter/main/extended/node_extended.js +588 -0
  89. package/src/deno-tree-sitter/main/extended/parser.js +87 -0
  90. package/src/deno-tree-sitter/main/extended/soft_node.js +32 -0
  91. package/src/deno-tree-sitter/main/extended/soft_text_node.js +11 -0
  92. package/src/deno-tree-sitter/main/extended/whitespace_node.js +11 -0
  93. package/src/deno-tree-sitter/main/extras/misc.js +12 -0
  94. package/src/deno-tree-sitter/main/tree_sitter/bindings.js +26 -0
  95. package/src/deno-tree-sitter/main/tree_sitter/constants.js +79 -0
  96. package/src/deno-tree-sitter/main/tree_sitter/language.js +289 -0
  97. package/src/deno-tree-sitter/main/tree_sitter/lookahead_iterator.js +74 -0
  98. package/src/deno-tree-sitter/main/tree_sitter/marshal.js +186 -0
  99. package/src/deno-tree-sitter/main/tree_sitter/node.js +616 -0
  100. package/src/deno-tree-sitter/main/tree_sitter/parser.js +273 -0
  101. package/src/deno-tree-sitter/main/tree_sitter/query.js +705 -0
  102. package/src/deno-tree-sitter/main/tree_sitter/tree.js +145 -0
  103. package/src/deno-tree-sitter/main/tree_sitter/tree_cursor.js +314 -0
  104. package/src/deno-tree-sitter/main/tree_sitter_wasm.js +0 -0
  105. package/src/deno-tree-sitter/main/wasm_loader.js +1702 -0
  106. package/src/deno-tree-sitter/main/wasm_loader_with_defaults.js +9 -0
  107. package/src/mod.ts +8 -0
@@ -0,0 +1,588 @@
1
+ import { Node } from "../tree_sitter/node.js"
2
+ import { Parser } from "../tree_sitter/parser.js"
3
+ import { BaseNode, _shadows } from "./base_node.js"
4
+ import { WhitespaceNode } from "./whitespace_node.js"
5
+ import { SoftTextNode } from "./soft_text_node.js"
6
+ import { Query, QueryError } from "../tree_sitter/query.js"
7
+ import { _getQueryCaptureTargets } from "../extras/misc.js"
8
+ import { Tree } from "../tree_sitter/tree.js"
9
+
10
+ // NOTE: the tree override is in here because it has a circular type dependency on HardNode
11
+ const realRootNodeGetter = Object.getOwnPropertyDescriptor(Tree.prototype, "rootNode").get
12
+ //
13
+ // complicated override in order to make the root node pretend to contain soft nodes (ex: leading and trailing whitespace)
14
+ //
15
+ // hard because a normal change to .startIndex/.endIndex breaks internal methods, so we have to selectively choose who sees the real/faked .startIndex/.endIndex
16
+ Object.defineProperty(Tree.prototype, "rootNode", {
17
+ get() {
18
+ const rootNode = realRootNodeGetter.call(this)
19
+ const rootShadow = {}
20
+ Object.setPrototypeOf(rootShadow, Object.getPrototypeOf(rootNode))
21
+ const descriptors = Object.assign(Object.getOwnPropertyDescriptors(Node.prototype), Object.getOwnPropertyDescriptors(rootNode))
22
+ const newDescriptors = {}
23
+ for (const [key, setting] of Object.entries(descriptors)) {
24
+ if (key == "startIndex" || key == "startPosition") {
25
+ continue
26
+ } else if (key == "text" || key == "replaceInnards") {
27
+ // use the faked .startIndex for these methods/getters
28
+ newDescriptors[key] = setting
29
+ } else {
30
+ // use the real .startIndex for these methods/getters (get it from the real thing)
31
+ // (otherwise stuff breaks when there is a whitespace prefix)
32
+ newDescriptors[key] = {
33
+ get: ()=>{
34
+ const output = rootNode[key]
35
+ if (typeof output == "function") {
36
+ return (...args)=>output.apply(rootNode, args)
37
+ }
38
+ return output
39
+ },
40
+ set: (value)=>rootNode[key] = value,
41
+ enumerable: setting.enumerable,
42
+ configurable: setting.configurable,
43
+ }
44
+ }
45
+ }
46
+ Object.setPrototypeOf(rootShadow, Node.prototype)
47
+ Object.defineProperties(rootShadow, newDescriptors)
48
+ rootShadow.startIndex = 0
49
+ rootShadow.startPosition = { row: 0, column: 0 }
50
+ // if the original file is just whitespace or non-matched text, then fill it with soft nodes even though it'd normally have no children
51
+ if (rootShadow.children.length == 0 && rootShadow.endIndex != 0) {
52
+ rootShadow._children = _childrenWithSoftNodes(rootShadow, [{startIndex: rootShadow.endIndex, endIndex: rootShadow.endIndex, endPosition: rootShadow.endPosition}], rootNode.tree.codeString).slice(0,-1)
53
+ }
54
+ _shadows[rootNode.id] = rootShadow
55
+ return rootShadow
56
+ }
57
+ })
58
+
59
+ // this only exists to help with type hints
60
+ export class ExtendedTree extends Tree {
61
+ /** @type {HardNode} */
62
+ rootNode
63
+ }
64
+
65
+ // this only exists to help with type hints
66
+ class Position {
67
+ /** @type {number} */
68
+ row
69
+ /** @type {number} */
70
+ column
71
+ }
72
+
73
+ const originalDescriptors = Object.getOwnPropertyDescriptors(Node.prototype)
74
+ const originalChildrenGetter = originalDescriptors.children.get
75
+ const originalEndIndex = originalDescriptors.endIndex.get
76
+ const originalEndPosition = originalDescriptors.endPosition.get
77
+ const originalParent = originalDescriptors.parent.get
78
+ const originalTextGetter = originalDescriptors.text.get
79
+ const originalEquals = originalDescriptors.equals.value
80
+ const originalToString = originalDescriptors.toString.value
81
+
82
+ export class HardNode extends BaseNode {
83
+ /** @internal */
84
+ _children
85
+ /** @internal */
86
+ _fields
87
+ /** @internal */
88
+ _depth
89
+
90
+ /** @type {number} */
91
+ id
92
+ /** @type {number} */
93
+ startIndex
94
+ /** @type {Position} */
95
+ startPosition
96
+ /** @type {ExtendedTree} */
97
+ tree
98
+ /** @type {string} */
99
+ type
100
+ /** @type {Boolean} */
101
+ isExtra
102
+ /** @type {Boolean} */
103
+ isError
104
+ /** @type {Boolean} */
105
+ isMissing
106
+ /** @type {Boolean} */
107
+ hasChanges
108
+ /** @type {Boolean} */
109
+ hasError
110
+ /**
111
+ * @param {HardNode} other -
112
+ * @returns {Boolean} output -
113
+ */
114
+ equals = originalEquals
115
+
116
+ /** @type {HardNode} */
117
+ get parent() {
118
+ const rawParent = originalParent.call(this)
119
+ return _shadows[rawParent?.id] || rawParent
120
+ }
121
+
122
+ /** @type {string} */
123
+ get text() {
124
+ return originalTextGetter.call(_shadows[this.id] || this)
125
+ }
126
+
127
+ /** @type {Number} */
128
+ get endIndex() {
129
+ if (this.depth == 0) {
130
+ return this.tree.codeString.length
131
+ }
132
+ return originalEndIndex.call(this)
133
+ }
134
+
135
+ /** @type {Position} */
136
+ get endPosition() {
137
+ if (this.depth == 0) {
138
+ const rowIndex = (this.tree.codeString.match(/\n/g)||[]).length
139
+ const columnIndex = this.tree.codeString.match(new RegExp(`^(?:.*\\r?\\n){${rowIndex}}(.*)`))[1].length
140
+ return { row: rowIndex, column: columnIndex }
141
+ }
142
+ return originalEndPosition.call(this)
143
+ }
144
+
145
+ /** @type {Array<HardNode|SoftNode>} */
146
+ get children() {
147
+ if (!this._children) {
148
+ // will set this._children
149
+ this._children = originalChildrenGetter.call(this)
150
+ // add soft nodes if needed
151
+ if (this.tree._enableSoftNodes) {
152
+ this._children = _childrenWithSoftNodes(this, this._children||[], this.tree.codeString)
153
+ }
154
+ }
155
+ return this._children || []
156
+ }
157
+
158
+ set children(value) {
159
+ this._children = value
160
+ }
161
+
162
+ /**
163
+ * Yields each child
164
+ *
165
+ * @generator
166
+ * @yields {HardNode} The current child or grandchild in the structure.
167
+ */
168
+ *traverse(arg = { _parentNodes: [] }) {
169
+ const { _parentNodes } = arg
170
+ const parentNodes = [this, ..._parentNodes]
171
+ if (this.children?.length == 0) {
172
+ yield [_parentNodes, this, "-"]
173
+ } else {
174
+ yield [_parentNodes, this, "->"]
175
+ for (const each of this.children||[]) {
176
+ if (each instanceof Node) {
177
+ for (const eachInner of each.traverse({ _parentNodes: parentNodes })) {
178
+ yield eachInner
179
+ }
180
+ } else {
181
+ yield [parentNodes, each, "-"]
182
+ }
183
+ }
184
+ yield [_parentNodes, this, "<-"]
185
+ }
186
+ }
187
+
188
+ /**
189
+ * A generator function that flattens the hierarchical structure of `children` and their descendants.
190
+ * It yields each child and their flattened descendants recursively.
191
+ *
192
+ * @param {Function} opts.filter - A function to filter the flattened elements.
193
+ * @param {Boolean} opts.includeSelf -
194
+ * @generator
195
+ * @yields {HardNode} The current child or grandchild in the structure.
196
+ */
197
+ *iterFlattened({filter, includeSelf=false}={}) {
198
+ if (includeSelf) {
199
+ yield this
200
+ }
201
+ if (typeof filter == "function") {
202
+ for (const each of this.children||[]) {
203
+ if (filter(each)) {
204
+ yield each
205
+ }
206
+ for (const eachGrandChild of each.iterFlattened({filter})) {
207
+ yield eachGrandChild
208
+ }
209
+ }
210
+ } else {
211
+ for (const each of this.children||[]) {
212
+ yield each
213
+ for (const eachGrandChild of each.iterFlattened({filter})) {
214
+ yield eachGrandChild
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ /** @internal */
221
+ iterFlatten() {
222
+ throw Error(`did you mean iterFlattened instead of iterFlatten?`)
223
+ }
224
+
225
+ /**
226
+ * Flattens the structure of `children` using the provided filter function.
227
+ * This method returns an array containing the flattened elements.
228
+ *
229
+ * @param {Function} opts.filter - A function to filter the flattened elements.
230
+ * @param {Boolean} opts.includeSelf -
231
+ * @returns {Array} An array containing the flattened elements that pass the filter.
232
+ */
233
+ flattened({filter, includeSelf=false}={}) {
234
+ return [...this.iterFlattened({filter, includeSelf})]
235
+ }
236
+ /** @internal */
237
+ flatten() {
238
+ throw Error(`did you mean flattened instead of flatten?`)
239
+ }
240
+
241
+ /**
242
+ * Query
243
+ *
244
+ * @example
245
+ * ```js
246
+ * import { createParser } from "https://deno.land/x/deno_tree_sitter/main/main.js"
247
+ * import javascript from "https://github.com/jeff-hykin/common_tree_sitter_languages/raw/4d8a6d34d7f6263ff570f333cdcf5ded6be89e3d/main/javascript.js"
248
+ * const parser = await createParser(javascript) // path or Uint8Array
249
+ * const tree = parser.parse('let a = 1;let b = 1;let c = 1;')
250
+ *
251
+ * tree.rootNode.query(`(identifier) @blahBlahBlah`, {matchLimit: 2})
252
+ * // returns:
253
+ * [
254
+ * {
255
+ * pattern: 0,
256
+ * captures: [
257
+ * {
258
+ * name: "blahBlahBlah",
259
+ * node: {
260
+ * type: "identifier",
261
+ * typeId: 1,
262
+ * startPosition: { row: 0, column: 4 },
263
+ * startIndex: 4,
264
+ * endPosition: { row: 0, column: 5 },
265
+ * endIndex: 5,
266
+ * indent: undefined,
267
+ * hasChildren: false,
268
+ * children: []
269
+ * }
270
+ * }
271
+ * ]
272
+ * },
273
+ * {
274
+ * pattern: 0,
275
+ * captures: [
276
+ * {
277
+ * name: "blahBlahBlah",
278
+ * node: {
279
+ * type: "identifier",
280
+ * typeId: 1,
281
+ * startPosition: { row: 0, column: 14 },
282
+ * startIndex: 14,
283
+ * endPosition: { row: 0, column: 15 },
284
+ * endIndex: 15,
285
+ * indent: undefined,
286
+ * hasChildren: false,
287
+ * children: []
288
+ * }
289
+ * }
290
+ * ]
291
+ * },
292
+ * {
293
+ * pattern: 0,
294
+ * captures: [
295
+ * {
296
+ * name: "blahBlahBlah",
297
+ * node: {
298
+ * type: "identifier",
299
+ * typeId: 1,
300
+ * startPosition: { row: 0, column: 24 },
301
+ * startIndex: 24,
302
+ * endPosition: { row: 0, column: 25 },
303
+ * endIndex: 25,
304
+ * indent: undefined,
305
+ * hasChildren: false,
306
+ * children: []
307
+ * }
308
+ * }
309
+ * ]
310
+ * }
311
+ * ]
312
+ * ```
313
+ *
314
+ * @param {String} queryString - see https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
315
+ * @param options.matchLimit - max number of results
316
+ * @param options.startPosition - {row: Number, column: number}
317
+ * @param options.endPosition - {row: Number, column: number}
318
+ * @param options.maxResultDepth - depth relative to the current node (1 = direct children, 2 = grandchildren, etc)
319
+ * @returns {[Object]} output
320
+ *
321
+ */
322
+ query(queryString, options) {
323
+ const { matchLimit, startPosition, endPosition, maxResultDepth } = options || {}
324
+ const realMaxResultDepth = maxResultDepth == null ? Infinity : maxResultDepth
325
+ let query
326
+ try {
327
+ query = new Query(this.tree.language, queryString)
328
+ } catch (error) {
329
+ if (error instanceof QueryError) {
330
+ error.message = `${error.message} in the following query: ${JSON.stringify(queryString)}`
331
+ }
332
+ throw error
333
+ }
334
+ const result = query.matches(this, startPosition || this.startPosition, endPosition || this.endPosition, matchLimit)
335
+ const results = result.filter((each) => each.captures.every((each) => each.node.depth - this.depth <= realMaxResultDepth))
336
+ const codeOrCallback = this.tree.codeString
337
+ // without this soft nodes will be missing
338
+ for (const eachResult of results) {
339
+ for (const eachCapture of eachResult.captures) {
340
+ eachCapture.node.children = _childrenWithSoftNodes(eachCapture.node, eachCapture.node.children, codeOrCallback)
341
+ }
342
+ }
343
+ return result.filter((each) => each.captures.every((each) => each.node.depth - this.depth <= realMaxResultDepth))
344
+ }
345
+ /**
346
+ * quickQuery
347
+ *
348
+ * @example
349
+ * ```js
350
+ * import { createParser } from "https://deno.land/x/deno_tree_sitter/main/main.js"
351
+ * import javascript from "https://github.com/jeff-hykin/common_tree_sitter_languages/raw/676ffa3b93768b8ac628fd5c61656f7dc41ba413/main/javascript.js"
352
+ * const parser = await createParser(javascript) // path or Uint8Array
353
+ * const tree = parser.parse('let a = 1;let b = 1;let c = 1;')
354
+ * // ex1: no capture names
355
+ * const nodes = tree.rootNode.quickQuery(
356
+ * `(identifier)`, {matchLimit: 2}
357
+ * )
358
+ * // ex2: with capture names
359
+ * const groups = tree.rootNode.quickQuery(
360
+ * `'(binding (attrpath) @myKey (list_expression) @myList ("\\"")? @optionalThing )`
361
+ * )
362
+ * groups[0].myKey // node
363
+ * groups[0].myList // node
364
+ * groups[0].optionalThing // node or null
365
+ * groups[0][0] // node (the whole match)
366
+ * ```
367
+ *
368
+ * @param {String} queryString - see https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
369
+ * @param options.matchLimit - max number of results
370
+ * @param options.startPosition - {row: Number, column: number}
371
+ * @param options.endPosition - {row: Number, column: number}
372
+ * @param options.maxResultDepth - depth relative to the current node (1 = direct children, 2 = grandchildren, etc)
373
+ * @returns {Array<HardNode|Record<any, HardNode>>} nodesOrObjsOfNodes
374
+ */
375
+ quickQuery(queryString, options) {
376
+ const possibleCaptureTargets = _getQueryCaptureTargets(queryString)
377
+ //
378
+ // get a base capture name
379
+ //
380
+ let baseCaptureName = "_"
381
+ while (possibleCaptureTargets.includes(baseCaptureName)) {
382
+ // append until it doesn't conflict
383
+ baseCaptureName = `${baseCaptureName}_`
384
+ }
385
+
386
+ // add the base capture always
387
+ queryString = `${queryString} @${baseCaptureName}`
388
+ const output = this.query(queryString, options).map((each) => {
389
+ const nodesByCaptureName = Object.fromEntries(each.captures.map((each) => [each.name, each.node]))
390
+ // no capture targets means just return the base
391
+ if (possibleCaptureTargets.length == 0) {
392
+ return nodesByCaptureName[baseCaptureName]
393
+ // return named targets
394
+ } else {
395
+ // 0 is not allowed as a capture name, so it will never conflict
396
+ nodesByCaptureName[0] = nodesByCaptureName[baseCaptureName]
397
+ delete nodesByCaptureName[baseCaptureName]
398
+ return nodesByCaptureName
399
+ }
400
+ })
401
+ return output
402
+ }
403
+ /**
404
+ * quickQueryFirst
405
+ *
406
+ * @example
407
+ * ```js
408
+ * import { createParser } from "https://deno.land/x/deno_tree_sitter/main/main.js"
409
+ * import javascript from "https://github.com/jeff-hykin/common_tree_sitter_languages/raw/4d8a6d34d7f6263ff570f333cdcf5ded6be89e3d/main/javascript.js"
410
+ * const parser = await createParser(javascript) // path or Uint8Array
411
+ * const tree = parser.parse('let a = 1;let b = 1;let c = 1;')
412
+ *
413
+ * // ex1: no capture names
414
+ * const node = tree.rootNode.quickQueryFirst(`(identifier)`)
415
+ *
416
+ * // ex2: with capture names
417
+ * const { myKey, myList, optionalThing } = tree.rootNode.quickQueryFirst(
418
+ * `'(binding (attrpath) @myKey (list_expression) @myList ("\\"")? @optionalThing )`
419
+ * )
420
+ * myKey // node
421
+ * myList // node
422
+ * optionalThing // node or null
423
+ * ```
424
+ *
425
+ * @param {String} queryString - see https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
426
+ * @param options.startPosition - {row: Number, column: number}
427
+ * @param options.endPosition - {row: Number, column: number}
428
+ * @param options.maxResultDepth - depth relative to the current node (1 = direct children, 2 = grandchildren, etc)
429
+ * @returns {HardNode|Record<any,HardNode>|null} nodeOrObjOfNodes
430
+ */
431
+ quickQueryFirst(queryString, options) {
432
+ return this.quickQuery(queryString, { ...options, matchLimit: 1 })[0]
433
+ }
434
+ get fields() {
435
+ if (!this._fields) {
436
+ this._fields = {}
437
+ let index = -1
438
+ for (let each of this.children||[]) {
439
+ // skip soft nodes
440
+ if (each.typeId <= 0) {
441
+ continue
442
+ }
443
+ index++
444
+ const name = this.fieldNameForChild(index)
445
+ if (name) {
446
+ this._fields[name] = each
447
+ }
448
+ }
449
+ }
450
+ return this._fields
451
+ }
452
+ get fieldNames() {
453
+ return Object.keys(this.fields)
454
+ }
455
+
456
+ getQueryForSelf() {
457
+ let current = this
458
+ let chunks = []
459
+ while (current) {
460
+ if (current.type) {
461
+ if (current.isNamed) {
462
+ if (current.type.match(/^([a-zA-Z0-9.\-_\$]+)$/)) {
463
+ chunks.push(current.type)
464
+ } else {
465
+ chunks.push(JSON.stringify(current.type))
466
+ }
467
+ } else {
468
+ // TODO: this might not always be correct. Probably should be JSON.stringify(current.text) but theres a risk of .text being massive
469
+ chunks.push(JSON.stringify(current.type))
470
+ }
471
+ }
472
+ current = current.parent
473
+ }
474
+ return "(" + chunks.reverse().join(" (") + ")".repeat(chunks.length)
475
+ }
476
+
477
+ /** @returns {string} */
478
+ asLispString() {
479
+ return originalToString.call(this)
480
+ }
481
+ }
482
+
483
+ // patch Node with all HardNode properties
484
+ const descriptors = Object.getOwnPropertyDescriptors(HardNode.prototype)
485
+ delete descriptors.constructor
486
+ Object.defineProperties(Node.prototype, descriptors)
487
+ // force it to inherit from BaseNode
488
+ Object.setPrototypeOf(Node.prototype, BaseNode.prototype)
489
+
490
+ export {
491
+ Node
492
+ }
493
+
494
+ // helper
495
+ export const _childrenWithSoftNodes = (node, children, string)=>{
496
+ if (children?.length > 0) {
497
+ const newChildren = []
498
+ const childrenCopy = [...children]
499
+ let firstChild = childrenCopy.shift()
500
+ // helper
501
+ const handleGaps = (gapText, getReferencePoint, parentNode) => {
502
+ const { index, position } = getReferencePoint()
503
+ let start = index
504
+ let startPosition = position
505
+ const chunks = gapText.split(/(?<!\s)(?=\s+)/g)
506
+ let colOffset = startPosition.column
507
+ let rowOffset = startPosition.row
508
+ for (const eachGap of chunks) {
509
+ if (eachGap.length == 0) {
510
+ continue
511
+ }
512
+ const end = start + eachGap.length
513
+ if (eachGap.match(/^\s/)) {
514
+ const rowOffsetBefore = rowOffset
515
+ const colOffsetBefore = colOffset
516
+ rowOffset += (eachGap.match(/\n/g)||[]).length
517
+ // reset column offset on new row
518
+ if (rowOffsetBefore != rowOffset) {
519
+ colOffset = eachGap.split("\n").slice(-1)[0].length
520
+ } else {
521
+ colOffset += eachGap.length
522
+ }
523
+ newChildren.push(new WhitespaceNode({
524
+ tree: node.tree,
525
+ parent: parentNode,
526
+ getReferencePoint,
527
+ text: eachGap,
528
+ _startIndexOffset: start-index,
529
+ _startRowOffset: rowOffsetBefore-position.row,
530
+ _startColOffset: colOffsetBefore-position.column,
531
+ _endIndexOffset: end-index,
532
+ _endRowOffset: rowOffset-position.row,
533
+ _endColOffset: colOffset-position.column,
534
+ children: [],
535
+ }))
536
+ // sometimes the gap isn't always whitespace
537
+ } else {
538
+ const colOffsetBefore = colOffset
539
+ colOffset += eachGap.length
540
+ newChildren.push(new SoftTextNode({
541
+ tree: node.tree,
542
+ parent: parentNode,
543
+ getReferencePoint,
544
+ text: eachGap,
545
+ _startIndexOffset: start-index,
546
+ _startRowOffset: rowOffset-position.row,
547
+ _startColOffset: colOffsetBefore-position.column,
548
+ _endIndexOffset: end-index,
549
+ _endRowOffset: rowOffset-position.row,
550
+ _endColOffset: colOffset-position.column,
551
+ children: [],
552
+ }))
553
+ }
554
+ start = end
555
+ }
556
+ }
557
+ // preceding whitespace
558
+ if (node.startIndex != firstChild.startIndex) {
559
+ const gapText = string.slice(node.startIndex, firstChild.startIndex)
560
+ const thisNode = node
561
+ // whitespace and non-whitespace chunks
562
+ handleGaps(gapText, ()=>({index: thisNode.startIndex, position: thisNode.startPosition}), node)
563
+ }
564
+ // firstChild.indent = indent
565
+ newChildren.push(firstChild)
566
+ // gaps between sibilings
567
+ let prevChild = firstChild
568
+ for (const eachSecondaryNode of childrenCopy) {
569
+ if (prevChild.endIndex != eachSecondaryNode.startIndex) {
570
+ const gapText = string.slice(prevChild.endIndex, eachSecondaryNode.startIndex)
571
+ const thisChild = prevChild
572
+ handleGaps(gapText, ()=>({index: thisChild.endIndex, position: thisChild.endPosition}), node)
573
+ }
574
+ // eachSecondaryNode.indent = indent
575
+ newChildren.push(eachSecondaryNode)
576
+ prevChild = eachSecondaryNode
577
+ }
578
+
579
+ // gap between last child and parent
580
+ if (prevChild.endIndex != node.endIndex) {
581
+ const gapText = string.slice(prevChild.endIndex, node.endIndex)
582
+ const thisChild = prevChild
583
+ handleGaps(gapText, ()=>({index: thisChild.endIndex, position: thisChild.endPosition}), node)
584
+ }
585
+
586
+ return newChildren
587
+ }
588
+ }
@@ -0,0 +1,87 @@
1
+ import { Node } from "../tree_sitter/node.js"
2
+ import { Tree } from "../tree_sitter/tree.js"
3
+ import { Parser } from "../tree_sitter/parser.js"
4
+ import { Language } from "../tree_sitter/language.js"
5
+ import "./node_extended.js" // note: redundant but might not be redundant in the future
6
+ import { HardNode, ExtendedTree, _childrenWithSoftNodes } from "./node_extended.js"
7
+
8
+ const realParseFunction = Parser.prototype.parse
9
+ Parser.prototype.parse = function(code, oldTree, options) {
10
+ if (typeof code == "function") {
11
+ console.warn("When calling .parse() the source code was a function instead of a string. The original tree sitter supports giving a function as a means of supporting edits (see: https://github.com/tree-sitter/tree-sitter/discussions/2553 ).\nHowever, this library supports edits directly (use node.replaceInnards(``))\nThe downside of making edits easy is that .parse() doesn't really accept a function argument. I'm just going to evaluate that function to grab the string once at the beginning. Use tree.codeString if you want to get the full string after a .replaceInnards() call.")
12
+ code = code(0)
13
+ }
14
+ let tree
15
+ tree = realParseFunction.apply(this, [
16
+ (index)=>(tree?.codeString??code).slice(index),
17
+ oldTree,
18
+ options
19
+ ])
20
+ tree.codeString = code
21
+ code = null
22
+ tree._enableSoftNodes = !this.disableSoftNodes
23
+ return tree
24
+ }
25
+
26
+ // this only exists to help with type hints
27
+ class ExtendedParser extends Parser {
28
+ /**
29
+ * Parse a slice of UTF8 text.
30
+ *
31
+ * @param {string | ParseCallback} callback - Source code to parse
32
+ *
33
+ * @param {Tree | null} [oldTree] - A previous syntax tree parsed from the same document. If the text of the
34
+ * document has changed since `oldTree` was created, then you must edit `oldTree` to match
35
+ * the new text using {@link Tree#edit}.
36
+ *
37
+ * @param {ParseOptions} [options] - Options for parsing the text.
38
+ * This can be used to set the included ranges, or a progress callback.
39
+ *
40
+ * @returns {ExtendedTree | null} A {@link Tree} if parsing succeeded, or `null` if:
41
+ * - The parser has not yet had a language assigned with {@link Parser#setLanguage}.
42
+ * - The progress callback returned true.
43
+ */
44
+ parse(inputString, oldTree, options) {
45
+ }
46
+ }
47
+
48
+ const langCache = new Map()
49
+ let hasBeenLoaded = false
50
+ /**
51
+ * Creates and returns a new parser instance, loading a language from a WebAssembly binary or file path.
52
+ * Optionally, the parser can be configured to disable soft nodes.
53
+ *
54
+ * @async
55
+ * @param {Uint8Array|string} wasmUint8ArrayOrFilePath - The WebAssembly binary as a `Uint8Array` or a file path to load the language.
56
+ * @param {Object} [options] - Optional configuration options.
57
+ * @param {boolean} [options.disableSoftNodes=false] - Whether to disable soft nodes in the parser (default is `false`).
58
+ * @returns {Promise<ExtendedParser>} A promise that resolves to the created parser instance.
59
+ */
60
+ export async function createParser(wasmUint8ArrayOrFilePath, { disableSoftNodes=false, moduleOptions }={}) {
61
+ // download if given a url
62
+ if (typeof wasmUint8ArrayOrFilePath == "string" && wasmUint8ArrayOrFilePath.match(/^https?:\/\//)) {
63
+ wasmUint8ArrayOrFilePath = await fetch(wasmUint8ArrayOrFilePath).then(async r=>new Uint8Array(await r.arrayBuffer()))
64
+ }
65
+ if (!hasBeenLoaded) {
66
+ hasBeenLoaded = true
67
+ await Parser.init(moduleOptions)
68
+ }
69
+ // this is a workaround for the a bug in the loader where loading the same language twice causes a freeze
70
+ // see: "WebAssembly.instantiate(binary, info).then" inside wasm_loader.js
71
+ let language
72
+ if (wasmUint8ArrayOrFilePath instanceof Uint8Array) {
73
+ const hashString = Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", wasmUint8ArrayOrFilePath))).map((b) => b.toString(16).padStart(2, "0")).join("")
74
+ if (langCache.has(hashString)) {
75
+ language = langCache.get(hashString)
76
+ } else {
77
+ language = await Language.load(wasmUint8ArrayOrFilePath)
78
+ langCache.set(hashString, language)
79
+ }
80
+ } else {
81
+ language = await Language.load(wasmUint8ArrayOrFilePath)
82
+ }
83
+ const parser = new Parser()
84
+ parser.setLanguage(language)
85
+ parser.disableSoftNodes = disableSoftNodes
86
+ return parser
87
+ }