Class: Jsonc::Merge::FileAnalysis
- Inherits:
-
Object
- Object
- Jsonc::Merge::FileAnalysis
- Includes:
- Ast::Merge::FileAnalyzable
- Defined in:
- lib/jsonc/merge/file_analysis.rb
Overview
Analyzes JSON/JSONC file structure, extracting nodes, comments, and freeze blocks.
This is the main analysis class that prepares JSON content for merging.
Supports JSONC (JSON with Comments) which allows single-line (//) and
multi-line (/* */) comments in JSON files. This is commonly used in
configuration files like tsconfig.json, VS Code settings, etc.
Constant Summary collapse
- DEFAULT_FREEZE_TOKEN =
Default freeze token for identifying freeze blocks
"json-merge"
Instance Attribute Summary collapse
-
#ast ⇒ TreeHaver::Tree?
readonly
Parsed AST.
-
#comment_tracker ⇒ CommentTracker
readonly
Comment tracker for this file.
-
#errors ⇒ Array
readonly
Parse errors if any.
Class Method Summary collapse
-
.find_parser_path ⇒ String?
Find the parser library path using TreeHaver::GrammarFinder.
Instance Method Summary collapse
-
#fallthrough_node?(value) ⇒ Boolean
Override to detect tree-sitter nodes for signature generator fallthrough.
-
#freeze_block_at(line_num) ⇒ FreezeNode?
Get the freeze block containing the given line.
-
#generate_signature(node) ⇒ Object
Override to handle special signature generation for root-level objects When there’s only one root object, we want it to match regardless of keys so that add_template_only_nodes can work properly.
-
#in_freeze_block?(line_num) ⇒ Boolean
Check if a line is within a freeze block.
-
#initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil, **options) ⇒ FileAnalysis
constructor
Initialize file analysis.
-
#root_node ⇒ NodeWrapper?
Get the root node of the parse tree.
-
#root_object ⇒ NodeWrapper?
Get the root object if the JSON document is an object.
-
#root_object_close_line ⇒ String?
Get the closing brace line of the root object (the line containing
}). -
#root_object_open_line ⇒ String?
Get the opening brace line of the root object (the line containing
{). -
#root_pairs ⇒ Array<NodeWrapper>
Get key-value pairs from the root object.
-
#statements ⇒ Array<NodeWrapper, FreezeNode>
(also: #nodes)
The base module uses ‘statements’ - provide both names for compatibility.
-
#valid? ⇒ Boolean
Check if parse was successful.
Constructor Details
#initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil, **options) ⇒ FileAnalysis
Initialize file analysis
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/jsonc/merge/file_analysis.rb', line 50 def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil, **) @source = source @lines = source.lines.map(&:chomp) @freeze_token = freeze_token @signature_generator = signature_generator @parser_path = parser_path || self.class.find_parser_path @errors = [] # **options captures any additional parameters (e.g., node_typing) for forward compatibility # Initialize comment tracking @comment_tracker = CommentTracker.new(source) # Parse the JSON DebugLogger.time("FileAnalysis#parse_json") { parse_json } # Extract freeze blocks and integrate with nodes @freeze_blocks = extract_freeze_blocks @nodes = integrate_nodes_and_freeze_blocks DebugLogger.debug("FileAnalysis initialized", { signature_generator: signature_generator ? "custom" : "default", nodes_count: @nodes.size, freeze_blocks: @freeze_blocks.size, valid: valid?, }) end |
Instance Attribute Details
#ast ⇒ TreeHaver::Tree? (readonly)
Returns Parsed AST.
27 28 29 |
# File 'lib/jsonc/merge/file_analysis.rb', line 27 def ast @ast end |
#comment_tracker ⇒ CommentTracker (readonly)
Returns Comment tracker for this file.
24 25 26 |
# File 'lib/jsonc/merge/file_analysis.rb', line 24 def comment_tracker @comment_tracker end |
#errors ⇒ Array (readonly)
Returns Parse errors if any.
30 31 32 |
# File 'lib/jsonc/merge/file_analysis.rb', line 30 def errors @errors end |
Class Method Details
.find_parser_path ⇒ String?
Find the parser library path using TreeHaver::GrammarFinder
Note: JSONC uses the tree-sitter-jsonc grammar (supports JSON with Comments)
38 39 40 |
# File 'lib/jsonc/merge/file_analysis.rb', line 38 def find_parser_path TreeHaver::GrammarFinder.new(:jsonc).find_library_path end |
Instance Method Details
#fallthrough_node?(value) ⇒ Boolean
Override to detect tree-sitter nodes for signature generator fallthrough
130 131 132 |
# File 'lib/jsonc/merge/file_analysis.rb', line 130 def fallthrough_node?(value) value.is_a?(NodeWrapper) || value.is_a?(FreezeNode) || super end |
#freeze_block_at(line_num) ⇒ FreezeNode?
Get the freeze block containing the given line.
104 105 106 |
# File 'lib/jsonc/merge/file_analysis.rb', line 104 def freeze_block_at(line_num) @freeze_blocks.find { |fb| fb.location.cover?(line_num) } end |
#generate_signature(node) ⇒ Object
Override to handle special signature generation for root-level objects
When there’s only one root object, we want it to match regardless of keys
so that add_template_only_nodes can work properly
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/jsonc/merge/file_analysis.rb', line 111 def generate_signature(node) # If custom signature generator provided, let it handle everything return super if @signature_generator return super unless node.is_a?(NodeWrapper) return super unless node.object? # Check if this is the sole root object if statements.size == 1 && statements.first == node # Root object gets a consistent signature so they always match return [:root_object] end super end |
#in_freeze_block?(line_num) ⇒ Boolean
Check if a line is within a freeze block.
96 97 98 |
# File 'lib/jsonc/merge/file_analysis.rb', line 96 def in_freeze_block?(line_num) @freeze_blocks.any? { |fb| fb.location.cover?(line_num) } end |
#root_node ⇒ NodeWrapper?
Get the root node of the parse tree
136 137 138 139 140 |
# File 'lib/jsonc/merge/file_analysis.rb', line 136 def root_node return unless valid? NodeWrapper.new(@ast.root_node, lines: @lines, source: @source) end |
#root_object ⇒ NodeWrapper?
Get the root object if the JSON document is an object
144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/jsonc/merge/file_analysis.rb', line 144 def root_object return unless valid? root = @ast.root_node return unless root # JSON root should be a document containing an object or array root.each do |child| if child.type.to_s == "object" return NodeWrapper.new(child, lines: @lines, source: @source) end end nil end |
#root_object_close_line ⇒ String?
Get the closing brace line of the root object (the line containing })
170 171 172 173 174 175 |
# File 'lib/jsonc/merge/file_analysis.rb', line 170 def root_object_close_line obj = root_object return unless obj&.end_line line_at(obj.end_line)&.chomp end |
#root_object_open_line ⇒ String?
Get the opening brace line of the root object (the line containing {)
161 162 163 164 165 166 |
# File 'lib/jsonc/merge/file_analysis.rb', line 161 def root_object_open_line obj = root_object return unless obj&.start_line line_at(obj.start_line)&.chomp end |
#root_pairs ⇒ Array<NodeWrapper>
Get key-value pairs from the root object
179 180 181 182 183 184 |
# File 'lib/jsonc/merge/file_analysis.rb', line 179 def root_pairs obj = root_object return [] unless obj obj.pairs end |
#statements ⇒ Array<NodeWrapper, FreezeNode> Also known as: nodes
The base module uses ‘statements’ - provide both names for compatibility
85 86 87 |
# File 'lib/jsonc/merge/file_analysis.rb', line 85 def statements @nodes ||= [] end |
#valid? ⇒ Boolean
Check if parse was successful
79 80 81 |
# File 'lib/jsonc/merge/file_analysis.rb', line 79 def valid? @errors.empty? && !@ast.nil? end |