Module: Toml::Merge

Defined in:
lib/toml/merge.rb,
lib/toml/merge/emitter.rb,
lib/toml/merge/version.rb,
lib/toml/merge/key_sorter.rb,
lib/toml/merge/debug_logger.rb,
lib/toml/merge/merge_result.rb,
lib/toml/merge/node_wrapper.rb,
lib/toml/merge/smart_merger.rb,
lib/toml/merge/file_analysis.rb,
lib/toml/merge/comment_tracker.rb,
lib/toml/merge/conflict_resolver.rb,
lib/toml/merge/table_match_refiner.rb,
lib/toml/merge/node_type_normalizer.rb,
sig/toml/merge.rbs

Overview

Smart merge system for TOML files using tree-sitter AST analysis. Provides intelligent merging by understanding TOML structure rather than treating files as plain text.

Defined Under Namespace

Modules: DebugLogger, NodeTypeNormalizer, Version Classes: CommentTracker, ConflictResolver, CorruptionDetectedError, DestinationParseError, Emitter, Error, FileAnalysis, KeySorter, MergeResult, NodeWrapper, ParseError, SmartMerger, TableMatchRefiner, TemplateParseError

Constant Summary collapse

PACKAGE_NAME =
'toml-merge'
DESTINATION_WINS_ARRAY_POLICY =
{
  surface: 'array',
  name: 'destination_wins_array'
}.freeze
TREE_SITTER_BACKEND_REFERENCE =
TreeHaver::BackendReference.new(id: 'kreuzberg-language-pack',
family: 'tree-sitter').freeze
BACKEND_REGISTRY =
Struct.new(:registered, :mutex).new(false, Mutex.new)
VERSION =

Current gem version exposed at the traditional constant location.

Returns:

  • (String)
Version::VERSION
NodeTypingNormalizer =

Alias for the shared normalizer module from ast-merge

Ast::Merge::NodeTyping::Normalizer

Class Method Summary collapse

Class Method Details

.analyze_toml_source(source, dialect) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/toml/merge.rb', line 164

def analyze_toml_source(source, dialect)
  return unsupported_feature_parse_result("Unsupported TOML dialect #{dialect}.") unless dialect == 'toml'

  analysis = FileAnalysis.new(source)
  return parse_error_result(analysis.errors.map(&:to_s).join('; ')) unless analysis.valid?

  {
    ok: true,
    diagnostics: [],
    analysis: {
      kind: 'toml',
      dialect: 'toml',
      normalized_source: normalize_toml_source(source),
      root_kind: 'table',
      owners: collect_file_analysis_owners(analysis)
    },
    policies: []
  }
rescue StandardError => e
  parse_error_result(e.message)
end

.available_toml_backendsObject



122
123
124
# File 'lib/toml/merge.rb', line 122

def available_toml_backends
  toml_backend_available_for_analysis?(TREE_SITTER_BACKEND_REFERENCE.id) ? [TREE_SITTER_BACKEND_REFERENCE] : []
end

.match_toml_owners(template, destination) ⇒ Object



186
187
188
# File 'lib/toml/merge.rb', line 186

def match_toml_owners(template, destination)
  Ast::Merge::OwnerSelection.match_by_path(template, destination)
end

.merge_toml(template_source, destination_source, dialect, backend: nil) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/toml/merge.rb', line 190

def merge_toml(template_source, destination_source, dialect, backend: nil)
  return unsupported_feature_merge_result("Unsupported TOML dialect #{dialect}.") unless dialect == 'toml'

  requested = requested_toml_backend_id(backend)
  return unsupported_feature_merge_result("Unsupported TOML backend #{requested}.") unless available_toml_backends.any? do |candidate|
    candidate.id == requested
  end

  output = TreeHaver.with_backend(requested) do
    SmartMerger.new(
      template_source,
      destination_source,
      preference: :destination,
      add_template_only_nodes: true
    ).merge_result.to_toml
  end

  {
    ok: true,
    diagnostics: [],
    output: output,
    policies: [DESTINATION_WINS_ARRAY_POLICY]
  }
