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 || self.class.find_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)


137
138
139
# File 'lib/toml/merge/file_analysis.rb', line 137

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

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

Build a shared comment augmenter for this analysis.

Parameters:

  • owners (Array<#start_line,#end_line>, nil) (defaults to: nil)

    Owners used for attachment inference

  • options (Hash)

    Additional augmenter options

Returns:



146
147
148
# File 'lib/toml/merge/file_analysis.rb', line 146

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)


117
118
119
# File 'lib/toml/merge/file_analysis.rb', line 117

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


109
110
111
# File 'lib/toml/merge/file_analysis.rb', line 109

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)


127
128
129
# File 'lib/toml/merge/file_analysis.rb', line 127

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

#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



153
154
155
# File 'lib/toml/merge/file_analysis.rb', line 153

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

#root_nodeNodeWrapper?

Get the root node of the parse tree

Returns:



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

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:



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/toml/merge/file_analysis.rb', line 199

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|
    canonical_type = NodeTypeNormalizer.canonical_type(child.type, @backend)
    if NodeTypeNormalizer.table_type?(canonical_type)
      child_line = child.respond_to?(:start_point) ? child.start_point.row + 1 : nil
      if child_line && (first_table_line.nil? || child_line < first_table_line)
        first_table_line = child_line
      end
    end
  end

  root.each do |child|
    canonical_type = NodeTypeNormalizer.canonical_type(child.type, @backend)
    next unless canonical_type == :pair

    # For Citrus backend, only include pairs before the first table
    if first_table_line
      child_line = child.respond_to?(:start_point) ? child.start_point.row + 1 : nil
      next if child_line && child_line >= first_table_line
    end

    result << wrap_node(child, document_root: root)
  end
  result
end

#signature_mapHash<Array, NodeWrapper>

Get a hash mapping signatures to nodes

Returns:



168
169
170
# File 'lib/toml/merge/file_analysis.rb', line 168

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:



176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/toml/merge/file_analysis.rb', line 176

def tables
  return [] unless valid?

  result = []
  root = @ast.root_node
  root.each do |child|
    canonical_type = NodeTypeNormalizer.canonical_type(child.type, @backend)
    next unless NodeTypeNormalizer.table_type?(canonical_type)

    result << wrap_node(child, document_root: root)
  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