class Jazzy::SymbolGraph::Symbol

A Symbol is a tidied-up SymbolGraph JSON object

Constants

KIND_MAP

Mapping SymbolGraph’s declkinds to SourceKit

Attributes

acl[RW]
attributes[RW]
constraints[RW]
declaration[RW]
doc_comments[RW]
generic_type_params[RW]
kind[RW]
location[RW]
parameter_names[RW]
path_components[RW]
spi[RW]
usr[RW]

Public Class Methods

new(hash) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 29
def initialize(hash)
  self.usr = hash[:identifier][:precise]
  self.path_components = hash[:pathComponents]
  raw_decl, keywords = parse_decl_fragments(hash[:declarationFragments])
  init_kind(hash[:kind][:identifier], keywords)
  init_declaration(raw_decl)
  if func_signature = hash[:functionSignature]
    init_func_signature(func_signature)
  end
  init_acl(hash[:accessLevel])
  self.spi = hash[:spi]
  if location = hash[:location]
    init_location(location)
  end
  init_constraints(hash, raw_decl)
  if comments_hash = hash[:docComment]
    init_doc_comments(comments_hash)
  end
  init_attributes(hash[:availability] || [])
  init_generic_type_params(hash)
end

Public Instance Methods

<=>(other) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 251
def <=>(other)
  # Things with location: order by file/line/column
  # (pls tell what wheel i am reinventing :/)
  if location && other_loc = other.location
    if location[:filename] == other_loc[:filename]
      if location[:line] == other_loc[:line]
        return location[:character] <=> other_loc[:character]
      end

      return location[:line] <=> other_loc[:line]
    end
    return location[:filename] <=> other_loc[:filename]
  end

  # Things with a location before things without a location
  return +1 if location.nil? && other.location
  return -1 if location && other.location.nil?

  # Things without a location: by name and then USR
  return usr <=> other.usr if name == other.name

  name <=> other.name
end
add_to_sourcekit(hash) click to toggle source

SourceKit common fields, shared by extension and regular symbols. Things we do not know for fabricated extensions.

# File lib/jazzy/symbol_graph/symbol.rb, line 231
def add_to_sourcekit(hash)
  unless doc_comments.nil?
    hash['key.doc.comment'] = doc_comments
    hash['key.doc.full_as_xml'] = ''
  end

  hash['key.accessibility'] = acl

  unless location.nil?
    hash['key.filepath'] = location[:filename]
    hash['key.doc.line'] = location[:line] + 1
    hash['key.doc.column'] = location[:character] + 1
  end

  hash
end
adjust_kind_for_declaration(kind, keywords) click to toggle source

We treat ‘static var’ differently to ‘class var’ We treat actors as first-class entities

# File lib/jazzy/symbol_graph/symbol.rb, line 113
def adjust_kind_for_declaration(kind, keywords)
  if kind == 'swift.class' && keywords.member?('actor')
    return 'swift.actor'
  end
  return kind unless keywords.member?('static')

  kind.gsub('type', 'static')
end
availability_attributes(avail_hash_list) click to toggle source

Availability Re-encode this as Swift. Should really teach Jazzy about these, could maybe then do something smarter here.

# File lib/jazzy/symbol_graph/symbol.rb, line 189
def availability_attributes(avail_hash_list)
  avail_hash_list.map do |avail|
    str = '@available('
    if avail[:isUnconditionallyDeprecated]
      str += '*, deprecated'
    elsif domain = avail[:domain]
      str += domain
      %i[introduced deprecated obsoleted].each do |event|
        if version = avail[event]
          str += ", #{event}: #{decode_version(version)}"
        end
      end
    else
      warn "Found confusing availability: #{avail}"
      next nil
    end

    str += ", message: \"#{avail[:message]}\"" if avail[:message]
    str += ", renamed: \"#{avail[:renamed]}\"" if avail[:renamed]

    str + ')'
  end.compact
end
decode_version(hash) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 213
def decode_version(hash)
  str = hash[:major].to_s
  str += ".#{hash[:minor]}" if hash[:minor]
  str += ".#{hash[:patch]}" if hash[:patch]
  str
