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