ast-merge 1.0.0 → 2.0.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 (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +194 -1
  4. data/README.md +235 -53
  5. data/exe/ast-merge-recipe +366 -0
  6. data/lib/ast/merge/ast_node.rb +224 -24
  7. data/lib/ast/merge/comment/block.rb +6 -0
  8. data/lib/ast/merge/comment/empty.rb +6 -0
  9. data/lib/ast/merge/comment/line.rb +6 -0
  10. data/lib/ast/merge/comment/parser.rb +9 -7
  11. data/lib/ast/merge/conflict_resolver_base.rb +8 -1
  12. data/lib/ast/merge/content_match_refiner.rb +278 -0
  13. data/lib/ast/merge/debug_logger.rb +6 -1
  14. data/lib/ast/merge/detector/base.rb +193 -0
  15. data/lib/ast/merge/detector/fenced_code_block.rb +227 -0
  16. data/lib/ast/merge/detector/mergeable.rb +369 -0
  17. data/lib/ast/merge/detector/toml_frontmatter.rb +82 -0
  18. data/lib/ast/merge/detector/yaml_frontmatter.rb +82 -0
  19. data/lib/ast/merge/file_analyzable.rb +5 -3
  20. data/lib/ast/merge/freeze_node_base.rb +1 -1
  21. data/lib/ast/merge/match_refiner_base.rb +1 -1
  22. data/lib/ast/merge/match_score_base.rb +1 -1
  23. data/lib/ast/merge/merge_result_base.rb +4 -1
  24. data/lib/ast/merge/merger_config.rb +33 -31
  25. data/lib/ast/merge/navigable_statement.rb +630 -0
  26. data/lib/ast/merge/partial_template_merger.rb +432 -0
  27. data/lib/ast/merge/recipe/config.rb +198 -0
  28. data/lib/ast/merge/recipe/preset.rb +171 -0
  29. data/lib/ast/merge/recipe/runner.rb +254 -0
  30. data/lib/ast/merge/recipe/script_loader.rb +181 -0
  31. data/lib/ast/merge/recipe.rb +26 -0
  32. data/lib/ast/merge/rspec/dependency_tags.rb +252 -0
  33. data/lib/ast/merge/rspec/shared_examples/reproducible_merge.rb +3 -2
  34. data/lib/ast/merge/rspec.rb +33 -2
  35. data/lib/ast/merge/section_typing.rb +52 -50
  36. data/lib/ast/merge/smart_merger_base.rb +86 -3
  37. data/lib/ast/merge/text/line_node.rb +42 -9
  38. data/lib/ast/merge/text/section_splitter.rb +12 -10
  39. data/lib/ast/merge/text/word_node.rb +47 -14
  40. data/lib/ast/merge/version.rb +1 -1
  41. data/lib/ast/merge.rb +10 -6
  42. data/sig/ast/merge.rbs +389 -2
  43. data.tar.gz.sig +0 -0
  44. metadata +76 -12
  45. metadata.gz.sig +0 -0
  46. data/lib/ast/merge/fenced_code_block_detector.rb +0 -211
  47. data/lib/ast/merge/region.rb +0 -124
  48. data/lib/ast/merge/region_detector_base.rb +0 -114
  49. data/lib/ast/merge/region_mergeable.rb +0 -364
  50. data/lib/ast/merge/toml_frontmatter_detector.rb +0 -88
  51. data/lib/ast/merge/yaml_frontmatter_detector.rb +0 -108
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ast-merge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -57,6 +57,26 @@ dependencies:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
59
  version: 1.1.9
60
+ - !ruby/object:Gem::Dependency
61
+ name: tree_haver
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '3.1'
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 3.1.1
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.1'
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 3.1.1
60
80
  - !ruby/object:Gem::Dependency
61
81
  name: kettle-dev
62
82
  requirement: !ruby/object:Gem::Requirement
@@ -133,6 +153,40 @@ dependencies:
133
153
  - - "~>"
134
154
  - !ruby/object:Gem::Version
135
155
  version: '3.0'
156
+ - !ruby/object:Gem::Dependency
157
+ name: benchmark
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: '0.5'
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '0.5'
170
+ - !ruby/object:Gem::Dependency
171
+ name: kettle-soup-cover
172
+ requirement: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: '1.1'
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: 1.1.1
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '1.1'
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 1.1.1
136
190
  - !ruby/object:Gem::Dependency
137
191
  name: kettle-test
138
192
  requirement: !ruby/object:Gem::Requirement
@@ -212,7 +266,8 @@ description: "☯️ Ast::Merge provides base classes, modules, and RSpec shared
212
266
  psych-merge, json-merge, and other format-specific merge gems."
213
267
  email:
214
268
  - floss@galtzo.com
215
- executables: []
269
+ executables:
270
+ - ast-merge-recipe
216
271
  extensions: []
217
272
  extra_rdoc_files:
218
273
  - CHANGELOG.md
@@ -236,6 +291,7 @@ files:
236
291
  - REEK
237
292
  - RUBOCOP.md
238
293
  - SECURITY.md
294
+ - exe/ast-merge-recipe
239
295
  - lib/ast-merge.rb
240
296
  - lib/ast/merge.rb
241
297
  - lib/ast/merge/ast_node.rb
@@ -246,8 +302,13 @@ files:
246
302
  - lib/ast/merge/comment/parser.rb
247
303
  - lib/ast/merge/comment/style.rb
248
304
  - lib/ast/merge/conflict_resolver_base.rb
305
+ - lib/ast/merge/content_match_refiner.rb
249
306
  - lib/ast/merge/debug_logger.rb
250
- - lib/ast/merge/fenced_code_block_detector.rb
307
+ - lib/ast/merge/detector/base.rb
308
+ - lib/ast/merge/detector/fenced_code_block.rb
309
+ - lib/ast/merge/detector/mergeable.rb
310
+ - lib/ast/merge/detector/toml_frontmatter.rb
311
+ - lib/ast/merge/detector/yaml_frontmatter.rb
251
312
  - lib/ast/merge/file_analyzable.rb
252
313
  - lib/ast/merge/freezable.rb
253
314
  - lib/ast/merge/freeze_node_base.rb
@@ -255,11 +316,16 @@ files:
255
316
  - lib/ast/merge/match_score_base.rb
256
317
  - lib/ast/merge/merge_result_base.rb
257
318
  - lib/ast/merge/merger_config.rb
319
+ - lib/ast/merge/navigable_statement.rb
258
320
  - lib/ast/merge/node_typing.rb
259
- - lib/ast/merge/region.rb
260
- - lib/ast/merge/region_detector_base.rb
261
- - lib/ast/merge/region_mergeable.rb
321
+ - lib/ast/merge/partial_template_merger.rb
322
+ - lib/ast/merge/recipe.rb
323
+ - lib/ast/merge/recipe/config.rb
324
+ - lib/ast/merge/recipe/preset.rb
325
+ - lib/ast/merge/recipe/runner.rb
326
+ - lib/ast/merge/recipe/script_loader.rb
262
327
  - lib/ast/merge/rspec.rb
328
+ - lib/ast/merge/rspec/dependency_tags.rb
263
329
  - lib/ast/merge/rspec/shared_examples.rb
264
330
  - lib/ast/merge/rspec/shared_examples/conflict_resolver_base.rb
265
331
  - lib/ast/merge/rspec/shared_examples/debug_logger.rb
@@ -279,19 +345,17 @@ files:
279
345
  - lib/ast/merge/text/section_splitter.rb
280
346
  - lib/ast/merge/text/smart_merger.rb
281
347
  - lib/ast/merge/text/word_node.rb
282
- - lib/ast/merge/toml_frontmatter_detector.rb
283
348
  - lib/ast/merge/version.rb
284
- - lib/ast/merge/yaml_frontmatter_detector.rb
285
349
  - sig/ast/merge.rbs
286
350
  homepage: https://github.com/kettle-rb/ast-merge
287
351
  licenses:
288
352
  - MIT
289
353
  metadata:
290
354
  homepage_uri: https://ast-merge.galtzo.com/
291
- source_code_uri: https://github.com/kettle-rb/ast-merge/tree/v1.0.0
292
- changelog_uri: https://github.com/kettle-rb/ast-merge/blob/v1.0.0/CHANGELOG.md
355
+ source_code_uri: https://github.com/kettle-rb/ast-merge/tree/v2.0.0
356
+ changelog_uri: https://github.com/kettle-rb/ast-merge/blob/v2.0.0/CHANGELOG.md
293
357
  bug_tracker_uri: https://github.com/kettle-rb/ast-merge/issues
294
- documentation_uri: https://www.rubydoc.info/gems/ast-merge/1.0.0
358
+ documentation_uri: https://www.rubydoc.info/gems/ast-merge/2.0.0
295
359
  funding_uri: https://github.com/sponsors/pboling
296
360
  wiki_uri: https://github.com/kettle-rb/ast-merge/wiki
297
361
  news_uri: https://www.railsbling.com/tags/ast-merge
@@ -320,7 +384,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
320
384
  - !ruby/object:Gem::Version
321
385
  version: '0'
322
386
  requirements: []
323
- rubygems_version: 4.0.1
387
+ rubygems_version: 4.0.3
324
388
  specification_version: 4
325
389
  summary: "☯️ Shared infrastructure for the *-merge gem family"
326
390
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,211 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ast
4
- module Merge
5
- # Detects fenced code blocks with a specific language identifier.
6
- #
7
- # This detector finds Markdown-style fenced code blocks (using ``` or ~~~)
8
- # that have a specific language identifier. It can be configured for any
9
- # language: ruby, json, yaml, mermaid, etc.
10
- #
11
- # @example Detecting Ruby code blocks
12
- # detector = FencedCodeBlockDetector.new("ruby", aliases: ["rb"])
13
- # regions = detector.detect_all(markdown_source)
14
- #
15
- # @example Using factory methods
16
- # detector = FencedCodeBlockDetector.ruby
17
- # detector = FencedCodeBlockDetector.yaml
18
- # detector = FencedCodeBlockDetector.json
19
- #
20
- # @api public
21
- class FencedCodeBlockDetector < RegionDetectorBase
22
- # @return [String] The primary language identifier
23
- attr_reader :language
24
-
25
- # @return [Array<String>] Alternative language identifiers
26
- attr_reader :aliases
27
-
28
- # Creates a new detector for the specified language.
29
- #
30
- # @param language [String, Symbol] The language identifier (e.g., "ruby", "json")
31
- # @param aliases [Array<String, Symbol>] Alternative identifiers (e.g., ["rb"] for ruby)
32
- def initialize(language, aliases: [])
33
- super()
34
- @language = language.to_s.downcase
35
- @aliases = aliases.map { |a| a.to_s.downcase }
36
- @all_identifiers = [@language] + @aliases
37
- end
38
-
39
- # @return [Symbol] The region type (e.g., :ruby_code_block)
40
- def region_type
41
- :"#{@language}_code_block"
42
- end
43
-
44
- # Check if a language identifier matches this detector.
45
- #
46
- # @param lang [String] The language identifier to check
47
- # @return [Boolean] true if the language matches
48
- def matches_language?(lang)
49
- @all_identifiers.include?(lang.to_s.downcase)
50
- end
51
-
52
- # Detects all fenced code blocks with the configured language.
53
- #
54
- # @param source [String] The full document content
55
- # @return [Array<Region>] All detected code blocks, sorted by start_line
56
- def detect_all(source)
57
- return [] if source.nil? || source.empty?
58
-
59
- regions = []
60
- lines = source.lines
61
- in_block = false
62
- start_line = nil
63
- content_lines = []
64
- current_language = nil
65
- fence_char = nil
66
- fence_length = nil
67
- indent = ""
68
-
69
- lines.each_with_index do |line, idx|
70
- line_num = idx + 1
71
-
72
- if !in_block
73
- # Match opening fence: ```lang or ~~~lang (optionally indented)
74
- match = line.match(/^(\s*)(`{3,}|~{3,})(\w*)\s*$/)
75
- if match
76
- indent = match[1] || ""
77
- fence = match[2]
78
- lang = match[3].downcase
79
-
80
- if @all_identifiers.include?(lang)
81
- in_block = true
82
- start_line = line_num
83
- content_lines = []
84
- current_language = lang
85
- fence_char = fence[0]
86
- fence_length = fence.length
87
- end
88
- end
89
- elsif line.match?(/^#{Regexp.escape(indent)}#{Regexp.escape(fence_char)}{#{fence_length},}\s*$/)
90
- # Match closing fence (must use same char, same indent, and at least same length)
91
- opening_fence = "#{fence_char * fence_length}#{current_language}"
92
- closing_fence = fence_char * fence_length
93
-
94
- regions << build_region(
95
- type: region_type,
96
- content: content_lines.join,
97
- start_line: start_line,
98
- end_line: line_num,
99
- delimiters: [opening_fence, closing_fence],
100
- metadata: {language: current_language, indent: indent.empty? ? nil : indent},
101
- )
102
- in_block = false
103
- start_line = nil
104
- content_lines = []
105
- current_language = nil
106
- fence_char = nil
107
- fence_length = nil
108
- indent = ""
109
- else
110
- # Accumulate content lines (strip the indent if present)
111
- content_lines << if indent.empty?
112
- line
113
- else
114
- # Strip the common indent from content lines
115
- line.sub(/^#{Regexp.escape(indent)}/, "")
116
- end
117
- end
118
- end
119
-
120
- # Note: Unclosed blocks are ignored (no region created)
121
- regions
122
- end
123
-
124
- # @return [String] A description of this detector
125
- def inspect
126
- aliases_str = @aliases.empty? ? "" : " aliases=#{@aliases.inspect}"
127
- "#<#{self.class.name} language=#{@language}#{aliases_str}>"
128
- end
129
-
130
- class << self
131
- # Creates a detector for Ruby code blocks.
132
- # @return [FencedCodeBlockDetector]
133
- def ruby
134
- new("ruby", aliases: ["rb"])
135
- end
136
-
137
- # Creates a detector for JSON code blocks.
138
- # @return [FencedCodeBlockDetector]
139
- def json
140
- new("json")
141
- end
142
-
143
- # Creates a detector for YAML code blocks.
144
- # @return [FencedCodeBlockDetector]
145
- def yaml
146
- new("yaml", aliases: ["yml"])
147
- end
148
-
149
- # Creates a detector for TOML code blocks.
150
- # @return [FencedCodeBlockDetector]
151
- def toml
152
- new("toml")
153
- end
154
-
155
- # Creates a detector for Mermaid diagram blocks.
156
- # @return [FencedCodeBlockDetector]
157
- def mermaid
158
- new("mermaid")
159
- end
160
-
161
- # Creates a detector for JavaScript code blocks.
162
- # @return [FencedCodeBlockDetector]
163
- def javascript
164
- new("javascript", aliases: ["js"])
165
- end
166
-
167
- # Creates a detector for TypeScript code blocks.
168
- # @return [FencedCodeBlockDetector]
169
- def typescript
170
- new("typescript", aliases: ["ts"])
171
- end
172
-
173
- # Creates a detector for Python code blocks.
174
- # @return [FencedCodeBlockDetector]
175
- def python
176
- new("python", aliases: ["py"])
177
- end
178
-
179
- # Creates a detector for Bash/Shell code blocks.
180
- # @return [FencedCodeBlockDetector]
181
- def bash
182
- new("bash", aliases: ["sh", "shell", "zsh"])
183
- end
184
-
185
- # Creates a detector for SQL code blocks.
186
- # @return [FencedCodeBlockDetector]
187
- def sql
188
- new("sql")
189
- end
190
-
191
- # Creates a detector for HTML code blocks.
192
- # @return [FencedCodeBlockDetector]
193
- def html
194
- new("html")
195
- end
196
-
197
- # Creates a detector for CSS code blocks.
198
- # @return [FencedCodeBlockDetector]
199
- def css
200
- new("css")
201
- end
202
-
203
- # Creates a detector for Markdown code blocks (nested markdown).
204
- # @return [FencedCodeBlockDetector]
205
- def markdown
206
- new("markdown", aliases: ["md"])
207
- end
208
- end
209
- end
210
- end
211
- end
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ast
4
- module Merge
5
- # Represents a detected region within a document.
6
- #
7
- # Regions are portions of a document that can be handled by a specialized
8
- # merger. For example, YAML frontmatter in a Markdown file, or a Ruby code
9
- # block that should be merged using a Ruby-aware merger.
10
- #
11
- # @example Creating a region for YAML frontmatter
12
- # Region.new(
13
- # type: :yaml_frontmatter,
14
- # content: "title: My Doc\nversion: 1.0\n",
15
- # start_line: 1,
16
- # end_line: 4,
17
- # delimiters: ["---", "---"],
18
- # metadata: { format: :yaml }
19
- # )
20
- #
21
- # @example Creating a region for a Ruby code block
22
- # Region.new(
23
- # type: :ruby_code_block,
24
- # content: "def hello\n puts 'world'\nend\n",
25
- # start_line: 5,
26
- # end_line: 9,
27
- # delimiters: ["```ruby", "```"],
28
- # metadata: { language: "ruby" }
29
- # )
30
- #
31
- # @api public
32
- Region = Struct.new(
33
- # @return [Symbol] The type of region (e.g., :yaml_frontmatter, :ruby_code_block)
34
- :type,
35
-
36
- # @return [String] The raw string content of this region (inner content, without delimiters)
37
- :content,
38
-
39
- # @return [Integer] 1-indexed start line in the original document
40
- :start_line,
41
-
42
- # @return [Integer] 1-indexed end line in the original document
43
- :end_line,
44
-
45
- # @return [Array<String>, nil] Delimiter strings to reconstruct the region
46
- # ["```ruby", "```"] - [opening_delimiter, closing_delimiter]
47
- :delimiters,
48
-
49
- # @return [Hash, nil] Optional metadata for detector-specific information
50
- # (e.g., { language: "ruby" }, { format: :yaml })
51
- :metadata,
52
- keyword_init: true,
53
- ) do
54
- # Returns the line range covered by this region.
55
- #
56
- # @return [Range] The range from start_line to end_line (inclusive)
57
- # @example
58
- # region.line_range # => 1..4
59
- def line_range
60
- start_line..end_line
61
- end
62
-
63
- # Returns the number of lines this region spans.
64
- #
65
- # @return [Integer] The number of lines
66
- # @example
67
- # region.line_count # => 4
68
- def line_count
69
- end_line - start_line + 1
70
- end
71
-
72
- # Reconstructs the full region text including delimiters.
73
- #
74
- # @return [String] The complete region with start and end delimiters
75
- # @example
76
- # region.full_text
77
- # # => "```ruby\ndef hello\n puts 'world'\nend\n```"
78
- def full_text
79
- return content if delimiters.nil? || delimiters.empty?
80
-
81
- opening = delimiters[0] || ""
82
- closing = delimiters[1] || ""
83
- "#{opening}\n#{content}#{closing}"
84
- end
85
-
86
- # Checks if this region overlaps with the given line number.
87
- #
88
- # @param line [Integer] The line number to check (1-indexed)
89
- # @return [Boolean] true if the line is within this region
90
- def contains_line?(line)
91
- line_range.cover?(line)
92
- end
93
-
94
- # Checks if this region overlaps with another region.
95
- #
96
- # @param other [Region] Another region to check for overlap
97
- # @return [Boolean] true if the regions overlap
98
- def overlaps?(other)
99
- line_range.cover?(other.start_line) ||
100
- line_range.cover?(other.end_line) ||
101
- other.line_range.cover?(start_line)
102
- end
103
-
104
- # Returns a short string representation of the region.
105
- #
106
- # @return [String] A concise string describing the region
107
- def to_s
108
- "Region<#{type}:#{start_line}-#{end_line}>"
109
- end
110
-
111
- # Returns a detailed human-readable representation of the region.
112
- #
113
- # @return [String] A string describing the region with truncated content
114
- def inspect
115
- truncated = if content && content.length > 30
116
- "#{content[0, 30]}..."
117
- else
118
- content.inspect
119
- end
120
- "#{self} #{truncated}"
121
- end
122
- end
123
- end
124
- end
@@ -1,114 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ast
4
- module Merge
5
- # Base class for region detection.
6
- #
7
- # Region detectors identify portions of a document that should be handled
8
- # by a specialized merger. For example, detecting YAML frontmatter in a
9
- # Markdown file, or Ruby code blocks that should be merged with Prism.
10
- #
11
- # Subclasses must implement:
12
- # - {#region_type} - Returns the type symbol for detected regions
13
- # - {#detect_all} - Finds all regions of this type in a document
14
- #
15
- # @example Implementing a custom detector
16
- # class MyBlockDetector < Ast::Merge::RegionDetectorBase
17
- # def region_type
18
- # :my_block
19
- # end
20
- #
21
- # def detect_all(source)
22
- # # Return array of Region structs
23
- # []
24
- # end
25
- # end
26
- #
27
- # @abstract Subclass and implement {#region_type} and {#detect_all}
28
- # @api public
29
- class RegionDetectorBase
30
- # Returns the type symbol for regions detected by this detector.
31
- #
32
- # This symbol is used to identify the region type in the Region struct
33
- # and for matching regions between template and destination documents.
34
- #
35
- # @return [Symbol] The region type (e.g., :yaml_frontmatter, :ruby_code_block)
36
- # @abstract Subclasses must implement this method
37
- def region_type
38
- raise NotImplementedError, "#{self.class}#region_type must be implemented"
39
- end
40
-
41
- # Detects all regions of this type in the given source.
42
- #
43
- # @param source [String] The full document content to scan
44
- # @return [Array<Region>] All detected regions, sorted by start_line
45
- # @abstract Subclasses must implement this method
46
- #
47
- # @example Return value structure
48
- # [
49
- # Region.new(
50
- # type: :yaml_frontmatter,
51
- # content: "title: My Doc\n",
52
- # start_line: 1,
53
- # end_line: 3,
54
- # delimiters: { start: "---\n", end: "---\n" },
55
- # metadata: { format: :yaml }
56
- # )
57
- # ]
58
- def detect_all(source)
59
- raise NotImplementedError, "#{self.class}#detect_all must be implemented"
60
- end
61
-
62
- # Whether to strip delimiters from content before passing to merger.
63
- #
64
- # When true (default), only the inner content is passed to the region's
65
- # merger. The delimiters are stored separately and reattached after merging.
66
- #
67
- # When false, the full content including delimiters is passed to the merger,
68
- # which must then handle the delimiters itself.
69
- #
70
- # @return [Boolean] true if delimiters should be stripped (default: true)
71
- def strip_delimiters?
72
- true
73
- end
74
-
75
- # A human-readable name for this detector.
76
- #
77
- # Used in error messages and debugging output.
78
- #
79
- # @return [String] The detector name
80
- def name
81
- self.class.name || "AnonymousDetector"
82
- end
83
-
84
- # Returns a string representation of this detector.
85
- #
86
- # @return [String] A description of the detector
87
- def inspect
88
- "#<#{name} region_type=#{region_type}>"
89
- end
90
-
91
- protected
92
-
93
- # Helper to build a Region struct with common defaults.
94
- #
95
- # @param type [Symbol] The region type
96
- # @param content [String] The inner content (without delimiters)
97
- # @param start_line [Integer] 1-indexed start line
98
- # @param end_line [Integer] 1-indexed end line
99
- # @param delimiters [Hash, nil] { start: String, end: String }
100
- # @param metadata [Hash, nil] Additional metadata
101
- # @return [Region] A new Region struct
102
- def build_region(type:, content:, start_line:, end_line:, delimiters: nil, metadata: nil)
103
- Region.new(
104
- type: type,
105
- content: content,
106
- start_line: start_line,
107
- end_line: end_line,
108
- delimiters: delimiters,
109
- metadata: metadata || {},
110
- )
111
- end
112
- end
113
- end
114
- end