rescue TemplateParseError => e
  { ok: false, diagnostics: [diagnostic('error', 'template_parse_error', e.message)], policies: [] }
rescue DestinationParseError => e
  { ok: false, diagnostics: [diagnostic('error', 'destination_parse_error', e.message)], policies: [] }
rescue StandardError => e
  { ok: false, diagnostics: [diagnostic('error', 'merge_error', e.message)], policies: [] }
end

.merge_toml_with_parser(template_source, destination_source, dialect) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/toml/merge.rb', line 221

def merge_toml_with_parser(template_source, destination_source, dialect)
  return unsupported_feature_merge_result("Unsupported TOML dialect #{dialect}.") unless dialect == 'toml'
  raise ArgumentError, 'merge_toml_with_parser requires a parser block' unless block_given?

  template_parse = yield(template_source, dialect)
  return provider_parse_failure(:template_parse_error, template_parse) unless template_parse[:ok]

  destination_parse = yield(destination_source, dialect)
  return provider_parse_failure(:destination_parse_error, destination_parse) unless destination_parse[:ok]

  output = SmartMerger.new(
    template_source,
    destination_source,
    preference: :destination,
    add_template_only_nodes: true
  ).merge_result.to_toml

  {
    ok: true,
    diagnostics: [],
    output: output,
    policies: [DESTINATION_WINS_ARRAY_POLICY]
  }
rescue TemplateParseError => e
  { ok: false, diagnostics: [diagnostic('error', 'template_parse_error', e.message)], policies: [] }
rescue DestinationParseError => e
  { ok: false, diagnostics: [diagnostic('error', 'destination_parse_error', e.message)], policies: [] }
rescue StandardError => e
  { ok: false, diagnostics: [diagnostic('error', 'merge_error', e.message)], policies: [] }
end

.parse_toml(source, dialect, backend: nil) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/toml/merge.rb', line 151

def parse_toml(source, dialect, backend: nil)
  return unsupported_feature_parse_result("Unsupported TOML dialect #{dialect}.") unless dialect == 'toml'

  requested = requested_toml_backend_id(backend)
  return unsupported_feature_parse_result("Unsupported TOML backend #{requested}.") unless available_toml_backends.any? do |candidate|
    candidate.id == requested
  end

  TreeHaver.with_backend(requested) { analyze_toml_source(source, dialect) }
rescue StandardError => e
  parse_error_result(e.message)
end

.register_backend!Object



252
253
254
255
256
257
258
259
260
261
262
# File 'lib/toml/merge.rb', line 252

def register_backend!
  BACKEND_REGISTRY.mutex.synchronize do
    return if BACKEND_REGISTRY.registered && TreeHaver.registered_languages(:toml).any?

    TreeHaver::BackendRegistry.register(TREE_SITTER_BACKEND_REFERENCE)

    register_tree_sitter_backend!

    BACKEND_REGISTRY.registered = true
  end
end

.toml_backend_feature_profile(backend: nil) ⇒ Object



126
127
128
129
130
131
132
133
134
135
# File 'lib/toml/merge.rb', line 126

def toml_backend_feature_profile(backend: nil)
  requested = requested_toml_backend_id(backend)
  backend_ref = available_toml_backends.find { |candidate| candidate.id == requested }
  return unsupported_feature_result("Unsupported TOML backend #{requested}.") unless backend_ref

  toml_feature_profile.merge(
    backend: backend_ref.id,
    backend_ref: backend_ref.to_h
  )
end

.toml_feature_profileObject



114
115
116
117
118
119
120
# File 'lib/toml/merge.rb', line 114

def toml_feature_profile
  {
    family: 'toml',
    supported_dialects: ['toml'],
    supported_policies: [DESTINATION_WINS_ARRAY_POLICY]
  }
end

.toml_plan_context(backend: nil) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/toml/merge.rb', line 137

def toml_plan_context(backend: nil)
  profile = toml_backend_feature_profile(backend: backend)
  return profile if profile[:ok] == false

  {
    family_profile: toml_feature_profile,
    feature_profile: {
      backend: profile[:backend],
      supports_dialects: false,
      supported_policies: profile[:supported_policies]
    }
  }
end