Class: Toml::Merge::FileAnalysis

Inherits:
Object
  • Object
show all
Includes:
Ast::Merge::FileAnalyzable
Defined in:
lib/toml/merge/file_analysis.rb

Overview

Analyzes TOML file structure, extracting statements for merging. This is the main analysis class that prepares TOML content for merging.

Examples:

Basic usage

analysis = FileAnalysis.new(toml_source)
analysis.valid? # => true
analysis.statements # => [NodeWrapper, ...]

Defined Under Namespace

Classes: CommentAugmenter

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, signature_generator: nil, parser_path: nil, **_options) ⇒ FileAnalysis

Note:

To force a specific backend, use TreeHaver.with_backend or TREE_HAVER_BACKEND env var. TreeHaver handles backend selection, auto-detection, and fallback.

Initialize file analysis



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/toml/merge/file_analysis.rb', line 72

def initialize(source, signature_generator: nil, parser_path: nil, **_options)
  @source = source
  @lines = source.lines.map(&:chomp)
  @signature_generator = signature_generator
  @parser_path = parser_path
  @errors = []
  @backend = :tree_sitter # Default, will be updated during parsing
  # **options captures any additional parameters (e.g., freeze_token, node_typing) for forward compatibility

  # Parse the TOML
  DebugLogger.time('FileAnalysis#parse_toml') { parse_toml }

  @statements = integrate_nodes

  DebugLogger.debug('FileAnalysis initialized', {
                      signature_generator: signature_generator ? 'custom' : 'default',
                      statements_count: @statements.size,
                      valid: valid?
                    })
end

Instance Attribute Details

#astTreeHaver::Tree? (readonly)



45
46
47
# File 'lib/toml/merge/file_analysis.rb', line 45

def ast
  @ast
end

#backendSymbol (readonly)



51
52
53
# File 'lib/toml/merge/file_analysis.rb', line 51

def backend
  @backend
end

#errorsArray (readonly)



48
49
50
# File 'lib/toml/merge/file_analysis.rb', line 48

def errors
  @errors
end

Class Method Details

.find_parser_pathString?

Find the parser library path using TreeHaver::GrammarFinder



57
58
59
# File 'lib/toml/merge/file_analysis.rb', line 57

def find_parser_path
  TreeHaver::GrammarFinder.new(:toml).find_library_path
end

Instance Method Details

#comment_attachment_for(owner, line_num: nil, **options) ⇒ Ast::Merge::Comment::Attachment

Build a shared comment attachment for an owner.



152
153
154
155
156
157
158
159
# File 'lib/toml/merge/file_analysis.rb', line 152

def comment_attachment_for(owner, line_num: nil, **options)
  shared_comment_attachment_for(
    owner,
    tracker_attachment: comment_tracker.comment_attachment_for(owner, line_num: line_num, **options),
    line_num: line_num,
    **options
  )
end

#comment_attachment_strategySymbol



162
163
164
# File 'lib/toml/merge/file_analysis.rb', line 162

def comment_attachment_strategy
  :normalize_tracked_layout_merge
end

#comment_augmenter(owners: nil, **options) ⇒ Object



174
175
176
# File 'lib/toml/merge/file_analysis.rb', line 174

def comment_augmenter(owners: nil, **options)
  CommentAugmenter.new(self, owners: owners || comment_augmenter_default_owners, **options)
end

#comment_capabilityAst::Merge::Comment::Capability

Get shared comment capability information for this analysis.



102
103
104
# File 'lib/toml/merge/file_analysis.rb', line 102

def comment_capability
  @comment_capability ||= build_comment_capability(owner_count: 0)
end

#comment_node_at(line_num) ⇒ Ast::Merge::Comment::Line?

Get a shared Ast::Merge comment node at a specific line.



132
133
134
# File 'lib/toml/merge/file_analysis.rb', line 132

def comment_node_at(line_num)
  comment_tracker.comment_node_at(line_num)
end

#comment_nodesArray<Ast::Merge::Comment::Line>

Get all comments converted to shared Ast::Merge comment nodes.



