@herb-tools/core 0.7.5 → 0.8.1

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.
@@ -3,8 +3,16 @@
3
3
  class Position {
4
4
  line;
5
5
  column;
6
- static from(position) {
7
- return new Position(position.line, position.column);
6
+ static from(positionOrLine, column) {
7
+ if (typeof positionOrLine === "number") {
8
+ return new Position(positionOrLine, column);
9
+ }
10
+ else {
11
+ return new Position(positionOrLine.line, positionOrLine.column);
12
+ }
13
+ }
14
+ static get zero() {
15
+ return new Position(0, 0);
8
16
  }
9
17
  constructor(line, column) {
10
18
  this.line = line;
@@ -30,10 +38,20 @@ class Position {
30
38
  class Location {
31
39
  start;
32
40
  end;
33
- static from(location) {
34
- const start = Position.from(location.start);
35
- const end = Position.from(location.end);
36
- return new Location(start, end);
41
+ static from(locationOrLine, column, endLine, endColumn) {
42
+ if (typeof locationOrLine === "number") {
43
+ const start = Position.from(locationOrLine, column);
44
+ const end = Position.from(endLine, endColumn);
45
+ return new Location(start, end);
46
+ }
47
+ else {
48
+ const start = Position.from(locationOrLine.start);
49
+ const end = Position.from(locationOrLine.end);
50
+ return new Location(start, end);
51
+ }
52
+ }
53
+ static get zero() {
54
+ return new Location(Position.zero, Position.zero);
37
55
  }
38
56
  constructor(start, end) {
39
57
  this.start = start;
@@ -65,8 +83,16 @@ class Location {
65
83
  class Range {
66
84
  start;
67
85
  end;
68
- static from(range) {
69
- return new Range(range[0], range[1]);
86
+ static from(rangeOrStart, end) {
87
+ if (typeof rangeOrStart === "number") {
88
+ return new Range(rangeOrStart, end);
89
+ }
90
+ else {
91
+ return new Range(rangeOrStart[0], rangeOrStart[1]);
92
+ }
93
+ }
94
+ static get zero() {
95
+ return new Range(0, 0);
70
96
  }
71
97
  constructor(start, end) {
72
98
  this.start = start;
@@ -131,7 +157,7 @@ class Token {
131
157
  }
132
158
 
133
159
  // NOTE: This file is generated by the templates/template.rb script and should not
134
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/errors.ts.erb
160
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.1/templates/javascript/packages/core/src/errors.ts.erb
135
161
  class HerbError {
136
162
  type;
137
163
  message;
@@ -556,6 +582,84 @@ class RubyParseError extends HerbError {
556
582
  return output;
557
583
  }
558
584
  }
585
+ class ERBControlFlowScopeError extends HerbError {
586
+ keyword;
587
+ static from(data) {
588
+ return new ERBControlFlowScopeError({
589
+ type: data.type,
590
+ message: data.message,
591
+ location: Location.from(data.location),
592
+ keyword: data.keyword,
593
+ });
594
+ }
595
+ constructor(props) {
596
+ super(props.type, props.message, props.location);
597
+ this.keyword = props.keyword;
598
+ }
599
+ toJSON() {
600
+ return {
601
+ ...super.toJSON(),
602
+ type: "ERB_CONTROL_FLOW_SCOPE_ERROR",
603
+ keyword: this.keyword,
604
+ };
605
+ }
606
+ toMonacoDiagnostic() {
607
+ return {
608
+ line: this.location.start.line,
609
+ column: this.location.start.column,
610
+ endLine: this.location.end.line,
611
+ endColumn: this.location.end.column,
612
+ message: this.message,
613
+ severity: 'error'
614
+ };
615
+ }
616
+ treeInspect() {
617
+ let output = "";
618
+ output += `@ ERBControlFlowScopeError ${this.location.treeInspectWithLabel()}\n`;
619
+ output += `├── message: "${this.message}"\n`;
620
+ output += `└── keyword: ${JSON.stringify(this.keyword)}\n`;
621
+ return output;
622
+ }
623
+ }
624
+ class MissingERBEndTagError extends HerbError {
625
+ keyword;
626
+ static from(data) {
627
+ return new MissingERBEndTagError({
628
+ type: data.type,
629
+ message: data.message,
630
+ location: Location.from(data.location),
631
+ keyword: data.keyword,
632
+ });
633
+ }
634
+ constructor(props) {
635
+ super(props.type, props.message, props.location);
636
+ this.keyword = props.keyword;
637
+ }
638
+ toJSON() {
639
+ return {
640
+ ...super.toJSON(),
641
+ type: "MISSINGERB_END_TAG_ERROR",
642
+ keyword: this.keyword,
643
+ };
644
+ }
645
+ toMonacoDiagnostic() {
646
+ return {
647
+ line: this.location.start.line,
648
+ column: this.location.start.column,
649
+ endLine: this.location.end.line,
650
+ endColumn: this.location.end.column,
651
+ message: this.message,
652
+ severity: 'error'
653
+ };
654
+ }
655
+ treeInspect() {
656
+ let output = "";
657
+ output += `@ MissingERBEndTagError ${this.location.treeInspectWithLabel()}\n`;
658
+ output += `├── message: "${this.message}"\n`;
659
+ output += `└── keyword: ${JSON.stringify(this.keyword)}\n`;
660
+ return output;
661
+ }
662
+ }
559
663
  function fromSerializedError(error) {
560
664
  switch (error.type) {
561
665
  case "UNEXPECTED_ERROR": return UnexpectedError.from(error);
@@ -567,6 +671,8 @@ function fromSerializedError(error) {
567
671
  case "VOID_ELEMENT_CLOSING_TAG_ERROR": return VoidElementClosingTagError.from(error);
568
672
  case "UNCLOSED_ELEMENT_ERROR": return UnclosedElementError.from(error);
569
673
  case "RUBY_PARSE_ERROR": return RubyParseError.from(error);
674
+ case "ERB_CONTROL_FLOW_SCOPE_ERROR": return ERBControlFlowScopeError.from(error);
675
+ case "MISSINGERB_END_TAG_ERROR": return MissingERBEndTagError.from(error);
570
676
  default:
571
677
  throw new Error(`Unknown node type: ${error.type}`);
572
678
  }
@@ -588,7 +694,7 @@ function convertToUTF8(string) {
588
694
  }
589
695
 
590
696
  // NOTE: This file is generated by the templates/template.rb script and should not
591
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/nodes.ts.erb
697
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.1/templates/javascript/packages/core/src/nodes.ts.erb
592
698
  class Node {
593
699
  type;
594
700
  location;
@@ -2841,7 +2947,7 @@ class ParseResult extends Result {
2841
2947
  }
2842
2948
 
2843
2949
  // NOTE: This file is generated by the templates/template.rb script and should not
2844
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/node-type-guards.ts.erb
2950
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.1/templates/javascript/packages/core/src/node-type-guards.ts.erb
2845
2951
  /**
2846
2952
  * Type guard functions for AST nodes.
2847
2953
  * These functions provide type checking by combining both instanceof
@@ -3462,7 +3568,7 @@ function filterERBInNodes(nodes) {
3462
3568
  * Checks if a node is an ERB output node (generates content: <%= %> or <%== %>)
3463
3569
  */
3464
3570
  function isERBOutputNode(node) {
3465
- if (!isNode(node, ERBContentNode))
3571
+ if (!isERBNode(node))
3466
3572
  return false;
3467
3573
  if (!node.tag_opening?.value)
3468
3574
  return false;
@@ -3734,8 +3840,184 @@ function toMonacoDiagnostic(diagnostic) {
3734
3840
  };
3735
3841
  }
3736
3842
 
3843
+ /*
3844
+ * The following code is derived from the "js-levenshtein" repository,
3845
+ * Copyright (c) 2017 Gustaf Andersson (https://github.com/gustf/js-levenshtein)
3846
+ * Licensed under the MIT License (https://github.com/gustf/js-levenshtein/blob/master/LICENSE).
3847
+ *
3848
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
3849
+ * of this software and associated documentation files (the "Software"), to deal
3850
+ * in the Software without restriction, including without limitation the rights
3851
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3852
+ * copies of the Software, and to permit persons to whom the Software is
3853
+ * furnished to do so, subject to the following conditions:
3854
+ *
3855
+ * The above copyright notice and this permission notice shall be included in all
3856
+ * copies or substantial portions of the Software.
3857
+ *
3858
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3859
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3860
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3861
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3862
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3863
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3864
+ * SOFTWARE.
3865
+ *
3866
+ * https://github.com/marcoroth/stimulus-lsp/blob/52268d4a4d06504dde6cb81f505a23b5db5d5759/server/src/levenshtein.ts
3867
+ *
3868
+ */
3869
+ function levenshtein(a, b) {
3870
+ function _min(d0, d1, d2, bx, ay) {
3871
+ return d0 < d1 || d2 < d1 ? (d0 > d2 ? d2 + 1 : d0 + 1) : bx === ay ? d1 : d1 + 1;
3872
+ }
3873
+ if (a === b) {
3874
+ return 0;
3875
+ }
3876
+ if (a.length > b.length) {
3877
+ const tmp = a;
3878
+ a = b;
3879
+ b = tmp;
3880
+ }
3881
+ let la = a.length;
3882
+ let lb = b.length;
3883
+ while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
3884
+ la--;
3885
+ lb--;
3886
+ }
3887
+ let offset = 0;
3888
+ while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
3889
+ offset++;
3890
+ }
3891
+ la -= offset;
3892
+ lb -= offset;
3893
+ if (la === 0 || lb < 3) {
3894
+ return lb;
3895
+ }
3896
+ let x = 0;
3897
+ let y;
3898
+ let d0;
3899
+ let d1;
3900
+ let d2;
3901
+ let d3;
3902
+ let dd;
3903
+ let dy;
3904
+ let ay;
3905
+ let bx0;
3906
+ let bx1;
3907
+ let bx2;
3908
+ let bx3;
3909
+ const vector = [];
3910
+ for (y = 0; y < la; y++) {
3911
+ vector.push(y + 1);
3912
+ vector.push(a.charCodeAt(offset + y));
3913
+ }
3914
+ const len = vector.length - 1;
3915
+ for (; x < lb - 3;) {
3916
+ bx0 = b.charCodeAt(offset + (d0 = x));
3917
+ bx1 = b.charCodeAt(offset + (d1 = x + 1));
3918
+ bx2 = b.charCodeAt(offset + (d2 = x + 2));
3919
+ bx3 = b.charCodeAt(offset + (d3 = x + 3));
3920
+ dd = x += 4;
3921
+ for (y = 0; y < len; y += 2) {
3922
+ dy = vector[y];
3923
+ ay = vector[y + 1];
3924
+ d0 = _min(dy, d0, d1, bx0, ay);
3925
+ d1 = _min(d0, d1, d2, bx1, ay);
3926
+ d2 = _min(d1, d2, d3, bx2, ay);
3927
+ dd = _min(d2, d3, dd, bx3, ay);
3928
+ vector[y] = dd;
3929
+ d3 = d2;
3930
+ d2 = d1;
3931
+ d1 = d0;
3932
+ d0 = dy;
3933
+ }
3934
+ }
3935
+ for (; x < lb;) {
3936
+ bx0 = b.charCodeAt(offset + (d0 = x));
3937
+ dd = ++x;
3938
+ for (y = 0; y < len; y += 2) {
3939
+ dy = vector[y];
3940
+ vector[y] = dd = _min(dy, d0, dd, bx0, vector[y + 1]);
3941
+ d0 = dy;
3942
+ }
3943
+ }
3944
+ return dd;
3945
+ }
3946
+
3947
+ /**
3948
+ * Ranks a list of strings by their Levenshtein distance from the input string.
3949
+ * Items are sorted in ascending order by distance, with closer matches first.
3950
+ *
3951
+ * @param input - The string to compare against
3952
+ * @param list - The list of strings to rank
3953
+ * @returns An array of objects containing the item and its distance score, sorted by score
3954
+ */
3955
+ function rank(input, list) {
3956
+ return list.map(item => {
3957
+ const score = levenshtein(input.toLowerCase(), item.toLowerCase());
3958
+ return { item, score };
3959
+ }).sort((a, b) => a.score - b.score);
3960
+ }
3961
+ /**
3962
+ * Finds the closest matching string from a list using Levenshtein distance.
3963
+ * Performs case-insensitive comparison.
3964
+ *
3965
+ * @param input - The string to match against
3966
+ * @param list - The list of candidate strings to search
3967
+ * @param threshold - Maximum Levenshtein distance to consider a match. If undefined, returns the closest match regardless of distance.
3968
+ * @returns The closest matching string from the list, or null if the list is empty or no match is within the threshold
3969
+ *
3970
+ * @example
3971
+ * ```ts
3972
+ * didyoumean('speling', ['spelling', 'writing', 'reading']) // Returns 'spelling'
3973
+ * didyoumean('test', []) // Returns null
3974
+ * didyoumean('speling', ['spelling', 'writing', 'reading'], 2) // Returns 'spelling' (distance: 1)
3975
+ * didyoumean('xyz', ['spelling', 'writing', 'reading'], 2) // Returns null (all distances > 2)
3976
+ * ```
3977
+ */
3978
+ function didyoumean(input, list, threshold) {
3979
+ if (list.length === 0)
3980
+ return null;
3981
+ const scores = rank(input, list);
3982
+ if (scores.length === 0)
3983
+ return null;
3984
+ const closest = scores[0];
3985
+ if (threshold !== undefined && closest.score > threshold)
3986
+ return null;
3987
+ return closest.item;
3988
+ }
3989
+ /**
3990
+ * Returns all strings from a list ranked by their Levenshtein distance from the input string.
3991
+ * Performs case-insensitive comparison. Results are sorted with closest matches first.
3992
+ *
3993
+ * @param input - The string to match against
3994
+ * @param list - The list of candidate strings to rank
3995
+ * @param threshold - Maximum Levenshtein distance to include in results. If undefined, returns all ranked results.
3996
+ * @returns An array of ranked results with items and scores, or an empty array if the list is empty or no matches are within the threshold
3997
+ *
3998
+ * @example
3999
+ * ```ts
4000
+ * didyoumeanRanked('speling', ['spelling', 'writing', 'reading'])
4001
+ * // Returns [{ item: 'spelling', score: 1 }, { item: 'reading', score: 5 }, { item: 'writing', score: 6 }]
4002
+ *
4003
+ * didyoumeanRanked('speling', ['spelling', 'writing', 'reading'], 2)
4004
+ * // Returns [{ item: 'spelling', score: 1 }]
4005
+ *
4006
+ * didyoumeanRanked('test', []) // Returns []
4007
+ * ```
4008
+ */
4009
+ function didyoumeanRanked(input, list, threshold) {
4010
+ if (list.length === 0)
4011
+ return [];
4012
+ const scores = rank(input, list);
4013
+ if (threshold !== undefined) {
4014
+ return scores.filter(result => result.score <= threshold);
4015
+ }
4016
+ return scores;
4017
+ }
4018
+
3737
4019
  var name = "@herb-tools/core";
3738
- var version = "0.7.5";
4020
+ var version = "0.8.1";
3739
4021
  var packageJSON = {
3740
4022
  name: name,
3741
4023
  version: version};
@@ -3958,7 +4240,7 @@ class HerbBackend {
3958
4240
  }
3959
4241
 
3960
4242
  // NOTE: This file is generated by the templates/template.rb script and should not
3961
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/visitor.ts.erb
4243
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.1/templates/javascript/packages/core/src/visitor.ts.erb
3962
4244
  class Visitor {
3963
4245
  visit(node) {
3964
4246
  if (!node)
@@ -4129,6 +4411,7 @@ exports.ERBBlockNode = ERBBlockNode;
4129
4411
  exports.ERBCaseMatchNode = ERBCaseMatchNode;
4130
4412
  exports.ERBCaseNode = ERBCaseNode;
4131
4413
  exports.ERBContentNode = ERBContentNode;
4414
+ exports.ERBControlFlowScopeError = ERBControlFlowScopeError;
4132
4415
  exports.ERBElseNode = ERBElseNode;
4133
4416
  exports.ERBEndNode = ERBEndNode;
4134
4417
  exports.ERBEnsureNode = ERBEnsureNode;
@@ -4158,6 +4441,7 @@ exports.LexResult = LexResult;
4158
4441
  exports.LiteralNode = LiteralNode;
4159
4442
  exports.Location = Location;
4160
4443
  exports.MissingClosingTagError = MissingClosingTagError;
4444
+ exports.MissingERBEndTagError = MissingERBEndTagError;
4161
4445
  exports.MissingOpeningTagError = MissingOpeningTagError;
4162
4446
  exports.NODE_TYPE_GUARDS = NODE_TYPE_GUARDS;
4163
4447
  exports.Node = Node;
@@ -4180,6 +4464,8 @@ exports.XMLDeclarationNode = XMLDeclarationNode;
4180
4464
  exports._TYPECHECK = _TYPECHECK;
4181
4465
  exports.areAllOfType = areAllOfType;
4182
4466
  exports.convertToUTF8 = convertToUTF8;
4467
+ exports.didyoumean = didyoumean;
4468
+ exports.didyoumeanRanked = didyoumeanRanked;
4183
4469
  exports.ensureLibHerbBackend = ensureLibHerbBackend;
4184
4470
  exports.ensureString = ensureString;
4185
4471
  exports.filterCDATANodes = filterCDATANodes;
@@ -4278,6 +4564,7 @@ exports.isPositionEqual = isPositionEqual;
4278
4564
  exports.isToken = isToken;
4279
4565
  exports.isWhitespaceNode = isWhitespaceNode;
4280
4566
  exports.isXMLDeclarationNode = isXMLDeclarationNode;
4567
+ exports.levenshtein = levenshtein;
4281
4568
  exports.splitNodesAroundLocation = splitNodesAroundLocation;
4282
4569
  exports.splitNodesAroundPosition = splitNodesAroundPosition;
4283
4570
  exports.toMonacoDiagnostic = toMonacoDiagnostic;