clarity-pattern-parser 11.0.21 → 11.0.23
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.
- package/dist/grammar/Grammar.d.ts +0 -1
- package/dist/index.browser.js +280 -256
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +280 -256
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +280 -256
- package/dist/index.js.map +1 -1
- package/dist/patterns/Sequence.d.ts +2 -1
- package/dist/patterns/generate_error_message.d.ts +3 -0
- package/package.json +1 -1
- package/src/grammar/Grammar.ts +11 -22
- package/src/patterns/Literal.ts +4 -0
- package/src/patterns/Reference.ts +3 -1
- package/src/patterns/Sequence.test.ts +11 -0
- package/src/patterns/Sequence.ts +13 -4
- package/src/patterns/generate_error_message.ts +33 -0
- package/src/query/query.test.ts +174 -0
- package/src/query/query.ts +152 -16
- package/src/query/selector.test.ts +262 -0
- package/src/query/selector.ts +280 -0
- package/src/query/selector_parser.ts +44 -3
package/dist/index.esm.js
CHANGED
|
@@ -600,6 +600,9 @@ class Literal {
|
|
|
600
600
|
_tryToParse(cursor) {
|
|
601
601
|
let passed = false;
|
|
602
602
|
const literalRuneLength = this._runes.length;
|
|
603
|
+
if (!cursor.hasNext) {
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
603
606
|
for (let i = 0; i < literalRuneLength; i++) {
|
|
604
607
|
const literalRune = this._runes[i];
|
|
605
608
|
const cursorRune = cursor.currentChar;
|
|
@@ -865,6 +868,7 @@ class Reference {
|
|
|
865
868
|
}
|
|
866
869
|
_cacheAncestors(id) {
|
|
867
870
|
if (!this._cachedAncestors) {
|
|
871
|
+
this._cachedAncestors = true;
|
|
868
872
|
let pattern = this.parent;
|
|
869
873
|
while (pattern != null) {
|
|
870
874
|
if (pattern.id === id) {
|
|
@@ -877,7 +881,7 @@ class Reference {
|
|
|
877
881
|
_isBeyondRecursiveAllowance() {
|
|
878
882
|
let depth = 0;
|
|
879
883
|
for (let pattern of this._recursiveAncestors) {
|
|
880
|
-
if (pattern.
|
|
884
|
+
if (pattern.startedOnIndex === this.startedOnIndex) {
|
|
881
885
|
depth++;
|
|
882
886
|
if (depth > 0) {
|
|
883
887
|
return true;
|
|
@@ -1763,7 +1767,7 @@ class Sequence {
|
|
|
1763
1767
|
else {
|
|
1764
1768
|
// We are at the end of the text, it may still be valid, if all the
|
|
1765
1769
|
// following patterns are optional.
|
|
1766
|
-
if (this.
|
|
1770
|
+
if (this._areRemainingPatternsOptional(i)) {
|
|
1767
1771
|
passed = true;
|
|
1768
1772
|
break;
|
|
1769
1773
|
}
|
|
@@ -1782,7 +1786,7 @@ class Sequence {
|
|
|
1782
1786
|
else {
|
|
1783
1787
|
// If we don't have any results from what we parsed then record error.
|
|
1784
1788
|
const lastNode = this.getLastValidNode();
|
|
1785
|
-
if (lastNode === null) {
|
|
1789
|
+
if (lastNode === null && !this._areAllPatternsOptional()) {
|
|
1786
1790
|
cursor.recordErrorAt(this._firstIndex, cursor.index, this);
|
|
1787
1791
|
break;
|
|
1788
1792
|
}
|
|
@@ -1806,7 +1810,10 @@ class Sequence {
|
|
|
1806
1810
|
}
|
|
1807
1811
|
return nodes[nodes.length - 1];
|
|
1808
1812
|
}
|
|
1809
|
-
|
|
1813
|
+
_areAllPatternsOptional() {
|
|
1814
|
+
return this._areRemainingPatternsOptional(-1);
|
|
1815
|
+
}
|
|
1816
|
+
_areRemainingPatternsOptional(fromIndex) {
|
|
1810
1817
|
const startOnIndex = fromIndex + 1;
|
|
1811
1818
|
const length = this._children.length;
|
|
1812
1819
|
for (let i = startOnIndex; i < length; i++) {
|
|
@@ -1819,6 +1826,10 @@ class Sequence {
|
|
|
1819
1826
|
}
|
|
1820
1827
|
createNode(cursor) {
|
|
1821
1828
|
const children = filterOutNull(this._nodes);
|
|
1829
|
+
if (children.length === 0) {
|
|
1830
|
+
cursor.moveTo(this._firstIndex);
|
|
1831
|
+
return null;
|
|
1832
|
+
}
|
|
1822
1833
|
const lastIndex = children[children.length - 1].lastIndex;
|
|
1823
1834
|
cursor.moveTo(lastIndex);
|
|
1824
1835
|
return new Node("sequence", this._name, this._firstIndex, lastIndex, children);
|
|
@@ -2362,239 +2373,6 @@ class Not {
|
|
|
2362
2373
|
}
|
|
2363
2374
|
}
|
|
2364
2375
|
|
|
2365
|
-
const defaultOptions = { greedyPatternNames: [], customTokens: {} };
|
|
2366
|
-
class AutoComplete {
|
|
2367
|
-
constructor(pattern, options = defaultOptions) {
|
|
2368
|
-
this._pattern = pattern;
|
|
2369
|
-
this._options = options;
|
|
2370
|
-
this._text = "";
|
|
2371
|
-
}
|
|
2372
|
-
suggestForWithCursor(cursor) {
|
|
2373
|
-
cursor.moveTo(0);
|
|
2374
|
-
this._cursor = cursor;
|
|
2375
|
-
this._text = cursor.text;
|
|
2376
|
-
this._cursor.startRecording();
|
|
2377
|
-
if (cursor.length === 0) {
|
|
2378
|
-
return {
|
|
2379
|
-
isComplete: false,
|
|
2380
|
-
options: this._createSuggestionsFromRoot(),
|
|
2381
|
-
error: new ParseError(0, 0, this._pattern),
|
|
2382
|
-
errorAtIndex: 0,
|
|
2383
|
-
cursor,
|
|
2384
|
-
ast: null
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
let errorAtIndex = null;
|
|
2388
|
-
let error = null;
|
|
2389
|
-
const ast = this._pattern.parse(this._cursor);
|
|
2390
|
-
const isComplete = (ast === null || ast === void 0 ? void 0 : ast.value) === this._text;
|
|
2391
|
-
const options = this._getAllOptions();
|
|
2392
|
-
if (!isComplete && options.length > 0 && !this._cursor.hasError) {
|
|
2393
|
-
const startIndex = options.reduce((lowestIndex, o) => {
|
|
2394
|
-
return Math.min(lowestIndex, o.startIndex);
|
|
2395
|
-
}, Infinity);
|
|
2396
|
-
const lastIndex = cursor.getLastIndex() + 1;
|
|
2397
|
-
error = new ParseError(startIndex, lastIndex, this._pattern);
|
|
2398
|
-
errorAtIndex = startIndex;
|
|
2399
|
-
}
|
|
2400
|
-
else if (!isComplete && options.length === 0 && ast != null) {
|
|
2401
|
-
const startIndex = ast.endIndex;
|
|
2402
|
-
const lastIndex = cursor.getLastIndex() + 1;
|
|
2403
|
-
error = new ParseError(startIndex, lastIndex, this._pattern);
|
|
2404
|
-
errorAtIndex = startIndex;
|
|
2405
|
-
}
|
|
2406
|
-
else if (!isComplete && this._cursor.hasError && this._cursor.furthestError != null) {
|
|
2407
|
-
errorAtIndex = this.getFurthestPosition(cursor);
|
|
2408
|
-
error = new ParseError(errorAtIndex, errorAtIndex, this._pattern);
|
|
2409
|
-
}
|
|
2410
|
-
return {
|
|
2411
|
-
isComplete: isComplete,
|
|
2412
|
-
options: options,
|
|
2413
|
-
error,
|
|
2414
|
-
errorAtIndex,
|
|
2415
|
-
cursor: cursor,
|
|
2416
|
-
ast,
|
|
2417
|
-
};
|
|
2418
|
-
}
|
|
2419
|
-
getFurthestPosition(cursor) {
|
|
2420
|
-
const furthestError = cursor.furthestError;
|
|
2421
|
-
const furthestMatch = cursor.allMatchedNodes[cursor.allMatchedNodes.length - 1];
|
|
2422
|
-
if (furthestError && furthestMatch) {
|
|
2423
|
-
if (furthestError.lastIndex > furthestMatch.endIndex) {
|
|
2424
|
-
return furthestMatch.endIndex;
|
|
2425
|
-
}
|
|
2426
|
-
else {
|
|
2427
|
-
return furthestError.lastIndex;
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
if (furthestError == null && furthestMatch != null) {
|
|
2431
|
-
return furthestMatch.endIndex;
|
|
2432
|
-
}
|
|
2433
|
-
if (furthestMatch == null && furthestError != null) {
|
|
2434
|
-
return furthestError.lastIndex;
|
|
2435
|
-
}
|
|
2436
|
-
return 0;
|
|
2437
|
-
}
|
|
2438
|
-
suggestFor(text) {
|
|
2439
|
-
return this.suggestForWithCursor(new Cursor(text));
|
|
2440
|
-
}
|
|
2441
|
-
_getAllOptions() {
|
|
2442
|
-
const errorMatches = this._getOptionsFromErrors();
|
|
2443
|
-
const leafMatches = this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
|
|
2444
|
-
const finalResults = [];
|
|
2445
|
-
[...leafMatches, ...errorMatches].forEach(m => {
|
|
2446
|
-
const index = finalResults.findIndex(f => m.text === f.text);
|
|
2447
|
-
if (index === -1) {
|
|
2448
|
-
finalResults.push(m);
|
|
2449
|
-
}
|
|
2450
|
-
});
|
|
2451
|
-
return finalResults;
|
|
2452
|
-
}
|
|
2453
|
-
_getOptionsFromErrors() {
|
|
2454
|
-
// These errored because the length of the string.
|
|
2455
|
-
const errors = this._cursor.errors.filter(e => e.lastIndex === this._cursor.length);
|
|
2456
|
-
const suggestions = errors.map(e => {
|
|
2457
|
-
const tokens = this._getTokensForPattern(e.pattern);
|
|
2458
|
-
const adjustedTokens = new Set();
|
|
2459
|
-
const currentText = this._cursor.getChars(e.startIndex, e.lastIndex);
|
|
2460
|
-
tokens.forEach((token) => {
|
|
2461
|
-
if (token.startsWith(currentText) && token.length > currentText.length) {
|
|
2462
|
-
const difference = token.length - currentText.length;
|
|
2463
|
-
const suggestedText = token.slice(-difference);
|
|
2464
|
-
adjustedTokens.add(suggestedText);
|
|
2465
|
-
}
|
|
2466
|
-
});
|
|
2467
|
-
return Array.from(adjustedTokens).map(t => {
|
|
2468
|
-
return {
|
|
2469
|
-
text: t,
|
|
2470
|
-
startIndex: e.lastIndex,
|
|
2471
|
-
};
|
|
2472
|
-
});
|
|
2473
|
-
});
|
|
2474
|
-
return suggestions.flat();
|
|
2475
|
-
}
|
|
2476
|
-
_createSuggestionsFromRoot() {
|
|
2477
|
-
const suggestions = [];
|
|
2478
|
-
const tokens = this._pattern.getTokens();
|
|
2479
|
-
for (const token of tokens) {
|
|
2480
|
-
if (suggestions.findIndex(s => s.text === token) === -1) {
|
|
2481
|
-
suggestions.push(this._createSuggestion("", token));
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
return suggestions;
|
|
2485
|
-
}
|
|
2486
|
-
_createSuggestionsFromMatch(match) {
|
|
2487
|
-
if (match.pattern == null) {
|
|
2488
|
-
return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
|
|
2489
|
-
}
|
|
2490
|
-
const leafPattern = match.pattern;
|
|
2491
|
-
const parent = match.pattern.parent;
|
|
2492
|
-
if (parent !== null && match.node != null) {
|
|
2493
|
-
const patterns = leafPattern.getNextPatterns();
|
|
2494
|
-
const tokens = patterns.reduce((acc, pattern) => {
|
|
2495
|
-
acc.push(...this._getTokensForPattern(pattern));
|
|
2496
|
-
return acc;
|
|
2497
|
-
}, []);
|
|
2498
|
-
return this._createSuggestions(match.node.lastIndex, tokens);
|
|
2499
|
-
}
|
|
2500
|
-
else {
|
|
2501
|
-
return [];
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
_getTokensForPattern(pattern) {
|
|
2505
|
-
const augmentedTokens = this._getAugmentedTokens(pattern);
|
|
2506
|
-
if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
|
|
2507
|
-
const nextPatterns = pattern.getNextPatterns();
|
|
2508
|
-
const tokens = [];
|
|
2509
|
-
const nextPatternTokens = nextPatterns.reduce((acc, pattern) => {
|
|
2510
|
-
acc.push(...this._getTokensForPattern(pattern));
|
|
2511
|
-
return acc;
|
|
2512
|
-
}, []);
|
|
2513
|
-
for (let token of augmentedTokens) {
|
|
2514
|
-
for (let nextPatternToken of nextPatternTokens) {
|
|
2515
|
-
tokens.push(token + nextPatternToken);
|
|
2516
|
-
}
|
|
2517
|
-
}
|
|
2518
|
-
return tokens;
|
|
2519
|
-
}
|
|
2520
|
-
else {
|
|
2521
|
-
return augmentedTokens;
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
_getAugmentedTokens(pattern) {
|
|
2525
|
-
const customTokensMap = this._options.customTokens || {};
|
|
2526
|
-
const leafPatterns = pattern.getPatterns();
|
|
2527
|
-
const tokens = customTokensMap[pattern.name] || [];
|
|
2528
|
-
leafPatterns.forEach(p => {
|
|
2529
|
-
const augmentedTokens = customTokensMap[p.name] || [];
|
|
2530
|
-
tokens.push(...p.getTokens(), ...augmentedTokens);
|
|
2531
|
-
});
|
|
2532
|
-
return tokens;
|
|
2533
|
-
}
|
|
2534
|
-
_createSuggestions(lastIndex, tokens) {
|
|
2535
|
-
let substring = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
|
|
2536
|
-
const suggestionStrings = [];
|
|
2537
|
-
const options = [];
|
|
2538
|
-
for (const token of tokens) {
|
|
2539
|
-
const suggestion = substring + token;
|
|
2540
|
-
const startsWith = suggestion.startsWith(substring);
|
|
2541
|
-
const alreadyExist = suggestionStrings.includes(suggestion);
|
|
2542
|
-
const isSameAsText = suggestion === this._text;
|
|
2543
|
-
if (startsWith && !alreadyExist && !isSameAsText) {
|
|
2544
|
-
suggestionStrings.push(suggestion);
|
|
2545
|
-
options.push(this._createSuggestion(this._cursor.text, suggestion));
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
const reducedOptions = getFurthestOptions(options);
|
|
2549
|
-
reducedOptions.sort((a, b) => a.text.localeCompare(b.text));
|
|
2550
|
-
return reducedOptions;
|
|
2551
|
-
}
|
|
2552
|
-
_createSuggestion(fullText, suggestion) {
|
|
2553
|
-
const furthestMatch = findMatchIndex(suggestion, fullText);
|
|
2554
|
-
const text = suggestion.slice(furthestMatch);
|
|
2555
|
-
return {
|
|
2556
|
-
text: text,
|
|
2557
|
-
startIndex: furthestMatch,
|
|
2558
|
-
};
|
|
2559
|
-
}
|
|
2560
|
-
static suggestFor(text, pattern, options) {
|
|
2561
|
-
return new AutoComplete(pattern, options).suggestFor(text);
|
|
2562
|
-
}
|
|
2563
|
-
static suggestForWithCursor(cursor, pattern, options) {
|
|
2564
|
-
return new AutoComplete(pattern, options).suggestForWithCursor(cursor);
|
|
2565
|
-
}
|
|
2566
|
-
}
|
|
2567
|
-
function findMatchIndex(str1, str2) {
|
|
2568
|
-
let matchCount = 0;
|
|
2569
|
-
let minLength = str1.length;
|
|
2570
|
-
if (str2.length < minLength) {
|
|
2571
|
-
minLength = str2.length;
|
|
2572
|
-
}
|
|
2573
|
-
for (let i = 0; i < minLength; i++) {
|
|
2574
|
-
if (str1[i] === str2[i]) {
|
|
2575
|
-
matchCount++;
|
|
2576
|
-
}
|
|
2577
|
-
else {
|
|
2578
|
-
break;
|
|
2579
|
-
}
|
|
2580
|
-
}
|
|
2581
|
-
return matchCount;
|
|
2582
|
-
}
|
|
2583
|
-
function getFurthestOptions(options) {
|
|
2584
|
-
let furthestOptions = [];
|
|
2585
|
-
let furthestIndex = -1;
|
|
2586
|
-
for (const option of options) {
|
|
2587
|
-
if (option.startIndex > furthestIndex) {
|
|
2588
|
-
furthestIndex = option.startIndex;
|
|
2589
|
-
furthestOptions = [];
|
|
2590
|
-
}
|
|
2591
|
-
if (option.startIndex === furthestIndex) {
|
|
2592
|
-
furthestOptions.push(option);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
return furthestOptions;
|
|
2596
|
-
}
|
|
2597
|
-
|
|
2598
2376
|
let contextId = 0;
|
|
2599
2377
|
class Context {
|
|
2600
2378
|
get id() {
|
|
@@ -3357,6 +3135,30 @@ class RightAssociated {
|
|
|
3357
3135
|
}
|
|
3358
3136
|
}
|
|
3359
3137
|
|
|
3138
|
+
function generateErrorMessage(pattern, cursor) {
|
|
3139
|
+
const furthestMatch = cursor.leafMatch;
|
|
3140
|
+
if (furthestMatch == null || furthestMatch.node == null || furthestMatch.pattern == null) {
|
|
3141
|
+
const suggestions = cleanSuggestions(pattern.getTokens()).join(", ");
|
|
3142
|
+
return `Error at line 1, column 1. Hint: ${suggestions}`;
|
|
3143
|
+
}
|
|
3144
|
+
const endIndex = furthestMatch.node.endIndex;
|
|
3145
|
+
if (endIndex === 0) {
|
|
3146
|
+
const suggestions = cleanSuggestions(pattern.getTokens()).join(", ");
|
|
3147
|
+
return `Error at line 1, column 1. Hint: ${suggestions}`;
|
|
3148
|
+
}
|
|
3149
|
+
const lastPattern = furthestMatch.pattern;
|
|
3150
|
+
const suggestions = cleanSuggestions(lastPattern.getTokens());
|
|
3151
|
+
const strUpToError = cursor.getChars(0, endIndex);
|
|
3152
|
+
const lines = strUpToError.split("\n");
|
|
3153
|
+
const lastLine = lines[lines.length - 1];
|
|
3154
|
+
const line = lines.length;
|
|
3155
|
+
const column = lastLine.length;
|
|
3156
|
+
return `Error at line ${line}, column ${column}. Hint: ${suggestions}`;
|
|
3157
|
+
}
|
|
3158
|
+
function cleanSuggestions(suggestions) {
|
|
3159
|
+
return suggestions.map(s => s.trim()).filter(s => s.length > 0);
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3360
3162
|
let anonymousIndexId = 0;
|
|
3361
3163
|
const patternNodes = {
|
|
3362
3164
|
"literal": true,
|
|
@@ -3384,9 +3186,6 @@ class Grammar {
|
|
|
3384
3186
|
this._originResource = (options === null || options === void 0 ? void 0 : options.originResource) == null ? null : options.originResource;
|
|
3385
3187
|
this._resolveImport = options.resolveImport == null ? defaultImportResolver : options.resolveImport;
|
|
3386
3188
|
this._parseContext = new ParseContext(this._params);
|
|
3387
|
-
this._autoComplete = new AutoComplete(grammar, {
|
|
3388
|
-
greedyPatternNames: ["spaces", "optional-spaces", "whitespace", "new-line"],
|
|
3389
|
-
});
|
|
3390
3189
|
}
|
|
3391
3190
|
import(path) {
|
|
3392
3191
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -3426,19 +3225,11 @@ class Grammar {
|
|
|
3426
3225
|
return patterns;
|
|
3427
3226
|
}
|
|
3428
3227
|
_tryToParse(expression) {
|
|
3429
|
-
const { ast, cursor
|
|
3430
|
-
if (
|
|
3431
|
-
const
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
const expectedTexts = "'" + options.map(o => {
|
|
3435
|
-
const startText = text.slice(Math.max(o.startIndex - 10), o.startIndex);
|
|
3436
|
-
return startText + o.text;
|
|
3437
|
-
}).join("' or '") + "'";
|
|
3438
|
-
const message = `[Parse Error] Found: '${foundText}', expected: ${expectedTexts}.`;
|
|
3439
|
-
throw new Error(message);
|
|
3440
|
-
}
|
|
3441
|
-
// If it is complete it will always have a node. So we have to cast it.
|
|
3228
|
+
const { ast, cursor } = grammar.exec(expression, true);
|
|
3229
|
+
if (ast == null) {
|
|
3230
|
+
const message = generateErrorMessage(grammar, cursor);
|
|
3231
|
+
throw new Error(`[Invalid Grammar] ${message}`);
|
|
3232
|
+
}
|
|
3442
3233
|
return ast;
|
|
3443
3234
|
}
|
|
3444
3235
|
_hasImports(ast) {
|
|
@@ -3574,9 +3365,9 @@ class Grammar {
|
|
|
3574
3365
|
const firstChild = pattern.children[0];
|
|
3575
3366
|
const lastChild = pattern.children[pattern.children.length - 1];
|
|
3576
3367
|
const isLongEnough = pattern.children.length >= 2;
|
|
3577
|
-
return
|
|
3578
|
-
(firstChild.name === name
|
|
3579
|
-
|
|
3368
|
+
return pattern.type === "sequence" && isLongEnough &&
|
|
3369
|
+
(firstChild.name === name ||
|
|
3370
|
+
lastChild.name === name);
|
|
3580
3371
|
}
|
|
3581
3372
|
_buildPattern(node) {
|
|
3582
3373
|
const type = node.name;
|
|
@@ -3805,6 +3596,239 @@ class Grammar {
|
|
|
3805
3596
|
}
|
|
3806
3597
|
}
|
|
3807
3598
|
|
|
3599
|
+
const defaultOptions = { greedyPatternNames: [], customTokens: {} };
|
|
3600
|
+
class AutoComplete {
|
|
3601
|
+
constructor(pattern, options = defaultOptions) {
|
|
3602
|
+
this._pattern = pattern;
|
|
3603
|
+
this._options = options;
|
|
3604
|
+
this._text = "";
|
|
3605
|
+
}
|
|
3606
|
+
suggestForWithCursor(cursor) {
|
|
3607
|
+
cursor.moveTo(0);
|
|
3608
|
+
this._cursor = cursor;
|
|
3609
|
+
this._text = cursor.text;
|
|
3610
|
+
this._cursor.startRecording();
|
|
3611
|
+
if (cursor.length === 0) {
|
|
3612
|
+
return {
|
|
3613
|
+
isComplete: false,
|
|
3614
|
+
options: this._createSuggestionsFromRoot(),
|
|
3615
|
+
error: new ParseError(0, 0, this._pattern),
|
|
3616
|
+
errorAtIndex: 0,
|
|
3617
|
+
cursor,
|
|
3618
|
+
ast: null
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
let errorAtIndex = null;
|
|
3622
|
+
let error = null;
|
|
3623
|
+
const ast = this._pattern.parse(this._cursor);
|
|
3624
|
+
const isComplete = (ast === null || ast === void 0 ? void 0 : ast.value) === this._text;
|
|
3625
|
+
const options = this._getAllOptions();
|
|
3626
|
+
if (!isComplete && options.length > 0 && !this._cursor.hasError) {
|
|
3627
|
+
const startIndex = options.reduce((lowestIndex, o) => {
|
|
3628
|
+
return Math.min(lowestIndex, o.startIndex);
|
|
3629
|
+
}, Infinity);
|
|
3630
|
+
const lastIndex = cursor.getLastIndex() + 1;
|
|
3631
|
+
error = new ParseError(startIndex, lastIndex, this._pattern);
|
|
3632
|
+
errorAtIndex = startIndex;
|
|
3633
|
+
}
|
|
3634
|
+
else if (!isComplete && options.length === 0 && ast != null) {
|
|
3635
|
+
const startIndex = ast.endIndex;
|
|
3636
|
+
const lastIndex = cursor.getLastIndex() + 1;
|
|
3637
|
+
error = new ParseError(startIndex, lastIndex, this._pattern);
|
|
3638
|
+
errorAtIndex = startIndex;
|
|
3639
|
+
}
|
|
3640
|
+
else if (!isComplete && this._cursor.hasError && this._cursor.furthestError != null) {
|
|
3641
|
+
errorAtIndex = this.getFurthestPosition(cursor);
|
|
3642
|
+
error = new ParseError(errorAtIndex, errorAtIndex, this._pattern);
|
|
3643
|
+
}
|
|
3644
|
+
return {
|
|
3645
|
+
isComplete: isComplete,
|
|
3646
|
+
options: options,
|
|
3647
|
+
error,
|
|
3648
|
+
errorAtIndex,
|
|
3649
|
+
cursor: cursor,
|
|
3650
|
+
ast,
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
getFurthestPosition(cursor) {
|
|
3654
|
+
const furthestError = cursor.furthestError;
|
|
3655
|
+
const furthestMatch = cursor.allMatchedNodes[cursor.allMatchedNodes.length - 1];
|
|
3656
|
+
if (furthestError && furthestMatch) {
|
|
3657
|
+
if (furthestError.lastIndex > furthestMatch.endIndex) {
|
|
3658
|
+
return furthestMatch.endIndex;
|
|
3659
|
+
}
|
|
3660
|
+
else {
|
|
3661
|
+
return furthestError.lastIndex;
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (furthestError == null && furthestMatch != null) {
|
|
3665
|
+
return furthestMatch.endIndex;
|
|
3666
|
+
}
|
|
3667
|
+
if (furthestMatch == null && furthestError != null) {
|
|
3668
|
+
return furthestError.lastIndex;
|
|
3669
|
+
}
|
|
3670
|
+
return 0;
|
|
3671
|
+
}
|
|
3672
|
+
suggestFor(text) {
|
|
3673
|
+
return this.suggestForWithCursor(new Cursor(text));
|
|
3674
|
+
}
|
|
3675
|
+
_getAllOptions() {
|
|
3676
|
+
const errorMatches = this._getOptionsFromErrors();
|
|
3677
|
+
const leafMatches = this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
|
|
3678
|
+
const finalResults = [];
|
|
3679
|
+
[...leafMatches, ...errorMatches].forEach(m => {
|
|
3680
|
+
const index = finalResults.findIndex(f => m.text === f.text);
|
|
3681
|
+
if (index === -1) {
|
|
3682
|
+
finalResults.push(m);
|
|
3683
|
+
}
|
|
3684
|
+
});
|
|
3685
|
+
return finalResults;
|
|
3686
|
+
}
|
|
3687
|
+
_getOptionsFromErrors() {
|
|
3688
|
+
// These errored because the length of the string.
|
|
3689
|
+
const errors = this._cursor.errors.filter(e => e.lastIndex === this._cursor.length);
|
|
3690
|
+
const suggestions = errors.map(e => {
|
|
3691
|
+
const tokens = this._getTokensForPattern(e.pattern);
|
|
3692
|
+
const adjustedTokens = new Set();
|
|
3693
|
+
const currentText = this._cursor.getChars(e.startIndex, e.lastIndex);
|
|
3694
|
+
tokens.forEach((token) => {
|
|
3695
|
+
if (token.startsWith(currentText) && token.length > currentText.length) {
|
|
3696
|
+
const difference = token.length - currentText.length;
|
|
3697
|
+
const suggestedText = token.slice(-difference);
|
|
3698
|
+
adjustedTokens.add(suggestedText);
|
|
3699
|
+
}
|
|
3700
|
+
});
|
|
3701
|
+
return Array.from(adjustedTokens).map(t => {
|
|
3702
|
+
return {
|
|
3703
|
+
text: t,
|
|
3704
|
+
startIndex: e.lastIndex,
|
|
3705
|
+
};
|
|
3706
|
+
});
|
|
3707
|
+
});
|
|
3708
|
+
return suggestions.flat();
|
|
3709
|
+
}
|
|
3710
|
+
_createSuggestionsFromRoot() {
|
|
3711
|
+
const suggestions = [];
|
|
3712
|
+
const tokens = this._pattern.getTokens();
|
|
3713
|
+
for (const token of tokens) {
|
|
3714
|
+
if (suggestions.findIndex(s => s.text === token) === -1) {
|
|
3715
|
+
suggestions.push(this._createSuggestion("", token));
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return suggestions;
|
|
3719
|
+
}
|
|
3720
|
+
_createSuggestionsFromMatch(match) {
|
|
3721
|
+
if (match.pattern == null) {
|
|
3722
|
+
return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
|
|
3723
|
+
}
|
|
3724
|
+
const leafPattern = match.pattern;
|
|
3725
|
+
const parent = match.pattern.parent;
|
|
3726
|
+
if (parent !== null && match.node != null) {
|
|
3727
|
+
const patterns = leafPattern.getNextPatterns();
|
|
3728
|
+
const tokens = patterns.reduce((acc, pattern) => {
|
|
3729
|
+
acc.push(...this._getTokensForPattern(pattern));
|
|
3730
|
+
return acc;
|
|
3731
|
+
}, []);
|
|
3732
|
+
return this._createSuggestions(match.node.lastIndex, tokens);
|
|
3733
|
+
}
|
|
3734
|
+
else {
|
|
3735
|
+
return [];
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
_getTokensForPattern(pattern) {
|
|
3739
|
+
const augmentedTokens = this._getAugmentedTokens(pattern);
|
|
3740
|
+
if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
|
|
3741
|
+
const nextPatterns = pattern.getNextPatterns();
|
|
3742
|
+
const tokens = [];
|
|
3743
|
+
const nextPatternTokens = nextPatterns.reduce((acc, pattern) => {
|
|
3744
|
+
acc.push(...this._getTokensForPattern(pattern));
|
|
3745
|
+
return acc;
|
|
3746
|
+
}, []);
|
|
3747
|
+
for (let token of augmentedTokens) {
|
|
3748
|
+
for (let nextPatternToken of nextPatternTokens) {
|
|
3749
|
+
tokens.push(token + nextPatternToken);
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
return tokens;
|
|
3753
|
+
}
|
|
3754
|
+
else {
|
|
3755
|
+
return augmentedTokens;
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
_getAugmentedTokens(pattern) {
|
|
3759
|
+
const customTokensMap = this._options.customTokens || {};
|
|
3760
|
+
const leafPatterns = pattern.getPatterns();
|
|
3761
|
+
const tokens = customTokensMap[pattern.name] || [];
|
|
3762
|
+
leafPatterns.forEach(p => {
|
|
3763
|
+
const augmentedTokens = customTokensMap[p.name] || [];
|
|
3764
|
+
tokens.push(...p.getTokens(), ...augmentedTokens);
|
|
3765
|
+
});
|
|
3766
|
+
return tokens;
|
|
3767
|
+
}
|
|
3768
|
+
_createSuggestions(lastIndex, tokens) {
|
|
3769
|
+
let substring = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
|
|
3770
|
+
const suggestionStrings = [];
|
|
3771
|
+
const options = [];
|
|
3772
|
+
for (const token of tokens) {
|
|
3773
|
+
const suggestion = substring + token;
|
|
3774
|
+
const startsWith = suggestion.startsWith(substring);
|
|
3775
|
+
const alreadyExist = suggestionStrings.includes(suggestion);
|
|
3776
|
+
const isSameAsText = suggestion === this._text;
|
|
3777
|
+
if (startsWith && !alreadyExist && !isSameAsText) {
|
|
3778
|
+
suggestionStrings.push(suggestion);
|
|
3779
|
+
options.push(this._createSuggestion(this._cursor.text, suggestion));
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
const reducedOptions = getFurthestOptions(options);
|
|
3783
|
+
reducedOptions.sort((a, b) => a.text.localeCompare(b.text));
|
|
3784
|
+
return reducedOptions;
|
|
3785
|
+
}
|
|
3786
|
+
_createSuggestion(fullText, suggestion) {
|
|
3787
|
+
const furthestMatch = findMatchIndex(suggestion, fullText);
|
|
3788
|
+
const text = suggestion.slice(furthestMatch);
|
|
3789
|
+
return {
|
|
3790
|
+
text: text,
|
|
3791
|
+
startIndex: furthestMatch,
|
|
3792
|
+
};
|
|
3793
|
+
}
|
|
3794
|
+
static suggestFor(text, pattern, options) {
|
|
3795
|
+
return new AutoComplete(pattern, options).suggestFor(text);
|
|
3796
|
+
}
|
|
3797
|
+
static suggestForWithCursor(cursor, pattern, options) {
|
|
3798
|
+
return new AutoComplete(pattern, options).suggestForWithCursor(cursor);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
function findMatchIndex(str1, str2) {
|
|
3802
|
+
let matchCount = 0;
|
|
3803
|
+
let minLength = str1.length;
|
|
3804
|
+
if (str2.length < minLength) {
|
|
3805
|
+
minLength = str2.length;
|
|
3806
|
+
}
|
|
3807
|
+
for (let i = 0; i < minLength; i++) {
|
|
3808
|
+
if (str1[i] === str2[i]) {
|
|
3809
|
+
matchCount++;
|
|
3810
|
+
}
|
|
3811
|
+
else {
|
|
3812
|
+
break;
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
return matchCount;
|
|
3816
|
+
}
|
|
3817
|
+
function getFurthestOptions(options) {
|
|
3818
|
+
let furthestOptions = [];
|
|
3819
|
+
let furthestIndex = -1;
|
|
3820
|
+
for (const option of options) {
|
|
3821
|
+
if (option.startIndex > furthestIndex) {
|
|
3822
|
+
furthestIndex = option.startIndex;
|
|
3823
|
+
furthestOptions = [];
|
|
3824
|
+
}
|
|
3825
|
+
if (option.startIndex === furthestIndex) {
|
|
3826
|
+
furthestOptions.push(option);
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
return furthestOptions;
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3808
3832
|
const kebabRegex = /-([a-z])/g; // Define the regex once
|
|
3809
3833
|
function kebabToCamelCase(str) {
|
|
3810
3834
|
return str.replace(kebabRegex, (_, char) => char.toUpperCase());
|