clarity-pattern-parser 11.0.22 → 11.0.24

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