herb 0.1.1-x86-linux-gnu → 0.2.0-x86-linux-gnu

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +20 -2
  3. data/ext/herb/extension.c +46 -14
  4. data/ext/herb/extension.h +9 -0
  5. data/ext/herb/extension_helpers.c +9 -27
  6. data/ext/herb/nodes.c +104 -1
  7. data/herb.gemspec +2 -1
  8. data/lib/herb/3.0/herb.so +0 -0
  9. data/lib/herb/3.1/herb.so +0 -0
  10. data/lib/herb/3.2/herb.so +0 -0
  11. data/lib/herb/3.3/herb.so +0 -0
  12. data/lib/herb/3.4/herb.so +0 -0
  13. data/lib/herb/ast/node.rb +40 -4
  14. data/lib/herb/ast/nodes.rb +892 -140
  15. data/lib/herb/ast.rb +1 -0
  16. data/lib/herb/cli.rb +4 -1
  17. data/lib/herb/errors.rb +84 -33
  18. data/lib/herb/lex_result.rb +4 -1
  19. data/lib/herb/libherb/array.rb +3 -0
  20. data/lib/herb/libherb/ast_node.rb +3 -0
  21. data/lib/herb/libherb/buffer.rb +3 -0
  22. data/lib/herb/libherb/extract_result.rb +3 -0
  23. data/lib/herb/libherb/lex_result.rb +3 -0
  24. data/lib/herb/libherb/libherb.rb +3 -0
  25. data/lib/herb/libherb/parse_result.rb +3 -0
  26. data/lib/herb/libherb/token.rb +3 -0
  27. data/lib/herb/libherb.rb +3 -0
  28. data/lib/herb/location.rb +15 -6
  29. data/lib/herb/parse_result.rb +10 -1
  30. data/lib/herb/position.rb +17 -8
  31. data/lib/herb/project.rb +13 -4
  32. data/lib/herb/range.rb +18 -8
  33. data/lib/herb/result.rb +7 -1
  34. data/lib/herb/token.rb +14 -4
  35. data/lib/herb/token_list.rb +10 -1
  36. data/lib/herb/version.rb +2 -1
  37. data/lib/herb/visitor.rb +175 -0
  38. data/lib/herb/warnings.rb +43 -0
  39. data/lib/herb.rb +6 -1
  40. data/sig/herb/ast/node.rbs +48 -0
  41. data/sig/herb/ast/nodes.rbs +941 -0
  42. data/sig/herb/ast.rbs +6 -0
  43. data/sig/herb/errors.rbs +193 -0
  44. data/sig/herb/lex_result.rbs +16 -0
  45. data/sig/herb/location.rbs +30 -0
  46. data/sig/herb/parse_result.rbs +22 -0
  47. data/sig/herb/position.rbs +30 -0
  48. data/sig/herb/range.rbs +33 -0
  49. data/sig/herb/result.rbs +20 -0
  50. data/sig/herb/token.rbs +31 -0
  51. data/sig/herb/token_list.rbs +13 -0
  52. data/sig/herb/version.rbs +5 -0
  53. data/sig/herb/visitor.rbs +104 -0
  54. data/sig/herb/warnings.rbs +28 -0
  55. data/sig/herb.rbs +4 -0
  56. data/sig/serialized.rbs +9 -0
  57. data/sig/serialized_ast_errors.rbs +53 -0
  58. data/sig/serialized_ast_nodes.rbs +221 -0
  59. data/src/analyze.c +138 -43
  60. data/src/analyze_helpers.c +44 -1
  61. data/src/analyzed_ruby.c +10 -1
  62. data/src/ast_nodes.c +103 -1
  63. data/src/ast_pretty_print.c +60 -0
  64. data/src/buffer.c +60 -27
  65. data/src/extract.c +57 -20
  66. data/src/include/analyze.h +3 -0
  67. data/src/include/analyze_helpers.h +6 -0
  68. data/src/include/analyzed_ruby.h +3 -0
  69. data/src/include/ast_nodes.h +32 -0
  70. data/src/include/buffer.h +5 -2
  71. data/src/include/lexer_peek_helpers.h +2 -2
  72. data/src/include/macros.h +2 -2
  73. data/src/include/version.h +1 -1
  74. data/src/lexer.c +1 -1
  75. data/src/parser.c +17 -7
  76. data/src/token.c +1 -1
  77. data/src/util.c +3 -1
  78. data/src/visitor.c +36 -0
  79. metadata +24 -3
  80. /data/{License.txt → LICENSE.txt} +0 -0
