utopia-project 0.37.6 → 0.38.0

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 (104) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/context/documentation-guidelines.md +12 -7
  4. data/lib/utopia/project/import_map.rb +1 -0
  5. data/lib/utopia/project/version.rb +1 -1
  6. data/pages/_page.xnode +8 -13
  7. data/public/_components/@socketry/syntax/Syntax/CodeElement.js +293 -0
  8. data/public/_components/@socketry/syntax/Syntax/Errors.js +52 -0
  9. data/public/_components/@socketry/syntax/Syntax/Language/apache.js +49 -0
  10. data/public/_components/@socketry/syntax/Syntax/Language/applescript.js +157 -0
  11. data/public/_components/@socketry/syntax/Syntax/Language/assembly.js +42 -0
  12. data/public/_components/@socketry/syntax/Syntax/Language/bash-script.js +108 -0
  13. data/public/_components/@socketry/syntax/Syntax/Language/bash.js +32 -0
  14. data/public/_components/@socketry/syntax/Syntax/Language/basic.js +232 -0
  15. data/public/_components/@socketry/syntax/Syntax/Language/c++.js +1 -0
  16. data/public/_components/@socketry/syntax/Syntax/Language/c.js +1 -0
  17. data/public/_components/@socketry/syntax/Syntax/Language/clang.js +201 -0
  18. data/public/_components/@socketry/syntax/Syntax/Language/cpp.js +1 -0
  19. data/public/_components/@socketry/syntax/Syntax/Language/csharp.js +166 -0
  20. data/public/_components/@socketry/syntax/Syntax/Language/css.js +244 -0
  21. data/public/_components/@socketry/syntax/Syntax/Language/diff.js +24 -0
  22. data/public/_components/@socketry/syntax/Syntax/Language/go.js +135 -0
  23. data/public/_components/@socketry/syntax/Syntax/Language/haskell.js +110 -0
  24. data/public/_components/@socketry/syntax/Syntax/Language/html.js +69 -0
  25. data/public/_components/@socketry/syntax/Syntax/Language/io.js +68 -0
  26. data/public/_components/@socketry/syntax/Syntax/Language/java.js +134 -0
  27. data/public/_components/@socketry/syntax/Syntax/Language/javascript.js +89 -0
  28. data/public/_components/@socketry/syntax/Syntax/Language/json.js +36 -0
  29. data/public/_components/@socketry/syntax/Syntax/Language/lisp.js +38 -0
  30. data/public/_components/@socketry/syntax/Syntax/Language/lua.js +87 -0
  31. data/public/_components/@socketry/syntax/Syntax/Language/markdown.js +112 -0
  32. data/public/_components/@socketry/syntax/Syntax/Language/nginx.js +37 -0
  33. data/public/_components/@socketry/syntax/Syntax/Language/objective-c.js +1 -0
  34. data/public/_components/@socketry/syntax/Syntax/Language/ocaml.js +225 -0
  35. data/public/_components/@socketry/syntax/Syntax/Language/pascal.js +166 -0
  36. data/public/_components/@socketry/syntax/Syntax/Language/patch.js +2 -0
  37. data/public/_components/@socketry/syntax/Syntax/Language/perl5.js +317 -0
  38. data/public/_components/@socketry/syntax/Syntax/Language/php-script.js +112 -0
  39. data/public/_components/@socketry/syntax/Syntax/Language/php.js +18 -0
  40. data/public/_components/@socketry/syntax/Syntax/Language/plain.js +20 -0
  41. data/public/_components/@socketry/syntax/Syntax/Language/protobuf.js +77 -0
  42. data/public/_components/@socketry/syntax/Syntax/Language/python.js +208 -0
  43. data/public/_components/@socketry/syntax/Syntax/Language/ruby.js +124 -0
  44. data/public/_components/@socketry/syntax/Syntax/Language/scala.js +81 -0
  45. data/public/_components/@socketry/syntax/Syntax/Language/smalltalk.js +30 -0
  46. data/public/_components/@socketry/syntax/Syntax/Language/sql.js +865 -0
  47. data/public/_components/@socketry/syntax/Syntax/Language/super-collider.js +70 -0
  48. data/public/_components/@socketry/syntax/Syntax/Language/swift.js +176 -0
  49. data/public/_components/@socketry/syntax/Syntax/Language/xml.js +76 -0
  50. data/public/_components/@socketry/syntax/Syntax/Language/xrb.js +33 -0
  51. data/public/_components/@socketry/syntax/Syntax/Language/yaml.js +29 -0
  52. data/public/_components/@socketry/syntax/Syntax/Language.js +276 -0
  53. data/public/_components/@socketry/syntax/Syntax/Loader.js +78 -0
  54. data/public/_components/@socketry/syntax/Syntax/Match.js +546 -0
  55. data/public/_components/@socketry/syntax/Syntax/Rule.js +306 -0
  56. data/public/_components/@socketry/syntax/Syntax.js +356 -0
  57. data/public/_components/@socketry/syntax/license.md +21 -0
  58. data/public/_components/@socketry/syntax/package.json +43 -0
  59. data/public/_components/@socketry/syntax/readme.md +162 -0
  60. data/public/_components/@socketry/syntax/themes/base/apache.css +1 -0
  61. data/public/_components/@socketry/syntax/themes/base/applescript.css +1 -0
  62. data/public/_components/@socketry/syntax/themes/base/assembly.css +1 -0
  63. data/public/_components/@socketry/syntax/themes/base/bash.css +1 -0
  64. data/public/_components/@socketry/syntax/themes/base/basic.css +1 -0
  65. data/public/_components/@socketry/syntax/themes/base/c.css +1 -0
  66. data/public/_components/@socketry/syntax/themes/base/clang.css +0 -0
  67. data/public/_components/@socketry/syntax/themes/base/csharp.css +1 -0
  68. data/public/_components/@socketry/syntax/themes/base/css.css +22 -0
  69. data/public/_components/@socketry/syntax/themes/base/diff.css +48 -0
  70. data/public/_components/@socketry/syntax/themes/base/go.css +1 -0
  71. data/public/_components/@socketry/syntax/themes/base/haskell.css +1 -0
  72. data/public/_components/@socketry/syntax/themes/base/html.css +1 -0
  73. data/public/_components/@socketry/syntax/themes/base/io.css +1 -0
  74. data/public/_components/@socketry/syntax/themes/base/java.css +1 -0
  75. data/public/_components/@socketry/syntax/themes/base/javascript.css +1 -0
  76. data/public/_components/@socketry/syntax/themes/base/json.css +41 -0
  77. data/public/_components/@socketry/syntax/themes/base/lisp.css +1 -0
  78. data/public/_components/@socketry/syntax/themes/base/lua.css +1 -0
  79. data/public/_components/@socketry/syntax/themes/base/markdown.css +16 -0
  80. data/public/_components/@socketry/syntax/themes/base/nginx.css +1 -0
  81. data/public/_components/@socketry/syntax/themes/base/ocaml.css +1 -0
  82. data/public/_components/@socketry/syntax/themes/base/pascal.css +1 -0
  83. data/public/_components/@socketry/syntax/themes/base/perl5.css +1 -0
  84. data/public/_components/@socketry/syntax/themes/base/php-script.css +1 -0
  85. data/public/_components/@socketry/syntax/themes/base/php.css +1 -0
  86. data/public/_components/@socketry/syntax/themes/base/plain.css +1 -0
  87. data/public/_components/@socketry/syntax/themes/base/protobuf.css +1 -0
  88. data/public/_components/@socketry/syntax/themes/base/python.css +1 -0
  89. data/public/_components/@socketry/syntax/themes/base/ruby.css +23 -0
  90. data/public/_components/@socketry/syntax/themes/base/scala.css +3 -0
  91. data/public/_components/@socketry/syntax/themes/base/smalltalk.css +1 -0
  92. data/public/_components/@socketry/syntax/themes/base/sql.css +1 -0
  93. data/public/_components/@socketry/syntax/themes/base/super-collider.css +33 -0
  94. data/public/_components/@socketry/syntax/themes/base/swift.css +1 -0
  95. data/public/_components/@socketry/syntax/themes/base/syntax.css +63 -0
  96. data/public/_components/@socketry/syntax/themes/base/xml.css +1 -0
  97. data/public/_components/@socketry/syntax/themes/base/xrb.css +29 -0
  98. data/public/_components/@socketry/syntax/themes/base/yaml.css +1 -0
  99. data/public/_components/@socketry/syntax/themes/theming.md +233 -0
  100. data/public/_static/sidebar.js +55 -22
  101. data/public/_static/site.css +0 -4
  102. data.tar.gz.sig +0 -0
  103. metadata +94 -1
  104. metadata.gz.sig +0 -0
