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

Parameters:

  • source (String)

    TOML source code to analyze

  • source (String)

    TOML source code to analyze

  • signature_generator (Proc, nil) (defaults to: nil)

    Custom signature generator

  • parser_path (String, nil) (defaults to: nil)

    Path to tree-sitter-toml parser library

  • options (Hash)

    Additional options (forward compatibility - freeze_token, node_typing, etc.)



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)

Returns Parsed AST.

Returns:

  • (TreeHaver::Tree, nil)

    Parsed AST



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

def ast
  @ast
end

#backendSymbol (readonly)

Returns The backend used for parsing (:tree_sitter or :citrus).

Returns:

  • (Symbol)

    The backend used for parsing (:tree_sitter or :citrus)



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

def backend
  @backend
end

#errorsArray (readonly)

Returns Parse errors if any.

Returns:

  • (Array)

    Parse errors if any



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

Returns:

  • (String, nil)

    Path to the parser library or nil if not found



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.

Parameters:

  • owner (Object)

    Structural owner for the attachment

  • line_num (Integer, nil) (defaults to: nil)

    Optional line number override

  • options (Hash)

    Additional attachment metadata

Returns:

  • (Ast::Merge::Comment::Attachment)


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

Returns:

  • (Symbol)


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.

Returns:

  • (Ast::Merge::Comment::Capability)


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.

Parameters:

  • line_num (Integer)

    1-based line number

Returns:

  • (Ast::Merge::Comment::Line, nil)


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.

Returns:

  • (Array<Ast::Merge::Comment::Line>)


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.

Parameters:

  • range (Range)

    Range of 1-based line numbers

  • kind (Symbol)

    Region kind (:leading, :inline, :orphan, etc.)

  • full_line_only (Boolean) (defaults to: false)

    Whether to keep only full-line comments

Returns:

  • (Ast::Merge::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.

Returns:

  • (Ast::Merge::Comment::SupportStyle)


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

Parameters:

  • value (Object)

    The value to check

Returns:

  • (Boolean)

    true if this is a fallthrough node



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

Returns:



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.

Returns:



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

Returns:



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).

Returns:



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

Returns:

  • (Boolean)


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

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