data/lib/herb/ast.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # typed: true
2
3
 
3
4
  module Herb
4
5
  module AST
data/lib/herb/cli.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "optparse"
4
7
 
@@ -41,7 +44,7 @@ class Herb::CLI
41
44
  end
42
45
 
43
46
  unless File.exist?(@file)
44
- puts "Not a directory: '#{@file}'."
47
+ puts "Not a file: '#{@file}'."
45
48
  puts
46
49
  end
47
50
 
data/lib/herb/errors.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ # typed: true
2
3
 
3
4
  # NOTE: This file is generated by the templates/template.rb script and should not be
4
5
  # modified manually. See /Users/marcoroth/Development/herb-release/templates/lib/herb/errors.rb.erb
@@ -6,16 +7,18 @@
6
7
  module Herb
7
8
  module Errors
8
9
  class Error
9
- attr_reader :type
10
- attr_reader :location
11
- attr_reader :message
10
+ attr_reader :type #: String
11
+ attr_reader :location #: Location
12
+ attr_reader :message #: String
12
13
 
14
+ #: (String, Location, String) -> void
13
15
  def initialize(type, location, message)
14
16
  @type = type
15
17
  @location = location
16
18
  @message = message
17
19
  end
18
20
 
21
+ #: () -> serialized_error
19
22
  def to_hash
20
23
  {
21
24
  type: type,
@@ -24,20 +27,33 @@ module Herb
24
27
  }
25
28
  end
26
29
 
30
+ #: () -> String
31
+ def class_name
32
+ self.class.name || "Error"
33
+ end
34
+
35
+ #: () -> String
27
36
  def error_name
28
- self.class.name.split("::").last
37
+ class_name.split("::").last || "Error"
38
+ end
39
+
40
+ #: (?untyped) -> String
41
+ def to_json(state = nil)
42
+ to_hash.to_json(state)
29
43
  end
30
44
 
31
- def to_json(*args)
32
- to_hash.to_json(*args)
45
+ #: (?Integer) -> String
46
+ def tree_inspect(_indent = 0)
47
+ raise NotImplementedError
33
48
  end
34
49
  end
35
50
 
36
51
  class UnexpectedError < Error
37
- attr_reader :description
38
- attr_reader :expected
39
- attr_reader :found
52
+ attr_reader :description #: String
53
+ attr_reader :expected #: String
54
+ attr_reader :found #: String
40
55
 
56
+ #: (String, Location, String, String, String, String) -> void
41
57
  def initialize(type, location, message, description, expected, found)
42
58
  super(type, location, message)
43
59
 
@@ -46,18 +62,21 @@ module Herb
46
62
  @found = found
47
63
  end
48
64
 
65
+ #: () -> String
49
66
  def inspect
50
67
  tree_inspect.rstrip.gsub(/\s+$/, "")
51
68
  end
52
69
 
70
+ #: () -> serialized_unexpected_error
53
71
  def to_hash
54
72
  super.merge({
55
73
  description: description,
56
74
  expected: expected,
57
75
  found: found,
58
- })
76
+ }) #: Herb::serialized_unexpected_error
59
77
  end
60
78
 
79
+ #: (?Integer) -> String
61
80
  def tree_inspect(indent = 0)
62
81
  output = +""
63
82
 
@@ -73,9 +92,10 @@ module Herb
73
92
  end
74
93
 
75
94
  class UnexpectedTokenError < Error
76
- attr_reader :expected_type
77
- attr_reader :found
95
+ attr_reader :expected_type #: String
96
+ attr_reader :found #: Herb::Token
78
97
 
98
+ #: (String, Location, String, String, Herb::Token) -> void
79
99
  def initialize(type, location, message, expected_type, found)
