jazzy 0.14.4 → 0.15.0
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/.github/workflows/Tests.yml +6 -5
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +40 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +62 -47
- data/README.md +115 -5
- data/bin/sourcekitten +0 -0
- data/js/package-lock.json +6 -6
- data/lib/jazzy/config.rb +156 -24
- data/lib/jazzy/doc.rb +2 -2
- data/lib/jazzy/doc_builder.rb +55 -29
- data/lib/jazzy/doc_index.rb +185 -0
- data/lib/jazzy/docset_builder/info_plist.mustache +1 -1
- data/lib/jazzy/docset_builder.rb +44 -13
- 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 +1 -1
- data/lib/jazzy/grouper.rb +130 -0
- data/lib/jazzy/podspec_documenter.rb +1 -1
- data/lib/jazzy/source_declaration/type.rb +10 -2
- data/lib/jazzy/source_declaration.rb +69 -8
- data/lib/jazzy/source_document.rb +5 -1
- data/lib/jazzy/source_module.rb +13 -11
- data/lib/jazzy/sourcekitten.rb +231 -237
- data/lib/jazzy/symbol_graph/ext_key.rb +37 -0
- data/lib/jazzy/symbol_graph/ext_node.rb +23 -6
- data/lib/jazzy/symbol_graph/graph.rb +31 -19
- data/lib/jazzy/symbol_graph/relationship.rb +21 -3
- data/lib/jazzy/symbol_graph/sym_node.rb +10 -22
- data/lib/jazzy/symbol_graph/symbol.rb +28 -0
- data/lib/jazzy/symbol_graph.rb +19 -16
- data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +10 -7
- data/lib/jazzy/themes/apple/assets/js/typeahead.jquery.js +3 -2
- data/lib/jazzy/themes/apple/templates/doc.mustache +8 -1
- data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +5 -5
- data/lib/jazzy/themes/fullwidth/assets/js/typeahead.jquery.js +3 -2
- data/lib/jazzy/themes/fullwidth/templates/doc.mustache +8 -1
- data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +6 -5
- data/lib/jazzy/themes/jony/templates/doc.mustache +9 -2
- data/spec/integration_spec.rb +8 -1
- metadata +5 -2
data/lib/jazzy/sourcekitten.rb
CHANGED
@@ -13,6 +13,8 @@ require 'jazzy/highlighter'
|
|
13
13
|
require 'jazzy/source_declaration'
|
14
14
|
require 'jazzy/source_mark'
|
15
15
|
require 'jazzy/stats'
|
16
|
+
require 'jazzy/grouper'
|
17
|
+
require 'jazzy/doc_index'
|
16
18
|
|
17
19
|
ELIDED_AUTOLINK_TOKEN = '36f8f5912051ae747ef441d6511ca4cb'
|
18
20
|
|
@@ -60,83 +62,9 @@ module Jazzy
|
|
60
62
|
).freeze
|
61
63
|
end
|
62
64
|
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
unlisted_prefix = Config.instance.custom_categories_unlisted_prefix
|
67
|
-
type_categories, uncategorized = group_type_categories(
|
68
|
-
docs, custom_categories.any? ? unlisted_prefix : ''
|
69
|
-
)
|
70
|
-
custom_categories + merge_categories(type_categories) + uncategorized
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.group_custom_categories(docs)
|
74
|
-
group = Config.instance.custom_categories.map do |category|
|
75
|
-
children = category['children'].flat_map do |name|
|
76
|
-
docs_with_name, docs = docs.partition { |doc| doc.name == name }
|
77
|
-
if docs_with_name.empty?
|
78
|
-
warn 'WARNING: No documented top-level declarations match ' \
|
79
|
-
"name \"#{name}\" specified in categories file"
|
80
|
-
end
|
81
|
-
docs_with_name
|
82
|
-
end
|
83
|
-
# Category config overrides alphabetization
|
84
|
-
children.each.with_index { |child, i| child.nav_order = i }
|
85
|
-
make_group(children, category['name'], '')
|
86
|
-
end
|
87
|
-
[group.compact, docs]
|
88
|
-
end
|
89
|
-
|
90
|
-
def self.group_type_categories(docs, type_category_prefix)
|
91
|
-
group = SourceDeclaration::Type.all.map do |type|
|
92
|
-
children, docs = docs.partition { |doc| doc.type == type }
|
93
|
-
make_group(
|
94
|
-
children,
|
95
|
-
type_category_prefix + type.plural_name,
|
96
|
-
"The following #{type.plural_name.downcase} are available globally.",
|
97
|
-
type_category_prefix + type.plural_url_name,
|
98
|
-
)
|
99
|
-
end
|
100
|
-
[group.compact, docs]
|
101
|
-
end
|
102
|
-
|
103
|
-
# Join categories with the same name (eg. ObjC and Swift classes)
|
104
|
-
def self.merge_categories(categories)
|
105
|
-
merged = []
|
106
|
-
categories.each do |new_category|
|
107
|
-
if existing = merged.find { |c| c.name == new_category.name }
|
108
|
-
existing.children += new_category.children
|
109
|
-
else
|
110
|
-
merged.append(new_category)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
merged
|
114
|
-
end
|
115
|
-
|
116
|
-
def self.make_group(group, name, abstract, url_name = nil)
|
117
|
-
group.reject! { |doc| doc.name.empty? }
|
118
|
-
unless group.empty?
|
119
|
-
SourceDeclaration.new.tap do |sd|
|
120
|
-
sd.type = SourceDeclaration::Type.overview
|
121
|
-
sd.name = name
|
122
|
-
sd.url_name = url_name
|
123
|
-
sd.abstract = Markdown.render(abstract)
|
124
|
-
sd.children = group
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
# Merge consecutive sections with the same mark into one section
|
130
|
-
def self.merge_consecutive_marks(docs)
|
131
|
-
prev_mark = nil
|
132
|
-
docs.each do |doc|
|
133
|
-
if prev_mark&.can_merge?(doc.mark)
|
134
|
-
doc.mark = prev_mark
|
135
|
-
end
|
136
|
-
prev_mark = doc.mark
|
137
|
-
merge_consecutive_marks(doc.children)
|
138
|
-
end
|
139
|
-
end
|
65
|
+
#
|
66
|
+
# URL assignment
|
67
|
+
#
|
140
68
|
|
141
69
|
def self.sanitize_filename(doc)
|
142
70
|
unsafe_filename = doc.docs_filename
|
@@ -149,7 +77,7 @@ module Jazzy
|
|
149
77
|
end
|
150
78
|
|
151
79
|
# rubocop:disable Metrics/MethodLength
|
152
|
-
# Generate doc URL by prepending its parents URLs
|
80
|
+
# Generate doc URL by prepending its parents' URLs
|
153
81
|
# @return [Hash] input docs with URLs
|
154
82
|
def self.make_doc_urls(docs)
|
155
83
|
docs.each do |doc|
|
@@ -189,18 +117,50 @@ module Jazzy
|
|
189
117
|
# Guides in the root for back-compatibility.
|
190
118
|
# Declarations under outer namespace type (Structures, Classes, etc.)
|
191
119
|
def self.subdir_for_doc(doc)
|
192
|
-
|
193
|
-
|
194
|
-
top_level_decl = doc.namespace_path.first
|
195
|
-
if top_level_decl.type.name
|
196
|
-
[top_level_decl.type.plural_url_name] +
|
197
|
-
doc.namespace_ancestors.map(&:name)
|
120
|
+
if Config.instance.multiple_modules?
|
121
|
+
subdir_for_doc_multi_module(doc)
|
198
122
|
else
|
199
|
-
#
|
200
|
-
|
123
|
+
# Back-compatibility layout version
|
124
|
+
subdir_for_doc_single_module(doc)
|
201
125
|
end
|
202
126
|
end
|
203
127
|
|
128
|
+
# Pre-multi-module site layout, does not allow for
|
129
|
+
# types with the same name.
|
130
|
+
def self.subdir_for_doc_single_module(doc)
|
131
|
+
# Guides + Groups in the root
|
132
|
+
return [] if doc.type.markdown? || doc.type.overview?
|
133
|
+
|
134
|
+
[doc.namespace_path.first.type.plural_url_name] +
|
135
|
+
doc.namespace_ancestors.map(&:name)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Multi-module site layout, separate each module that
|
139
|
+
# is being documented.
|
140
|
+
def self.subdir_for_doc_multi_module(doc)
|
141
|
+
# Guides + Groups in the root
|
142
|
+
return [] if doc.type.markdown? || doc.type.overview?
|
143
|
+
|
144
|
+
root_decl = doc.namespace_path.first
|
145
|
+
|
146
|
+
# Extensions need an extra dir to allow for extending
|
147
|
+
# ExternalModule1.TypeName and ExternalModule2.TypeName
|
148
|
+
namespace_subdir =
|
149
|
+
if root_decl.type.swift_extension?
|
150
|
+
['Extensions', root_decl.module_name]
|
151
|
+
else
|
152
|
+
[doc.namespace_path.first.type.plural_url_name]
|
153
|
+
end
|
154
|
+
|
155
|
+
[root_decl.doc_module_name] +
|
156
|
+
namespace_subdir +
|
157
|
+
doc.namespace_ancestors.map(&:name)
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
# CLI argument calculation
|
162
|
+
#
|
163
|
+
|
204
164
|
# returns all subdirectories of specified path
|
205
165
|
def self.rec_path(path)
|
206
166
|
path.children.collect do |child|
|
@@ -210,42 +170,42 @@ module Jazzy
|
|
210
170
|
end.select { |x| x }.flatten(1)
|
211
171
|
end
|
212
172
|
|
213
|
-
def self.use_spm?(
|
214
|
-
|
215
|
-
(!
|
173
|
+
def self.use_spm?(module_config)
|
174
|
+
module_config.swift_build_tool == :spm ||
|
175
|
+
(!module_config.swift_build_tool_configured &&
|
216
176
|
Dir['*.xcodeproj', '*.xcworkspace'].empty? &&
|
217
|
-
!
|
218
|
-
!
|
177
|
+
!module_config.build_tool_arguments.include?('-project') &&
|
178
|
+
!module_config.build_tool_arguments.include?('-workspace'))
|
219
179
|
end
|
220
180
|
|
221
181
|
# Builds SourceKitten arguments based on Jazzy options
|
222
|
-
def self.arguments_from_options(
|
182
|
+
def self.arguments_from_options(module_config)
|
223
183
|
arguments = ['doc']
|
224
|
-
if
|
225
|
-
arguments += objc_arguments_from_options(
|
184
|
+
if module_config.objc_mode
|
185
|
+
arguments += objc_arguments_from_options(module_config)
|
226
186
|
else
|
227
|
-
arguments += ['--spm'] if use_spm?(
|
228
|
-
unless
|
229
|
-
arguments += ['--module-name',
|
187
|
+
arguments += ['--spm'] if use_spm?(module_config)
|
188
|
+
unless module_config.module_name.empty?
|
189
|
+
arguments += ['--module-name', module_config.module_name]
|
230
190
|
end
|
231
191
|
arguments += ['--']
|
232
192
|
end
|
233
193
|
|
234
|
-
arguments +
|
194
|
+
arguments + module_config.build_tool_arguments
|
235
195
|
end
|
236
196
|
|
237
|
-
def self.objc_arguments_from_options(
|
197
|
+
def self.objc_arguments_from_options(module_config)
|
238
198
|
arguments = []
|
239
|
-
if
|
240
|
-
arguments += ['--objc',
|
199
|
+
if module_config.build_tool_arguments.empty?
|
200
|
+
arguments += ['--objc', module_config.umbrella_header.to_s, '--', '-x',
|
241
201
|
'objective-c', '-isysroot',
|
242
|
-
`xcrun --show-sdk-path --sdk #{
|
243
|
-
'-I',
|
202
|
+
`xcrun --show-sdk-path --sdk #{module_config.sdk}`.chomp,
|
203
|
+
'-I', module_config.framework_root.to_s,
|
244
204
|
'-fmodules']
|
245
205
|
end
|
246
206
|
# add additional -I arguments for each subdirectory of framework_root
|
247
|
-
unless
|
248
|
-
rec_path(Pathname.new(
|
207
|
+
unless module_config.framework_root.nil?
|
208
|
+
rec_path(Pathname.new(module_config.framework_root.to_s)).collect do |child|
|
249
209
|
if child.directory?
|
250
210
|
arguments += ['-I', child.to_s]
|
251
211
|
end
|
@@ -270,6 +230,10 @@ module Jazzy
|
|
270
230
|
output
|
271
231
|
end
|
272
232
|
|
233
|
+
#
|
234
|
+
# SourceDeclaration generation
|
235
|
+
#
|
236
|
+
|
273
237
|
def self.make_default_doc_info(declaration)
|
274
238
|
# @todo: Fix these
|
275
239
|
declaration.abstract = ''
|
@@ -352,18 +316,10 @@ module Jazzy
|
|
352
316
|
end
|
353
317
|
end
|
354
318
|
|
355
|
-
# Call things undocumented if they were compiled properly
|
356
|
-
# and came from our module.
|
357
|
-
def self.should_mark_undocumented(declaration)
|
358
|
-
declaration.usr &&
|
359
|
-
(declaration.modulename.nil? ||
|
360
|
-
declaration.modulename == Config.instance.module_name)
|
361
|
-
end
|
362
|
-
|
363
319
|
def self.process_undocumented_token(doc, declaration)
|
364
320
|
make_default_doc_info(declaration)
|
365
321
|
|
366
|
-
if
|
322
|
+
if declaration.mark_undocumented?
|
367
323
|
@stats.add_undocumented(declaration)
|
368
324
|
return nil if @skip_undocumented
|
369
325
|
|
@@ -621,7 +577,15 @@ module Jazzy
|
|
621
577
|
declaration.file = Pathname(doc['key.filepath']) if doc['key.filepath']
|
622
578
|
declaration.usr = doc['key.usr']
|
623
579
|
declaration.type_usr = doc['key.typeusr']
|
624
|
-
declaration.
|
580
|
+
declaration.module_name =
|
581
|
+
if declaration.swift?
|
582
|
+
# Filter out Apple sub-framework implementation names
|
583
|
+
doc['key.modulename']&.sub(/\..*$/, '')
|
584
|
+
else
|
585
|
+
# ObjC best effort, category original module is unavailable
|
586
|
+
@current_module_name
|
587
|
+
end
|
588
|
+
declaration.doc_module_name = @current_module_name
|
625
589
|
declaration.name = documented_name
|
626
590
|
declaration.mark = current_mark
|
627
591
|
declaration.access_control_level =
|
@@ -665,6 +629,10 @@ module Jazzy
|
|
665
629
|
Regexp.last_match[1].gsub(/\s+/, ' ')
|
666
630
|
end
|
667
631
|
|
632
|
+
#
|
633
|
+
# SourceDeclaration generation - extension management
|
634
|
+
#
|
635
|
+
|
668
636
|
# Expands extensions of nested types declared at the top level into
|
669
637
|
# a tree so they can be deduplicated properly
|
670
638
|
def self.expand_extensions(decls)
|
@@ -689,7 +657,8 @@ module Jazzy
|
|
689
657
|
SourceDeclaration.new.tap do |decl|
|
690
658
|
make_default_doc_info(decl)
|
691
659
|
decl.name = name
|
692
|
-
decl.
|
660
|
+
decl.module_name = extension.module_name
|
661
|
+
decl.doc_module_name = extension.doc_module_name
|
693
662
|
decl.type = extension.type
|
694
663
|
decl.mark = extension.mark
|
695
664
|
decl.usr = candidates.first.usr unless candidates.empty?
|
@@ -720,9 +689,10 @@ module Jazzy
|
|
720
689
|
|
721
690
|
# Returns true if an Objective-C declaration is mergeable.
|
722
691
|
def self.mergeable_objc?(decl, root_decls)
|
723
|
-
decl.type.objc_class?
|
724
|
-
|
725
|
-
|
692
|
+
decl.type.objc_class? ||
|
693
|
+
(decl.type.objc_category? &&
|
694
|
+
(category_classname = decl.objc_category_name[0]) &&
|
695
|
+
root_decls.any? { _1.name == category_classname })
|
726
696
|
end
|
727
697
|
|
728
698
|
# Returns if a Swift declaration is mergeable.
|
@@ -733,22 +703,45 @@ module Jazzy
|
|
733
703
|
decl.type.swift_typealias?
|
734
704
|
end
|
735
705
|
|
706
|
+
# Normally merge all extensions into their types and each other.
|
707
|
+
#
|
708
|
+
# :none means only merge within a module -- so two extensions to
|
709
|
+
# some type get merged, but an extension to a type from
|
710
|
+
# another documented module does not get merged into that type
|
711
|
+
# :extensions means extensions of documented modules get merged,
|
712
|
+
# but if we're documenting ModA and ModB, and they both provide
|
713
|
+
# extensions to Swift.String, then those two extensions still
|
714
|
+
# appear separately.
|
715
|
+
#
|
716
|
+
# (The USR part of the dedup key means ModA.Foo and ModB.Foo do not
|
717
|
+
# get merged.)
|
718
|
+
def self.module_deduplication_key(decl)
|
719
|
+
if (Config.instance.merge_modules == :none) ||
|
720
|
+
(Config.instance.merge_modules == :extensions &&
|
721
|
+
decl.extension_of_external_type?)
|
722
|
+
decl.doc_module_name
|
723
|
+
else
|
724
|
+
''
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
736
728
|
# Two declarations get merged if they have the same deduplication key.
|
737
729
|
def self.deduplication_key(decl, root_decls)
|
730
|
+
mod_key = module_deduplication_key(decl)
|
738
731
|
# Swift extension of objc class
|
739
732
|
if decl.swift_objc_extension?
|
740
|
-
[decl.swift_extension_objc_name, :objc_class_and_categories]
|
733
|
+
[decl.swift_extension_objc_name, :objc_class_and_categories, mod_key]
|
741
734
|
# Swift type or Swift extension of Swift type
|
742
735
|
elsif mergeable_swift?(decl)
|
743
|
-
[decl.usr, decl.name]
|
736
|
+
[decl.usr, decl.name, mod_key]
|
744
737
|
# Objc categories and classes
|
745
738
|
elsif mergeable_objc?(decl, root_decls)
|
746
739
|
# Using the ObjC name to match swift_objc_extension.
|
747
740
|
name, _ = decl.objc_category_name || decl.objc_name
|
748
|
-
[name, :objc_class_and_categories]
|
741
|
+
[name, :objc_class_and_categories, mod_key]
|
749
742
|
# Non-mergable declarations (funcs, typedefs etc...)
|
750
743
|
else
|
751
|
-
[decl.usr, decl.name, decl.type.kind]
|
744
|
+
[decl.usr, decl.name, decl.type.kind, '']
|
752
745
|
end
|
753
746
|
end
|
754
747
|
|
@@ -904,81 +897,45 @@ module Jazzy
|
|
904
897
|
# declaration: public protocol conformances and, for top-level extensions,
|
905
898
|
# further conditional extensions of the same type.
|
906
899
|
def self.merge_code_declaration(decls)
|
907
|
-
first = decls.first
|
908
|
-
|
909
900
|
declarations = decls[1..].select do |decl|
|
910
901
|
decl.type.swift_extension? &&
|
911
902
|
(decl.other_inherited_types?(@inaccessible_protocols) ||
|
912
|
-
(first.type.swift_extension? && decl.constrained_extension?))
|
913
|
-
end.
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
def self.filter_files(json)
|
922
|
-
json = filter_included_files(json) if Config.instance.included_files.any?
|
923
|
-
json = filter_excluded_files(json) if Config.instance.excluded_files.any?
|
924
|
-
json.map do |doc|
|
925
|
-
key = doc.keys.first
|
926
|
-
doc[key]
|
927
|
-
end.compact
|
928
|
-
end
|
929
|
-
|
930
|
-
# Filter based on the "included" flag.
|
931
|
-
def self.filter_included_files(json)
|
932
|
-
included_files = Config.instance.included_files
|
933
|
-
json.map do |doc|
|
934
|
-
key = doc.keys.first
|
935
|
-
doc if included_files.detect do |include|
|
936
|
-
File.fnmatch?(include, key)
|
903
|
+
(decls.first.type.swift_extension? && decl.constrained_extension?))
|
904
|
+
end.prepend(decls.first)
|
905
|
+
|
906
|
+
html_declaration = ''
|
907
|
+
until declarations.empty?
|
908
|
+
module_decls, declarations = next_doc_module_group(declarations)
|
909
|
+
first = module_decls.first
|
910
|
+
if need_doc_module_note?(first, html_declaration)
|
911
|
+
html_declaration += "<span class='declaration-note'>From #{first.doc_module_name}:</span>"
|
937
912
|
end
|
938
|
-
|
939
|
-
|
913
|
+
html_declaration += module_decls.map(&:declaration).uniq.join
|
914
|
+
end
|
940
915
|
|
941
|
-
|
942
|
-
|
943
|
-
excluded_files = Config.instance.excluded_files
|
944
|
-
json.map do |doc|
|
945
|
-
key = doc.keys.first
|
946
|
-
doc unless excluded_files.detect do |exclude|
|
947
|
-
File.fnmatch?(exclude, key)
|
948
|
-
end
|
949
|
-
end.compact
|
916
|
+
# Must preserve `nil` for edge cases
|
917
|
+
decls.first.declaration = html_declaration unless html_declaration.empty?
|
950
918
|
end
|
951
919
|
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
wildcard_expansion = Regexp.escape(name_part)
|
956
|
-
.gsub('\.\.\.', '[^)]*')
|
957
|
-
.gsub(/<.*>/, '')
|
958
|
-
|
959
|
-
whole_name_pat = /\A#{wildcard_expansion}\Z/
|
960
|
-
docs.find do |doc|
|
961
|
-
whole_name_pat =~ doc.name
|
962
|
-
end
|
920
|
+
# Grab all the extensions from the same doc module
|
921
|
+
def self.next_doc_module_group(decls)
|
922
|
+
decls.partition { _1.doc_module_name == decls.first.doc_module_name }
|
963
923
|
end
|
964
924
|
|
965
|
-
#
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
925
|
+
# Does this extension/type need a note explaining which doc module it is from?
|
926
|
+
# Only for extensions, if there actually are multiple modules.
|
927
|
+
# Last condition avoids it for simple 'extension Array'.
|
928
|
+
def self.need_doc_module_note?(decl, html_declaration)
|
929
|
+
Config.instance.multiple_modules? &&
|
930
|
+
decl.type.swift_extension? &&
|
931
|
+
!(html_declaration.empty? &&
|
932
|
+
!decl.constrained_extension? &&
|
933
|
+
!decl.inherited_types?)
|
973
934
|
end
|
974
935
|
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
doc = name_match(next_part, doc.children)
|
979
|
-
end
|
980
|
-
doc
|
981
|
-
end
|
936
|
+
#
|
937
|
+
# Autolinking
|
938
|
+
#
|
982
939
|
|
983
940
|
# Links recognized top-level declarations within
|
984
941
|
# - inlined code within docs
|
@@ -987,85 +944,97 @@ module Jazzy
|
|
987
944
|
# The `after_highlight` flag is used to differentiate between the two modes.
|
988
945
|
#
|
989
946
|
# DocC link format - follow Xcode and don't display slash-separated parts.
|
990
|
-
|
991
|
-
def self.autolink_text(text, doc, root_decls, after_highlight: false)
|
947
|
+
def self.autolink_text(text, doc, after_highlight: false)
|
992
948
|
text.autolink_block(doc.url, '[^\s]+', after_highlight) do |raw_name|
|
993
949
|
sym_name =
|
994
950
|
(raw_name[/^<doc:(.*)>$/, 1] || raw_name).sub(/(?<!^)-.+$/, '')
|
995
951
|
|
996
|
-
|
997
|
-
.sub(/^@/, '') # ignore for custom attribute ref
|
998
|
-
.split(%r{(?<!\.)[/.](?!\.)}) # dot or slash, but not '...'
|
999
|
-
.reject(&:empty?)
|
1000
|
-
|
1001
|
-
# First dot-separated component can match any ancestor or top-level doc
|
1002
|
-
first_part = parts.shift
|
1003
|
-
name_root = ancestor_name_match(first_part, doc) ||
|
1004
|
-
name_match(first_part, root_decls)
|
1005
|
-
|
1006
|
-
# Traverse children via subsequent components, if any
|
1007
|
-
[name_traversal(parts, name_root), sym_name.sub(%r{^.*/}, '')]
|
952
|
+
[@doc_index.lookup(sym_name, doc), sym_name.sub(%r{^.*/}, '')]
|
1008
953
|
end.autolink_block(doc.url, '[+-]\[\w+(?: ?\(\w+\))? [\w:]+\]',
|
1009
954
|
after_highlight) do |raw_name|
|
1010
|
-
|
1011
|
-
|
1012
|
-
# Subject component can match any ancestor or top-level doc
|
1013
|
-
subject = match[2].delete(' ')
|
1014
|
-
name_root = ancestor_name_match(subject, doc) ||
|
1015
|
-
name_match(subject, root_decls)
|
1016
|
-
|
1017
|
-
if name_root
|
1018
|
-
# Look up the verb in the subject’s children
|
1019
|
-
[name_match(match[1] + match[3], name_root.children), raw_name]
|
1020
|
-
end
|
955
|
+
[@doc_index.lookup(raw_name, doc), raw_name]
|
1021
956
|
end.autolink_block(doc.url, '[+-]\w[\w:]*', after_highlight) do |raw_name|
|
1022
|
-
[
|
957
|
+
[@doc_index.lookup(raw_name, doc), raw_name]
|
1023
958
|
end
|
1024
959
|
end
|
1025
|
-
# rubocop:enable Metrics/MethodLength
|
1026
960
|
|
1027
961
|
AUTOLINK_TEXT_FIELDS = %w[return
|
1028
962
|
abstract
|
1029
963
|
unavailable_message
|
1030
964
|
deprecation_message].freeze
|
1031
965
|
|
1032
|
-
def self.autolink_text_fields(doc
|
966
|
+
def self.autolink_text_fields(doc)
|
1033
967
|
AUTOLINK_TEXT_FIELDS.each do |field|
|
1034
968
|
if text = doc.send(field)
|
1035
|
-
doc.send(field + '=', autolink_text(text, doc
|
969
|
+
doc.send(field + '=', autolink_text(text, doc))
|
1036
970
|
end
|
1037
971
|
end
|
1038
972
|
|
1039
973
|
(doc.parameters || []).each do |param|
|
1040
974
|
param[:discussion] =
|
1041
|
-
autolink_text(param[:discussion], doc
|
975
|
+
autolink_text(param[:discussion], doc)
|
1042
976
|
end
|
1043
977
|
end
|
1044
978
|
|
1045
979
|
AUTOLINK_HIGHLIGHT_FIELDS = %w[declaration
|
1046
980
|
other_language_declaration].freeze
|
1047
981
|
|
1048
|
-
def self.autolink_highlight_fields(doc
|
982
|
+
def self.autolink_highlight_fields(doc)
|
1049
983
|
AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
|
1050
984
|
if text = doc.send(field)
|
1051
985
|
doc.send(field + '=',
|
1052
|
-
autolink_text(text, doc,
|
986
|
+
autolink_text(text, doc, after_highlight: true))
|
1053
987
|
end
|
1054
988
|
end
|
1055
989
|
end
|
1056
990
|
|
1057
|
-
def self.autolink(docs
|
1058
|
-
@autolink_root_decls = root_decls
|
991
|
+
def self.autolink(docs)
|
1059
992
|
docs.each do |doc|
|
1060
|
-
doc.children = autolink(doc.children
|
1061
|
-
autolink_text_fields(doc
|
1062
|
-
autolink_highlight_fields(doc
|
993
|
+
doc.children = autolink(doc.children)
|
994
|
+
autolink_text_fields(doc)
|
995
|
+
autolink_highlight_fields(doc)
|
1063
996
|
end
|
1064
997
|
end
|
1065
998
|
|
1066
999
|
# For autolinking external markdown documents
|
1067
1000
|
def self.autolink_document(html, doc)
|
1068
|
-
autolink_text(html, doc
|
1001
|
+
autolink_text(html, doc)
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
#
|
1005
|
+
# Entrypoint and misc filtering
|
1006
|
+
#
|
1007
|
+
|
1008
|
+
# Apply filtering based on the "included" and "excluded" flags.
|
1009
|
+
def self.filter_files(json)
|
1010
|
+
json = filter_included_files(json) if Config.instance.included_files.any?
|
1011
|
+
json = filter_excluded_files(json) if Config.instance.excluded_files.any?
|
1012
|
+
json.map do |doc|
|
1013
|
+
key = doc.keys.first
|
1014
|
+
doc[key]
|
1015
|
+
end.compact
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
# Filter based on the "included" flag.
|
1019
|
+
def self.filter_included_files(json)
|
1020
|
+
included_files = Config.instance.included_files
|
1021
|
+
json.map do |doc|
|
1022
|
+
key = doc.keys.first
|
1023
|
+
doc if included_files.detect do |include|
|
1024
|
+
File.fnmatch?(include, key)
|
1025
|
+
end
|
1026
|
+
end.compact
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
# Filter based on the "excluded" flag.
|
1030
|
+
def self.filter_excluded_files(json)
|
1031
|
+
excluded_files = Config.instance.excluded_files
|
1032
|
+
json.map do |doc|
|
1033
|
+
key = doc.keys.first
|
1034
|
+
doc unless excluded_files.detect do |exclude|
|
1035
|
+
File.fnmatch?(exclude, key)
|
1036
|
+
end
|
1037
|
+
end.compact
|
1069
1038
|
end
|
1070
1039
|
|
1071
1040
|
def self.reject_objc_types(docs)
|
@@ -1083,26 +1052,51 @@ module Jazzy
|
|
1083
1052
|
end
|
1084
1053
|
end
|
1085
1054
|
|
1055
|
+
# Remove top-level enum cases because it means they have an ACL lower
|
1056
|
+
# than min_acl
|
1057
|
+
def self.reject_swift_types(docs)
|
1058
|
+
docs.reject { _1.type.swift_enum_element? }
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
# Spot and mark any categories on classes not declared in these docs
|
1062
|
+
def self.mark_objc_external_categories(docs)
|
1063
|
+
class_names = docs.select { _1.type.objc_class? }.to_set(&:name)
|
1064
|
+
|
1065
|
+
docs.map do |doc|
|
1066
|
+
if (names = doc.objc_category_name) && !class_names.include?(names.first)
|
1067
|
+
doc.module_name = '(Imported)'
|
1068
|
+
end
|
1069
|
+
doc
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1086
1073
|
# Parse sourcekitten STDOUT output as JSON
|
1087
1074
|
# @return [Hash] structured docs
|
1088
|
-
def self.parse(sourcekitten_output,
|
1089
|
-
@min_acl = min_acl
|
1090
|
-
@skip_undocumented = skip_undocumented
|
1075
|
+
def self.parse(sourcekitten_output, options, inject_docs)
|
1076
|
+
@min_acl = options.min_acl
|
1077
|
+
@skip_undocumented = options.skip_undocumented
|
1091
1078
|
@stats = Stats.new
|
1092
1079
|
@inaccessible_protocols = []
|
1093
|
-
|
1094
|
-
|
1080
|
+
|
1081
|
+
# Process each module separately to inject the source module name
|
1082
|
+
docs = sourcekitten_output.zip(options.module_names).map do |json, name|
|
1083
|
+
@current_module_name = name
|
1084
|
+
sourcekitten_dicts = filter_files(JSON.parse(json).flatten)
|
1085
|
+
make_source_declarations(sourcekitten_dicts)
|
1086
|
+
end.flatten + inject_docs
|
1087
|
+
|
1095
1088
|
docs = expand_extensions(docs)
|
1096
1089
|
docs = deduplicate_declarations(docs)
|
1097
1090
|
docs = reject_objc_types(docs)
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1091
|
+
docs = reject_swift_types(docs)
|
1092
|
+
docs = mark_objc_external_categories(docs)
|
1093
|
+
|
1094
|
+
@doc_index = DocIndex.new(docs)
|
1095
|
+
|
1096
|
+
docs = Grouper.group_docs(docs, @doc_index)
|
1097
|
+
|
1104
1098
|
make_doc_urls(docs)
|
1105
|
-
autolink(docs
|
1099
|
+
autolink(docs)
|
1106
1100
|
[docs, @stats]
|
1107
1101
|
end
|
1108
1102
|
end
|