ruby-merge 7.1.1 → 7.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98b5dba95c151b6d6a08ae403679f78af429360fcd4b2a1e0c5ba023cfc5639e
4
- data.tar.gz: cb85a47f7e47fbb0af358f69f8ce0503b790967f76aa04a0ad99282711cede45
3
+ metadata.gz: aa7e44e52e059397107051f945efaceb307742e3e40c36f61bf900ac06dd84e6
4
+ data.tar.gz: 1bb04482ae836481586da92f9266f0e03781d351b5ac25eb7eaf7fd83ad4f649
5
5
  SHA512:
6
- metadata.gz: 830b7a910c65e024d2d160150c8efe01e0f2b1a9e789fde40b072d4bb45e0ffb60f1a6643f7d07c392661c8736af207da7a994651a35328660c478787316edcd
7
- data.tar.gz: 48538e4ce4cb76543058e5a14fe94c8bc4bc3ed03f75d195c5e4d713d6e478d1e0ac4ffbf4307218a22f87918cc06ba647f4fc8037422d5fcb6a3aa89457f29a
6
+ metadata.gz: 1828b958a01c2017de25ef21616c5a650cf3483b20804e93c54f9158a40c8eac146bdd6cde14118fae50b2fc6e85b439072a5bce5596cadb0ee2248213fab509
7
+ data.tar.gz: 12f7bb144511bc0d9ff4ba0f42241666ce394f416e1e2444fd329930aae8444ce158cbee7a950a5ddccb93398816b466083ab2f40d7faf032825fdb4a934e713
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -46,7 +46,7 @@ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-ta
46
46
 
47
47
  ### Compatibility
48
48
 
49
- Compatible with MRI Ruby 4.0.0+, and concordant releases of JRuby, and TruffleRuby.
49
+ Compatible with MRI Ruby 4.0.0+, JRuby, and TruffleRuby.
50
50
  CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
51
51
  This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
52
52
  may be higher than the gem's runtime compatibility floor when legacy Rubies are
@@ -122,20 +122,12 @@ See [SECURITY.md][🔐security].
122
122
  ## 🤝 Contributing
123
123
 
124
124
  If you need some ideas of where to help, you could work on adding more code coverage,
125
- or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
126
- or use the gem and think about how it could be better.
125
+ check [issues][🤝gh-issues] or [PRs][🤝gh-pulls], or use the gem and think about how it could be better.
127
126
 
128
127
  We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
129
128
 
130
129
  See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
131
130
 
132
- ### Code Coverage
133
-
134
- <details markdown="1">
135
- <summary>Coverage service badges</summary>
136
-
137
- </details>
138
-
139
131
  ## 📌 Versioning
140
132
 
141
133
  This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruby
4
+ # Portable Ruby workflow integration.
5
+ module Merge
6
+ # Ruby workflow selector delegating source-preserving operations to Prism.
7
+ class Provider
8
+ BACKEND = :prism
9
+ DELEGATED_PROVIDER_ID = 'ruby.ruby.prism'
10
+ DEFAULT_PROFILE = :source_preserving
11
+
12
+ def provider_id = 'ruby.ruby'
13
+ def family = 'ruby'
14
+
15
+ def capabilities
16
+ {
17
+ operations: Ast::Merge::ProviderContract::OPERATIONS,
18
+ dialects: %i[ruby],
19
+ backends: [BACKEND],
20
+ profiles: [DEFAULT_PROFILE],
21
+ role: :workflow,
22
+ source_preservation: Prism::Merge.merge_provider.capabilities.fetch(:source_preservation)
23
+ }.freeze
24
+ end
25
+
26
+ Ast::Merge::ProviderContract::OPERATIONS.each do |operation|
27
+ define_method(operation) do |request|
28
+ delegated = Ast::Merge.dispatch_provider(operation, delegated_request(request))
29
+ delegated.merge(provider: workflow_identity(request, delegated))
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def delegated_request(request)
36
+ request.merge(
37
+ provider_id: DELEGATED_PROVIDER_ID,
38
+ family: :ruby,
39
+ dialect: :ruby,
40
+ backend: BACKEND,
41
+ profile_id: DEFAULT_PROFILE
42
+ )
43
+ end
44
+
45
+ def workflow_identity(request, delegated)
46
+ {
47
+ provider_id: provider_id,
48
+ family: family,
49
+ dialect: request[:dialect] || :ruby,
50
+ backend: request[:backend] || BACKEND,
51
+ package: PACKAGE_NAME,
52
+ package_version: Ruby::Merge::Version::VERSION,
53
+ delegated_provider: delegated_identity(delegated.fetch(:provider))
54
+ }
55
+ end
56
+
57
+ def delegated_identity(identity)
58
+ {
59
+ provider_id: identity.fetch(:provider_id),
60
+ backend: identity.fetch(:backend),
61
+ package: identity[:package],
62
+ package_version: identity[:package_version]
63
+ }.compact
64
+ end
65
+ end
66
+
67
+ class << self
68
+ def merge_provider
69
+ @merge_provider ||= Provider.new
70
+ end
71
+
72
+ def register_provider!(replace: false)
73
+ Ast::Merge.register_provider(merge_provider, replace: replace)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -5,7 +5,7 @@ module Ruby
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = '7.1.1'
8
+ VERSION = '7.1.3'
9
9
  end