80
100
  super(type, location, message)
81
101
 
@@ -83,17 +103,20 @@ module Herb
83
103
  @found = found
84
104
  end
85
105
 
106
+ #: () -> String
86
107
  def inspect
87
108
  tree_inspect.rstrip.gsub(/\s+$/, "")
88
109
  end
89
110
 
111
+ #: () -> serialized_unexpected_token_error
90
112
  def to_hash
91
113
  super.merge({
92
114
  expected_type: expected_type,
93
115
  found: found,
94
- })
116
+ }) #: Herb::serialized_unexpected_token_error
95
117
  end
96
118
 
119
+ #: (?Integer) -> String
97
120
  def tree_inspect(indent = 0)
98
121
  output = +""
99
122
 
@@ -108,24 +131,28 @@ module Herb
108
131
  end
109
132
 
110
133
  class MissingOpeningTagError < Error
111
- attr_reader :closing_tag
134
+ attr_reader :closing_tag #: Herb::Token
112
135
 
136
+ #: (String, Location, String, Herb::Token) -> void
113
137
  def initialize(type, location, message, closing_tag)
114
138
  super(type, location, message)
115
139
 
116
140
  @closing_tag = closing_tag
117
141
  end
118
142
 
143
+ #: () -> String
119
144
  def inspect
120
145
  tree_inspect.rstrip.gsub(/\s+$/, "")
121
146
  end
122
147
 
148
+ #: () -> serialized_missing_opening_tag_error
123
149
  def to_hash
124
150
  super.merge({
125
151
  closing_tag: closing_tag,
126
- })
152
+ }) #: Herb::serialized_missing_opening_tag_error
127
153
  end
128
154
 
155
+ #: (?Integer) -> String
129
156
  def tree_inspect(indent = 0)
130
157
  output = +""
131
158
 
@@ -139,24 +166,28 @@ module Herb
139
166
  end
140
167
 
141
168
  class MissingClosingTagError < Error
142
- attr_reader :opening_tag
169
+ attr_reader :opening_tag #: Herb::Token
143
170
 
171
+ #: (String, Location, String, Herb::Token) -> void
144
172
  def initialize(type, location, message, opening_tag)
145
173
  super(type, location, message)
146
174
 
147
175
  @opening_tag = opening_tag
148
176
  end
149
177
 
178
+ #: () -> String
150
179
  def inspect
151
180
  tree_inspect.rstrip.gsub(/\s+$/, "")
152
181
  end
153
182
 
183
+ #: () -> serialized_missing_closing_tag_error
154
184
  def to_hash
155
185
  super.merge({
156
186
  opening_tag: opening_tag,
157
- })
187
+ }) #: Herb::serialized_missing_closing_tag_error
158
188
  end
159
189
 
190
+ #: (?Integer) -> String
160
191
  def tree_inspect(indent = 0)
161
192
  output = +""
162
193
 
@@ -170,9 +201,10 @@ module Herb
170
201
  end
171
202
 
172
203
  class TagNamesMismatchError < Error
173
- attr_reader :opening_tag
174
- attr_reader :closing_tag
204
+ attr_reader :opening_tag #: Herb::Token
205
+ attr_reader :closing_tag #: Herb::Token
175
206
 
207
+ #: (String, Location, String, Herb::Token, Herb::Token) -> void
176
208
  def initialize(type, location, message, opening_tag, closing_tag)
177
209
  super(type, location, message)
178
210
 
@@ -180,17 +212,20 @@ module Herb
180
212
  @closing_tag = closing_tag
181
213
  end
182
214
 
215
+ #: () -> String
183
216
  def inspect
184
217
  tree_inspect.rstrip.gsub(/\s+$/, "")
185
218
  end
186
219
 
220
+ #: () -> serialized_tag_names_mismatch_error
187
221
  def to_hash
188
222
  super.merge({
189
223
  opening_tag: opening_tag,
190
224
  closing_tag: closing_tag,
191
- })
225
+ }) #: Herb::serialized_tag_names_mismatch_error
192
226
  end
193
227
 
228
+ #: (?Integer) -> String
194
229
  def tree_inspect(indent = 0)
