jazzy 0.13.5 → 0.13.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +6 -3
- data/CHANGELOG.md +18 -0
- data/Gemfile.lock +35 -35
- data/README.md +57 -4
- data/bin/sourcekitten +0 -0
- data/lib/jazzy/config.rb +5 -5
- data/lib/jazzy/doc_builder.rb +7 -1
- data/lib/jazzy/gem_version.rb +1 -1
- data/lib/jazzy/sourcekitten.rb +5 -6
- data/lib/jazzy/symbol_graph.rb +95 -0
- data/lib/jazzy/symbol_graph/constraint.rb +94 -0
- data/lib/jazzy/symbol_graph/ext_node.rb +114 -0
- data/lib/jazzy/symbol_graph/graph.rb +193 -0
- data/lib/jazzy/symbol_graph/relationship.rb +39 -0
- data/lib/jazzy/symbol_graph/sym_node.rb +154 -0
- data/lib/jazzy/symbol_graph/symbol.rb +208 -0
- data/spec/integration_spec.rb +17 -8
- metadata +13 -7
@@ -0,0 +1,94 @@
|
|
1
|
+
module Jazzy
|
2
|
+
module SymbolGraph
|
3
|
+
# Constraint is a tidied-up JSON object, used by both Symbol and
|
4
|
+
# Relationship, and key to reconstructing extensions.
|
5
|
+
class Constraint
|
6
|
+
attr_accessor :kind
|
7
|
+
attr_accessor :lhs
|
8
|
+
attr_accessor :rhs
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def initialize(kind, lhs, rhs)
|
13
|
+
self.kind = kind # "==" or ":"
|
14
|
+
self.lhs = lhs
|
15
|
+
self.rhs = rhs
|
16
|
+
end
|
17
|
+
|
18
|
+
public
|
19
|
+
|
20
|
+
KIND_MAP = {
|
21
|
+
'conformance' => ':',
|
22
|
+
'superclass' => ':',
|
23
|
+
'sameType' => '==',
|
24
|
+
}.freeze
|
25
|
+
|
26
|
+
# Init from a JSON hash
|
27
|
+
def self.new_hash(hash)
|
28
|
+
kind = KIND_MAP[hash[:kind]]
|
29
|
+
raise "Unknown constraint kind '#{kind}'" unless kind
|
30
|
+
lhs = hash[:lhs].sub(/^Self\./, '')
|
31
|
+
rhs = hash[:rhs].sub(/^Self\./, '')
|
32
|
+
new(kind, lhs, rhs)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Init from a Swift declaration fragment eg. 'A : B'
|
36
|
+
def self.new_declaration(decl)
|
37
|
+
decl =~ /^(.*?)\s*([:<=]+)\s*(.*)$/
|
38
|
+
new(Regexp.last_match[2],
|
39
|
+
Regexp.last_match[1],
|
40
|
+
Regexp.last_match[3])
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_swift
|
44
|
+
"#{lhs} #{kind} #{rhs}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# The first component of types in the constraint
|
48
|
+
def type_names
|
49
|
+
Set.new([lhs, rhs].map { |n| n.sub(/\..*$/, '') })
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.new_list(hash_list)
|
53
|
+
hash_list.map { |h| Constraint.new_hash(h) }.sort.uniq
|
54
|
+
end
|
55
|
+
|
56
|
+
# Swift protocols and reqs have an implementation/hidden conformance
|
57
|
+
# to their own protocol: we don't want to think about this in docs.
|
58
|
+
def self.new_list_for_symbol(hash_list, path_components)
|
59
|
+
hash_list.map do |hash|
|
60
|
+
if hash[:lhs] == 'Self' &&
|
61
|
+
hash[:kind] == 'conformance' &&
|
62
|
+
path_components.include?(hash[:rhs])
|
63
|
+
next nil
|
64
|
+
end
|
65
|
+
Constraint.new_hash(hash)
|
66
|
+
end.compact
|
67
|
+
end
|
68
|
+
|
69
|
+
# Workaround Swift 5.3 bug with missing constraint rels
|
70
|
+
def self.new_list_from_declaration(decl)
|
71
|
+
decl.split(/\s*,\s*/).map { |cons| Constraint.new_declaration(cons) }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sort order - by Swift text
|
75
|
+
include Comparable
|
76
|
+
|
77
|
+
def <=>(other)
|
78
|
+
to_swift <=> other.to_swift
|
79
|
+
end
|
80
|
+
|
81
|
+
alias eql? ==
|
82
|
+
|
83
|
+
def hash
|
84
|
+
to_swift.hash
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Array
|
91
|
+
def to_where_clause
|
92
|
+
empty? ? '' : ' where ' + map(&:to_swift).join(', ')
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Jazzy
|
2
|
+
module SymbolGraph
|
3
|
+
# For extensions we need to track constraints of the extended type
|
4
|
+
# and the constraints introduced by the extension.
|
5
|
+
class ExtConstraints
|
6
|
+
attr_accessor :type # array
|
7
|
+
attr_accessor :ext # array
|
8
|
+
|
9
|
+
# all constraints inherited by members of the extension
|
10
|
+
def merged
|
11
|
+
(type + ext).sort
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(type_constraints, ext_constraints)
|
15
|
+
self.type = type_constraints || []
|
16
|
+
self.ext = ext_constraints || []
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# An ExtNode is a node of the reconstructed syntax tree representing
|
21
|
+
# an extension that we fabricate to resolve certain relationships.
|
22
|
+
class ExtNode < BaseNode
|
23
|
+
attr_accessor :usr
|
24
|
+
attr_accessor :name
|
25
|
+
attr_accessor :all_constraints # ExtConstraints
|
26
|
+
attr_accessor :conformances # array, can be empty
|
27
|
+
|
28
|
+
# Deduce an extension from a member of an unknown type or
|
29
|
+
# of known type with additional constraints
|
30
|
+
def self.new_for_member(type_usr,
|
31
|
+
member,
|
32
|
+
constraints)
|
33
|
+
new(type_usr,
|
34
|
+
member.parent_qualified_name,
|
35
|
+
constraints).tap { |o| o.add_child(member) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Deduce an extension from a protocol conformance for some type
|
39
|
+
def self.new_for_conformance(type_usr,
|
40
|
+
type_name,
|
41
|
+
protocol,
|
42
|
+
constraints)
|
43
|
+
new(type_usr, type_name, constraints).tap do |o|
|
44
|
+
o.add_conformance(protocol)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def initialize(usr, name, constraints)
|
51
|
+
self.usr = usr
|
52
|
+
self.name = name
|
53
|
+
self.all_constraints = constraints
|
54
|
+
self.conformances = []
|
55
|
+
super()
|
56
|
+
end
|
57
|
+
|
58
|
+
public
|
59
|
+
|
60
|
+
def constraints
|
61
|
+
all_constraints.merged
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_conformance(protocol)
|
65
|
+
conformances.append(protocol).sort!
|
66
|
+
end
|
67
|
+
|
68
|
+
def full_declaration
|
69
|
+
decl = "extension #{name}"
|
70
|
+
unless conformances.empty?
|
71
|
+
decl += ' : ' + conformances.join(', ')
|
72
|
+
end
|
73
|
+
decl + all_constraints.ext.to_where_clause
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_sourcekit(module_name)
|
77
|
+
declaration = full_declaration
|
78
|
+
xml_declaration = "<swift>#{CGI.escapeHTML(declaration)}</swift>"
|
79
|
+
|
80
|
+
hash = {
|
81
|
+
'key.kind' => 'source.lang.swift.decl.extension',
|
82
|
+
'key.usr' => usr,
|
83
|
+
'key.name' => name,
|
84
|
+
'key.modulename' => module_name,
|
85
|
+
'key.parsed_declaration' => declaration,
|
86
|
+
'key.annotated_decl' => xml_declaration,
|
87
|
+
}
|
88
|
+
|
89
|
+
unless conformances.empty?
|
90
|
+
hash['key.inheritedtypes'] = conformances.map do |conformance|
|
91
|
+
{ 'key.name' => conformance }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
unless children.empty?
|
96
|
+
hash['key.substructure'] = children_to_sourcekit
|
97
|
+
end
|
98
|
+
|
99
|
+
hash
|
100
|
+
end
|
101
|
+
|
102
|
+
# Sort order - by type name then constraint
|
103
|
+
include Comparable
|
104
|
+
|
105
|
+
def sort_key
|
106
|
+
name + constraints.map(&:to_swift).join
|
107
|
+
end
|
108
|
+
|
109
|
+
def <=>(other)
|
110
|
+
sort_key <=> other.sort_key
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# rubocop:disable Metrics/ClassLength
|
2
|
+
module Jazzy
|
3
|
+
module SymbolGraph
|
4
|
+
# A Graph is the coordinator to import a symbolgraph json file.
|
5
|
+
# Deserialize it to Symbols and Relationships, then rebuild
|
6
|
+
# the AST shape using SymNodes and ExtNodes and extract SourceKit json.
|
7
|
+
class Graph
|
8
|
+
attr_accessor :module_name
|
9
|
+
attr_accessor :symbol_nodes # usr -> SymNode
|
10
|
+
attr_accessor :relationships # [Relationship]
|
11
|
+
attr_accessor :ext_nodes # (usr, constraints) -> ExtNode
|
12
|
+
|
13
|
+
# Parse the JSON into flat tables of data
|
14
|
+
def initialize(json, module_name)
|
15
|
+
self.module_name = module_name
|
16
|
+
graph = JSON.parse(json, symbolize_names: true)
|
17
|
+
|
18
|
+
self.symbol_nodes = {}
|
19
|
+
graph[:symbols].each do |hash|
|
20
|
+
symbol = Symbol.new(hash)
|
21
|
+
symbol_nodes[symbol.usr] = SymNode.new(symbol)
|
22
|
+
end
|
23
|
+
|
24
|
+
self.relationships =
|
25
|
+
graph[:relationships].map { |hash| Relationship.new(hash) }
|
26
|
+
|
27
|
+
self.ext_nodes = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# ExtNode index. (type USR, extension constraints) -> ExtNode.
|
31
|
+
# This minimizes the number of extensions
|
32
|
+
|
33
|
+
def ext_key(usr, constraints)
|
34
|
+
usr + constraints.map(&:to_swift).join
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_ext_member(type_usr, member_node, constraints)
|
38
|
+
key = ext_key(type_usr, constraints.ext)
|
39
|
+
if ext_node = ext_nodes[key]
|
40
|
+
ext_node.add_child(member_node)
|
41
|
+
else
|
42
|
+
ext_nodes[key] =
|
43
|
+
ExtNode.new_for_member(type_usr, member_node, constraints)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_ext_conformance(type_usr,
|
48
|
+
type_name,
|
49
|
+
protocol,
|
50
|
+
constraints)
|
51
|
+
key = ext_key(type_usr, constraints.ext)
|
52
|
+
if ext_node = ext_nodes[key]
|
53
|
+
ext_node.add_conformance(protocol)
|
54
|
+
else
|
55
|
+
ext_nodes[key] =
|
56
|
+
ExtNode.new_for_conformance(type_usr,
|
57
|
+
type_name,
|
58
|
+
protocol,
|
59
|
+
constraints)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Increasingly desparate ways to find the name of the symbol
|
64
|
+
# at the target end of a relationship
|
65
|
+
def rel_target_name(rel, target_node)
|
66
|
+
(target_node && target_node.symbol.name) ||
|
67
|
+
rel.target_fallback ||
|
68
|
+
Jazzy::SymbolGraph.demangle(rel.target_usr)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Same for the source end. Less help from the tool here
|
72
|
+
def rel_source_name(rel, source_node)
|
73
|
+
(source_node && source_node.qualified_name) ||
|
74
|
+
Jazzy::SymbolGraph.demangle(rel.source_usr)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Protocol conformance is redundant if it's unconditional
|
78
|
+
# and already expressed in the type's declaration.
|
79
|
+
def redundant_conformance?(rel, type, protocol)
|
80
|
+
type && rel.constraints.empty? && type.conformance?(protocol)
|
81
|
+
end
|
82
|
+
|
83
|
+
# source is a member/protocol requirement of target
|
84
|
+
def rebuild_member(rel, source, target)
|
85
|
+
return unless source
|
86
|
+
|
87
|
+
source.protocol_requirement = rel.protocol_requirement?
|
88
|
+
constraints =
|
89
|
+
ExtConstraints.new(target && target.constraints,
|
90
|
+
source.unique_context_constraints(target))
|
91
|
+
|
92
|
+
# Add to its parent or invent an extension
|
93
|
+
unless target && target.try_add_child(source, constraints.ext)
|
94
|
+
add_ext_member(rel.target_usr, source, constraints)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# "source : target" either from type decl or ext decl
|
99
|
+
def rebuild_conformance(rel, source, target)
|
100
|
+
protocol_name = rel_target_name(rel, target)
|
101
|
+
|
102
|
+
return if redundant_conformance?(rel, source, protocol_name)
|
103
|
+
|
104
|
+
type_constraints = (source && source.constraints) || []
|
105
|
+
constraints =
|
106
|
+
ExtConstraints.new(type_constraints,
|
107
|
+
rel.constraints - type_constraints)
|
108
|
+
|
109
|
+
# Create an extension or enhance an existing one
|
110
|
+
add_ext_conformance(rel.source_usr,
|
111
|
+
rel_source_name(rel, source),
|
112
|
+
protocol_name,
|
113
|
+
constraints)
|
114
|
+
end
|
115
|
+
|
116
|
+
# "source is a default implementation of protocol requirement target"
|
117
|
+
def rebuild_default_implementation(_rel, source, target)
|
118
|
+
return unless source
|
119
|
+
|
120
|
+
unless target &&
|
121
|
+
(target_parent = target.parent) &&
|
122
|
+
target_parent.is_a?(SymNode)
|
123
|
+
# Could probably figure this out with demangle, but...
|
124
|
+
warn "Can't resolve membership of default implementation "\
|
125
|
+
"#{source.symbol.usr}."
|
126
|
+
source.unlisted = true
|
127
|
+
return
|
128
|
+
end
|
129
|
+
constraints =
|
130
|
+
ExtConstraints.new(target_parent.constraints,
|
131
|
+
source.unique_context_constraints(target_parent))
|
132
|
+
|
133
|
+
add_ext_member(target_parent.symbol.usr,
|
134
|
+
source,
|
135
|
+
constraints)
|
136
|
+
end
|
137
|
+
|
138
|
+
# "source is a class that inherits from target"
|
139
|
+
def rebuild_inherits(_rel, source, target)
|
140
|
+
if source && target
|
141
|
+
source.superclass_name = target.symbol.name
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Process a structural relationship to link nodes
|
146
|
+
def rebuild_rel(rel)
|
147
|
+
source = symbol_nodes[rel.source_usr]
|
148
|
+
target = symbol_nodes[rel.target_usr]
|
149
|
+
|
150
|
+
case rel.kind
|
151
|
+
when :memberOf, :optionalRequirementOf, :requirementOf
|
152
|
+
rebuild_member(rel, source, target)
|
153
|
+
|
154
|
+
when :conformsTo
|
155
|
+
rebuild_conformance(rel, source, target)
|
156
|
+
|
157
|
+
when :defaultImplementationOf
|
158
|
+
rebuild_default_implementation(rel, source, target)
|
159
|
+
|
160
|
+
when :inheritsFrom
|
161
|
+
rebuild_inherits(rel, source, target)
|
162
|
+
end
|
163
|
+
# don't seem to care about:
|
164
|
+
# - overrides: not bothered, also unimplemented for protocols
|
165
|
+
end
|
166
|
+
|
167
|
+
# Rebuild the AST structure and convert to SourceKit
|
168
|
+
def to_sourcekit
|
169
|
+
# Do default impls after the others so we can find protocol
|
170
|
+
# type nodes from protocol requirements.
|
171
|
+
default_impls, other_rels =
|
172
|
+
relationships.partition(&:default_implementation?)
|
173
|
+
(other_rels + default_impls).each { |r| rebuild_rel(r) }
|
174
|
+
|
175
|
+
root_symbol_nodes =
|
176
|
+
symbol_nodes.values
|
177
|
+
.select(&:top_level_decl?)
|
178
|
+
.sort
|
179
|
+
.map(&:to_sourcekit)
|
180
|
+
|
181
|
+
root_ext_nodes =
|
182
|
+
ext_nodes.values
|
183
|
+
.sort
|
184
|
+
.map { |n| n.to_sourcekit(module_name) }
|
185
|
+
{
|
186
|
+
'key.diagnostic_stage' => 'parse',
|
187
|
+
'key.substructure' => root_symbol_nodes + root_ext_nodes,
|
188
|
+
}
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Jazzy
|
2
|
+
module SymbolGraph
|
3
|
+
# A Relationship is a tidied-up SymbolGraph JSON object
|
4
|
+
class Relationship
|
5
|
+
attr_accessor :kind
|
6
|
+
attr_accessor :source_usr
|
7
|
+
attr_accessor :target_usr
|
8
|
+
attr_accessor :target_fallback # can be nil
|
9
|
+
attr_accessor :constraints # array, can be empty
|
10
|
+
|
11
|
+
KINDS = %w[memberOf conformsTo defaultImplementationOf
|
12
|
+
overrides inheritsFrom requirementOf
|
13
|
+
optionalRequirementOf].freeze
|
14
|
+
|
15
|
+
def protocol_requirement?
|
16
|
+
%i[requirementOf optionalRequirementOf].include? kind
|
17
|
+
end
|
18
|
+
|
19
|
+
def default_implementation?
|
20
|
+
kind == :defaultImplementationOf
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(hash)
|
24
|
+
kind = hash[:kind]
|
25
|
+
unless KINDS.include?(kind)
|
26
|
+
raise "Unknown relationship kind '#{kind}'"
|
27
|
+
end
|
28
|
+
self.kind = kind.to_sym
|
29
|
+
self.source_usr = hash[:source]
|
30
|
+
self.target_usr = hash[:target]
|
31
|
+
if fallback = hash[:targetFallback]
|
32
|
+
# Strip the leading module name
|
33
|
+
self.target_fallback = fallback.sub(/^.*?\./, '')
|
34
|
+
end
|
35
|
+
self.constraints = Constraint.new_list(hash[:swiftConstraints] || [])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Jazzy
|
2
|
+
module SymbolGraph
|
3
|
+
# The rebuilt syntax tree is made of nodes that either match
|
4
|
+
# symbols or that we fabricate for extensions. This is the common
|
5
|
+
# treeishness.
|
6
|
+
class BaseNode
|
7
|
+
attr_accessor :children # array, can be empty
|
8
|
+
attr_accessor :parent # can be nil
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
self.children = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_child(child)
|
15
|
+
child.parent = self
|
16
|
+
children.append(child)
|
17
|
+
end
|
18
|
+
|
19
|
+
def children_to_sourcekit
|
20
|
+
children.sort.map(&:to_sourcekit)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# A SymNode is a node of the reconstructed syntax tree holding a symbol.
|
25
|
+
# It can turn itself into SourceKit and helps decode extensions.
|
26
|
+
class SymNode < BaseNode
|
27
|
+
attr_accessor :symbol
|
28
|
+
attr_writer :override
|
29
|
+
attr_writer :protocol_requirement
|
30
|
+
attr_writer :unlisted
|
31
|
+
attr_accessor :superclass_name
|
32
|
+
|
33
|
+
def override?
|
34
|
+
@override
|
35
|
+
end
|
36
|
+
|
37
|
+
def protocol_requirement?
|
38
|
+
@protocol_requirement
|
39
|
+
end
|
40
|
+
|
41
|
+
def top_level_decl?
|
42
|
+
!@unlisted && parent.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(symbol)
|
46
|
+
self.symbol = symbol
|
47
|
+
super()
|
48
|
+
end
|
49
|
+
|
50
|
+
def qualified_name
|
51
|
+
symbol.path_components.join('.')
|
52
|
+
end
|
53
|
+
|
54
|
+
def parent_qualified_name
|
55
|
+
symbol.path_components[0...-1].join('.')
|
56
|
+
end
|
57
|
+
|
58
|
+
def protocol?
|
59
|
+
symbol.kind.end_with?('protocol')
|
60
|
+
end
|
61
|
+
|
62
|
+
def constraints
|
63
|
+
symbol.constraints
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add another SymNode as a member if possible.
|
67
|
+
# It must go in an extension if either:
|
68
|
+
# - it has different generic constraints to us; or
|
69
|
+
# - we're a protocol and it's a default impl / ext method
|
70
|
+
def try_add_child(node, unique_context_constraints)
|
71
|
+
unless unique_context_constraints.empty? &&
|
72
|
+
(!protocol? || node.protocol_requirement?)
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
add_child(node)
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
# The `Constraint`s on this decl that are both:
|
80
|
+
# 1. Unique, ie. not just inherited from its context; and
|
81
|
+
# 2. Constraining the *context's* gen params rather than our own.
|
82
|
+
def unique_context_constraints(context)
|
83
|
+
return symbol.constraints unless context
|
84
|
+
|
85
|
+
new_generic_type_params =
|
86
|
+
symbol.generic_type_params - context.symbol.generic_type_params
|
87
|
+
|
88
|
+
(symbol.constraints - context.symbol.constraints)
|
89
|
+
.select { |con| con.type_names.disjoint?(new_generic_type_params) }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Messy check whether we need to fabricate an extension for a protocol
|
93
|
+
# conformance: don't bother if it's already in the type declaration.
|
94
|
+
def conformance?(protocol)
|
95
|
+
return false unless symbol.declaration =~ /(?<=:).*?(?=(where|$))/
|
96
|
+
Regexp.last_match[0] =~ /\b#{protocol}\b/
|
97
|
+
end
|
98
|
+
|
99
|
+
# Generate the 'where' clause for the declaration
|
100
|
+
def where_clause
|
101
|
+
parent_constraints = (parent && parent.constraints) || []
|
102
|
+
(constraints - parent_constraints).to_where_clause
|
103
|
+
end
|
104
|
+
|
105
|
+
def inherits_clause
|
106
|
+
return '' unless superclass_name
|
107
|
+
" : #{superclass_name}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def full_declaration
|
111
|
+
symbol.availability
|
112
|
+
.append(symbol.declaration + inherits_clause + where_clause)
|
113
|
+
.join("\n")
|
114
|
+
end
|
115
|
+
|
116
|
+
# rubocop:disable Metrics/MethodLength
|
117
|
+
def to_sourcekit
|
118
|
+
declaration = full_declaration
|
119
|
+
xml_declaration = "<swift>#{CGI.escapeHTML(declaration)}</swift>"
|
120
|
+
|
121
|
+
hash = {
|
122
|
+
'key.kind' => symbol.kind,
|
123
|
+
'key.usr' => symbol.usr,
|
124
|
+
'key.name' => symbol.name,
|
125
|
+
'key.accessibility' => symbol.acl,
|
126
|
+
'key.parsed_decl' => declaration,
|
127
|
+
'key.annotated_decl' => xml_declaration,
|
128
|
+
}
|
129
|
+
if docs = symbol.doc_comments
|
130
|
+
hash['key.doc.comment'] = docs
|
131
|
+
hash['key.doc.full_as_xml'] = ''
|
132
|
+
end
|
133
|
+
if location = symbol.location
|
134
|
+
hash['key.filepath'] = location[:filename]
|
135
|
+
hash['key.doc.line'] = location[:line]
|
136
|
+
hash['key.doc.column'] = location[:character]
|
137
|
+
end
|
138
|
+
unless children.empty?
|
139
|
+
hash['key.substructure'] = children_to_sourcekit
|
140
|
+
end
|
141
|
+
|
142
|
+
hash
|
143
|
+
end
|
144
|
+
# rubocop:enable Metrics/MethodLength
|
145
|
+
|
146
|
+
# Sort order - by symbol
|
147
|
+
include Comparable
|
148
|
+
|
149
|
+
def <=>(other)
|
150
|
+
symbol <=> other.symbol
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|