@@ -0,0 +1,78 @@
1
+ export class Loader {
2
+ #cache = new Map();
3
+ #pending = new Map();
4
+ #load;
5
+
6
+ /**
7
+ * Create a new loader
8
+ * @param {Function} load - Async function that resolves the key to a resource.
9
+ */
10
+ constructor(load) {
11
+ this.#load = load;
12
+ }
13
+
14
+ /**
15
+ * Load a resource with automatic deduplication and caching.
16
+ * @param {string|URL} key - Unique identifier for the resource.
17
+ * @returns {Promise<any>} The loaded resource.
18
+ */
19
+ async load(key) {
20
+ const id = key.toString();
21
+
22
+ // Return cached result if available:
23
+ if (this.#cache.has(id)) {
24
+ return this.#cache.get(id);
25
+ }
26
+
27
+ // Return pending promise if already fetching:
28
+ if (this.#pending.has(id)) {
29
+ return this.#pending.get(id);
30
+ }
31
+
32
+ // Start new fetch and track it:
33
+ const promise = (async () => {
34
+ try {
35
+ const result = await this.#load(this, key);
36
+ this.#cache.set(id, result);
37
+ return result;
38
+ } finally {
39
+ this.#pending.delete(id);
40
+ }
41
+ })();
42
+
43
+ this.#pending.set(id, promise);
44
+ return promise;
45
+ }
46
+
47
+ /**
48
+ * Check if a resource is cached
49
+ */
50
+ has(key) {
51
+ return this.#cache.has(key.toString());
52
+ }
53
+
54
+ /**
55
+ * Get a cached resource (synchronous)
56
+ */
57
+ get(key) {
58
+ return this.#cache.get(key.toString());
59
+ }
60
+
61
+ /**
62
+ * Set a resource in the cache
63
+ */
64
+ set(key, value) {
65
+ this.#cache.set(key.toString(), value);
66
+ return value;
67
+ }
68
+
69
+ /**
70
+ * Clear the cache
71
+ */
72
+ clear() {
73
+ this.#cache.clear();
74
+ this.#pending.clear();
75
+ }
76
+ }
77
+
78
+ export default Loader;
@@ -0,0 +1,546 @@
1
+ export class Match {
2
+ // Public properties
3
+ offset;
4
+ endOffset;
5
+ length;
6
+ expression;
7
+ type;
8
+ value;
9
+ children = [];
10
+ parent = null;
11
+ // A pointer to the next match if a match is bisected.
12
+ next = null;
13
+ complete;
14
+
15
+ constructor(offset, length, expression, value) {
16
+ this.offset = offset;
17
+ this.endOffset = offset + length;
18
+ this.length = length;
19
+
20
+ // Handle expression being either a string (type) or an object
21
+ if (typeof expression === 'string') {
22
+ this.type = expression;
23
+ this.expression = {type: expression};
24
+ } else {
25
+ this.expression = expression;
26
+ this.type = expression?.type;
27
+ }
28
+
29
+ this.value = value;
30
+ }
31
+
32
+ /**
33
+ * Shifts an entire tree forward or backwards.
34
+ */
35
+ shift(offset, text) {
36
+ this.adjust(offset, null, text);
37
+
38
+ for (const child of this.children) {
39
+ child.shift(offset, text);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Adjust the current match to have different offset and length.
45
+ */
46
+ adjust(offset, length, text) {
47
+ this.offset += offset;
48
+ this.endOffset += offset;
49
+
50
+ if (length) {
51
+ this.length = length;
52
+ this.endOffset = this.offset + length;
53
+ }
54
+
55
+ if (text) {
56
+ this.value = text.substr(this.offset, this.length);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Sort helper for sorting matches in forward order
62
+ */
63
+ static sort(a, b) {
64
+ return a.offset - b.offset || b.length - a.length;
65
+ }
66
+
67
+ /**
68
+ * Is the given match contained in the range of the parent match?
69
+ */
70
+ contains(match) {
71
+ return match.offset >= this.offset && match.endOffset <= this.endOffset;
72
+ }
73
+
74
+ /**
75
+ * Default reduce callback - converts nodes to DOM
76
+ */
77
+ static defaultReduceCallback(node, container) {
78
+ // We avoid using complex DOM manipulation for performance
79
+ if (typeof node === 'string') {
80
+ node = document.createTextNode(node);
81
+ }
82
+
83
+ container.appendChild(node);
84
+ }
85
+
86
+ /**
87
+ * Convert a tree of matches into DOM nodes
88
+ */
89
+ reduce(append, process) {
90
+ let start = this.offset;
91
+ let container = null;
92
+
93
+ if (this.expression && this.expression.element) {
94
+ container = this.expression.element.cloneNode(false);
95
+ } else {
96
+ container = document.createElement('span');
97
+ }
98
+
99
+ append = append || Match.defaultReduceCallback;
100
+
101
+ if (this.expression && this.expression.type) {
102
+ if (container.className.length > 0) {
103
+ container.className += ' ';
104
+ }
105
+
106
+ container.className += this.expression.type;
107
+ }
108
+
109
+ for (const child of this.children) {
110
+ const end = child.offset;
111
+
112
+ if (child.offset < this.offset) {
113
+ console.warn(
114
+ 'Syntax Warning: Offset of child',
115
+ child,
116
+ 'is before offset of parent',
117
+ this
118
+ );
119
+ }
120
+
121
+ const text = this.value.substr(start - this.offset, end - start);
122
+
123
+ append(text, container);
124
+ append(child.reduce(append, process), container);
125
+
126
+ start = child.endOffset;
127
+ }
128
+
129
+ if (start === this.offset) {
130
+ append(this.value, container);
131
+ } else if (start < this.endOffset) {
132
+ append(
133
+ this.value.substr(start - this.offset, this.endOffset - start),
134
+ container
135
+ );
136
+ } else if (start > this.endOffset) {
137
+ console.warn(
138
+ 'Syntax Warning: Start position ' +
139
+ start +
140
+ ' exceeds end of value ' +
141
+ this.endOffset
142
+ );
143
+ }
144
+
145
+ if (process) {
146
+ container = process(container, this);
147
+ }
148
+
149
+ return container;
150
+ }
151
+
152
+ /**
153
+ * Main nesting check - can a match contain the given match?
154
+ */
155
+ canContain(match) {
156
+ // This is a special conditional for explicitly added ranges by the user.
157
+ if (match.expression.force) {
158
+ return true;
159
+ }
160
+
161
+ // Can't add anything into complete trees.
162
+ if (this.complete) {
163
+ return false;
164
+ }
165
+
166
+ // match.expression.only will be checked on insertion using this.canHaveChild(match)
167
+ if (match.expression.only) {
168
+ return true;
169
+ }
170
+
171
+ // If allow is undefined, default behaviour is no children.
172
+ if (typeof this.expression.allow === 'undefined') {
173
+ return false;
174
+ }
175
+
176
+ // false if {disallow: [..., type, ...]}
177
+ if (
178
+ Array.isArray(this.expression.disallow) &&
179
+ this.expression.disallow.includes(match.expression.type)
180
+ ) {
181
+ return false;
182
+ }
183
+
184
+ // true if {allow: '*'}
185
+ if (this.expression.allow === '*') {
186
+ return true;
187
+ }
188
+
189
+ // true if {allow: [..., type, ...]}
190
+ if (
191
+ Array.isArray(this.expression.allow) &&
192
+ this.expression.allow.includes(match.expression.type)
193
+ ) {
194
+ return true;
195
+ }
196
+
197
+ return false;
198
+ }
199
+
200
+ /**
201
+ * Return true if the given match can be spliced in as a child.
202
+ */
203
+ canHaveChild(match) {
204
+ const only = match.expression.only;
205
+
206
+ if (only) {
207
+ let cur = this;
208
+
209
+ while (cur !== null) {
210
+ if (only.includes(cur.expression.type)) {
211
+ return true;
212
+ }
213
+
214
+ cur = cur.parent;
215
+
216
+ // We don't traverse into other trees.
217
+ if (cur && cur.complete) {
218
+ break;
219
+ }
220
+ }
221
+
222
+ return false;
223
+ }
224
+
225
+ return true;
226
+ }
227
+
228
+ /**
229
+ * Add a child into the list of children for a given match.
230
+ */
231
+ #splice(i, match) {
232
+ if (this.canHaveChild(match)) {
233
+ this.children.splice(i, 0, match);
234
+ match.parent = this;
235
+
236
+ // For matches added using tags.
237
+ if (!match.expression.owner) {
238
+ match.expression.owner = this.expression.owner;
239
+ }
240
+
241
+ return this;
242
+ } else {
243
+ return null;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Insert a match, potentially splitting the tree to fit.
249
+ */
250
+ insert(match, whole) {
251
+ if (!this.contains(match)) {
252
+ return null;
253
+ }
254
+
255
+ if (whole) {
256
+ let top = this,
257
+ i = 0;
258
+ while (i < top.children.length) {
259
+ if (top.children[i].contains(match)) {
260
+ top = top.children[i];
261
+ i = 0;
262
+ } else {
263
+ i += 1;
264
+ }
265
+ }
266
+
267
+ return top.#insertWhole(match);
268
+ } else {
269
+ return this.#insert(match);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Insert a whole match, splitting children as needed.
275
+ */
276
+ #insertWhole(match) {
277
+ const parts = this.bisectAtOffsets([match.offset, match.endOffset]);
278
+ this.children = [];
279
+
280
+ if (parts[0]) {
281
+ this.children = this.children.concat(parts[0].children);
282
+ }
283
+
284
+ if (parts[1]) {
285
+ match.children = [];
286
+
287
+ // Update the match's expression based on the current position in the tree:
288
+ if (this.expression && this.expression.owner) {
289
+ match.expression =
290
+ this.expression.owner.getRuleForType(match.expression.type) ||
291
+ match.expression;
292
+ }
293
+
294
+ for (const child of parts[1].children) {
295
+ if (match.canContain(child)) {
296
+ match.children.push(child);
297
+ }
298
+ }
299
+
300
+ this.children.push(match);
301
+ }
302
+
303
+ if (parts[2]) {
304
+ this.children = this.children.concat(parts[2].children);
305
+ }
306
+
307
+ return this;
308
+ }
309
+
310
+ /**
311
+ * Insert at end - optimized for sorted insertion.
312
+ */
313
+ insertAtEnd(match) {
314
+ if (!this.contains(match)) {
315
+ console.error('Syntax Error: Child is not contained in parent node!');
316
+ return null;
317
+ }
318
+
319
+ if (!this.canContain(match)) {
320
+ return null;
321
+ }
322
+
323
+ if (this.children.length > 0) {
324
+ const i = this.children.length - 1;
325
+ const child = this.children[i];
326
+
327
+ if (match.offset < child.offset) {
328
+ // Displacement: Before or LHS Overlap
329
+ if (match.force) {
330
+ return this.#insert(match);
331
+ } else {
332
+ return null;
333
+ }
334
+ } else if (match.offset < child.endOffset) {
335
+ if (match.endOffset <= child.endOffset) {
336
+ // Displacement: Contains
337
+ return child.insertAtEnd(match);
338
+ } else {
339
+ // Displacement: RHS Overlap
340
+ if (match.force) {
341
+ return this.#insert(match);
342
+ } else {
343
+ return null;
344
+ }
345
+ }
346
+ } else {
347
+ // Displacement: After
348
+ return this.#splice(i + 1, match);
349
+ }
350
+ } else {
351
+ // Displacement: Contains [but currently no children]
352
+ return this.#splice(0, match);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * General insertion function that splits match over children.
358
+ */
359
+ #insert(match) {
360
+ if (this.children.length === 0) {
361
+ return this.#splice(0, match);
362
+ }
363
+
364
+ for (let i = 0; i < this.children.length; i += 1) {
365
+ const child = this.children[i];
366
+
367
+ // If the match ends before this child, it must be before it.
368
+ if (match.endOffset <= child.offset) {
369
+ return this.#splice(i, match);
370
+ }
371
+
372
+ // If the match starts after this child, we continue.
373
+ if (match.offset >= child.endOffset) {
374
+ continue;
375
+ }
376
+
377
+ // First, the easiest case:
378
+ if (child.contains(match)) {
379
+ return child.#insert(match);
380
+ }
381
+
382
+ const parts = match.bisectAtOffsets([child.offset, child.endOffset]);
383
+
384
+ if (parts[0]) {
385
+ this.#splice(i, parts[0]);
386
+ }
387
+
388
+ if (parts[1]) {
389
+ child.insert(parts[1]);
390
+ }
391
+
392
+ // Continue insertion at this level with remainder.
393
+ if (parts[2]) {
394
+ match = parts[2];
395
+ } else {
396
+ return this;
397
+ }
398
+ }
399
+
400
+ // If we got this far, insert at the end.
401
+ this.#splice(this.children.length, match);
402
+ }
403
+
404
+ /**
405
+ * Recursively bisect the tree at given offsets.
406
+ */
407
+ bisectAtOffsets(splits) {
408
+ const parts = [];
409
+ let start = this.offset;
410
+ let prev = null;
411
+ const children = [...this.children];
412
+
413
+ // Copy the array so we can modify it.
414
+ splits = splits.slice(0);
415
+
416
+ // We need to split including the last part.
417
+ splits.push(this.endOffset);
418
+
419
+ splits.sort((a, b) => a - b);
420
+
421
+ for (let i = 0; i < splits.length; i += 1) {
422
+ const offset = splits[i];
423
+
424
+ if (offset > this.endOffset) {
425
+ break;
426
+ }
427
+
428
+ if (offset < this.offset || offset - start === 0) {
429
+ parts.push(null);
430
+ start = offset;
431
+ continue;
432
+ }
433
+
434
+ if (start < this.offset) {
435
+ start = this.offset;
436
+ }
437
+
438
+ const match = new Match(start, offset - start, this.expression);
439
+ match.value = this.value.substr(start - this.offset, match.length);
440
+
441
+ if (prev) {
442
+ prev.next = match;
443
+ }
444
+
445
+ prev = match;
446
+ start = match.endOffset;
447
+ parts.push(match);
448
+ }
449
+
450
+ splits.length = parts.length;
451
+
452
+ for (let i = 0; i < parts.length; i += 1) {
453
+ if (parts[i] === null) {
454
+ continue;
455
+ }
456
+
457
+ while (children.length > 0) {
458
+ if (children[0].endOffset <= parts[i].endOffset) {
459
+ parts[i].children.push(children.shift());
460
+ } else {
461
+ break;
462
+ }
463
+ }
464
+
465
+ if (children.length) {
466
+ if (children[0].offset < parts[i].endOffset) {
467
+ const children_parts = children.shift().bisectAtOffsets(splits);
468
+ let j = 0;
469
+
470
+ for (; j < children_parts.length; j += 1) {
471
+ if (children_parts[j] === null) continue;
472
+
473
+ parts[i + j].children.push(children_parts[j]);
474
+ }
475
+
476
+ i += children_parts.length - 2;
477
+ splits.splice(0, children_parts.length - 2);
478
+ }
479
+ }
480
+
481
+ splits.shift();
482
+ }
483
+
484
+ if (children.length) {
485
+ console.error(
486
+ 'Syntax Error: Children nodes not consumed',
487
+ children.length,
488
+ ' remaining!'
489
+ );
490
+ }
491
+
492
+ return parts;
493
+ }
494
+
495
+ /**
496
+ * Split a match at points that match a specific pattern.
497
+ */
498
+ split(pattern) {
499
+ const splits = [];
500
+
501
+ // Clone the regex and ensure it is global
502
+ if (!pattern.global) {
503
+ pattern = new RegExp(pattern.source, pattern.flags + 'g');
504
+ }
505
+
506
+ let match;
507
+ while ((match = pattern.exec(this.value)) !== null) {
508
+ splits.push(pattern.lastIndex);
509
+ }
510
+
511
+ const matches = this.bisectAtOffsets(splits);
512
+
513
+ // Remove any null placeholders.
514
+ return matches.filter(n => n);
515
+ }
516
+
517
+ /**
518
+ * Split into lines with indent/text structure.
519
+ */
520
+ splitLines() {
521
+ const lines = this.split(/\n/g);
522
+
523
+ for (let i = 0; i < lines.length; i += 1) {
524
+ const line = lines[i];
525
+ const indentOffset = line.value.search(/\S/);
526
+
527
+ const top = new Match(line.offset, line.length, line.expression, line.value);
528
+
529
+ if (indentOffset > 0) {
530
+ const parts = line.bisectAtOffsets([line.offset + indentOffset]);
531
+ top.children = parts;
532
+ parts[0].expression = {type: 'indent'};
533
+ parts[1].expression = {type: 'text'};
534
+ } else {
535
+ line.expression = {type: 'text'};
536
+ top.children = [line];
537
+ }
538
+
539
+ lines[i] = top;
540
+ }
541
+
542
+ return lines;
543
+ }
544
+ }
545
+
546
+ export default Match;