195
230
  output = +""
196
231
 
@@ -205,9 +240,10 @@ module Herb
205
240
  end
206
241
 
207
242
  class QuotesMismatchError < Error
208
- attr_reader :opening_quote
209
- attr_reader :closing_quote
243
+ attr_reader :opening_quote #: Herb::Token
244
+ attr_reader :closing_quote #: Herb::Token
210
245
 
246
+ #: (String, Location, String, Herb::Token, Herb::Token) -> void
211
247
  def initialize(type, location, message, opening_quote, closing_quote)
212
248
  super(type, location, message)
213
249
 
@@ -215,17 +251,20 @@ module Herb
215
251
  @closing_quote = closing_quote
216
252
  end
217
253
 
254
+ #: () -> String
218
255
  def inspect
219
256
  tree_inspect.rstrip.gsub(/\s+$/, "")
220
257
  end
221
258
 
259
+ #: () -> serialized_quotes_mismatch_error
222
260
  def to_hash
223
261
  super.merge({
224
262
  opening_quote: opening_quote,
225
263
  closing_quote: closing_quote,
226
- })
264
+ }) #: Herb::serialized_quotes_mismatch_error
227
265
  end
228
266
 
267
+ #: (?Integer) -> String
229
268
  def tree_inspect(indent = 0)
230
269
  output = +""
231
270
 
@@ -240,10 +279,11 @@ module Herb
240
279
  end
241
280
 
242
281
  class VoidElementClosingTagError < Error
243
- attr_reader :tag_name
244
- attr_reader :expected
245
- attr_reader :found
282
+ attr_reader :tag_name #: Herb::Token
283
+ attr_reader :expected #: String
284
+ attr_reader :found #: String
246
285
 
286
+ #: (String, Location, String, Herb::Token, String, String) -> void
247
287
  def initialize(type, location, message, tag_name, expected, found)
248
288
  super(type, location, message)
249
289
 
@@ -252,18 +292,21 @@ module Herb
252
292
  @found = found
253
293
  end
254
294
 
295
+ #: () -> String
255
296
  def inspect
256
297
  tree_inspect.rstrip.gsub(/\s+$/, "")
257
298
  end
258
299
 
300
+ #: () -> serialized_void_element_closing_tag_error
259
301
  def to_hash
260
302
  super.merge({
261
303
  tag_name: tag_name,
262
304
  expected: expected,
263
305
  found: found,
264
- })
306
+ }) #: Herb::serialized_void_element_closing_tag_error
265
307
  end
266
308
 
309
+ #: (?Integer) -> String
267
310
  def tree_inspect(indent = 0)
268
311
  output = +""
269
312
 
@@ -279,24 +322,28 @@ module Herb
279
322
  end
280
323
 
281
324
  class UnclosedElementError < Error
282
- attr_reader :opening_tag
325
+ attr_reader :opening_tag #: Herb::Token
283
326
 
327
+ #: (String, Location, String, Herb::Token) -> void
284
328
  def initialize(type, location, message, opening_tag)
285
329
  super(type, location, message)
286
330
 
287
331
  @opening_tag = opening_tag
288
332
  end
289
333
 
334
+ #: () -> String
290
335
  def inspect
291
336
  tree_inspect.rstrip.gsub(/\s+$/, "")
292
337
  end
293
338
 
339
+ #: () -> serialized_unclosed_element_error
294
340
  def to_hash
295
341
  super.merge({
296
342
  opening_tag: opening_tag,
297
- })
343
+ }) #: Herb::serialized_unclosed_element_error
298
344
  end
299
345
 
346
+ #: (?Integer) -> String
300
347
  def tree_inspect(indent = 0)
301
348
  output = +""
302
349
 
@@ -310,10 +357,11 @@ module Herb
310
357
  end
311
358
 
312
359
  class RubyParseError < Error
313
- attr_reader :error_message
314
- attr_reader :diagnostic_id
315
- attr_reader :level
360
+ attr_reader :error_message #: String
361
+ attr_reader :diagnostic_id #: String
362
+ attr_reader :level #: String
316
363
 
