jazzy 0.13.5 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Tests.yml +52 -0
  3. data/.rubocop.yml +139 -24
  4. data/CHANGELOG.md +105 -0
  5. data/CONTRIBUTING.md +5 -5
  6. data/Dangerfile +11 -8
  7. data/Gemfile +3 -1
  8. data/Gemfile.lock +101 -77
  9. data/README.md +81 -13
  10. data/Rakefile +13 -12
  11. data/bin/jazzy +3 -2
  12. data/bin/sourcekitten +0 -0
  13. data/jazzy.gemspec +8 -6
  14. data/js/package-lock.json +30 -25
  15. data/js/package.json +3 -3
  16. data/lib/jazzy/config.rb +125 -72
  17. data/lib/jazzy/doc.rb +3 -1
  18. data/lib/jazzy/doc_builder.rb +79 -84
  19. data/lib/jazzy/docset_builder.rb +3 -1
  20. data/lib/jazzy/documentation_generator.rb +6 -2
  21. data/lib/jazzy/executable.rb +3 -0
  22. data/lib/jazzy/extensions/bitbucket/img/bitbucket.svg +11 -0
  23. data/lib/jazzy/{themes/apple/assets → extensions/github}/img/gh.png +0 -0
  24. data/lib/jazzy/extensions/gitlab/img/gitlab.svg +23 -0
  25. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.ttf +0 -0
  26. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff +0 -0
  27. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  28. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  29. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  30. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  31. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  32. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  33. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  34. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  35. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  36. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  37. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  38. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  39. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  40. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.ttf +0 -0
  41. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff +0 -0
  42. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff2 +0 -0
  43. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  44. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  45. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  46. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.ttf +0 -0
  47. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff +0 -0
  48. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff2 +0 -0
  49. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.ttf +0 -0
  50. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff +0 -0
  51. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff2 +0 -0
  52. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  53. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  54. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  55. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.ttf +0 -0
  56. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff +0 -0
  57. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff2 +0 -0
  58. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  59. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  60. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  61. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  62. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  63. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  64. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  65. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  66. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  67. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.ttf +0 -0
  68. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff +0 -0
  69. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff2 +0 -0
  70. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.ttf +0 -0
  71. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff +0 -0
  72. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  73. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.ttf +0 -0
  74. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff +0 -0
  75. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  76. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.ttf +0 -0
  77. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff +0 -0
  78. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  79. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.ttf +0 -0
  80. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff +0 -0
  81. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  82. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  83. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  84. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  85. data/lib/jazzy/extensions/katex/css/katex.min.css +1 -1
  86. data/lib/jazzy/extensions/katex/js/katex.min.js +1 -1
  87. data/lib/jazzy/gem_version.rb +3 -1
  88. data/lib/jazzy/highlighter.rb +17 -3
  89. data/lib/jazzy/jazzy_markdown.rb +112 -31
  90. data/lib/jazzy/podspec_documenter.rb +14 -16
  91. data/lib/jazzy/search_builder.rb +4 -3
  92. data/lib/jazzy/source_declaration/access_control_level.rb +7 -5
  93. data/lib/jazzy/source_declaration/type.rb +29 -3
  94. data/lib/jazzy/source_declaration.rb +18 -3
  95. data/lib/jazzy/source_document.rb +8 -5
  96. data/lib/jazzy/source_host.rb +111 -0
  97. data/lib/jazzy/source_mark.rb +8 -6
  98. data/lib/jazzy/source_module.rb +6 -6
  99. data/lib/jazzy/sourcekitten.rb +157 -86
  100. data/lib/jazzy/stats.rb +14 -3
  101. data/lib/jazzy/symbol_graph/constraint.rb +98 -0
  102. data/lib/jazzy/symbol_graph/ext_node.rb +116 -0
  103. data/lib/jazzy/symbol_graph/graph.rb +200 -0
  104. data/lib/jazzy/symbol_graph/relationship.rb +48 -0
  105. data/lib/jazzy/symbol_graph/sym_node.rb +176 -0
  106. data/lib/jazzy/symbol_graph/symbol.rb +248 -0
  107. data/lib/jazzy/symbol_graph.rb +95 -0
  108. data/lib/jazzy/themes/apple/assets/css/highlight.css.scss +63 -59
  109. data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +26 -2
  110. data/lib/jazzy/themes/apple/assets/js/jazzy.js +4 -0
  111. data/lib/jazzy/themes/apple/assets/js/jazzy.search.js +4 -0
  112. data/lib/jazzy/themes/apple/assets/js/jquery.min.js +2 -2
  113. data/lib/jazzy/themes/apple/assets/js/lunr.min.js +3 -3
  114. data/lib/jazzy/themes/apple/templates/doc.mustache +4 -5
  115. data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
  116. data/lib/jazzy/themes/apple/templates/header.mustache +6 -6
  117. data/lib/jazzy/themes/apple/templates/task.mustache +6 -11
  118. data/lib/jazzy/themes/fullwidth/assets/css/highlight.css.scss +63 -59
  119. data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +11 -2
  120. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.js +4 -0
  121. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.search.js +4 -0
  122. data/lib/jazzy/themes/fullwidth/assets/js/jquery.min.js +2 -2
  123. data/lib/jazzy/themes/fullwidth/assets/js/lunr.min.js +3 -3
  124. data/lib/jazzy/themes/fullwidth/templates/doc.mustache +4 -5
  125. data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
  126. data/lib/jazzy/themes/fullwidth/templates/header.mustache +8 -8
  127. data/lib/jazzy/themes/fullwidth/templates/task.mustache +6 -11
  128. data/lib/jazzy/themes/jony/assets/css/highlight.css.scss +63 -59
  129. data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +26 -2
  130. data/lib/jazzy/themes/jony/assets/js/jazzy.js +4 -0
  131. data/lib/jazzy/themes/jony/assets/js/jquery.min.js +2 -2
  132. data/lib/jazzy/themes/jony/templates/doc.mustache +4 -5
  133. data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
  134. data/lib/jazzy/themes/jony/templates/header.mustache +6 -6
  135. data/lib/jazzy/themes/jony/templates/task.mustache +6 -11
  136. data/lib/jazzy.rb +2 -0
  137. data/spec/integration_spec.rb +59 -45
  138. data/spec/spec_helper/pre_flight.rb +2 -0
  139. data/spec/spec_helper.rb +3 -1
  140. metadata +39 -19
  141. data/.circleci/config.yml +0 -83
  142. data/lib/jazzy/themes/fullwidth/assets/img/gh.png +0 -0
  143. data/lib/jazzy/themes/jony/assets/img/gh.png +0 -0
  144. data/spec/sourcekitten_spec.rb +0 -6
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jazzy
4
+ module SymbolGraph
5
+ # For extensions we need to track constraints of the extended type
6
+ # and the constraints introduced by the extension.
7
+ class ExtConstraints
8
+ attr_accessor :type # array
9
+ attr_accessor :ext # array
10
+
11
+ # all constraints inherited by members of the extension
12
+ def merged
13
+ (type + ext).sort
14
+ end
15
+
16
+ def initialize(type_constraints, ext_constraints)
17
+ self.type = type_constraints || []
18
+ self.ext = ext_constraints || []
19
+ end
20
+ end
21
+
22
+ # An ExtNode is a node of the reconstructed syntax tree representing
23
+ # an extension that we fabricate to resolve certain relationships.
24
+ class ExtNode < BaseNode
25
+ attr_accessor :usr
26
+ attr_accessor :name
27
+ attr_accessor :all_constraints # ExtConstraints
28
+ attr_accessor :conformances # array, can be empty
29
+
30
+ # Deduce an extension from a member of an unknown type or
31
+ # of known type with additional constraints
32
+ def self.new_for_member(type_usr,
33
+ member,
34
+ constraints)
35
+ new(type_usr,
36
+ member.parent_qualified_name,
37
+ constraints).tap { |o| o.add_child(member) }
38
+ end
39
+
40
+ # Deduce an extension from a protocol conformance for some type
41
+ def self.new_for_conformance(type_usr,
42
+ type_name,
43
+ protocol,
44
+ constraints)
45
+ new(type_usr, type_name, constraints).tap do |o|
46
+ o.add_conformance(protocol)
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def initialize(usr, name, constraints)
53
+ self.usr = usr
54
+ self.name = name
55
+ self.all_constraints = constraints
56
+ self.conformances = []
57
+ super()
58
+ end
59
+
60
+ public
61
+
62
+ def constraints
63
+ all_constraints.merged
64
+ end
65
+
66
+ def add_conformance(protocol)
67
+ conformances.append(protocol).sort!
68
+ end
69
+
70
+ def full_declaration
71
+ decl = "extension #{name}"
72
+ unless conformances.empty?
73
+ decl += " : #{conformances.join(', ')}"
74
+ end
75
+ decl + all_constraints.ext.to_where_clause
76
+ end
77
+
78
+ def to_sourcekit(module_name)
79
+ declaration = full_declaration
80
+ xml_declaration = "<swift>#{CGI.escapeHTML(declaration)}</swift>"
81
+
82
+ hash = {
83
+ 'key.kind' => 'source.lang.swift.decl.extension',
84
+ 'key.usr' => usr,
85
+ 'key.name' => name,
86
+ 'key.modulename' => module_name,
87
+ 'key.parsed_declaration' => declaration,
88
+ 'key.annotated_decl' => xml_declaration,
89
+ }
90
+
91
+ unless conformances.empty?
92
+ hash['key.inheritedtypes'] = conformances.map do |conformance|
93
+ { 'key.name' => conformance }
94
+ end
95
+ end
96
+
97
+ unless children.empty?
98
+ hash['key.substructure'] = children_to_sourcekit
99
+ end
100
+
101
+ hash
102
+ end
103
+
104
+ # Sort order - by type name then constraint
105
+ include Comparable
106
+
107
+ def sort_key
108
+ name + constraints.map(&:to_swift).join
109
+ end
110
+
111
+ def <=>(other)
112
+ sort_key <=> other.sort_key
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/ClassLength
4
+ module Jazzy
5
+ module SymbolGraph
6
+ # A Graph is the coordinator to import a symbolgraph json file.
7
+ # Deserialize it to Symbols and Relationships, then rebuild
8
+ # the AST shape using SymNodes and ExtNodes and extract SourceKit json.
9
+ class Graph
10
+ attr_accessor :module_name
11
+ attr_accessor :symbol_nodes # usr -> SymNode
12
+ attr_accessor :relationships # [Relationship]
13
+ attr_accessor :ext_nodes # (usr, constraints) -> ExtNode
14
+
15
+ # Parse the JSON into flat tables of data
16
+ def initialize(json, module_name)
17
+ self.module_name = module_name
18
+ graph = JSON.parse(json, symbolize_names: true)
19
+
20
+ self.symbol_nodes = {}
21
+ graph[:symbols].each do |hash|
22
+ symbol = Symbol.new(hash)
23
+ symbol_nodes[symbol.usr] = SymNode.new(symbol)
24
+ end
25
+
26
+ self.relationships =
27
+ graph[:relationships].map { |hash| Relationship.new(hash) }
28
+
29
+ self.ext_nodes = {}
30
+ end
31
+
32
+ # ExtNode index. (type USR, extension constraints) -> ExtNode.
33
+ # This minimizes the number of extensions
34
+
35
+ def ext_key(usr, constraints)
36
+ usr + constraints.map(&:to_swift).join
37
+ end
38
+
39
+ def add_ext_member(type_usr, member_node, constraints)
40
+ key = ext_key(type_usr, constraints.ext)
41
+ if ext_node = ext_nodes[key]
42
+ ext_node.add_child(member_node)
43
+ else
44
+ ext_nodes[key] =
45
+ ExtNode.new_for_member(type_usr, member_node, constraints)
46
+ end
47
+ end
48
+
49
+ def add_ext_conformance(type_usr,
50
+ type_name,
51
+ protocol,
52
+ constraints)
53
+ key = ext_key(type_usr, constraints.ext)
54
+ if ext_node = ext_nodes[key]
55
+ ext_node.add_conformance(protocol)
56
+ else
57
+ ext_nodes[key] =
58
+ ExtNode.new_for_conformance(type_usr,
59
+ type_name,
60
+ protocol,
61
+ constraints)
62
+ end
63
+ end
64
+
65
+ # Increasingly desparate ways to find the name of the symbol
66
+ # at the target end of a relationship
67
+ def rel_target_name(rel, target_node)
68
+ target_node&.symbol&.name ||
69
+ rel.target_fallback ||
70
+ Jazzy::SymbolGraph.demangle(rel.target_usr)
71
+ end
72
+
73
+ # Same for the source end. Less help from the tool here
74
+ def rel_source_name(rel, source_node)
75
+ source_node&.qualified_name ||
76
+ Jazzy::SymbolGraph.demangle(rel.source_usr)
77
+ end
78
+
79
+ # Protocol conformance is redundant if it's unconditional
80
+ # and already expressed in the type's declaration.
81
+ #
82
+ # Skip implementation-detail conformances.
83
+ def redundant_conformance?(rel, type, protocol)
84
+ return false unless type
85
+
86
+ (rel.constraints.empty? && type.conformance?(protocol)) ||
87
+ (type.actor? && rel.actor_protocol?)
88
+ end
89
+
90
+ # source is a member/protocol requirement of target
91
+ def rebuild_member(rel, source, target)
92
+ return unless source
93
+
94
+ source.protocol_requirement = rel.protocol_requirement?
95
+ constraints =
96
+ ExtConstraints.new(target&.constraints,
97
+ source.unique_context_constraints(target))
98
+
99
+ # Add to its parent or invent an extension
100
+ unless target&.try_add_child(source, constraints.ext)
101
+ add_ext_member(rel.target_usr, source, constraints)
102
+ end
103
+ end
104
+
105
+ # "source : target" either from type decl or ext decl
106
+ def rebuild_conformance(rel, source, target)
107
+ protocol_name = rel_target_name(rel, target)
108
+
109
+ return if redundant_conformance?(rel, source, protocol_name)
110
+
111
+ type_constraints = source&.constraints || []
112
+ constraints =
113
+ ExtConstraints.new(type_constraints,
114
+ rel.constraints - type_constraints)
115
+
116
+ # Create an extension or enhance an existing one
117
+ add_ext_conformance(rel.source_usr,
118
+ rel_source_name(rel, source),
119
+ protocol_name,
120
+ constraints)
121
+ end
122
+
123
+ # "source is a default implementation of protocol requirement target"
124
+ def rebuild_default_implementation(_rel, source, target)
125
+ return unless source
126
+
127
+ unless target &&
128
+ (target_parent = target.parent) &&
129
+ target_parent.is_a?(SymNode)
130
+ # Could probably figure this out with demangle, but...
131
+ warn "Can't resolve membership of default implementation "\
132
+ "#{source.symbol.usr}."
133
+ source.unlisted = true
134
+ return
135
+ end
136
+ constraints =
137
+ ExtConstraints.new(target_parent.constraints,
138
+ source.unique_context_constraints(target_parent))
139
+
140
+ add_ext_member(target_parent.symbol.usr,
141
+ source,
142
+ constraints)
143
+ end
144
+
145
+ # "source is a class that inherits from target"
146
+ def rebuild_inherits(_rel, source, target)
147
+ if source && target
148
+ source.superclass_name = target.symbol.name
149
+ end
150
+ end
151
+
152
+ # Process a structural relationship to link nodes
153
+ def rebuild_rel(rel)
154
+ source = symbol_nodes[rel.source_usr]
155
+ target = symbol_nodes[rel.target_usr]
156
+
157
+ case rel.kind
158
+ when :memberOf, :optionalRequirementOf, :requirementOf
159
+ rebuild_member(rel, source, target)
160
+
161
+ when :conformsTo
162
+ rebuild_conformance(rel, source, target)
163
+
164
+ when :defaultImplementationOf
165
+ rebuild_default_implementation(rel, source, target)
166
+
167
+ when :inheritsFrom
168
+ rebuild_inherits(rel, source, target)
169
+ end
170
+ # don't seem to care about:
171
+ # - overrides: not bothered, also unimplemented for protocols
172
+ end
173
+
174
+ # Rebuild the AST structure and convert to SourceKit
175
+ def to_sourcekit
176
+ # Do default impls after the others so we can find protocol
177
+ # type nodes from protocol requirements.
178
+ default_impls, other_rels =
179
+ relationships.partition(&:default_implementation?)
180
+ (other_rels + default_impls).each { |r| rebuild_rel(r) }
181
+
182
+ root_symbol_nodes =
183
+ symbol_nodes.values
184
+ .select(&:top_level_decl?)
185
+ .sort
186
+ .map(&:to_sourcekit)
187
+
188
+ root_ext_nodes =
189
+ ext_nodes.values
190
+ .sort
191
+ .map { |n| n.to_sourcekit(module_name) }
192
+ {
193
+ 'key.diagnostic_stage' => 'parse',
194
+ 'key.substructure' => root_symbol_nodes + root_ext_nodes,
195
+ }
196
+ end
197
+ end
198
+ end
199
+ end
200
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jazzy
4
+ module SymbolGraph
5
+ # A Relationship is a tidied-up SymbolGraph JSON object
6
+ class Relationship
7
+ attr_accessor :kind
8
+ attr_accessor :source_usr
9
+ attr_accessor :target_usr
10
+ attr_accessor :target_fallback # can be nil
11
+ attr_accessor :constraints # array, can be empty
12
+
13
+ KINDS = %w[memberOf conformsTo defaultImplementationOf
14
+ overrides inheritsFrom requirementOf
15
+ optionalRequirementOf].freeze
16
+
17
+ def protocol_requirement?
18
+ %i[requirementOf optionalRequirementOf].include? kind
19
+ end
20
+
21
+ def default_implementation?
22
+ kind == :defaultImplementationOf
23
+ end
24
+
25
+ # Protocol conformances added by compiler to actor decls that
26
+ # users aren't interested in.
27
+ def actor_protocol?
28
+ %w[Actor Sendable].include?(target_fallback)
29
+ end
30
+
31
+ def initialize(hash)
32
+ kind = hash[:kind]
33
+ unless KINDS.include?(kind)
34
+ raise "Unknown relationship kind '#{kind}'"
35
+ end
36
+
37
+ self.kind = kind.to_sym
38
+ self.source_usr = hash[:source]
39
+ self.target_usr = hash[:target]
40
+ if fallback = hash[:targetFallback]
41
+ # Strip the leading module name
42
+ self.target_fallback = fallback.sub(/^.*?\./, '')
43
+ end
44
+ self.constraints = Constraint.new_list(hash[:swiftConstraints] || [])
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jazzy
4
+ module SymbolGraph
5
+ # The rebuilt syntax tree is made of nodes that either match
6
+ # symbols or that we fabricate for extensions. This is the common
7
+ # treeishness.
8
+ class BaseNode
9
+ attr_accessor :children # array, can be empty
10
+ attr_accessor :parent # can be nil
11
+
12
+ def initialize
13
+ self.children = []
14
+ end
15
+
16
+ def add_child(child)
17
+ child.parent = self
18
+ children.append(child)
19
+ end
20
+
21
+ def children_to_sourcekit
22
+ children.sort.map(&:to_sourcekit)
23
+ end
24
+ end
25
+
26
+ # A SymNode is a node of the reconstructed syntax tree holding a symbol.
27
+ # It can turn itself into SourceKit and helps decode extensions.
28
+ # rubocop:disable Metrics/ClassLength
29
+ class SymNode < BaseNode
30
+ attr_accessor :symbol
31
+ attr_writer :override
32
+ attr_writer :protocol_requirement
33
+ attr_writer :unlisted
34
+ attr_accessor :superclass_name
35
+
36
+ def override?
37
+ @override
38
+ end
39
+
40
+ def protocol_requirement?
41
+ @protocol_requirement
42
+ end
43
+
44
+ def top_level_decl?
45
+ !@unlisted && parent.nil?
46
+ end
47
+
48
+ def initialize(symbol)
49
+ self.symbol = symbol
50
+ super()
51
+ end
52
+
53
+ def qualified_name
54
+ symbol.path_components.join('.')
55
+ end
56
+
57
+ def parent_qualified_name
58
+ symbol.path_components[0...-1].join('.')
59
+ end
60
+
61
+ def protocol?
62
+ symbol.kind.end_with?('protocol')
63
+ end
64
+
65
+ def actor?
66
+ symbol.kind.end_with?('actor')
67
+ end
68
+
69
+ def constraints
70
+ symbol.constraints
71
+ end
72
+
73
+ # Add another SymNode as a member if possible.
74
+ # It must go in an extension if either:
75
+ # - it has different generic constraints to us; or
76
+ # - we're a protocol and it's a default impl / ext method
77
+ def try_add_child(node, unique_context_constraints)
78
+ unless unique_context_constraints.empty? &&
79
+ (!protocol? || node.protocol_requirement?)
80
+ return false
81
+ end
82
+
83
+ add_child(node)
84
+ true
85
+ end
86
+
87
+ # The `Constraint`s on this decl that are both:
88
+ # 1. Unique, ie. not just inherited from its context; and
89
+ # 2. Constraining the *context's* gen params rather than our own.
90
+ def unique_context_constraints(context)
91
+ return symbol.constraints unless context
92
+
93
+ new_generic_type_params =
94
+ symbol.generic_type_params - context.symbol.generic_type_params
95
+
96
+ (symbol.constraints - context.symbol.constraints)
97
+ .select { |con| con.type_names.disjoint?(new_generic_type_params) }
98
+ end
99
+
100
+ # Messy check whether we need to fabricate an extension for a protocol
101
+ # conformance: don't bother if it's already in the type declaration.
102
+ def conformance?(protocol)
103
+ return false unless symbol.declaration =~ /(?<=:).*?(?=(where|$))/
104
+
105
+ Regexp.last_match[0] =~ /\b#{protocol}\b/
106
+ end
107
+
108
+ # Generate the 'where' clause for the declaration
109
+ def where_clause
110
+ parent_constraints = parent&.constraints || []
111
+ (constraints - parent_constraints).to_where_clause
112
+ end
113
+
114
+ def inherits_clause
115
+ return '' unless superclass_name
116
+
117
+ " : #{superclass_name}"
118
+ end
119
+
120
+ # approximately...
121
+ def async?
122
+ symbol.declaration =~ /\basync\b[^)]*$/
123
+ end
124
+
125
+ def full_declaration
126
+ symbol.attributes
127
+ .append(symbol.declaration + inherits_clause + where_clause)
128
+ .join("\n")
129
+ end
130
+
131
+ # rubocop:disable Metrics/MethodLength
132
+ def to_sourcekit
133
+ declaration = full_declaration
134
+ xml_declaration = "<swift>#{CGI.escapeHTML(declaration)}</swift>"
135
+
136
+ hash = {
137
+ 'key.kind' => symbol.kind,
138
+ 'key.usr' => symbol.usr,
139
+ 'key.name' => symbol.name,
140
+ 'key.accessibility' => symbol.acl,
141
+ 'key.parsed_decl' => declaration,
142
+ 'key.annotated_decl' => xml_declaration,
143
+ 'key.symgraph_async' => async?,
144
+ }
145
+ if docs = symbol.doc_comments
146
+ hash['key.doc.comment'] = docs
147
+ hash['key.doc.full_as_xml'] = ''
148
+ end
149
+ if params = symbol.parameter_names
150
+ hash['key.doc.parameters'] =
151
+ params.map { |name| { 'name' => name } }
152
+ end
153
+ if location = symbol.location
154
+ hash['key.filepath'] = location[:filename]
155
+ hash['key.doc.line'] = location[:line] + 1
156
+ hash['key.doc.column'] = location[:character] + 1
157
+ end
158
+ unless children.empty?
159
+ hash['key.substructure'] = children_to_sourcekit
160
+ end
161
+ hash['key.symgraph_spi'] = true if symbol.spi
162
+
163
+ hash
164
+ end
165
+ # rubocop:enable Metrics/MethodLength
166
+
167
+ # Sort order - by symbol
168
+ include Comparable
169
+
170
+ def <=>(other)
171
+ symbol <=> other.symbol
172
+ end
173
+ end
174
+ # rubocop:enable Metrics/ClassLength
175
+ end
176
+ end