module Jazzy module SymbolGraph # For extensions we need to track constraints of the extended type # and the constraints introduced by the extension. class ExtConstraints attr_accessor :type # array attr_accessor :ext # array # all constraints inherited by members of the extension def merged (type + ext).sort end def initialize(type_constraints, ext_constraints) self.type = type_constraints || [] self.ext = ext_constraints || [] end end # An ExtNode is a node of the reconstructed syntax tree representing # an extension that we fabricate to resolve certain relationships. class ExtNode < BaseNode attr_accessor :usr attr_accessor :name attr_accessor :all_constraints # ExtConstraints attr_accessor :conformances # array, can be empty # Deduce an extension from a member of an unknown type or # of known type with additional constraints def self.new_for_member(type_usr, member, constraints) new(type_usr, member.parent_qualified_name, constraints).tap { |o| o.add_child(member) } end # Deduce an extension from a protocol conformance for some type def self.new_for_conformance(type_usr, type_name, protocol, constraints) new(type_usr, type_name, constraints).tap do |o| o.add_conformance(protocol) end end private def initialize(usr, name, constraints) self.usr = usr self.name = name self.all_constraints = constraints self.conformances = [] super() end public def constraints all_constraints.merged end def add_conformance(protocol) conformances.append(protocol).sort! end def full_declaration decl = "extension #{name}" unless conformances.empty? decl += ' : ' + conformances.join(', ') end decl + all_constraints.ext.to_where_clause end def to_sourcekit(module_name) declaration = full_declaration xml_declaration = "<swift>#{CGI.escapeHTML(declaration)}</swift>" hash = { 'key.kind' => 'source.lang.swift.decl.extension', 'key.usr' => usr, 'key.name' => name, 'key.modulename' => module_name, 'key.parsed_declaration' => declaration, 'key.annotated_decl' => xml_declaration, } unless conformances.empty? hash['key.inheritedtypes'] = conformances.map do |conformance| { 'key.name' => conformance } end end unless children.empty? hash['key.substructure'] = children_to_sourcekit end hash end # Sort order - by type name then constraint include Comparable def sort_key name + constraints.map(&:to_swift).join end def <=>(other) sort_key <=> other.sort_key end end end end