364
+ #: (String, Location, String, String, String, String) -> void
317
365
  def initialize(type, location, message, error_message, diagnostic_id, level)
318
366
  super(type, location, message)
319
367
 
@@ -322,18 +370,21 @@ module Herb
322
370
  @level = level
323
371
  end
324
372
 
373
+ #: () -> String
325
374
  def inspect
326
375
  tree_inspect.rstrip.gsub(/\s+$/, "")
327
376
  end
328
377
 
378
+ #: () -> serialized_ruby_parse_error
329
379
  def to_hash
330
380
  super.merge({
331
381
  error_message: error_message,
332
382
  diagnostic_id: diagnostic_id,
333
383
  level: level,
334
- })
384
+ }) #: Herb::serialized_ruby_parse_error
335
385
  end
336
386
 
387
+ #: (?Integer) -> String
337
388
  def tree_inspect(indent = 0)
338
389
  output = +""
339
390
 
@@ -2,17 +2,20 @@
2
2
 
3
3
  module Herb
4
4
  class LexResult < Result
5
- attr_reader :value
5
+ attr_reader :value #: TokenList
6
6
 
7
+ #: (Array[Herb::Token], String, Array[Herb::Warnings::Warning], Array[Herb::Errors::Error]) -> void
7
8
  def initialize(value, source, warnings, errors)
8
9
  @value = TokenList.new(value)
9
10
  super(source, warnings, errors)
10
11
  end
11
12
 
13
+ #: () -> bool
12
14
  def success?
13
15
  errors.empty?
14
16
  end
15
17
 
18
+ #: () -> bool
16
19
  def failed?
17
20
  errors.any?
18
21
  end
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "forwardable"
4
7
 
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  module Herb
4
7
  module LibHerb
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  module Herb
4
7
  module LibHerb
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "forwardable"
4
7
 
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "forwardable"
4
7
 
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "herb/libherb/ast_node"
4
7
  require "herb/libherb/buffer"
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "forwardable"
4
7
 
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  module Herb
4
7
  module LibHerb
data/lib/herb/libherb.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "ffi"
4
7
  require "rbconfig"
data/lib/herb/location.rb CHANGED
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
+ # typed: true
2
3
 
3
4
  module Herb
4
5
  class Location
5
- attr_reader :start, :end
6
+ attr_reader :start #: Position
7
+ attr_reader :end #: Position
6
8
 
9
+ #: (Position, Position) -> void
7
10
  def initialize(start_position, end_position)
8
11
  @start = start_position
9
12
  @end = end_position
10
13
  end
11
14
 
15
+ #: (Integer, Integer, Integer, Integer) -> Location
12
16
  def self.from(start_line, start_column, end_line, end_column)
13
17
  new(
14
18
  Position.new(start_line, start_column),
@@ -16,25 +20,30 @@ module Herb
16
20
  )
17
21
  end
18
22
 
19
- def self.[](...)
20
- from(...)
23
+ #: (Integer, Integer, Integer, Integer) -> Location
24
+ def self.[](start_line, start_column, end_line, end_column)
25
+ from(start_line, start_column, end_line, end_column)
21
26
  end
22
27
 
28
+ #: () -> serialized_location
23
29
  def to_hash
24
30
  {
25
31
  start: start,
26
32
  end: self.end,
27
- }
33
+ } #: Herb::serialized_location
28
34
  end
29
35
 
30
- def to_json(*args)
31
- to_hash.to_json(*args)
36
+ #: (?untyped) -> String
37
+ def to_json(state = nil)
38
+ to_hash.to_json(state)
32
39
  end
33
40
 
41
+ #: () -> String
34
42
  def tree_inspect