124
125
126
# File 'lib/toml/merge/file_analysis.rb', line 124

def comment_nodes
  comment_tracker.comment_nodes
end

#comment_region_for_range(range, kind:, full_line_only: false) ⇒ Ast::Merge::Comment::Region

Get comments in a line range converted to a shared comment region.



142
143
144
# File 'lib/toml/merge/file_analysis.rb', line 142

def comment_region_for_range(range, kind:, full_line_only: false)
  comment_tracker.comment_region_for_range(range, kind: kind, full_line_only: full_line_only)
end

#comment_support_styleAst::Merge::Comment::SupportStyle

Describe how TOML merges currently own and emit comments.

Native backends expose comment nodes, but TOML still emits comments through its synthetic merge layer. Source-only backends use the same synthetic ownership model with a source-augmented read path.



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

def comment_support_style
  @comment_support_style ||= shared_comment_support_style(
    source: native_comment_backend? ? @backend : :toml_source,
    style: :hash_comment,
    read_strategy: native_comment_backend? ? :native_read_portable_write : :source_augmented_portable_write
  )
end

#fallthrough_node?(value) ⇒ Boolean

Override to detect tree-sitter nodes for signature generator fallthrough



181
182
183
# File 'lib/toml/merge/file_analysis.rb', line 181

def fallthrough_node?(value)
  value.is_a?(NodeWrapper) || super
end

#root_nodeNodeWrapper?

Get the root node of the parse tree



187
188
189
190
191
192
# File 'lib/toml/merge/file_analysis.rb', line 187

def root_node
  return unless valid?

  root = @ast.root_node
  wrap_node(root, document_root: root)
end

#root_pairsArray<NodeWrapper>

Get all top-level key-value pairs (not in tables)

For tree-sitter backend: pairs are nested under tables, so root-level pairs are direct children of the document.

For Citrus backend: ALL pairs are siblings at document level (flat structure). We must filter to only include pairs that appear BEFORE the first table header.



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/toml/merge/file_analysis.rb', line 228

def root_pairs
  return [] unless valid?

  result = []
  root = @ast.root_node

  # Find the line number of the first table (if any)
  first_table_line = nil
  root.each do |child|
    wrapper = wrap_node(child, document_root: root)
    next unless wrapper

    if wrapper.table? || wrapper.array_of_tables?
      child_line = wrapper.start_line
      first_table_line = child_line if child_line && (first_table_line.nil? || child_line < first_table_line)
      next
    end

    next unless wrapper.pair?

    # For Citrus backend, only include pairs before the first table
    if first_table_line
      child_line = wrapper.start_line
      next if child_line && child_line >= first_table_line
    end

    result << wrapper
  end
  result
end

#ruleset_owner_selectorObject



166
167
168
# File 'lib/toml/merge/file_analysis.rb', line 166

def ruleset_owner_selector
  :line_bound_statements
end

#ruleset_render_familyObject



170
171
172
# File 'lib/toml/merge/file_analysis.rb', line 170

def ruleset_render_family
  :toml_pairs_and_tables
end

#signature_mapHash<Array, NodeWrapper>

Get a hash mapping signatures to nodes



196
197
198
# File 'lib/toml/merge/file_analysis.rb', line 196

def signature_map
  @signature_map ||= build_signature_map
end

#tablesArray<NodeWrapper>

Get all top-level tables (sections) in the TOML document Uses NodeTypeNormalizer for backend-agnostic type checking. Passes document_root to enable Citrus backend normalization (pairs as siblings).



204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/toml/merge/file_analysis.rb', line 204

def tables
  return [] unless valid?

  result = []
  root = @ast.root_node
  root.each do |child|
    wrapper = wrap_node(child, document_root: root)
    next unless wrapper
    next unless wrapper.table? || wrapper.array_of_tables?

    result << wrapper
  end
  result
end

#valid?Boolean

Check if parse was successful



95
96
97
# File 'lib/toml/merge/file_analysis.rb', line 95

def valid?
  @errors.empty? && !@ast.nil?
end