10
10
  # Current gem version exposed at the traditional constant location.
11
11
  VERSION = Version::VERSION # Traditional Constant Location
data/lib/ruby/merge.rb CHANGED
@@ -1,20 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'digest'
4
+ require 'prism/merge'
4
5
  require 'tree_haver'
5
6
  require 'ast/merge'
6
7
  require_relative 'merge/version'
7
- require_relative 'merge/block_directive_detector'
8
- require_relative 'merge/block_binding_support'
9
- require_relative 'merge/doc_comment_support'
10
- require_relative 'merge/gemspec_support'
11
- require_relative 'merge/magic_comment_support'
12
- require_relative 'merge/method_similarity'
13
- require_relative 'merge/nocov_node_base'
14
- require_relative 'merge/nocov_wrapper_base'
15
- require_relative 'merge/rescue_semantics'
16
- require_relative 'merge/scaffold_chunk_support'
17
- require_relative 'merge/signature_support'
18
8
 
19
9
  module Ruby
20
10
  module Merge
@@ -3143,6 +3133,8 @@ module Ruby
3143
3133
  end
3144
3134
 
3145
3135
  Ruby::Merge.register_backend!
3136
+ require_relative 'merge/provider'
3137
+ Ruby::Merge.register_provider!
3146
3138
 
