@herb-tools/core 0.7.5 → 0.8.0

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.
@@ -7,8 +7,16 @@
7
7
  class Position {
8
8
  line;
9
9
  column;
10
- static from(position) {
11
- return new Position(position.line, position.column);
10
+ static from(positionOrLine, column) {
11
+ if (typeof positionOrLine === "number") {
12
+ return new Position(positionOrLine, column);
13
+ }
14
+ else {
15
+ return new Position(positionOrLine.line, positionOrLine.column);
16
+ }
17
+ }
18
+ static get zero() {
19
+ return new Position(0, 0);
12
20
  }
13
21
  constructor(line, column) {
14
22
  this.line = line;
@@ -34,10 +42,20 @@
34
42
  class Location {
35
43
  start;
36
44
  end;
37
- static from(location) {
38
- const start = Position.from(location.start);
39
- const end = Position.from(location.end);
40
- return new Location(start, end);
45
+ static from(locationOrLine, column, endLine, endColumn) {
46
+ if (typeof locationOrLine === "number") {
47
+ const start = Position.from(locationOrLine, column);
48
+ const end = Position.from(endLine, endColumn);
49
+ return new Location(start, end);
50
+ }
51
+ else {
52
+ const start = Position.from(locationOrLine.start);
53
+ const end = Position.from(locationOrLine.end);
54
+ return new Location(start, end);
55
+ }
56
+ }
57
+ static get zero() {
58
+ return new Location(Position.zero, Position.zero);
41
59
  }
42
60
  constructor(start, end) {
43
61
  this.start = start;
@@ -69,8 +87,16 @@
69
87
  class Range {
70
88
  start;
71
89
  end;
72
- static from(range) {
73
- return new Range(range[0], range[1]);
90
+ static from(rangeOrStart, end) {
91
+ if (typeof rangeOrStart === "number") {
92
+ return new Range(rangeOrStart, end);
93
+ }
94
+ else {
95
+ return new Range(rangeOrStart[0], rangeOrStart[1]);
96
+ }
97
+ }
98
+ static get zero() {
99
+ return new Range(0, 0);
74
100
  }
75
101
  constructor(start, end) {
76
102
  this.start = start;
@@ -135,7 +161,7 @@
135
161
  }
136
162
 
137
163
  // NOTE: This file is generated by the templates/template.rb script and should not
138
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/errors.ts.erb
164
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/errors.ts.erb
139
165
  class HerbError {
140
166
  type;
141
167
  message;
@@ -560,6 +586,84 @@
560
586
  return output;
561
587
  }
562
588
  }
589
+ class ERBControlFlowScopeError extends HerbError {
590
+ keyword;
591
+ static from(data) {
592
+ return new ERBControlFlowScopeError({
593
+ type: data.type,
594
+ message: data.message,
595
+ location: Location.from(data.location),
596
+ keyword: data.keyword,
597
+ });
598
+ }
599
+ constructor(props) {
600
+ super(props.type, props.message, props.location);
601
+ this.keyword = props.keyword;
602
+ }
603
+ toJSON() {
604
+ return {
605
+ ...super.toJSON(),
606
+ type: "ERB_CONTROL_FLOW_SCOPE_ERROR",
607
+ keyword: this.keyword,
608
+ };
609
+ }
610
+ toMonacoDiagnostic() {
611
+ return {
612
+ line: this.location.start.line,
613
+ column: this.location.start.column,
614
+ endLine: this.location.end.line,
615
+ endColumn: this.location.end.column,
616
+ message: this.message,
617
+ severity: 'error'
618
+ };
619
+ }
620
+ treeInspect() {
621
+ let output = "";
622
+ output += `@ ERBControlFlowScopeError ${this.location.treeInspectWithLabel()}\n`;
623
+ output += `├── message: "${this.message}"\n`;
624
+ output += `└── keyword: ${JSON.stringify(this.keyword)}\n`;
625
+ return output;
626
+ }
627
+ }
628
+ class MissingERBEndTagError extends HerbError {
629
+ keyword;
630
+ static from(data) {
631
+ return new MissingERBEndTagError({
632
+ type: data.type,
633
+ message: data.message,
634
+ location: Location.from(data.location),
635
+ keyword: data.keyword,
636
+ });
637
+ }
638
+ constructor(props) {
639
+ super(props.type, props.message, props.location);
640
+ this.keyword = props.keyword;
641
+ }
642
+ toJSON() {
643
+ return {
644
+ ...super.toJSON(),
645
+ type: "MISSINGERB_END_TAG_ERROR",
646
+ keyword: this.keyword,
647
+ };
648
+ }
649
+ toMonacoDiagnostic() {
650
+ return {
651
+ line: this.location.start.line,
652
+ column: this.location.start.column,
653
+ endLine: this.location.end.line,
654
+ endColumn: this.location.end.column,
655
+ message: this.message,
656
+ severity: 'error'
657
+ };
658
+ }
659
+ treeInspect() {
660
+ let output = "";
661
+ output += `@ MissingERBEndTagError ${this.location.treeInspectWithLabel()}\n`;
662
+ output += `├── message: "${this.message}"\n`;
663
+ output += `└── keyword: ${JSON.stringify(this.keyword)}\n`;
664
+ return output;
665
+ }
666
+ }
563
667
  function fromSerializedError(error) {
564
668
  switch (error.type) {
565
669
  case "UNEXPECTED_ERROR": return UnexpectedError.from(error);
@@ -571,6 +675,8 @@
571
675
  case "VOID_ELEMENT_CLOSING_TAG_ERROR": return VoidElementClosingTagError.from(error);
572
676
  case "UNCLOSED_ELEMENT_ERROR": return UnclosedElementError.from(error);
573
677
  case "RUBY_PARSE_ERROR": return RubyParseError.from(error);
678
+ case "ERB_CONTROL_FLOW_SCOPE_ERROR": return ERBControlFlowScopeError.from(error);
679
+ case "MISSINGERB_END_TAG_ERROR": return MissingERBEndTagError.from(error);
574
680
  default:
575
681
  throw new Error(`Unknown node type: ${error.type}`);
576
682
  }
@@ -592,7 +698,7 @@
592
698
  }
593
699
 
594
700
  // NOTE: This file is generated by the templates/template.rb script and should not
595
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/nodes.ts.erb
701
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/nodes.ts.erb
596
702
  class Node {
597
703
  type;
598
704
  location;
@@ -2845,7 +2951,7 @@
2845
2951
  }
2846
2952
 
2847
2953
  // NOTE: This file is generated by the templates/template.rb script and should not
2848
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/node-type-guards.ts.erb
2954
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/node-type-guards.ts.erb
2849
2955
  /**
2850
2956
  * Type guard functions for AST nodes.
2851
2957
  * These functions provide type checking by combining both instanceof
@@ -3466,7 +3572,7 @@
3466
3572
  * Checks if a node is an ERB output node (generates content: <%= %> or <%== %>)
3467
3573
  */
3468
3574
  function isERBOutputNode(node) {
3469
- if (!isNode(node, ERBContentNode))
3575
+ if (!isERBNode(node))
3470
3576
  return false;
3471
3577
  if (!node.tag_opening?.value)
3472
3578
  return false;
@@ -3738,8 +3844,184 @@
3738
3844
  };
3739
3845
  }
3740
3846
 
3847
+ /*
3848
+ * The following code is derived from the "js-levenshtein" repository,
3849
+ * Copyright (c) 2017 Gustaf Andersson (https://github.com/gustf/js-levenshtein)
3850
+ * Licensed under the MIT License (https://github.com/gustf/js-levenshtein/blob/master/LICENSE).
3851
+ *
3852
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
3853
+ * of this software and associated documentation files (the "Software"), to deal
3854
+ * in the Software without restriction, including without limitation the rights
3855
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3856
+ * copies of the Software, and to permit persons to whom the Software is
3857
+ * furnished to do so, subject to the following conditions:
3858
+ *
3859
+ * The above copyright notice and this permission notice shall be included in all
3860
+ * copies or substantial portions of the Software.
3861
+ *
3862
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3863
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3864
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3865
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3866
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3867
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3868
+ * SOFTWARE.
3869
+ *
3870
+ * https://github.com/marcoroth/stimulus-lsp/blob/52268d4a4d06504dde6cb81f505a23b5db5d5759/server/src/levenshtein.ts
3871
+ *
3872
+ */
3873
+ function levenshtein(a, b) {
3874
+ function _min(d0, d1, d2, bx, ay) {
3875
+ return d0 < d1 || d2 < d1 ? (d0 > d2 ? d2 + 1 : d0 + 1) : bx === ay ? d1 : d1 + 1;
3876
+ }
3877
+ if (a === b) {
3878
+ return 0;
3879
+ }
3880
+ if (a.length > b.length) {
3881
+ const tmp = a;
3882
+ a = b;
3883
+ b = tmp;
3884
+ }
3885
+ let la = a.length;
3886
+ let lb = b.length;
3887
+ while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
3888
+ la--;
3889
+ lb--;
3890
+ }
3891
+ let offset = 0;
3892
+ while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
3893
+ offset++;
3894
+ }
3895
+ la -= offset;
3896
+ lb -= offset;
3897
+ if (la === 0 || lb < 3) {
3898
+ return lb;
3899
+ }
3900
+ let x = 0;
3901
+ let y;
3902
+ let d0;
3903
+ let d1;
3904
+ let d2;
3905
+ let d3;
3906
+ let dd;
3907
+ let dy;
3908
+ let ay;
3909
+ let bx0;
3910
+ let bx1;
3911
+ let bx2;
3912
+ let bx3;
3913
+ const vector = [];
3914
+ for (y = 0; y < la; y++) {
3915
+ vector.push(y + 1);
3916
+ vector.push(a.charCodeAt(offset + y));
3917
+ }
3918
+ const len = vector.length - 1;
3919
+ for (; x < lb - 3;) {
3920
+ bx0 = b.charCodeAt(offset + (d0 = x));
3921
+ bx1 = b.charCodeAt(offset + (d1 = x + 1));
3922
+ bx2 = b.charCodeAt(offset + (d2 = x + 2));
3923
+ bx3 = b.charCodeAt(offset + (d3 = x + 3));
3924
+ dd = x += 4;
3925
+ for (y = 0; y < len; y += 2) {
3926
+ dy = vector[y];
3927
+ ay = vector[y + 1];
3928
+ d0 = _min(dy, d0, d1, bx0, ay);
3929
+ d1 = _min(d0, d1, d2, bx1, ay);
3930
+ d2 = _min(d1, d2, d3, bx2, ay);
3931
+ dd = _min(d2, d3, dd, bx3, ay);
3932
+ vector[y] = dd;
3933
+ d3 = d2;
3934
+ d2 = d1;
3935
+ d1 = d0;
3936
+ d0 = dy;
3937
+ }
3938
+ }
3939
+ for (; x < lb;) {
3940
+ bx0 = b.charCodeAt(offset + (d0 = x));
3941
+ dd = ++x;
3942
+ for (y = 0; y < len; y += 2) {
3943
+ dy = vector[y];
3944
+ vector[y] = dd = _min(dy, d0, dd, bx0, vector[y + 1]);
3945
+ d0 = dy;
3946
+ }
3947
+ }
3948
+ return dd;
3949
+ }
3950
+
3951
+ /**
3952
+ * Ranks a list of strings by their Levenshtein distance from the input string.
3953
+ * Items are sorted in ascending order by distance, with closer matches first.
3954
+ *
3955
+ * @param input - The string to compare against
3956
+ * @param list - The list of strings to rank
3957
+ * @returns An array of objects containing the item and its distance score, sorted by score
3958
+ */
3959
+ function rank(input, list) {
3960
+ return list.map(item => {
3961
+ const score = levenshtein(input.toLowerCase(), item.toLowerCase());
3962
+ return { item, score };
3963
+ }).sort((a, b) => a.score - b.score);
3964
+ }
3965
+ /**
3966
+ * Finds the closest matching string from a list using Levenshtein distance.
3967
+ * Performs case-insensitive comparison.
3968
+ *
3969
+ * @param input - The string to match against
3970
+ * @param list - The list of candidate strings to search
3971
+ * @param threshold - Maximum Levenshtein distance to consider a match. If undefined, returns the closest match regardless of distance.
3972
+ * @returns The closest matching string from the list, or null if the list is empty or no match is within the threshold
3973
+ *
3974
+ * @example
3975
+ * ```ts
3976
+ * didyoumean('speling', ['spelling', 'writing', 'reading']) // Returns 'spelling'
3977
+ * didyoumean('test', []) // Returns null
3978
+ * didyoumean('speling', ['spelling', 'writing', 'reading'], 2) // Returns 'spelling' (distance: 1)
3979
+ * didyoumean('xyz', ['spelling', 'writing', 'reading'], 2) // Returns null (all distances > 2)
3980
+ * ```
3981
+ */
3982
+ function didyoumean(input, list, threshold) {
3983
+ if (list.length === 0)
3984
+ return null;
3985
+ const scores = rank(input, list);
3986
+ if (scores.length === 0)
3987
+ return null;
3988
+ const closest = scores[0];
3989
+ if (threshold !== undefined && closest.score > threshold)
3990
+ return null;
3991
+ return closest.item;
3992
+ }
3993
+ /**
3994
+ * Returns all strings from a list ranked by their Levenshtein distance from the input string.
3995
+ * Performs case-insensitive comparison. Results are sorted with closest matches first.
3996
+ *
3997
+ * @param input - The string to match against
3998
+ * @param list - The list of candidate strings to rank
3999
+ * @param threshold - Maximum Levenshtein distance to include in results. If undefined, returns all ranked results.
4000
+ * @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
4001
+ *
4002
+ * @example
4003
+ * ```ts
4004
+ * didyoumeanRanked('speling', ['spelling', 'writing', 'reading'])
4005
+ * // Returns [{ item: 'spelling', score: 1 }, { item: 'reading', score: 5 }, { item: 'writing', score: 6 }]
4006
+ *
4007
+ * didyoumeanRanked('speling', ['spelling', 'writing', 'reading'], 2)
4008
+ * // Returns [{ item: 'spelling', score: 1 }]
4009
+ *
4010
+ * didyoumeanRanked('test', []) // Returns []
4011
+ * ```
4012
+ */
4013
+ function didyoumeanRanked(input, list, threshold) {
4014
+ if (list.length === 0)
4015
+ return [];
4016
+ const scores = rank(input, list);
4017
+ if (threshold !== undefined) {
4018
+ return scores.filter(result => result.score <= threshold);
4019
+ }
4020
+ return scores;
4021
+ }
4022
+
3741
4023
  var name = "@herb-tools/core";
3742
- var version = "0.7.5";
4024
+ var version = "0.8.0";
3743
4025
  var packageJSON = {
3744
4026
  name: name,
3745
4027
  version: version};
@@ -3962,7 +4244,7 @@
3962
4244
  }
3963
4245
 
3964
4246
  // NOTE: This file is generated by the templates/template.rb script and should not
3965
- // be modified manually. See /Users/marcoroth/Development/herb-release-0.7.5/templates/javascript/packages/core/src/visitor.ts.erb
4247
+ // be modified manually. See /Users/marcoroth/Development/herb-release-0.8.0/templates/javascript/packages/core/src/visitor.ts.erb
3966
4248
  class Visitor {
3967
4249
  visit(node) {
3968
4250
  if (!node)
@@ -4133,6 +4415,7 @@
4133
4415
  exports.ERBCaseMatchNode = ERBCaseMatchNode;
4134
4416
  exports.ERBCaseNode = ERBCaseNode;
4135
4417
  exports.ERBContentNode = ERBContentNode;
4418
+ exports.ERBControlFlowScopeError = ERBControlFlowScopeError;
4136
4419
  exports.ERBElseNode = ERBElseNode;
4137
4420
  exports.ERBEndNode = ERBEndNode;
4138
4421
  exports.ERBEnsureNode = ERBEnsureNode;
@@ -4162,6 +4445,7 @@
4162
4445
  exports.LiteralNode = LiteralNode;
4163
4446
  exports.Location = Location;
4164
4447
  exports.MissingClosingTagError = MissingClosingTagError;
4448
+ exports.MissingERBEndTagError = MissingERBEndTagError;
4165
4449
  exports.MissingOpeningTagError = MissingOpeningTagError;
4166
4450
  exports.NODE_TYPE_GUARDS = NODE_TYPE_GUARDS;
4167
4451
  exports.Node = Node;
@@ -4184,6 +4468,8 @@
4184
4468
  exports._TYPECHECK = _TYPECHECK;
4185
4469
  exports.areAllOfType = areAllOfType;
4186
4470
  exports.convertToUTF8 = convertToUTF8;
4471
+ exports.didyoumean = didyoumean;
4472
+ exports.didyoumeanRanked = didyoumeanRanked;
4187
4473
  exports.ensureLibHerbBackend = ensureLibHerbBackend;
4188
4474
  exports.ensureString = ensureString;
4189
4475
  exports.filterCDATANodes = filterCDATANodes;
@@ -4282,6 +4568,7 @@
4282
4568
  exports.isToken = isToken;
4283
4569
  exports.isWhitespaceNode = isWhitespaceNode;
4284
4570
  exports.isXMLDeclarationNode = isXMLDeclarationNode;
4571
+ exports.levenshtein = levenshtein;
4285
4572
  exports.splitNodesAroundLocation = splitNodesAroundLocation;
4286
4573
  exports.splitNodesAroundPosition = splitNodesAroundPosition;
4287
4574
  exports.toMonacoDiagnostic = toMonacoDiagnostic;