jazzy 0.13.4 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/Tests.yml +52 -0
- data/.rubocop.yml +123 -24
- data/CHANGELOG.md +103 -0
- data/CONTRIBUTING.md +5 -5
- data/Dangerfile +11 -8
- data/Gemfile +3 -1
- data/Gemfile.lock +91 -69
- data/README.md +85 -13
- data/Rakefile +19 -13
- data/bin/jazzy +3 -2
- data/bin/sourcekitten +0 -0
- data/jazzy.gemspec +8 -6
- data/js/package-lock.json +30 -25
- data/js/package.json +3 -3
- data/lib/jazzy.rb +2 -0
- data/lib/jazzy/config.rb +124 -70
- data/lib/jazzy/doc.rb +3 -1
- data/lib/jazzy/doc_builder.rb +75 -81
- data/lib/jazzy/docset_builder.rb +3 -1
- data/lib/jazzy/documentation_generator.rb +6 -2
- data/lib/jazzy/executable.rb +3 -0
- data/lib/jazzy/extensions/bitbucket/img/bitbucket.svg +11 -0
- data/lib/jazzy/{themes/apple/assets → extensions/github}/img/gh.png +0 -0
- data/lib/jazzy/extensions/gitlab/img/gitlab.svg +23 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- data/lib/jazzy/extensions/katex/css/katex.min.css +1 -1
- data/lib/jazzy/extensions/katex/js/katex.min.js +1 -1
- data/lib/jazzy/gem_version.rb +3 -1
- data/lib/jazzy/highlighter.rb +5 -3
- data/lib/jazzy/jazzy_markdown.rb +101 -30
- data/lib/jazzy/podspec_documenter.rb +14 -16
- data/lib/jazzy/search_builder.rb +4 -3
- data/lib/jazzy/source_declaration.rb +24 -5
- data/lib/jazzy/source_declaration/access_control_level.rb +7 -5
- data/lib/jazzy/source_declaration/type.rb +33 -1
- data/lib/jazzy/source_document.rb +8 -5
- data/lib/jazzy/source_host.rb +111 -0
- data/lib/jazzy/source_mark.rb +8 -6
- data/lib/jazzy/source_module.rb +6 -6
- data/lib/jazzy/sourcekitten.rb +105 -79
- data/lib/jazzy/stats.rb +4 -2
- data/lib/jazzy/symbol_graph.rb +95 -0
- data/lib/jazzy/symbol_graph/constraint.rb +98 -0
- data/lib/jazzy/symbol_graph/ext_node.rb +116 -0
- data/lib/jazzy/symbol_graph/graph.rb +195 -0
- data/lib/jazzy/symbol_graph/relationship.rb +42 -0
- data/lib/jazzy/symbol_graph/sym_node.rb +163 -0
- data/lib/jazzy/symbol_graph/symbol.rb +222 -0
- data/lib/jazzy/themes/apple/assets/css/highlight.css.scss +63 -59
- data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +101 -4
- data/lib/jazzy/themes/apple/assets/img/spinner.gif +0 -0
- data/lib/jazzy/themes/apple/assets/js/jazzy.js +4 -0
- data/lib/jazzy/themes/apple/assets/js/jazzy.search.js +74 -0
- data/lib/jazzy/themes/apple/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/apple/assets/js/lunr.min.js +6 -0
- data/lib/jazzy/themes/apple/assets/js/typeahead.jquery.js +1694 -0
- data/lib/jazzy/themes/apple/templates/doc.mustache +35 -0
- data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
- data/lib/jazzy/themes/apple/templates/header.mustache +10 -3
- data/lib/jazzy/themes/apple/templates/task.mustache +4 -4
- data/lib/jazzy/themes/fullwidth/assets/css/highlight.css.scss +63 -59
- data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +11 -2
- data/lib/jazzy/themes/fullwidth/assets/js/jazzy.js +4 -0
- data/lib/jazzy/themes/fullwidth/assets/js/jazzy.search.js +4 -0
- data/lib/jazzy/themes/fullwidth/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/fullwidth/assets/js/lunr.min.js +3 -3
- data/lib/jazzy/themes/fullwidth/templates/doc.mustache +30 -0
- data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
- data/lib/jazzy/themes/fullwidth/templates/header.mustache +5 -5
- data/lib/jazzy/themes/fullwidth/templates/task.mustache +4 -4
- data/lib/jazzy/themes/jony/assets/css/highlight.css.scss +63 -59
- data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +34 -2
- data/lib/jazzy/themes/jony/assets/js/jazzy.js +4 -0
- data/lib/jazzy/themes/jony/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/jony/templates/doc.mustache +30 -0
- data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
- data/lib/jazzy/themes/jony/templates/header.mustache +5 -5
- data/lib/jazzy/themes/jony/templates/task.mustache +4 -4
- data/spec/integration_spec.rb +54 -42
- data/spec/spec_helper.rb +3 -1
- data/spec/spec_helper/pre_flight.rb +2 -0
- metadata +43 -19
- data/.circleci/config.yml +0 -83
- data/lib/jazzy/themes/fullwidth/assets/img/gh.png +0 -0
- data/lib/jazzy/themes/jony/assets/img/gh.png +0 -0
- data/spec/sourcekitten_spec.rb +0 -6
data/lib/jazzy/stats.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Jazzy
|
2
4
|
# Collect + report metadata about a processed module
|
3
5
|
class Stats
|
4
6
|
include Config::Mixin
|
5
7
|
|
6
|
-
attr_reader :documented, :acl_skipped
|
7
|
-
attr_reader :undocumented_decls
|
8
|
+
attr_reader :documented, :acl_skipped, :undocumented_decls
|
8
9
|
|
9
10
|
def add_documented
|
10
11
|
@documented += 1
|
@@ -57,6 +58,7 @@ module Jazzy
|
|
57
58
|
|
58
59
|
def doc_coverage
|
59
60
|
return 0 if acl_included == 0
|
61
|
+
|
60
62
|
(100 * documented) / acl_included
|
61
63
|
end
|
62
64
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'jazzy/symbol_graph/graph'
|
5
|
+
require 'jazzy/symbol_graph/constraint'
|
6
|
+
require 'jazzy/symbol_graph/symbol'
|
7
|
+
require 'jazzy/symbol_graph/relationship'
|
8
|
+
require 'jazzy/symbol_graph/sym_node'
|
9
|
+
require 'jazzy/symbol_graph/ext_node'
|
10
|
+
|
11
|
+
# This is the top-level symbolgraph driver that deals with
|
12
|
+
# figuring out arguments, running the tool, and loading the
|
13
|
+
# results.
|
14
|
+
|
15
|
+
module Jazzy
|
16
|
+
module SymbolGraph
|
17
|
+
# Run `swift symbolgraph-extract` with configured args,
|
18
|
+
# parse the results, and return as JSON in SourceKit[ten]
|
19
|
+
# format.
|
20
|
+
def self.build(config)
|
21
|
+
Dir.mktmpdir do |tmp_dir|
|
22
|
+
args = arguments(config, tmp_dir)
|
23
|
+
|
24
|
+
Executable.execute_command('swift',
|
25
|
+
args.unshift('symbolgraph-extract'),
|
26
|
+
true) # raise on error
|
27
|
+
|
28
|
+
Dir[tmp_dir + '/*.symbols.json'].map do |filename|
|
29
|
+
# The @ part is for extensions in our module (before the @)
|
30
|
+
# of types in another module (after the @).
|
31
|
+
filename =~ /(.*?)(@(.*?))?\.symbols/
|
32
|
+
module_name = Regexp.last_match[3] || Regexp.last_match[1]
|
33
|
+
{
|
34
|
+
filename =>
|
35
|
+
Graph.new(File.read(filename), module_name).to_sourcekit,
|
36
|
+
}
|
37
|
+
end.to_json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Figure out the args to pass to symbolgraph-extract
|
42
|
+
def self.arguments(config, output_path)
|
43
|
+
if config.module_name.empty?
|
44
|
+
raise 'error: `--swift-build-tool symbolgraph` requires `--module`.'
|
45
|
+
end
|
46
|
+
|
47
|
+
user_args = config.build_tool_arguments.join
|
48
|
+
|
49
|
+
if user_args =~ /-(?:module-name|minimum-access-level|output-dir)/
|
50
|
+
raise 'error: `--build-tool-arguments` for '\
|
51
|
+
"`--swift-build-tool symbolgraph` can't use `-module`, "\
|
52
|
+
'`-minimum-access-level`, or `-output-dir`.'
|
53
|
+
end
|
54
|
+
|
55
|
+
# Default set
|
56
|
+
args = [
|
57
|
+
'-module-name', config.module_name,
|
58
|
+
'-minimum-access-level', 'private',
|
59
|
+
'-output-dir', output_path,
|
60
|
+
'-skip-synthesized-members'
|
61
|
+
]
|
62
|
+
|
63
|
+
# Things user can override
|
64
|
+
args += ['-sdk', sdk(config)] unless user_args =~ /-sdk/
|
65
|
+
args += ['-target', target] unless user_args =~ /-target/
|
66
|
+
args += ['-F', config.source_directory.to_s] unless user_args =~ /-F(?!s)/
|
67
|
+
args += ['-I', config.source_directory.to_s] unless user_args =~ /-I/
|
68
|
+
|
69
|
+
args + config.build_tool_arguments
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the SDK path. On !darwin this just isn't needed.
|
73
|
+
def self.sdk(config)
|
74
|
+
`xcrun --show-sdk-path --sdk #{config.sdk}`.chomp
|
75
|
+
end
|
76
|
+
|
77
|
+
# Guess a default LLVM target. Feels like the tool should figure this
|
78
|
+
# out from sdk + the binary somehow?
|
79
|
+
def self.target
|
80
|
+
`swift -version` =~ /Target: (.*?)$/
|
81
|
+
Regexp.last_match[1] || 'x86_64-apple-macosx10.15'
|
82
|
+
end
|
83
|
+
|
84
|
+
# This is a last-ditch fallback for when symbolgraph doesn't
|
85
|
+
# provide a name - at least conforming external types to local
|
86
|
+
# protocols.
|
87
|
+
def self.demangle(usr)
|
88
|
+
args = %w[demangle -simplified -compact].append(usr.sub(/^s:/, 's'))
|
89
|
+
output, = Executable.execute_command('swift', args, true)
|
90
|
+
output.chomp
|
91
|
+
rescue StandardError
|
92
|
+
usr
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jazzy
|
4
|
+
module SymbolGraph
|
5
|
+
# Constraint is a tidied-up JSON object, used by both Symbol and
|
6
|
+
# Relationship, and key to reconstructing extensions.
|
7
|
+
class Constraint
|
8
|
+
attr_accessor :kind
|
9
|
+
attr_accessor :lhs
|
10
|
+
attr_accessor :rhs
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def initialize(kind, lhs, rhs)
|
15
|
+
self.kind = kind # "==" or ":"
|
16
|
+
self.lhs = lhs
|
17
|
+
self.rhs = rhs
|
18
|
+
end
|
19
|
+
|
20
|
+
public
|
21
|
+
|
22
|
+
KIND_MAP = {
|
23
|
+
'conformance' => ':',
|
24
|
+
'superclass' => ':',
|
25
|
+
'sameType' => '==',
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# Init from a JSON hash
|
29
|
+
def self.new_hash(hash)
|
30
|
+
kind = KIND_MAP[hash[:kind]]
|
31
|
+
raise "Unknown constraint kind '#{kind}'" unless kind
|
32
|
+
|
33
|
+
lhs = hash[:lhs].sub(/^Self\./, '')
|
34
|
+
rhs = hash[:rhs].sub(/^Self\./, '')
|
35
|
+
new(kind, lhs, rhs)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Init from a Swift declaration fragment eg. 'A : B'
|
39
|
+
def self.new_declaration(decl)
|
40
|
+
decl =~ /^(.*?)\s*([:<=]+)\s*(.*)$/
|
41
|
+
new(Regexp.last_match[2],
|
42
|
+
Regexp.last_match[1],
|
43
|
+
Regexp.last_match[3])
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_swift
|
47
|
+
"#{lhs} #{kind} #{rhs}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# The first component of types in the constraint
|
51
|
+
def type_names
|
52
|
+
Set.new([lhs, rhs].map { |n| n.sub(/\..*$/, '') })
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.new_list(hash_list)
|
56
|
+
hash_list.map { |h| Constraint.new_hash(h) }.sort.uniq
|
57
|
+
end
|
58
|
+
|
59
|
+
# Swift protocols and reqs have an implementation/hidden conformance
|
60
|
+
# to their own protocol: we don't want to think about this in docs.
|
61
|
+
def self.new_list_for_symbol(hash_list, path_components)
|
62
|
+
hash_list.map do |hash|
|
63
|
+
if hash[:lhs] == 'Self' &&
|
64
|
+
hash[:kind] == 'conformance' &&
|
65
|
+
path_components.include?(hash[:rhs])
|
66
|
+
next nil
|
67
|
+
end
|
68
|
+
|
69
|
+
Constraint.new_hash(hash)
|
70
|
+
end.compact
|
71
|
+
end
|
72
|
+
|
73
|
+
# Workaround Swift 5.3 bug with missing constraint rels
|
74
|
+
def self.new_list_from_declaration(decl)
|
75
|
+
decl.split(/\s*,\s*/).map { |cons| Constraint.new_declaration(cons) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sort order - by Swift text
|
79
|
+
include Comparable
|
80
|
+
|
81
|
+
def <=>(other)
|
82
|
+
to_swift <=> other.to_swift
|
83
|
+
end
|
84
|
+
|
85
|
+
alias eql? ==
|
86
|
+
|
87
|
+
def hash
|
88
|
+
to_swift.hash
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Array
|
95
|
+
def to_where_clause
|
96
|
+
empty? ? '' : " where #{map(&:to_swift).join(', ')}"
|
97
|
+
end
|
98
|
+
end
|
@@ -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,195 @@
|
|
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
|
+
def redundant_conformance?(rel, type, protocol)
|
82
|
+
type && rel.constraints.empty? && type.conformance?(protocol)
|
83
|
+
end
|
84
|
+
|
85
|
+
# source is a member/protocol requirement of target
|
86
|
+
def rebuild_member(rel, source, target)
|
87
|
+
return unless source
|
88
|
+
|
89
|
+
source.protocol_requirement = rel.protocol_requirement?
|
90
|
+
constraints =
|
91
|
+
ExtConstraints.new(target&.constraints,
|
92
|
+
source.unique_context_constraints(target))
|
93
|
+
|
94
|
+
# Add to its parent or invent an extension
|
95
|
+
unless target&.try_add_child(source, constraints.ext)
|
96
|
+
add_ext_member(rel.target_usr, source, constraints)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# "source : target" either from type decl or ext decl
|
101
|
+
def rebuild_conformance(rel, source, target)
|
102
|
+
protocol_name = rel_target_name(rel, target)
|
103
|
+
|
104
|
+
return if redundant_conformance?(rel, source, protocol_name)
|
105
|
+
|
106
|
+
type_constraints = source&.constraints || []
|
107
|
+
constraints =
|
108
|
+
ExtConstraints.new(type_constraints,
|
109
|
+
rel.constraints - type_constraints)
|
110
|
+
|
111
|
+
# Create an extension or enhance an existing one
|
112
|
+
add_ext_conformance(rel.source_usr,
|
113
|
+
rel_source_name(rel, source),
|
114
|
+
protocol_name,
|
115
|
+
constraints)
|
116
|
+
end
|
117
|
+
|
118
|
+
# "source is a default implementation of protocol requirement target"
|
119
|
+
def rebuild_default_implementation(_rel, source, target)
|
120
|
+
return unless source
|
121
|
+
|
122
|
+
unless target &&
|
123
|
+
(target_parent = target.parent) &&
|
124
|
+
target_parent.is_a?(SymNode)
|
125
|
+
# Could probably figure this out with demangle, but...
|
126
|
+
warn "Can't resolve membership of default implementation "\
|
127
|
+
"#{source.symbol.usr}."
|
128
|
+
source.unlisted = true
|
129
|
+
return
|
130
|
+
end
|
131
|
+
constraints =
|
132
|
+
ExtConstraints.new(target_parent.constraints,
|
133
|
+
source.unique_context_constraints(target_parent))
|
134
|
+
|
135
|
+
add_ext_member(target_parent.symbol.usr,
|
136
|
+
source,
|
137
|
+
constraints)
|
138
|
+
end
|
139
|
+
|
140
|
+
# "source is a class that inherits from target"
|
141
|
+
def rebuild_inherits(_rel, source, target)
|
142
|
+
if source && target
|
143
|
+
source.superclass_name = target.symbol.name
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Process a structural relationship to link nodes
|
148
|
+
def rebuild_rel(rel)
|
149
|
+
source = symbol_nodes[rel.source_usr]
|
150
|
+
target = symbol_nodes[rel.target_usr]
|
151
|
+
|
152
|
+
case rel.kind
|
153
|
+
when :memberOf, :optionalRequirementOf, :requirementOf
|
154
|
+
rebuild_member(rel, source, target)
|
155
|
+
|
156
|
+
when :conformsTo
|
157
|
+
rebuild_conformance(rel, source, target)
|
158
|
+
|
159
|
+
when :defaultImplementationOf
|
160
|
+
rebuild_default_implementation(rel, source, target)
|
161
|
+
|
162
|
+
when :inheritsFrom
|
163
|
+
rebuild_inherits(rel, source, target)
|
164
|
+
end
|
165
|
+
# don't seem to care about:
|
166
|
+
# - overrides: not bothered, also unimplemented for protocols
|
167
|
+
end
|
168
|
+
|
169
|
+
# Rebuild the AST structure and convert to SourceKit
|
170
|
+
def to_sourcekit
|
171
|
+
# Do default impls after the others so we can find protocol
|
172
|
+
# type nodes from protocol requirements.
|
173
|
+
default_impls, other_rels =
|
174
|
+
relationships.partition(&:default_implementation?)
|
175
|
+
(other_rels + default_impls).each { |r| rebuild_rel(r) }
|
176
|
+
|
177
|
+
root_symbol_nodes =
|
178
|
+
symbol_nodes.values
|
179
|
+
.select(&:top_level_decl?)
|
180
|
+
.sort
|
181
|
+
.map(&:to_sourcekit)
|
182
|
+
|
183
|
+
root_ext_nodes =
|
184
|
+
ext_nodes.values
|
185
|
+
.sort
|
186
|
+
.map { |n| n.to_sourcekit(module_name) }
|
187
|
+
{
|
188
|
+
'key.diagnostic_stage' => 'parse',
|
189
|
+
'key.substructure' => root_symbol_nodes + root_ext_nodes,
|
190
|
+
}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
# rubocop:enable Metrics/ClassLength
|