Class: Json::Merge::FileAnalysis

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

Overview

Analyzes JSON / JSONC file structure, extracting nodes, comments, and freeze blocks for merging.

Constant Summary collapse

DEFAULT_FREEZE_TOKEN =
'json-merge'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil, dialect: :jsonc, **_options) ⇒ FileAnalysis

Returns a new instance of FileAnalysis.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/json/merge/file_analysis.rb', line 20

def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, parser_path: nil, dialect: :jsonc,
               **_options)
  @source = source
  @lines = source.lines.map(&:chomp)
  @freeze_token = freeze_token
  @signature_generator = signature_generator
  @parser_path = parser_path
  @dialect = dialect.to_sym
  @errors = []

  @comment_tracker = CommentTracker.new(source)

  DebugLogger.time('FileAnalysis#parse_json') { parse_json }
  validate_jsonc_dialect! if valid? && @dialect == :jsonc

  @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

#astObject (readonly)

Returns the value of attribute ast.



12
13
14
# File 'lib/json/merge/file_analysis.rb', line 12

def ast
  @ast
end

#comment_trackerObject (readonly)

Returns the value of attribute comment_tracker.



12
13
14
# File 'lib/json/merge/file_analysis.rb', line 12

def comment_tracker
  @comment_tracker
end

#dialectObject (readonly)

Returns the value of attribute dialect.



12
13
14
# File 'lib/json/merge/file_analysis.rb', line 12

def dialect
  @dialect
end

#errorsObject (readonly)

Returns the value of attribute errors.



12
13
14
# File 'lib/json/merge/file_analysis.rb', line 12

def errors
  @errors
end

Class Method Details

.find_parser_pathObject



15
16
17
# File 'lib/json/merge/file_analysis.rb', line 15

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

Instance Method Details

#comment_attachment_for(owner, line_num: nil, **options) ⇒ Object



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

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)


167
168
169
# File 'lib/json/merge/file_analysis.rb', line 167

def comment_attachment_strategy
  :augmenter_preferred_tracker_layout
end

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



78
79
80
81
82
83
# File 'lib/json/merge/file_analysis.rb', line 78

def comment_augmenter(owners: nil, **options)
  comment_tracker.augment(
    owners: owners || comment_augmenter_default_owners,
    **options
  )
end

#comment_capabilityObject



50
51
52
# File 'lib/json/merge/file_analysis.rb', line 50

def comment_capability
  @comment_capability ||= comment_tracker.augment(owners: []).capability
end

#comment_node_at(line_num) ⇒ Object



66
67
68
# File 'lib/json/merge/file_analysis.rb', line 66

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

#comment_nodesObject



62
63
64
# File 'lib/json/merge/file_analysis.rb', line 62

def comment_nodes
  comment_tracker.comment_nodes
end

#comment_region_for_range(range, kind:, full_line_only: false) ⇒ Object



70
71
72
73
74
75
76
# File 'lib/json/merge/file_analysis.rb', line 70

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_styleObject



54
55
56
57
58
59
60
# File 'lib/json/merge/file_analysis.rb', line 54

def comment_support_style
  @comment_support_style ||= shared_comment_support_style(
    source: :json_source,
    style: :c_style_line,
    read_strategy: :source_augmented_portable_write
  )
end

#fallthrough_node?(value) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
# File 'lib/json/merge/file_analysis.rb', line 108

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

#freeze_block_at(line_num) ⇒ Object



94
95
96
# File 'lib/json/merge/file_analysis.rb', line 94

def freeze_block_at(line_num)
  @freeze_blocks.find { |fb| fb.location.cover?(line_num) }
end

#generate_signature(node) ⇒ Object



98
99
100
101
102
103
104
105
106
# File 'lib/json/merge/file_analysis.rb', line 98

def generate_signature(node)
  return super if @signature_generator
  return super unless node.is_a?(NodeWrapper)
  return super unless node.object?

  return [:root_object] if statements.size == 1 && statements.first == node

  super
end

#in_freeze_block?(line_num) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/json/merge/file_analysis.rb', line 90

def in_freeze_block?(line_num)
  @freeze_blocks.any? { |fb| fb.location.cover?(line_num) }
end

#root_nodeObject



112
113
114
115
116
117
# File 'lib/json/merge/file_analysis.rb', line 112

def root_node
  return @root_node if defined?(@root_node)
  return @root_node = nil unless valid?

  @root_node = NodeWrapper.new(@ast.root_node, lines: @lines, source: @source, dialect: @dialect)
end

#root_objectObject



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/json/merge/file_analysis.rb', line 119

def root_object
  return @root_object if defined?(@root_object)
  return @root_object = nil unless valid?

  root = @ast.root_node
  return @root_object = nil unless root

  root.each do |child|
    if child.type.to_s == 'object'
      return @root_object = NodeWrapper.new(child, lines: @lines, source: @source,
                                                   dialect: @dialect)
    end
  end

  @root_object = nil
end

#root_object_close_lineObject



143
144
145
146
147
148
# File 'lib/json/merge/file_analysis.rb', line 143

def root_object_close_line
  obj = root_object
  return unless obj&.end_line

  line_at(obj.end_line)&.chomp
end

#root_object_open_lineObject



136
137
138
139
140
141
# File 'lib/json/merge/file_analysis.rb', line 136

def root_object_open_line
  obj = root_object
  return unless obj&.start_line

  line_at(obj.start_line)&.chomp
end

#root_pairsObject



150
151
152
153
154
155
# File 'lib/json/merge/file_analysis.rb', line 150

def root_pairs
  @root_pairs ||= begin
    obj = root_object
    obj ? obj.pairs : []
  end
end

#ruleset_owner_selectorObject



171
172
173
# File 'lib/json/merge/file_analysis.rb', line 171

def ruleset_owner_selector
  :line_bound_statements
end

#ruleset_render_familyObject



175
176
177
# File 'lib/json/merge/file_analysis.rb', line 175

def ruleset_render_family
  :json_object_pairs
end

#statementsObject Also known as: nodes



85
86
87
# File 'lib/json/merge/file_analysis.rb', line 85

def statements
  @nodes || []
end

#valid?Boolean

Returns:

  • (Boolean)


46
47
48
# File 'lib/json/merge/file_analysis.rb', line 46

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