end
extension?() click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 130
def extension?
  kind.end_with?('extension')
end
full_name() click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 25
def full_name
  path_components.join('.')
end
init_acl(acl) click to toggle source

Mapping SymbolGraph’s ACL to SourceKit

# File lib/jazzy/symbol_graph/symbol.rb, line 136
def init_acl(acl)
  self.acl = "source.lang.swift.accessibility.#{acl}"
end
init_attributes(avail_hash_list) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 224
def init_attributes(avail_hash_list)
  self.attributes =
    availability_attributes(avail_hash_list) + spi_attributes
end
init_constraints(hash, raw_decl) click to toggle source

Generic constraints: in one or both of two places. There can be duplicates; these are removed by ‘Constraint`.

# File lib/jazzy/symbol_graph/symbol.rb, line 151
def init_constraints(hash, raw_decl)
  raw_constraints = %i[swiftGenerics swiftExtension].flat_map do |key|
    next [] unless container = hash[key]

    container[:constraints] || []
  end

  constraints =
    Constraint.new_list_for_symbol(raw_constraints, path_components)
  if raw_decl =~ / where (.*)$/
    constraints +=
      Constraint.new_list_from_declaration(Regexp.last_match[1])
  end

  self.constraints = constraints.sort.uniq
end
init_declaration(raw_decl) click to toggle source

Repair problems with SymbolGraph’s declprinter

# File lib/jazzy/symbol_graph/symbol.rb, line 63
def init_declaration(raw_decl)
  # Too much 'Self.TypeName'; omitted arg labels look odd;
  # duplicated constraints; swift 5.3 vs. master workaround
  self.declaration = raw_decl
    .gsub(/\bSelf\./, '')
    .gsub(/(?<=\(|, )_: /, '_ arg: ')
    .gsub(/ where.*$/, '')
  if kind == 'source.lang.swift.decl.class'
    declaration.sub!(/\s*:.*$/, '')
  end
end
init_doc_comments(comments_hash) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 180
def init_doc_comments(comments_hash)
  self.doc_comments = comments_hash[:lines]
    .map { |l| l[:text] }
    .join("\n")
end
init_func_signature(func_signature) click to toggle source

Remember pieces of methods for later markdown parsing

# File lib/jazzy/symbol_graph/symbol.rb, line 77
def init_func_signature(func_signature)
  self.parameter_names =
    (func_signature[:parameters] || []).map { |h| h[:name] }
end
init_generic_type_params(hash) click to toggle source

Generic type params

# File lib/jazzy/symbol_graph/symbol.rb, line 169
def init_generic_type_params(hash)
  self.generic_type_params = Set.new(
    if (generics = hash[:swiftGenerics]) &&
       (parameters = generics[:parameters])
      parameters.map { |p| p[:name] }
    else
      []
    end,
  )
end
init_kind(kind, keywords) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 122
def init_kind(kind, keywords)
  adjusted = adjust_kind_for_declaration(kind, keywords)
  sourcekit_kind = KIND_MAP[adjusted.sub('swift.', '')]
  raise "Unknown symbol kind '#{kind}'" unless sourcekit_kind

  self.kind = "source.lang.swift.decl.#{sourcekit_kind}"
end
init_location(loc_hash) click to toggle source

Symbol location - only available for public+ decls

# File lib/jazzy/symbol_graph/symbol.rb, line 142
def init_location(loc_hash)
  self.location = {}
  location[:filename] = loc_hash[:uri].sub(%r{^file://}, '')
  location[:line] = loc_hash[:position][:line]
  location[:character] = loc_hash[:position][:character]
end
name() click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 21
def name
  path_components[-1] || '??'
end
parse_decl_fragments(fragments) click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 51
def parse_decl_fragments(fragments)
  decl = ''
  keywords = Set.new
  fragments.each do |frag|
    decl += frag[:spelling]
    keywords.add(frag[:spelling]) if frag[:kind] == 'keyword'
  end
  [decl, keywords]
end
spi_attributes() click to toggle source
# File lib/jazzy/symbol_graph/symbol.rb, line 220
def spi_attributes
  spi ? ['@_spi(Unknown)'] : []
end