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