3147
3139
  TreeHaver::BackendRegistry.register_tag(
3148
3140
  :tslp_ruby_import_records,
data/sig/ruby/merge.rbs CHANGED
@@ -1,5 +1,18 @@
1
1
  module Ruby
2
2
  module Merge
3
+ class Provider
4
+ def provider_id: () -> String
5
+ def family: () -> String
6
+ def capabilities: () -> Hash[Symbol, untyped]
7
+ def analyze: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
8
+ def diff2: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
9
+ def merge2: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
10
+ def merge3: (Hash[Symbol, untyped] request) -> Hash[Symbol, untyped]
11
+ end
12
+
13
+ def self.merge_provider: () -> Provider
14
+ def self.register_provider!: (?replace: bool) -> untyped
15
+
3
16
  module Version
4
17
  VERSION: String
5
18
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-merge
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.1
4
+ version: 7.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -43,28 +43,42 @@ dependencies:
43
43
  requirements:
44
44
  - - '='
45
45
  - !ruby/object:Gem::Version
46
- version: 7.1.1
46
+ version: 7.1.3
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: 7.1.1
53
+ version: 7.1.3
54
+ - !ruby/object:Gem::Dependency
55
+ name: prism-merge
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '='
59
+ - !ruby/object:Gem::Version
60
+ version: 7.1.3
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '='
66
+ - !ruby/object:Gem::Version
67
+ version: 7.1.3
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: tree_haver
56
70
  requirement: !ruby/object:Gem::Requirement
57
71
  requirements:
58
72
  - - '='
59
73
  - !ruby/object:Gem::Version
60
- version: 7.1.1
74
+ version: 7.1.3
61
75
  type: :runtime
62
76
  prerelease: false
63
77
  version_requirements: !ruby/object:Gem::Requirement
64
78
  requirements:
65
79
  - - '='
66
80
  - !ruby/object:Gem::Version
67
- version: 7.1.1
81
+ version: 7.1.3
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: version_gem
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -91,20 +105,20 @@ dependencies:
91
105
  requirements:
92
106
  - - "~>"
93
107
  - !ruby/object:Gem::Version
94
- version: '2.5'
108
+ version: '3.0'
95
109
  - - ">="
96
110
  - !ruby/object:Gem::Version
97
- version: 2.5.17
111
+ version: 3.0.0
98
112
  type: :development
99
113
  prerelease: false
100
114
  version_requirements: !ruby/object:Gem::Requirement
101
115
  requirements:
102
116
  - - "~>"
103
117
  - !ruby/object:Gem::Version
104
- version: '2.5'
118
+ version: '3.0'
105
119
  - - ">="
106
120
  - !ruby/object:Gem::Version
107
- version: 2.5.17
121
+ version: 3.0.0
108
122
  - !ruby/object:Gem::Dependency
109
123
  name: bundler-audit
110
124
  requirement: !ruby/object:Gem::Requirement
@@ -182,7 +196,7 @@ dependencies:
182
196
  version: '3.2'
183
197
  - - ">="
184
198
  - !ruby/object:Gem::Version
185
- version: 3.2.0
199
+ version: 3.2.2
186
200
  type: :development
187
201
  prerelease: false
188
202
  version_requirements: !ruby/object:Gem::Requirement
@@ -192,7 +206,7 @@ dependencies:
192
206
  version: '3.2'
193
207
  - - ">="
194
208
  - !ruby/object:Gem::Version
195
- version: 3.2.0
209
+ version: 3.2.2
196
210
  - !ruby/object:Gem::Dependency
197
211
  name: kettle-test
198
212
  requirement: !ruby/object:Gem::Requirement
@@ -222,7 +236,7 @@ dependencies:
222
236
  version: '3.2'
223
237
  - - ">="
224
238
  - !ruby/object:Gem::Version
225
- version: 3.2.4
239
+ version: 3.2.5
226
240
  type: :development
227
241
  prerelease: false
228
242
  version_requirements: !ruby/object:Gem::Requirement
@@ -232,7 +246,7 @@ dependencies:
232
246
  version: '3.2'
233
247
  - - ">="
234
248
  - !ruby/object:Gem::Version
235
- version: 3.2.4
249
+ version: 3.2.5
236
250
  - !ruby/object:Gem::Dependency
237
251
  name: ruby-progressbar
238
252
  requirement: !ruby/object:Gem::Requirement
@@ -298,17 +312,7 @@ files:
298
312
  - README.md
299
313
  - lib/ruby-merge.rb
300
314
  - lib/ruby/merge.rb
301
- - lib/ruby/merge/block_binding_support.rb
302
- - lib/ruby/merge/block_directive_detector.rb
303
- - lib/ruby/merge/doc_comment_support.rb
304
- - lib/ruby/merge/gemspec_support.rb
305
- - lib/ruby/merge/magic_comment_support.rb
306
- - lib/ruby/merge/method_similarity.rb
307
- - lib/ruby/merge/nocov_node_base.rb
308
- - lib/ruby/merge/nocov_wrapper_base.rb
309
- - lib/ruby/merge/rescue_semantics.rb
310
- - lib/ruby/merge/scaffold_chunk_support.rb
311
- - lib/ruby/merge/signature_support.rb
315
+ - lib/ruby/merge/provider.rb
312
316
  - lib/ruby/merge/version.rb
313
317
  - sig/ruby/merge.rbs
314
318
  homepage: https://github.com/structuredmerge/structuredmerge-ruby
@@ -317,10 +321,10 @@ licenses:
317
321
  - PolyForm-Small-Business-1.0.0
318
322
  metadata:
319
323
  homepage_uri: https://structuredmerge.org
320
- source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.1
321
- changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.1/CHANGELOG.md
324
+ source_code_uri: https://github.com/structuredmerge/structuredmerge-ruby/tree/v7.1.3
325
+ changelog_uri: https://github.com/structuredmerge/structuredmerge-ruby/blob/v7.1.3/CHANGELOG.md
322
326
  bug_tracker_uri: https://github.com/structuredmerge/structuredmerge-ruby/issues
323
- documentation_uri: https://www.rubydoc.info/gems/ruby-merge/7.1.1
327
+ documentation_uri: https://www.rubydoc.info/gems/ruby-merge/7.1.3
324
328
  funding_uri: https://github.com/sponsors/pboling
325
329
  wiki_uri: https://github.com/structuredmerge/structuredmerge-ruby/wiki
326
330
  news_uri: https://www.railsbling.com/tags/ruby-merge
metadata.gz.sig CHANGED
Binary file
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ruby
4
- module Merge
5
- # Parser-neutral naming policy for a block-local receiver.
6
- module BlockBindingSupport
7
- module_function
8
-
9
- def effective_receiver(receiver, block_var, placeholder: :__block_binding__)
10
- block_var && receiver == block_var ? placeholder : receiver
11
- end
12
-
13
- def preferred_block_var(template_var, destination_var)
14
- template_var if template_var && destination_var && template_var != destination_var
15
- end
16
-
17
- def merged_block_var(var, preferred_var)
18
- preferred_var || var
19
- end
20
- end
21
- end
22
- end
@@ -1,208 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ruby
4
- module Merge
5
- # Detects Ruby comment block directive pairs in source lines.
6
- #
7
- # This is Ruby-specific substrate behavior shared by Ruby parser providers.
8
- # Parser-specific gems may consume the spans and project them into their own
9
- # node types, but directive token semantics should live here.
10
- class BlockDirectiveDetector
11
- Span = Struct.new(:kind, :start_line, :end_line, :open_marker, :close_marker, keyword_init: true)
12
-
13
- NOCOV_TOKEN = ':nocov:'
14
- SIMPLECOV_DISABLE_RE = /\A\s*#\s*simplecov\s*:\s*disable\b/i
15
- SIMPLECOV_ENABLE_RE = /\A\s*#\s*simplecov\s*:\s*enable\b/i
16
- DIRECTIVE_CONTENT_RE = /\A(?::nocov:|[\w-]+:(?:freeze|unfreeze))\z/i
17
-
18
- class << self
19
- def coverage_directive_line?(line, nocov_token: NOCOV_TOKEN)
20
- stripped = line.to_s.chomp
21
- stripped.match?(simplecov_disable_re) ||
22
- stripped.match?(simplecov_enable_re) ||
23
- stripped.match?(nocov_re(nocov_token))
24
- end
25
-
26
- def directive_content?(content)
27
- DIRECTIVE_CONTENT_RE.match?(content.to_s.strip) ||
28
- coverage_directive_line?("# #{content}")
29
- end
30
-
31
- def simplecov_disable_re
32
- SIMPLECOV_DISABLE_RE
33
- end
34
-
35
- def simplecov_enable_re
36
- SIMPLECOV_ENABLE_RE
37
- end
38
-
39
- def nocov_re(nocov_token = NOCOV_TOKEN)
40
- /\A\s*#\s?#{Regexp.escape(nocov_token)}\s*\z/i
41
- end
42
- end
43
-
44
- def initialize(lines, freeze_token: nil, nocov_token: NOCOV_TOKEN, source_label: nil)
45
- @lines = lines
46
- @freeze_token = freeze_token
47
- @nocov_token = nocov_token
48
- @source_label = source_label
49
- end
50
-
51
- def detect_spans
52
- raw_spans = []
53
- raw_spans.concat(detect_freeze_spans) if @freeze_token
54
- raw_spans.concat(detect_nocov_spans)
55
-
56
- validate_no_crossing(raw_spans.sort_by(&:start_line))
57
- end
58
-
59
- private
60
-
61
- def detector_name
62
- 'ruby-merge'
63
- end
64
-
65
- def directive_error_class
66
- Ast::Merge::Error
67
- end
68
-
69
- def warn_prefix
70
- if @source_label
71
- "[#{detector_name}] BlockDirectiveDetector (#{@source_label}):"
72
- else
73
- "[#{detector_name}] BlockDirectiveDetector:"
74
- end
75
- end
76
-
77
- def report_unbalanced(message)
78
- raise directive_error_class, "#{warn_prefix} #{message}" if @source_label
79
-
80
- warn("#{warn_prefix} #{message} - ignoring")
81
- end
82
-
83
- def detect_freeze_spans
84
- freeze_pat = /\A\s*#\s?#{Regexp.escape(@freeze_token)}:freeze\b/i
85
- unfreeze_pat = /\A\s*#\s?#{Regexp.escape(@freeze_token)}:unfreeze\b/i
86
-
87
- spans = []
88
- stack = []
89
-
90
- @lines.each_with_index do |line, index|
91
- line_num = index + 1
92
- stripped = line.to_s.chomp
93
- if stripped.match?(freeze_pat)
94
- stack.push({ start_line: line_num, open_marker: stripped })
95
- elsif stripped.match?(unfreeze_pat)
96
- if (open = stack.pop)
97
- spans << Span.new(
98
- kind: :freeze,
99
- start_line: open[:start_line],
100
- end_line: line_num,
101
- open_marker: open[:open_marker],
102
- close_marker: stripped
103
- )
104
- else
105
- report_unbalanced("unmatched #{@freeze_token}:unfreeze at line #{line_num}")
106
- end
107
- end
108
- end
109
-
110
- stack.each do |open|
111
- report_unbalanced("unclosed #{@freeze_token}:freeze at line #{open[:start_line]}")
112
- end
113
-
114
- spans
115
- end
116
-
117
- def detect_nocov_spans
118
- return [] unless @nocov_token
119
-
120
- nocov_pat = self.class.nocov_re(@nocov_token)
121
-
122
- spans = []
123
- stack = []
124
-
125
- @lines.each_with_index do |line, index|
126
- line_num = index + 1
127
- stripped = line.to_s.chomp
128
- if stripped.match?(self.class.simplecov_disable_re)
129
- stack.push({ start_line: line_num, open_marker: stripped })
130
- elsif stripped.match?(self.class.simplecov_enable_re)
131
- if (open = stack.pop)
132
- spans << Span.new(
133
- kind: :nocov,
134
- start_line: open[:start_line],
135
- end_line: line_num,
136
- open_marker: open[:open_marker],
137
- close_marker: stripped
138
- )
139
- else
140
- report_unbalanced("unmatched simplecov:enable at line #{line_num}")
141
- end
142
- elsif stripped.match?(nocov_pat)
143
- if stack.empty?
144
- stack.push({ start_line: line_num, open_marker: stripped })
145
- else
146
- open = stack.pop
147
- spans << Span.new(
148
- kind: :nocov,
149
- start_line: open[:start_line],
150
- end_line: line_num,
151
- open_marker: open[:open_marker],
152
- close_marker: stripped
153
- )
154
- end
155
- end
156
- end
157
-
158
- stack.each do |open|
159
- report_unbalanced("unclosed coverage directive at line #{open[:start_line]}")
160
- end
161
-
162
- spans
163
- end
164
-
165
- def validate_no_crossing(spans)
166
- valid = []
167
- invalid_indices = Set.new
168
-
169
- spans.each_with_index do |first, first_index|
170
- next if invalid_indices.include?(first_index)
171
-
172
- crossing = false
173
- spans.each_with_index do |second, second_index|
174
- next if first_index == second_index || invalid_indices.include?(second_index)
175
- next unless crossing_spans?(first, second)
176
-
177
- report_unbalanced("offset-overlapping #{first.kind} block " \
178
- "(lines #{first.start_line}..#{first.end_line}) and #{second.kind} block " \
179
- "(lines #{second.start_line}..#{second.end_line}) - both treated as plain comments")
180
- invalid_indices.add(first_index)
181
- invalid_indices.add(second_index)
182
- crossing = true
183
- break
184
- end
185
-
186
- valid << first unless crossing
187
- end
188
-
189
- valid
190
- end
191
-
192
- def crossing_spans?(first, second)
193
- (first.start_line < second.start_line && first.end_line > second.start_line && first.end_line < second.end_line) ||
194
- (second.start_line < first.start_line && second.end_line > first.start_line && second.end_line < first.end_line)
195
- end
196
-
197
- def top_level_spans_only(spans)
198
- spans.reject do |span|
199
- spans.any? do |other|
200
- other != span &&
201
- other.start_line <= span.start_line &&
202
- other.end_line >= span.end_line
203
- end
204
- end
205
- end
206
- end
207
- end
208
- end
@@ -1,97 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ruby
4
- module Merge
5
- # Ruby-specific doc-comment semantics shared by Ruby parser providers.
6
- module DocCommentSupport
7
- TAG_PREFIX = /\A@[a-z_]+\b/
8
- EXAMPLE_TAG = /\A@example\b(?<rest>.*)\z/
9
- MAGIC_COMMENT_PREFIXES = %w[
10
- coding
11
- encoding
12
- frozen_string_literal
13
- shareable_constant_value
14
- typed
15
- warn_indent
16
- ].freeze
17
-
18
- module_function
19
-
20
- def normalize_comment_content(raw)
21
- raw.to_s.sub(/\A\s*#\s?/, '').strip
22
- end
23
-
24
- def comment_prefix_for(raw)
25
- raw.to_s[/\A\s*#\s*/] || '# '
26
- end
27
-
28
- def doc_comment_content?(raw, magic_comment: false)
29
- content = normalize_comment_content(raw)
30
- return false if content.empty?
31
- return false if BlockDirectiveDetector.directive_content?(content)
32
- return false if magic_comment || magic_comment_content?(content)
33
-
34
- true
35
- end
36
-
37
- def magic_comment_content?(content)
38
- MAGIC_COMMENT_PREFIXES.any? { |prefix| content.to_s.start_with?("#{prefix}:") }
39
- end
40
-
41
- def declared_example_language(rest)
42
- match = rest.to_s.strip.match(/\A\[(?<language>[^\]]+)\]/)
43
- normalize_language(match && match[:language])
44
- end
45
-
46
- def declared_example_language_for_tag(content)
47
- match = EXAMPLE_TAG.match(content.to_s)
48
- return unless match
49
-
50
- declared_example_language(match[:rest])
51
- end
52
-
53
- def normalize_language(language)
54
- return if language.nil?
55
-
56
- normalized = language.to_s.strip.downcase.tr('-', '_')
57
- return if normalized.empty?
58
-
59
- normalized
60
- end
61
-
62
- def next_tag_index(normalized_lines, start_index)
63
- normalized_lines.each_with_index do |content, index|
64
- next if index < start_index
65
-
66
- return index if TAG_PREFIX.match?(content)
67
- end
68
- nil
69
- end
70
-
71
- def example_blocks(entries)
72
- normalized = entries.map { |entry| normalize_comment_content(entry[:raw]) }
73
- normalized.each_with_index.filter_map do |content, tag_index|
74
- match = EXAMPLE_TAG.match(content)
75
- next unless match
76
-
77
- body_start_index = tag_index + 1
78
- body_end_index = next_tag_index(normalized, body_start_index) || normalized.length
79
- next if body_start_index >= body_end_index
80
-
81
- body_entries = entries[body_start_index...body_end_index]
82
- next if body_entries.nil? || body_entries.empty?
83
-
84
- {
85
- tag_index: tag_index,
86
- tag_line: entries[tag_index][:line],
87
- tag_text: normalized[tag_index],
88
- body_start_index: body_start_index,
89
- body_end_index: body_end_index,
90
- body_entries: body_entries,
91
- declared_language: declared_example_language(match[:rest])
92
- }
93
- end
94
- end
95
- end
96
- end
97
- end