class Jazzy::SymbolGraph::Graph
A Graph
is the coordinator to import a symbolgraph json file. Deserialize it to Symbols and Relationships, then rebuild the AST shape using SymNodes and ExtNodes and extract SourceKit json.
Attributes
Public Class Methods
Parse the JSON into flat tables of data
# File lib/jazzy/symbol_graph/graph.rb, line 17 def initialize(json, module_name, ext_module_name) self.module_name = module_name self.ext_module_name = ext_module_name graph = JSON.parse(json, symbolize_names: true) self.symbol_nodes = {} self.ext_nodes = {} graph[:symbols].each do |hash| symbol = Symbol.new(hash) if symbol.extension? node = ExtSymNode.new(symbol) ext_nodes[node.ext_key] = node else symbol_nodes[symbol.usr] = SymNode.new(symbol) end end self.relationships = graph[:relationships].map { |hash| Relationship.new(hash) } end
Public Instance Methods
# File lib/jazzy/symbol_graph/graph.rb, line 52 def add_ext_conformance(type_usr, type_name, protocol, constraints) key = ExtKey.new(type_usr, constraints.ext) if ext_node = ext_nodes[key] ext_node.add_conformance(protocol) else ext_nodes[key] = ExtNode.new_for_conformance(type_usr, type_name, protocol, constraints) end end
ExtNode
index. ExtKey
(type USR, extension constraints) -> ExtNode
. This minimizes the number of extensions
# File lib/jazzy/symbol_graph/graph.rb, line 42 def add_ext_member(type_usr, member_node, constraints) key = ExtKey.new(type_usr, constraints.ext) if ext_node = ext_nodes[key] ext_node.add_child(member_node) else ext_nodes[key] = ExtNode.new_for_member(type_usr, member_node, constraints) end end
“source : target” either from type decl or ext decl
# File lib/jazzy/symbol_graph/graph.rb, line 109 def rebuild_conformance(rel, source, target) protocol_name = rel_target_name(rel, target) return if redundant_conformance?(rel, source, protocol_name) type_constraints = source&.constraints || [] constraints = ExtConstraints.new(type_constraints, rel.constraints - type_constraints) # Create an extension or enhance an existing one add_ext_conformance(rel.source_usr, rel_source_name(rel, source), protocol_name, constraints) end
“source is a default implementation of protocol requirement target”
# File lib/jazzy/symbol_graph/graph.rb, line 127 def rebuild_default_implementation(_rel, source, target) return unless source unless target && (target_parent = target.parent) && target_parent.is_a?(SymNode) # Could probably figure this out with demangle, but... warn "Can't resolve membership of default implementation " \ "#{source.symbol.usr}." source.unlisted = true return end constraints = ExtConstraints.new(target_parent.constraints, source.unique_context_constraints(target_parent)) add_ext_member(target_parent.symbol.usr, source, constraints) end
“source is a class that inherits from target”
# File lib/jazzy/symbol_graph/graph.rb, line 149 def rebuild_inherits(_rel, source, target) if source && target source.superclass_name = target.symbol.name end end
source is a member/protocol requirement of target
# File lib/jazzy/symbol_graph/graph.rb, line 94 def rebuild_member(rel, source, target) return unless source source.protocol_requirement = rel.protocol_requirement? constraints = ExtConstraints.new(target&.constraints, source.unique_context_constraints(target)) # Add to its parent or invent an extension unless target&.try_add_child(source, constraints.ext) add_ext_member(rel.target_usr, source, constraints) end end
Process a structural relationship to link nodes
# File lib/jazzy/symbol_graph/graph.rb, line 165 def rebuild_rel(rel) source = symbol_nodes[rel.source_usr] target = symbol_nodes[rel.target_usr] case rel.kind when :memberOf, :optionalRequirementOf, :requirementOf rebuild_member(rel, source, target) when :conformsTo rebuild_conformance(rel, source, target) when :defaultImplementationOf rebuild_default_implementation(rel, source, target) when :inheritsFrom rebuild_inherits(rel, source, target) when :extensionTo unalias_extensions(rel.source_usr, rel.target_usr) end # don't seem to care about: # - overrides: not bothered, also unimplemented for protocols end
Protocol conformance is redundant if it’s unconditional and already expressed in the type’s declaration.
Skip implementation-detail conformances.
# File lib/jazzy/symbol_graph/graph.rb, line 86 def redundant_conformance?(rel, type, protocol) return false unless type (rel.constraints.empty? && type.conformance?(protocol)) || (type.actor? && rel.actor_protocol?) end
Same for the source end. Less help from the tool here
# File lib/jazzy/symbol_graph/graph.rb, line 77 def rel_source_name(rel, source_node) source_node&.qualified_name || Jazzy::SymbolGraph.demangle(rel.source_usr) end
Increasingly desparate ways to find the name of the symbol at the target end of a relationship
# File lib/jazzy/symbol_graph/graph.rb, line 70 def rel_target_name(rel, target_node) target_node&.symbol&.name || rel.target_fallback || Jazzy::SymbolGraph.demangle(rel.target_usr) end
Rebuild the AST structure and convert to SourceKit
# File lib/jazzy/symbol_graph/graph.rb, line 191 def to_sourcekit relationships.sort.each { |r| rebuild_rel(r) } root_symbol_nodes = symbol_nodes.values .select(&:top_level_decl?) .sort .map { |n| n.to_sourcekit(module_name) } root_ext_nodes = ext_nodes.values .sort .map { |n| n.to_sourcekit(module_name, ext_module_name) } { 'key.diagnostic_stage' => 'parse', 'key.substructure' => root_symbol_nodes + root_ext_nodes, } end
“References to fake_usr should be real_usr”
# File lib/jazzy/symbol_graph/graph.rb, line 156 def unalias_extensions(fake_usr, real_usr) ext_nodes.each_pair do |key, ext| if key.usr == fake_usr ext.real_usr = real_usr end end end