35
43
  %((location: #{start.tree_inspect}-#{self.end.tree_inspect}))
36
44
  end
37
45
 
46
+ #: () -> String
38
47
  def inspect
39
48
  %(#<Herb::Location #{tree_inspect}>)
40
49
  end
@@ -4,23 +4,32 @@ require "json"
4
4
 
5
5
  module Herb
6
6
  class ParseResult < Result
7
- attr_reader :value
7
+ attr_reader :value #: Herb::AST::DocumentNode
8
8
 
9
+ #: (Herb::AST::DocumentNode, String, Array[Herb::Warnings::Warning], Array[Herb::Errors::Error]) -> void
9
10
  def initialize(value, source, warnings, errors)
10
11
  @value = value
11
12
  super(source, warnings, errors)
12
13
  end
13
14
 
15
+ #: () -> bool
14
16
  def failed?
15
17
  errors.any? || value.errors.any? # TODO: this should probably be recursive
16
18
  end
17
19
 
20
+ #: () -> bool
18
21
  def success?
19
22
  !failed?
20
23
  end
21
24
 
25
+ #: () -> String
22
26
  def pretty_errors
23
27
  JSON.pretty_generate(errors + value.errors)
24
28
  end
29
+
30
+ #: (Visitor) -> void
31
+ def visit(visitor)
32
+ value.accept(visitor)
33
+ end
25
34
  end
26
35
  end
data/lib/herb/position.rb CHANGED
@@ -1,34 +1,43 @@
1
1
  # frozen_string_literal: true
2
+ # typed: true
2
3
 
3
4
  module Herb
4
5
  class Position
5
- attr_reader :line, :column
6
+ attr_reader :line #: Integer
7
+ attr_reader :column #: Integer
6
8
 
9
+ #: (Integer, Integer) -> void
7
10
  def initialize(line, column)
8
11
  @line = line
9
12
  @column = column
10
13
  end
11
14
 
12
- def self.[](...)
13
- new(...)
15
+ #: (Integer, Integer) -> Position
16
+ def self.[](line, column)
17
+ new(line, column)
14
18
  end
15
19
 
16
- def self.from(...)
17
- new(...)
20
+ #: (Integer, Integer) -> Position
21
+ def self.from(line, column)
22
+ new(line, column)
18
23
  end
19
24
 
25
+ #: () -> serialized_position
20
26
  def to_hash
21
- { line: line, column: column }
27
+ { line: line, column: column } #: Herb::serialized_position
22
28
  end
23
29
 
24
- def to_json(*args)
25
- to_hash.to_json(*args)
30
+ #: (?untyped) -> String
31
+ def to_json(state = nil)
32
+ to_hash.to_json(state)
26
33
  end
27
34
 
35
+ #: () -> String
28
36
  def tree_inspect
29
37
  "(#{line}:#{column})"
30
38
  end
31
39
 
40
+ #: () -> String
32
41
  def inspect
33
42
  %(#<Herb::Position #{tree_inspect}>)
34
43
  end
data/lib/herb/project.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ # typed: ignore
3
+
4
+ # rbs_inline: disabled
2
5
 
3
6
  require "io/console"
4
7
  require "timeout"
@@ -221,10 +224,10 @@ module Herb
221
224
  summary = [
222
225
  heading("Summary"),
223
226
  "Total files: #{files.count}",
224
- "✅ Successful: #{successful_files.count}",
225
- "❌ Failed: #{failed_files.count}",
226
- "⚠️ Parse errors: #{error_files.count}",
227
- "⏱️ Timed out: #{timeout_files.count}"
227
+ "✅ Successful: #{successful_files.count} (#{percentage(successful_files.count, files.count)}%)",
228
+ "❌ Failed: #{failed_files.count} (#{percentage(failed_files.count, files.count)}%)",
229
+ "⚠️ Parse errors: #{error_files.count} (#{percentage(error_files.count, files.count)}%)",
230
+ "⏱️ Timed out: #{timeout_files.count} (#{percentage(timeout_files.count, files.count)}%)"
228
231
  ]
229
232
 
230
233
  summary.each do |line|
@@ -352,6 +355,12 @@ module Herb
352
355
  "[#{completed}#{partial}#{remaining}] #{percentage}% (#{current}/#{total})"
353
356
  end
354
357
 
358
+ def percentage(part, total)
359
+ return 0.0 if total.zero?
360
+
361
+ ((part.to_f / total) * 100).round(1)
362
+ end
363
+
355
364
  def heading(text)
356
365
  prefix = "--- #{text.upcase} "
357
366