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/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._firstIndex === this._firstIndex) {
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.areRemainingPatternsOptional(i)) {
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
- areRemainingPatternsOptional(fromIndex) {
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, options, isComplete } = this._autoComplete.suggestFor(expression);
3434
- if (!isComplete) {
3435
- const text = (cursor === null || cursor === void 0 ? void 0 : cursor.text) || "";
3436
- const index = options.reduce((num, o) => Math.max(o.startIndex, num), 0);
3437
- const foundText = text.slice(Math.max(index - 10, 0), index + 10);
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 (pattern.type === "sequence" && isLongEnough &&
3582
- (firstChild.name === name) ||
3583
- (lastChild.name === name));
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());