tree_haver 5.0.5 → 7.1.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.md +13 -0
  4. data/README.md +299 -660
  5. data/lib/tree_haver/backend_api.rb +78 -35
  6. data/lib/tree_haver/backend_context.rb +28 -0
  7. data/lib/tree_haver/backend_registry.rb +119 -382
  8. data/lib/tree_haver/backends/citrus.rb +55 -53
  9. data/lib/tree_haver/backends/ffi.rb +105 -101
  10. data/lib/tree_haver/backends/java.rb +92 -76
  11. data/lib/tree_haver/backends/mri.rb +44 -39
  12. data/lib/tree_haver/backends/parslet.rb +53 -48
  13. data/lib/tree_haver/backends/prism.rb +140 -43
  14. data/lib/tree_haver/backends/psych.rb +30 -26
  15. data/lib/tree_haver/backends/rust.rb +21 -17
  16. data/lib/tree_haver/backends/tslp.rb +274 -0
  17. data/lib/tree_haver/base/comment.rb +320 -0
  18. data/lib/tree_haver/base/language.rb +2 -2
  19. data/lib/tree_haver/base/node.rb +39 -31
  20. data/lib/tree_haver/base/parser.rb +4 -0
  21. data/lib/tree_haver/base/point.rb +3 -3
  22. data/lib/tree_haver/base/tree.rb +1 -1
  23. data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
  24. data/lib/tree_haver/contracts.rb +1025 -0
  25. data/lib/tree_haver/grammar_finder.rb +125 -70
  26. data/lib/tree_haver/kaitai_backend.rb +30 -0
  27. data/lib/tree_haver/language.rb +36 -37
  28. data/lib/tree_haver/language_pack.rb +41 -0
  29. data/lib/tree_haver/language_registry.rb +34 -3
  30. data/lib/tree_haver/library_path_utils.rb +7 -7
  31. data/lib/tree_haver/node.rb +40 -31
  32. data/lib/tree_haver/parser.rb +44 -37
  33. data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
  34. data/lib/tree_haver/path_validator.rb +39 -36
  35. data/lib/tree_haver/peg_backends.rb +76 -0
  36. data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
  37. data/lib/tree_haver/rspec.rb +1 -31
  38. data/lib/tree_haver/tree.rb +16 -7
  39. data/lib/tree_haver/version.rb +5 -14
  40. data/lib/tree_haver.rb +536 -1240
  41. data/sig/tree_haver.rbs +1 -229
  42. data.tar.gz.sig +0 -0
  43. metadata +154 -70
  44. metadata.gz.sig +0 -0
  45. data/CHANGELOG.md +0 -1393
  46. data/CITATION.cff +0 -20
  47. data/CODE_OF_CONDUCT.md +0 -134
  48. data/CONTRIBUTING.md +0 -359
  49. data/FUNDING.md +0 -74
  50. data/LICENSE.txt +0 -21
  51. data/REEK +0 -0
  52. data/RUBOCOP.md +0 -71
  53. data/SECURITY.md +0 -21
  54. data/lib/tree_haver/base.rb +0 -12
  55. data/lib/tree_haver/compat.rb +0 -43
  56. data/lib/tree_haver/rspec/testable_node.rb +0 -217
  57. data/sig/tree_haver/backends.rbs +0 -352
  58. data/sig/tree_haver/grammar_finder.rbs +0 -29
  59. data/sig/tree_haver/path_validator.rbs +0 -32
@@ -0,0 +1,320 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeHaver
4
+ module Base
5
+ # Base class for backend comment wrappers.
6
+ #
7
+ # This defines the parser-facing contract for normalized comment wrappers in
8
+ # TreeHaver. Backends that can expose comment objects should subclass this and
9
+ # implement text/type/location accessors using their native parser data.
10
+ class Comment
11
+ ATTACHMENT_HINTS = %i[leading inline trailing].freeze
12
+
13
+ # The underlying backend-specific comment object.
14
+ # @return [Object]
15
+ attr_reader :inner_comment
16
+
17
+ # The source text used for fallback range extraction.
18
+ # @return [String, nil]
19
+ attr_reader :source
20
+
21
+ # Optional parser-provided attachment hint.
22
+ # @return [Symbol, nil]
23
+ attr_reader :attachment_hint
24
+
25
+ def initialize(comment, source: nil, attachment_hint: nil)
26
+ @inner_comment = comment
27
+ @source = source
28
+ @attachment_hint = normalize_attachment_hint(attachment_hint)
29
+ end
30
+
31
+ # Get the normalized comment type.
32
+ # Examples: "inline_comment", "block_comment".
33
+ # @return [String]
34
+ def type
35
+ raise NotImplementedError, "#{self.class}#type must be implemented"
36
+ end
37
+
38
+ # Get the comment text including delimiters when appropriate.
39
+ # @return [String]
40
+ def text
41
+ raise NotImplementedError, "#{self.class}#text must be implemented"
42
+ end
43
+
44
+ # Get the start byte offset of the comment.
45
+ # @return [Integer]
46
+ def start_byte
47
+ raise NotImplementedError, "#{self.class}#start_byte must be implemented"
48
+ end
49
+
50
+ # Get the end byte offset of the comment.
51
+ # @return [Integer]
52
+ def end_byte
53
+ raise NotImplementedError, "#{self.class}#end_byte must be implemented"
54
+ end
55
+
56
+ # Get the start position (row/column, 0-based).
57
+ # @return [Hash{Symbol => Integer}]
58
+ def start_point
59
+ { row: 0, column: 0 }
60
+ end
61
+
62
+ # Get the end position (row/column, 0-based).
63
+ # @return [Hash{Symbol => Integer}]
64
+ def end_point
65
+ { row: 0, column: 0 }
66
+ end
67
+
68
+ # Get the normalized delimiter style.
69
+ #
70
+ # @return [Symbol, nil]
71
+ def style
72
+ nil
73
+ end
74
+
75
+ # Get the opening delimiter for the comment, when the backend can provide it.
76
+ #
77
+ # Examples:
78
+ # - `#` for hash comments
79
+ # - `//` for C-style line comments
80
+ # - `<!--` for HTML/XML block comments
81
+ #
82
+ # @return [String, nil]
83
+ def opening_delimiter
84
+ nil
85
+ end
86
+
87
+ # Get the closing delimiter for the comment, when applicable.
88
+ #
89
+ # This is typically `nil` for line comments.
90
+ #
91
+ # @return [String, nil]
92
+ def closing_delimiter
93
+ nil
94
+ end
95
+
96
+ # Get the comment body with outer delimiters removed when possible.
97
+ #
98
+ # Backends with richer native comment models should override this when they
99
+ # can provide a more semantically accurate body than simple delimiter
100
+ # trimming.
101
+ #
102
+ # @return [String]
103
+ def body_text
104
+ extract_body_text(text.to_s)
105
+ end
106
+
107
+ # Get a matching-friendly normalized comment body.
108
+ #
109
+ # This strips leading and trailing whitespace from {#body_text} while
110
+ # preserving the raw rendered form in {#text}.
111
+ #
112
+ # @return [String]
113
+ def normalized_text
114
+ body_text.strip
115
+ end
116
+
117
+ # Get delimiter/body metadata in one normalized hash.
118
+ #
119
+ # @return [Hash{Symbol => String, nil}]
120
+ def delimiter_metadata
121
+ {
122
+ opening: opening_delimiter,
123
+ closing: closing_delimiter,
124
+ body: body_text
125
+ }
126
+ end
127
+
128
+ # Whether this comment uses a line-comment style.
129
+ #
130
+ # @return [Boolean]
131
+ def line?
132
+ style == :line
133
+ end
134
+
135
+ # Whether this comment uses a block-comment style.
136
+ #
137
+ # @return [Boolean]
138
+ def block?
139
+ style == :block
140
+ end
141
+
142
+ # Whether this comment spans multiple source lines.
143
+ #
144
+ # @return [Boolean]
145
+ def multiline?
146
+ start_line != end_line
147
+ end
148
+
149
+ # Whether this comment has a parser-provided attachment hint.
150
+ #
151
+ # @return [Boolean]
152
+ def attachment_hint?
153
+ !attachment_hint.nil?
154
+ end
155
+
156
+ # Whether this comment is hinted as leading its owner.
157
+ #
158
+ # @return [Boolean]
159
+ def leading?
160
+ attachment_hint == :leading
161
+ end
162
+
163
+ # Whether this comment is hinted as inline with its owner.
164
+ #
165
+ # @return [Boolean]
166
+ def inline?
167
+ attachment_hint == :inline
168
+ end
169
+
170
+ # Whether this comment is hinted as trailing its owner.
171
+ #
172
+ # @return [Boolean]
173
+ def trailing?
174
+ attachment_hint == :trailing
175
+ end
176
+
177
+ # Get the 1-based start line.
178
+ #
179
+ # @return [Integer]
180
+ def start_line
181
+ start_point[:row] + 1
182
+ end
183
+
184
+ # Get the 1-based end line.
185
+ #
186
+ # @return [Integer]
187
+ def end_line
188
+ end_point[:row] + 1
189
+ end
190
+
191
+ # Get a normalized source-position hash.
192
+ #
193
+ # @return [Hash{Symbol => Integer}]
194
+ def source_position
195
+ {
196
+ start_line: start_line,
197
+ end_line: end_line,
198
+ start_column: start_point[:column],
199
+ end_column: end_point[:column]
200
+ }
201
+ end
202
+
203
+ # Whether this comment starts at the first line of the available source.
204
+ #
205
+ # @return [Boolean]
206
+ def at_file_start?
207
+ start_line == 1
208
+ end
209
+
210
+ # Whether this comment ends at the final line of the available source.
211
+ #
212
+ # @return [Boolean]
213
+ def at_file_end?
214
+ return false if source_lines.none?
215
+
216
+ end_line >= source_lines.length
217
+ end
218
+
219
+ # Return the contiguous blank source lines immediately before this comment.
220
+ #
221
+ # This is parser-facing layout metadata. It does not decide ownership; it
222
+ # simply exposes spacing adjacent to the comment so merge layers can reason
223
+ # about floating vs attached behavior without rescanning source text.
224
+ #
225
+ # @return [Array<String>]
226
+ def blank_lines_before
227
+ return [] if at_file_start?
228
+
229
+ line_num = start_line - 1
230
+ blanks = []
231
+
232
+ while line_num >= 1
233
+ line = source_line(line_num)
234
+ break unless line && line.strip.empty?
235
+
236
+ blanks.unshift(line)
237
+ line_num -= 1
238
+ end
239
+
240
+ blanks
241
+ end
242
+
243
+ # Return the contiguous blank source lines immediately after this comment.
244
+ #
245
+ # @return [Array<String>]
246
+ def blank_lines_after
247
+ return [] if at_file_end?
248
+
249
+ line_num = end_line + 1
250
+ blanks = []
251
+
252
+ while line_num <= source_lines.length
253
+ line = source_line(line_num)
254
+ break unless line && line.strip.empty?
255
+
256
+ blanks << line
257
+ line_num += 1
258
+ end
259
+
260
+ blanks
261
+ end
262
+
263
+ # Return the count of immediately preceding blank source lines.
264
+ #
265
+ # @return [Integer]
266
+ def blank_line_count_before
267
+ blank_lines_before.length
268
+ end
269
+
270
+ # Return the count of immediately following blank source lines.
271
+ #
272
+ # @return [Integer]
273
+ def blank_line_count_after
274
+ blank_lines_after.length
275
+ end
276
+
277
+ def inspect
278
+ "#<#{self.class} type=#{type.inspect} range=#{start_byte}...#{end_byte}>"
279
+ end
280
+
281
+ private
282
+
283
+ def source_lines
284
+ @source_lines ||= begin
285
+ values = source.to_s.split("\n", -1)
286
+ values.pop if values.last&.empty? && source.to_s.end_with?("\n")
287
+ values
288
+ end
289
+ end
290
+
291
+ def source_line(line_number)
292
+ return if line_number < 1 || line_number > source_lines.length
293
+
294
+ source_lines[line_number - 1]
295
+ end
296
+
297
+ def extract_body_text(raw_text)
298
+ text_without_opening = if opening_delimiter
299
+ raw_text.sub(/\A#{Regexp.escape(opening_delimiter)}[ \t]?/, '')
300
+ else
301
+ raw_text
302
+ end
303
+
304
+ return text_without_opening unless closing_delimiter
305
+
306
+ text_without_opening.sub(/[ \t]*#{Regexp.escape(closing_delimiter)}\z/m, '')
307
+ end
308
+
309
+ def normalize_attachment_hint(hint)
310
+ return if hint.nil?
311
+
312
+ normalized = hint.to_sym
313
+ return normalized if ATTACHMENT_HINTS.include?(normalized)
314
+
315
+ raise ArgumentError,
316
+ "Unknown comment attachment hint: #{hint.inspect}. Expected one of: #{ATTACHMENT_HINTS.join(', ')}"
317
+ end
318
+ end
319
+ end
320
+ end
@@ -36,7 +36,7 @@ module TreeHaver
36
36
  end
37
37
 
38
38
  # Alias for name (tree-sitter compatibility)
39
- alias_method :language_name, :name
39
+ alias language_name name
40
40
 
41
41
  # -- Shared Implementation ------------------------------------------------
42
42
 
@@ -68,7 +68,7 @@ module TreeHaver
68
68
  # Human-readable representation
69
69
  # @return [String]
70
70
  def inspect
71
- opts = options.empty? ? "" : " options=#{options}"
71
+ opts = options.empty? ? '' : " options=#{options}"
72
72
  class_name = self.class.name || "#{self.class.superclass.name}(anonymous)"
73
73
  "#<#{class_name} name=#{name} backend=#{backend}#{opts}>"
74
74
  end
@@ -68,6 +68,14 @@ module TreeHaver
68
68
  raise NotImplementedError, "#{self.class}#type must be implemented"
69
69
  end
70
70
 
71
+ # The parser's original node type before a backend applies any portable
72
+ # vocabulary aliases. Backends without aliases use their public type.
73
+ #
74
+ # @return [String]
75
+ def native_type
76
+ type
77
+ end
78
+
71
79
  # Get byte offset where the node starts
72
80
  # @return [Integer] Start byte offset
73
81
  def start_byte
@@ -155,7 +163,7 @@ module TreeHaver
155
163
  end
156
164
 
157
165
  # Alias for named?
158
- alias_method :structural?, :named?
166
+ alias structural? named?
159
167
 
160
168
  # Check if this node represents a syntax error
161
169
  # @return [Boolean] true on error
@@ -172,9 +180,9 @@ module TreeHaver
172
180
  # Get the text content of this node
173
181
  # @return [String] Node text
174
182
  def text
175
- return "" unless source
183
+ return '' unless source
176
184
 
177
- source[start_byte...end_byte] || ""
185
+ source[start_byte...end_byte] || ''
178
186
  end
179
187
 
180
188
  # Get a child by field name
@@ -187,13 +195,13 @@ module TreeHaver
187
195
  # Get start position (row/col) - 0-based
188
196
  # @return [Hash{Symbol => Integer}] {row: 0, column: 0}
189
197
  def start_point
190
- {row: 0, column: 0}
198
+ { row: 0, column: 0 }
191
199
  end
192
200
 
193
201
  # Get end position (row/col) - 0-based
194
202
  # @return [Hash{Symbol => Integer}] {row: 0, column: 0}
195
203
  def end_point
196
- {row: 0, column: 0}
204
+ { row: 0, column: 0 }
197
205
  end
198
206
 
199
207
  # -- Shared Implementation -----------------------------------------------
@@ -215,10 +223,10 @@ module TreeHaver
215
223
  def start_line
216
224
  sp = start_point
217
225
  row = if sp.is_a?(Hash)
218
- sp[:row]
219
- else
220
- (sp.respond_to?(:row) ? sp.row : 0)
221
- end
226
+ sp[:row]
227
+ else
228
+ (sp.respond_to?(:row) ? sp.row : 0)
229
+ end
222
230
  row + 1
223
231
  end
224
232
 
@@ -227,10 +235,10 @@ module TreeHaver
227
235
  def end_line
228
236
  ep = end_point
229
237
  row = if ep.is_a?(Hash)
230
- ep[:row]
231
- else
232
- (ep.respond_to?(:row) ? ep.row : 0)
233
- end
238
+ ep[:row]
239
+ else
240
+ (ep.respond_to?(:row) ? ep.row : 0)
241
+ end
234
242
  row + 1
235
243
  end
236
244
 
@@ -241,31 +249,31 @@ module TreeHaver
241
249
  ep = end_point
242
250
 
243
251
  sp_row = if sp.is_a?(Hash)
244
- sp[:row]
245
- else
246
- (sp.respond_to?(:row) ? sp.row : 0)
247
- end
252
+ sp[:row]
253
+ else
254
+ (sp.respond_to?(:row) ? sp.row : 0)
255
+ end
248
256
  sp_col = if sp.is_a?(Hash)
249
- sp[:column]
250
- else
251
- (sp.respond_to?(:column) ? sp.column : 0)
252
- end
257
+ sp[:column]
258
+ else
259
+ (sp.respond_to?(:column) ? sp.column : 0)
260
+ end
253
261
  ep_row = if ep.is_a?(Hash)
254
- ep[:row]
255
- else
256
- (ep.respond_to?(:row) ? ep.row : 0)
257
- end
262
+ ep[:row]
263
+ else
264
+ (ep.respond_to?(:row) ? ep.row : 0)
265
+ end
258
266
  ep_col = if ep.is_a?(Hash)
259
- ep[:column]
260
- else
261
- (ep.respond_to?(:column) ? ep.column : 0)
262
- end
267
+ ep[:column]
268
+ else
269
+ (ep.respond_to?(:column) ? ep.column : 0)
270
+ end
263
271
 
264
272
  {
265
273
  start_line: sp_row + 1,
266
274
  end_line: ep_row + 1,
267
275
  start_column: sp_col,
268
- end_column: ep_col,
276
+ end_column: ep_col
269
277
  }
270
278
  end
271
279
 
@@ -276,7 +284,7 @@ module TreeHaver
276
284
  node_type = begin
277
285
  type
278
286
  rescue NotImplementedError
279
- "(not implemented)"
287
+ '(not implemented)'
280
288
  end
281
289
  "#<#{class_name} type=#{node_type}>"
282
290
  end
@@ -19,6 +19,10 @@ module TreeHaver
19
19
  def parse_string(_old_tree, source)
20
20
  parse(source)
21
21
  end
22
+
23
+ def backend
24
+ language.respond_to?(:backend) ? language.backend : nil
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -19,9 +19,9 @@ module TreeHaver
19
19
  # @return [Integer, nil]
20
20
  def [](key)
21
21
  case key
22
- when :row, "row", 0
22
+ when :row, 'row', 0
23
23
  row
24
- when :column, "column", 1
24
+ when :column, 'column', 1
25
25
  column
26
26
  end
27
27
  end
@@ -29,7 +29,7 @@ module TreeHaver
29
29
  # Convert to hash
30
30
  # @return [Hash{Symbol => Integer}]
31
31
  def to_h
32
- {row: row, column: column}
32
+ { row: row, column: column }
33
33
  end
34
34
 
35
35
  # String representation
@@ -79,7 +79,7 @@ module TreeHaver
79
79
  end
80
80
 
81
81
  # Get comments from the document
82
- # @return [Array] Comments (empty for most pure-Ruby backends)
82
+ # @return [Array] Backend comment wrappers (empty for most backends)
83
83
  def comments
84
84
  []
85
85
  end
@@ -67,7 +67,7 @@ module TreeHaver
67
67
  return @available if @load_attempted
68
68
 
69
69
  @load_attempted = true
70
- debug = ENV["TREE_HAVER_DEBUG"]
70
+ debug = ENV['TREE_HAVER_DEBUG']
71
71
 
72
72
  # Guard against nil require_path (can happen if gem_name was nil)
73
73
  if @require_path.nil? || @require_path.empty?
@@ -85,13 +85,13 @@ module TreeHaver
85
85
 
86
86
  # Verify it responds to parse
87
87
  unless @grammar_module.respond_to?(:parse)
88
- # :nocov: defensive - requires a gem with malformed grammar module
88
+ # simplecov:disable defensive - requires a gem with malformed grammar module
89
89
  # Show what methods ARE available to help diagnose the issue
90
90
  if debug
91
91
  available_methods = @grammar_module.methods(false).sort.first(20)
92
92
  warn("CitrusGrammarFinder: #{@grammar_const} doesn't respond to :parse")
93
93
  warn("CitrusGrammarFinder: #{@grammar_const}.class = #{@grammar_module.class}")
94
- warn("CitrusGrammarFinder: #{@grammar_const} is a #{@grammar_module.is_a?(Module) ? "Module" : "non-Module"}")
94
+ warn("CitrusGrammarFinder: #{@grammar_const} is a #{@grammar_module.is_a?(Module) ? 'Module' : 'non-Module'}")
95
95
  warn("CitrusGrammarFinder: Available singleton methods (first 20): #{available_methods.inspect}")
96
96
  if @grammar_module.respond_to?(:instance_methods)
97
97
  instance_methods = @grammar_module.instance_methods(false).sort.first(20)
@@ -100,49 +100,45 @@ module TreeHaver
100
100
  end
101
101
  @available = false
102
102
  return false
103
- # :nocov:
103
+ # simplecov:enable
104
104
  end
105
105
 
106
106
  @available = true
107
107
  rescue LoadError => e
108
- # :nocov: defensive - requires gem to not be installed
108
+ # simplecov:disable defensive - requires gem to not be installed
109
109
  # Only show LoadError details when debugging
110
110
  if debug
111
111
  warn("CitrusGrammarFinder: Failed to load '#{@require_path}': #{e.class}: #{e.message}")
112
112
  warn("CitrusGrammarFinder: LoadError backtrace:\n #{e.backtrace&.first(10)&.join("\n ")}")
113
113
  end
114
114
  @available = false
115
- # :nocov:
115
+ # simplecov:enable
116
116
  rescue NameError => e
117
- # :nocov: defensive - requires gem with missing constant
117
+ # simplecov:disable defensive - requires gem with missing constant
118
118
  # Only show NameError details when debugging
119
119
  if debug
120
120
  warn("CitrusGrammarFinder: Failed to resolve '#{@grammar_const}': #{e.class}: #{e.message}")
121
121
  warn("CitrusGrammarFinder: NameError backtrace:\n #{e.backtrace&.first(10)&.join("\n ")}")
122
122
  end
123
123
  @available = false
124
- # :nocov:
124
+ # simplecov:enable
125
125
  rescue TypeError => e
126
- # :nocov: defensive - TruffleRuby-specific edge case
126
+ # simplecov:disable defensive - TruffleRuby-specific edge case
127
127
  # TruffleRuby's bundled_gems.rb can raise TypeError when File.path is called on nil
128
128
  # This happens in bundled_gems.rb:124 warning? method when caller locations return nil
129
129
  # Always warn about TypeError as it indicates a platform-specific issue
130
130
  warn("CitrusGrammarFinder: TypeError during load of '#{@require_path}': #{e.class}: #{e.message}")
131
- warn("CitrusGrammarFinder: This may be a TruffleRuby bundled_gems.rb issue")
132
- if debug
133
- warn("CitrusGrammarFinder: TypeError backtrace:\n #{e.backtrace&.first(10)&.join("\n ")}")
134
- end
131
+ warn('CitrusGrammarFinder: This may be a TruffleRuby bundled_gems.rb issue')
132
+ warn("CitrusGrammarFinder: TypeError backtrace:\n #{e.backtrace&.first(10)&.join("\n ")}") if debug
135
133
  @available = false
136
- # :nocov:
137
- rescue => e
138
- # :nocov: defensive - catch-all for unexpected errors
134
+ # simplecov:enable
135
+ rescue StandardError => e
136
+ # simplecov:disable defensive - catch-all for unexpected errors
139
137
  # Always warn about unexpected errors
140
138
  warn("CitrusGrammarFinder: Unexpected error: #{e.class}: #{e.message}")
141
- if debug
142
- warn("CitrusGrammarFinder: backtrace:\n #{e.backtrace&.first(10)&.join("\n ")}")
143
- end
139
+ warn("CitrusGrammarFinder: backtrace:\n #{e.backtrace&.first(10)&.join("\n ")}") if debug
144
140
  @available = false
145
- # :nocov:
141
+ # simplecov:enable
146
142
  end
147
143
 
148
144
  @available
@@ -166,16 +162,15 @@ module TreeHaver
166
162
  # @raise [NotAvailable] if grammar not available and raise_on_missing is true
167
163
  def register!(raise_on_missing: false)
168
164
  unless available?
169
- if raise_on_missing
170
- raise NotAvailable, not_found_message
171
- end
165
+ raise NotAvailable, not_found_message if raise_on_missing
166
+
172
167
  return false
173
168
  end
174
169
 
175
170
  TreeHaver.register_language(
176
171
  @language_name,
177
172
  grammar_module: @grammar_module,
178
- gem_name: @gem_name,
173
+ gem_name: @gem_name
179
174
  )
180
175
  true
181
176
  end
@@ -190,7 +185,7 @@ module TreeHaver
190
185
  grammar_const: @grammar_const,
191
186
  require_path: @require_path,
192
187
  available: available?,
193
- grammar_module: @grammar_module&.name,
188
+ grammar_module: @grammar_module&.name
194
189
  }
195
190
  end
196
191
 
@@ -210,7 +205,7 @@ module TreeHaver
210
205
  # @return [Object] the constant
211
206
  # @raise [NameError] if constant not found
212
207
  def resolve_constant(const_path)
213
- const_path.split("::").reduce(Object) do |mod, const_name|
208
+ const_path.split('::').reduce(Object) do |mod, const_name|
214
209
  mod.const_get(const_name)
215
210
  end
216
211
  end