jazzy 0.10.0 → 0.13.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/.circleci/config.yml +3 -20
- data/CHANGELOG.md +139 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile.lock +36 -37
- data/README.md +67 -9
- data/bin/sourcekitten +0 -0
- data/jazzy.gemspec +1 -1
- data/lib/jazzy/config.rb +45 -9
- data/lib/jazzy/doc.rb +0 -4
- data/lib/jazzy/doc_builder.rb +17 -24
- data/lib/jazzy/docset_builder.rb +1 -1
- data/lib/jazzy/gem_version.rb +1 -1
- data/lib/jazzy/highlighter.rb +10 -8
- data/lib/jazzy/jazzy_markdown.rb +4 -5
- data/lib/jazzy/source_declaration.rb +66 -6
- data/lib/jazzy/source_declaration/type.rb +8 -0
- data/lib/jazzy/source_document.rb +3 -2
- data/lib/jazzy/source_mark.rb +11 -0
- data/lib/jazzy/sourcekitten.rb +261 -109
- data/lib/jazzy/stats.rb +4 -0
- data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +36 -2
- data/lib/jazzy/themes/apple/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/apple/templates/task.mustache +4 -3
- data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +35 -7
- data/lib/jazzy/themes/fullwidth/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/fullwidth/templates/doc.mustache +1 -1
- data/lib/jazzy/themes/fullwidth/templates/task.mustache +4 -3
- data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +35 -2
- data/lib/jazzy/themes/jony/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/jony/templates/task.mustache +4 -3
- data/spec/integration_spec.rb +25 -11
- metadata +5 -5
data/lib/jazzy/source_mark.rb
CHANGED
@@ -28,6 +28,12 @@ module Jazzy
|
|
28
28
|
self.name = mark_string[start_index..end_index]
|
29
29
|
end
|
30
30
|
|
31
|
+
def self.new_generic_requirements(requirements)
|
32
|
+
marked_up = requirements.gsub(/\b([^=:]\S*)\b/, '`\1`')
|
33
|
+
text = "Available where #{marked_up}"
|
34
|
+
new(text)
|
35
|
+
end
|
36
|
+
|
31
37
|
def empty?
|
32
38
|
!name && !has_start_dash && !has_end_dash
|
33
39
|
end
|
@@ -37,5 +43,10 @@ module Jazzy
|
|
37
43
|
self.has_start_dash = other.has_start_dash
|
38
44
|
self.has_end_dash = other.has_end_dash
|
39
45
|
end
|
46
|
+
|
47
|
+
# Can we merge the contents of another mark into our own?
|
48
|
+
def can_merge?(other)
|
49
|
+
other.empty? || other.name == name
|
50
|
+
end
|
40
51
|
end
|
41
52
|
end
|
data/lib/jazzy/sourcekitten.rb
CHANGED
@@ -65,7 +65,7 @@ module Jazzy
|
|
65
65
|
type_categories, uncategorized = group_type_categories(
|
66
66
|
docs, custom_categories.any? ? 'Other ' : ''
|
67
67
|
)
|
68
|
-
custom_categories + type_categories + uncategorized
|
68
|
+
custom_categories + merge_categories(type_categories) + uncategorized
|
69
69
|
end
|
70
70
|
|
71
71
|
def self.group_custom_categories(docs)
|
@@ -98,6 +98,19 @@ module Jazzy
|
|
98
98
|
[group.compact, docs]
|
99
99
|
end
|
100
100
|
|
101
|
+
# Join categories with the same name (eg. ObjC and Swift classes)
|
102
|
+
def self.merge_categories(categories)
|
103
|
+
merged = []
|
104
|
+
categories.each do |new_category|
|
105
|
+
if existing = merged.find { |c| c.name == new_category.name }
|
106
|
+
existing.children += new_category.children
|
107
|
+
else
|
108
|
+
merged.append(new_category)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
merged
|
112
|
+
end
|
113
|
+
|
101
114
|
def self.make_group(group, name, abstract, url_name = nil)
|
102
115
|
group.reject! { |doc| doc.name.empty? }
|
103
116
|
unless group.empty?
|
@@ -111,6 +124,18 @@ module Jazzy
|
|
111
124
|
end
|
112
125
|
end
|
113
126
|
|
127
|
+
# Merge consecutive sections with the same mark into one section
|
128
|
+
def self.merge_consecutive_marks(docs)
|
129
|
+
prev_mark = nil
|
130
|
+
docs.each do |doc|
|
131
|
+
if prev_mark && prev_mark.can_merge?(doc.mark)
|
132
|
+
doc.mark = prev_mark
|
133
|
+
end
|
134
|
+
prev_mark = doc.mark
|
135
|
+
merge_consecutive_marks(doc.children)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
114
139
|
def self.sanitize_filename(doc)
|
115
140
|
unsafe_filename = doc.url_name || doc.name
|
116
141
|
sanitzation_enabled = Config.instance.use_safe_filenames
|
@@ -126,12 +151,11 @@ module Jazzy
|
|
126
151
|
# @return [Hash] input docs with URLs
|
127
152
|
def self.make_doc_urls(docs)
|
128
153
|
docs.each do |doc|
|
129
|
-
if
|
130
|
-
# Create HTML page for this doc if it has children or is root-level
|
154
|
+
if doc.render_as_page?
|
131
155
|
doc.url = (
|
132
156
|
subdir_for_doc(doc) +
|
133
157
|
[sanitize_filename(doc) + '.html']
|
134
|
-
).join('/')
|
158
|
+
).map { |path| ERB::Util.url_encode(path) }.join('/')
|
135
159
|
doc.children = make_doc_urls(doc.children)
|
136
160
|
else
|
137
161
|
# Don't create HTML page for this doc if it doesn't have children
|
@@ -140,8 +164,8 @@ module Jazzy
|
|
140
164
|
warn 'A compile error prevented ' + doc.fully_qualified_name +
|
141
165
|
' from receiving a unique USR. Documentation may be ' \
|
142
166
|
'incomplete. Please check for compile errors by running ' \
|
143
|
-
'`xcodebuild ' \
|
144
|
-
"
|
167
|
+
'`xcodebuild` or `swift build` with arguments ' \
|
168
|
+
"`#{Config.instance.build_tool_arguments.shelljoin}`."
|
145
169
|
end
|
146
170
|
id = doc.usr
|
147
171
|
unless id
|
@@ -159,17 +183,17 @@ module Jazzy
|
|
159
183
|
end
|
160
184
|
# rubocop:enable Metrics/MethodLength
|
161
185
|
|
162
|
-
# Determine the subdirectory in which a doc should be placed
|
186
|
+
# Determine the subdirectory in which a doc should be placed.
|
187
|
+
# Guides in the root for back-compatibility.
|
188
|
+
# Declarations under outer namespace type (Structures, Classes, etc.)
|
163
189
|
def self.subdir_for_doc(doc)
|
164
|
-
|
165
|
-
# Class, etc).
|
190
|
+
return [] if doc.type.markdown?
|
166
191
|
top_level_decl = doc.namespace_path.first
|
167
|
-
if top_level_decl
|
168
|
-
# File program elements under top ancestor’s type (Struct, Class, etc.)
|
192
|
+
if top_level_decl.type.name
|
169
193
|
[top_level_decl.type.plural_url_name] +
|
170
194
|
doc.namespace_ancestors.map(&:name)
|
171
195
|
else
|
172
|
-
#
|
196
|
+
# Category - in the root
|
173
197
|
[]
|
174
198
|
end
|
175
199
|
end
|
@@ -183,22 +207,33 @@ module Jazzy
|
|
183
207
|
end.select { |x| x }.flatten(1)
|
184
208
|
end
|
185
209
|
|
210
|
+
def self.use_spm?(options)
|
211
|
+
options.swift_build_tool == :spm ||
|
212
|
+
(!options.swift_build_tool_configured &&
|
213
|
+
Dir['*.xcodeproj', '*.xcworkspace'].empty? &&
|
214
|
+
!options.build_tool_arguments.include?('-project') &&
|
215
|
+
!options.build_tool_arguments.include?('-workspace'))
|
216
|
+
end
|
217
|
+
|
186
218
|
# Builds SourceKitten arguments based on Jazzy options
|
187
219
|
def self.arguments_from_options(options)
|
188
220
|
arguments = ['doc']
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
221
|
+
if options.objc_mode
|
222
|
+
arguments += objc_arguments_from_options(options)
|
223
|
+
else
|
224
|
+
arguments += ['--spm'] if use_spm?(options)
|
225
|
+
unless options.module_name.empty?
|
226
|
+
arguments += ['--module-name', options.module_name]
|
227
|
+
end
|
228
|
+
arguments += ['--']
|
229
|
+
end
|
230
|
+
|
231
|
+
arguments + options.build_tool_arguments
|
197
232
|
end
|
198
233
|
|
199
234
|
def self.objc_arguments_from_options(options)
|
200
235
|
arguments = []
|
201
|
-
if options.
|
236
|
+
if options.build_tool_arguments.empty?
|
202
237
|
arguments += ['--objc', options.umbrella_header.to_s, '--', '-x',
|
203
238
|
'objective-c', '-isysroot',
|
204
239
|
`xcrun --show-sdk-path --sdk #{options.sdk}`.chomp,
|
@@ -252,8 +287,13 @@ module Jazzy
|
|
252
287
|
def self.should_document?(doc)
|
253
288
|
return false if doc['key.doc.comment'].to_s.include?(':nodoc:')
|
254
289
|
|
290
|
+
type = SourceDeclaration::Type.new(doc['key.kind'])
|
291
|
+
|
255
292
|
# Always document Objective-C declarations.
|
256
|
-
return true
|
293
|
+
return true unless type.swift_type?
|
294
|
+
|
295
|
+
# Don't document Swift types if we are hiding Swift
|
296
|
+
return false if Config.instance.hide_swift?
|
257
297
|
|
258
298
|
# Don't document @available declarations with no USR, since it means
|
259
299
|
# they're unavailable.
|
@@ -261,22 +301,29 @@ module Jazzy
|
|
261
301
|
return false
|
262
302
|
end
|
263
303
|
|
264
|
-
# Document
|
265
|
-
type = SourceDeclaration::Type.new(doc['key.kind'])
|
304
|
+
# Document enum elements, since we can't tell their ACL.
|
266
305
|
return true if type.swift_enum_element?
|
267
|
-
if
|
268
|
-
|
269
|
-
subtype = SourceDeclaration::Type.new(subdoc['key.kind'])
|
270
|
-
!subtype.mark? && should_document?(subdoc)
|
271
|
-
end
|
272
|
-
end
|
306
|
+
# Document extensions if they might have parts covered by the ACL.
|
307
|
+
return should_document_swift_extension?(doc) if type.swift_extension?
|
273
308
|
|
274
309
|
acl_ok = SourceDeclaration::AccessControlLevel.from_doc(doc) >= @min_acl
|
275
|
-
|
310
|
+
unless acl_ok
|
311
|
+
@stats.add_acl_skipped
|
312
|
+
@inaccessible_protocols.append(doc['key.name']) if type.swift_protocol?
|
313
|
+
end
|
314
|
+
acl_ok
|
276
315
|
end
|
277
316
|
# rubocop:enable Metrics/CyclomaticComplexity
|
278
317
|
# rubocop:enable Metrics/PerceivedComplexity
|
279
318
|
|
319
|
+
def self.should_document_swift_extension?(doc)
|
320
|
+
doc['key.inheritedtypes'] ||
|
321
|
+
Array(doc['key.substructure']).any? do |subdoc|
|
322
|
+
subtype = SourceDeclaration::Type.new(subdoc['key.kind'])
|
323
|
+
!subtype.mark? && should_document?(subdoc)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
280
327
|
def self.should_mark_undocumented(filepath)
|
281
328
|
source_directory = Config.instance.source_directory.to_s
|
282
329
|
(filepath || '').start_with?(source_directory)
|
@@ -286,14 +333,14 @@ module Jazzy
|
|
286
333
|
make_default_doc_info(declaration)
|
287
334
|
|
288
335
|
filepath = doc['key.filepath']
|
289
|
-
|
290
|
-
if
|
336
|
+
|
337
|
+
if !declaration.swift? || should_mark_undocumented(filepath)
|
291
338
|
@stats.add_undocumented(declaration)
|
292
339
|
return nil if @skip_undocumented
|
293
340
|
declaration.abstract = undocumented_abstract
|
294
341
|
else
|
295
342
|
declaration.abstract = Markdown.render(doc['key.doc.comment'] || '',
|
296
|
-
|
343
|
+
declaration.highlight_language)
|
297
344
|
end
|
298
345
|
|
299
346
|
declaration
|
@@ -312,16 +359,7 @@ module Jazzy
|
|
312
359
|
def self.make_doc_info(doc, declaration)
|
313
360
|
return unless should_document?(doc)
|
314
361
|
|
315
|
-
|
316
|
-
declaration.declaration =
|
317
|
-
Highlighter.highlight(doc['key.parsed_declaration'])
|
318
|
-
declaration.other_language_declaration =
|
319
|
-
Highlighter.highlight(doc['key.swift_declaration'], 'swift')
|
320
|
-
else
|
321
|
-
declaration.declaration =
|
322
|
-
Highlighter.highlight(make_swift_declaration(doc, declaration))
|
323
|
-
end
|
324
|
-
|
362
|
+
highlight_declaration(doc, declaration)
|
325
363
|
make_deprecation_info(doc, declaration)
|
326
364
|
|
327
365
|
unless doc['key.doc.full_as_xml']
|
@@ -329,7 +367,7 @@ module Jazzy
|
|
329
367
|
end
|
330
368
|
|
331
369
|
declaration.abstract = Markdown.render(doc['key.doc.comment'] || '',
|
332
|
-
|
370
|
+
declaration.highlight_language)
|
333
371
|
declaration.discussion = ''
|
334
372
|
declaration.return = Markdown.rendered_returns
|
335
373
|
declaration.parameters = parameters(doc, Markdown.rendered_parameters)
|
@@ -337,6 +375,18 @@ module Jazzy
|
|
337
375
|
@stats.add_documented
|
338
376
|
end
|
339
377
|
|
378
|
+
def self.highlight_declaration(doc, declaration)
|
379
|
+
if declaration.swift?
|
380
|
+
declaration.declaration =
|
381
|
+
Highlighter.highlight_swift(make_swift_declaration(doc, declaration))
|
382
|
+
else
|
383
|
+
declaration.declaration =
|
384
|
+
Highlighter.highlight_objc(doc['key.parsed_declaration'])
|
385
|
+
declaration.other_language_declaration =
|
386
|
+
Highlighter.highlight_swift(doc['key.swift_declaration'])
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
340
390
|
def self.make_deprecation_info(doc, declaration)
|
341
391
|
if declaration.deprecated
|
342
392
|
declaration.deprecation_message =
|
@@ -391,8 +441,7 @@ module Jazzy
|
|
391
441
|
parsed &&
|
392
442
|
(annotated.include?(' = default') || # SR-2608
|
393
443
|
parsed.match('@autoclosure|@escaping') || # SR-6321
|
394
|
-
parsed.include?("\n")
|
395
|
-
parsed.include?('extension '))
|
444
|
+
parsed.include?("\n"))
|
396
445
|
end
|
397
446
|
|
398
447
|
# Replace the fully qualified name of a type with its base name
|
@@ -414,6 +463,9 @@ module Jazzy
|
|
414
463
|
# From source code
|
415
464
|
parsed_decl = doc['key.parsed_declaration']
|
416
465
|
|
466
|
+
# Don't present type attributes on extensions
|
467
|
+
return parsed_decl if declaration.type.extension?
|
468
|
+
|
417
469
|
decl =
|
418
470
|
if prefer_parsed_decl?(parsed_decl, annotated_decl_body)
|
419
471
|
# Strip any attrs captured by parsed version
|
@@ -435,14 +487,10 @@ module Jazzy
|
|
435
487
|
end
|
436
488
|
|
437
489
|
def self.make_substructure(doc, declaration)
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
)
|
443
|
-
else
|
444
|
-
[]
|
445
|
-
end
|
490
|
+
return [] unless subdocs = doc['key.substructure']
|
491
|
+
make_source_declarations(subdocs,
|
492
|
+
declaration,
|
493
|
+
declaration.mark_for_children)
|
446
494
|
end
|
447
495
|
|
448
496
|
# rubocop:disable Metrics/MethodLength
|
@@ -463,8 +511,7 @@ module Jazzy
|
|
463
511
|
declaration.type = SourceDeclaration::Type.new(doc['key.kind'])
|
464
512
|
declaration.typename = doc['key.typename']
|
465
513
|
declaration.objc_name = doc['key.name']
|
466
|
-
documented_name = if Config.instance.
|
467
|
-
doc['key.swift_name']
|
514
|
+
documented_name = if Config.instance.hide_objc? && doc['key.swift_name']
|
468
515
|
doc['key.swift_name']
|
469
516
|
else
|
470
517
|
declaration.objc_name
|
@@ -500,10 +547,17 @@ module Jazzy
|
|
500
547
|
declaration.end_line = doc['key.parsed_scope.end']
|
501
548
|
declaration.deprecated = doc['key.always_deprecated']
|
502
549
|
declaration.unavailable = doc['key.always_unavailable']
|
550
|
+
declaration.generic_requirements =
|
551
|
+
find_generic_requirements(doc['key.parsed_declaration'])
|
552
|
+
inherited_types = doc['key.inheritedtypes'] || []
|
553
|
+
declaration.inherited_types =
|
554
|
+
inherited_types.map { |type| type['key.name'] }.compact
|
503
555
|
|
504
556
|
next unless make_doc_info(doc, declaration)
|
505
|
-
make_substructure(doc, declaration)
|
506
|
-
next if declaration.type.extension? &&
|
557
|
+
declaration.children = make_substructure(doc, declaration)
|
558
|
+
next if declaration.type.extension? &&
|
559
|
+
declaration.children.empty? &&
|
560
|
+
!declaration.inherited_types?
|
507
561
|
declarations << declaration
|
508
562
|
end
|
509
563
|
declarations
|
@@ -512,12 +566,22 @@ module Jazzy
|
|
512
566
|
# rubocop:enable Metrics/CyclomaticComplexity
|
513
567
|
# rubocop:enable Metrics/MethodLength
|
514
568
|
|
569
|
+
def self.find_generic_requirements(parsed_declaration)
|
570
|
+
parsed_declaration =~ /\bwhere\s+(.*)$/m
|
571
|
+
return nil unless Regexp.last_match
|
572
|
+
Regexp.last_match[1].gsub(/\s+/, ' ')
|
573
|
+
end
|
574
|
+
|
515
575
|
# Expands extensions of nested types declared at the top level into
|
516
576
|
# a tree so they can be deduplicated properly
|
517
577
|
def self.expand_extensions(decls)
|
518
578
|
decls.map do |decl|
|
519
579
|
next decl unless decl.type.extension? && decl.name.include?('.')
|
520
580
|
|
581
|
+
# Don't expand the Swift namespace if we're in ObjC mode.
|
582
|
+
# ex: NS_SWIFT_NAME(Foo.Bar) should not create top-level Foo
|
583
|
+
next decl if decl.swift_objc_extension? && !Config.instance.hide_objc?
|
584
|
+
|
521
585
|
name_parts = decl.name.split('.')
|
522
586
|
decl.name = name_parts.pop
|
523
587
|
expand_extension(decl, name_parts, decls)
|
@@ -531,6 +595,7 @@ module Jazzy
|
|
531
595
|
SourceDeclaration.new.tap do |decl|
|
532
596
|
make_default_doc_info(decl)
|
533
597
|
decl.name = name
|
598
|
+
decl.modulename = extension.modulename
|
534
599
|
decl.type = extension.type
|
535
600
|
decl.mark = extension.mark
|
536
601
|
decl.usr = candidates.first.usr unless candidates.empty?
|
@@ -553,10 +618,10 @@ module Jazzy
|
|
553
618
|
.group_by { |d| deduplication_key(d, declarations) }
|
554
619
|
.values
|
555
620
|
|
556
|
-
duplicate_groups.
|
621
|
+
duplicate_groups.flat_map do |group|
|
557
622
|
# Put extended type (if present) before extensions
|
558
623
|
merge_declarations(group)
|
559
|
-
end
|
624
|
+
end.compact
|
560
625
|
end
|
561
626
|
|
562
627
|
# Returns true if an Objective-C declaration is mergeable.
|
@@ -566,13 +631,28 @@ module Jazzy
|
|
566
631
|
&& name_match(decl.objc_category_name[0], root_decls))
|
567
632
|
end
|
568
633
|
|
634
|
+
# Returns if a Swift declaration is mergeable.
|
635
|
+
# Start off merging in typealiases to help understand extensions.
|
636
|
+
def self.mergeable_swift?(decl)
|
637
|
+
decl.type.swift_extensible? ||
|
638
|
+
decl.type.swift_extension? ||
|
639
|
+
decl.type.swift_typealias?
|
640
|
+
end
|
641
|
+
|
569
642
|
# Two declarations get merged if they have the same deduplication key.
|
570
643
|
def self.deduplication_key(decl, root_decls)
|
571
|
-
|
644
|
+
# Swift extension of objc class
|
645
|
+
if decl.swift_objc_extension?
|
646
|
+
[decl.swift_extension_objc_name, :objc_class_and_categories]
|
647
|
+
# Swift type or Swift extension of Swift type
|
648
|
+
elsif mergeable_swift?(decl)
|
572
649
|
[decl.usr, decl.name]
|
650
|
+
# Objc categories and classes
|
573
651
|
elsif mergeable_objc?(decl, root_decls)
|
574
|
-
|
652
|
+
# Using the ObjC name to match swift_objc_extension.
|
653
|
+
name, _ = decl.objc_category_name || decl.objc_name
|
575
654
|
[name, :objc_class_and_categories]
|
655
|
+
# Non-mergable declarations (funcs, typedefs etc...)
|
576
656
|
else
|
577
657
|
[decl.usr, decl.name, decl.type.kind]
|
578
658
|
end
|
@@ -591,17 +671,36 @@ module Jazzy
|
|
591
671
|
end
|
592
672
|
typedecl = typedecls.first
|
593
673
|
|
674
|
+
extensions = reject_inaccessible_extensions(typedecl, extensions)
|
675
|
+
|
594
676
|
if typedecl
|
595
677
|
if typedecl.type.swift_protocol?
|
596
|
-
|
597
|
-
mark_members_from_protocol_extension(extensions)
|
678
|
+
mark_and_merge_protocol_extensions(typedecl, extensions)
|
598
679
|
extensions.reject! { |ext| ext.children.empty? }
|
599
680
|
end
|
600
681
|
|
601
|
-
|
682
|
+
merge_objc_declaration_marks(typedecl, extensions)
|
602
683
|
end
|
603
684
|
|
604
|
-
|
685
|
+
# Keep type-aliases separate from any extensions
|
686
|
+
if typedecl && typedecl.type.swift_typealias?
|
687
|
+
[merge_type_and_extensions(typedecls, []),
|
688
|
+
merge_type_and_extensions([], extensions)]
|
689
|
+
else
|
690
|
+
merge_type_and_extensions(typedecls, extensions)
|
691
|
+
end
|
692
|
+
end
|
693
|
+
# rubocop:enable Metrics/MethodLength
|
694
|
+
|
695
|
+
def self.merge_type_and_extensions(typedecls, extensions)
|
696
|
+
# Constrained extensions at the end
|
697
|
+
constrained, regular_exts = extensions.partition(&:constrained_extension?)
|
698
|
+
decls = typedecls + regular_exts + constrained
|
699
|
+
return nil if decls.empty?
|
700
|
+
|
701
|
+
move_merged_extension_marks(decls)
|
702
|
+
merge_code_declaration(decls)
|
703
|
+
|
605
704
|
decls.first.tap do |merged|
|
606
705
|
merged.children = deduplicate_declarations(
|
607
706
|
decls.flat_map(&:children).uniq,
|
@@ -611,58 +710,112 @@ module Jazzy
|
|
611
710
|
end
|
612
711
|
end
|
613
712
|
end
|
614
|
-
# rubocop:enable Metrics/MethodLength
|
615
713
|
|
714
|
+
# Now we know all the public types and all the private protocols,
|
715
|
+
# reject extensions that add public protocols to private types
|
716
|
+
# or add private protocols to public types.
|
717
|
+
def self.reject_inaccessible_extensions(typedecl, extensions)
|
718
|
+
swift_exts, objc_exts = extensions.partition(&:swift?)
|
719
|
+
|
720
|
+
# Reject extensions that are just conformances to private protocols
|
721
|
+
unwanted_exts, wanted_exts = swift_exts.partition do |ext|
|
722
|
+
ext.children.empty? &&
|
723
|
+
!ext.other_inherited_types?(@inaccessible_protocols)
|
724
|
+
end
|
725
|
+
|
726
|
+
# Given extensions of a type from this module, without the
|
727
|
+
# type itself, the type must be private and the extensions
|
728
|
+
# should be rejected.
|
729
|
+
if !typedecl &&
|
730
|
+
wanted_exts.first &&
|
731
|
+
wanted_exts.first.type_from_doc_module?
|
732
|
+
unwanted_exts += wanted_exts
|
733
|
+
wanted_exts = []
|
734
|
+
end
|
735
|
+
|
736
|
+
# Don't tell the user to document them
|
737
|
+
unwanted_exts.each { |e| @stats.remove_undocumented(e) }
|
738
|
+
|
739
|
+
objc_exts + wanted_exts
|
740
|
+
end
|
741
|
+
|
742
|
+
# Protocol extensions.
|
743
|
+
#
|
616
744
|
# If any of the extensions provide default implementations for methods in
|
617
745
|
# the given protocol, merge those members into the protocol doc instead of
|
618
746
|
# keeping them on the extension. These get a “Default implementation”
|
619
|
-
# annotation in the generated docs.
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
747
|
+
# annotation in the generated docs. Default implementations added by
|
748
|
+
# conditional extensions are annotated but listed separately.
|
749
|
+
#
|
750
|
+
# Protocol methods provided only in an extension and not in the protocol
|
751
|
+
# itself are a special beast: they do not use dynamic dispatch. These get an
|
752
|
+
# “Extension method” annotation in the generated docs.
|
753
|
+
def self.mark_and_merge_protocol_extensions(protocol, extensions)
|
754
|
+
extensions.each do |ext|
|
755
|
+
ext.children = ext.children.select do |ext_member|
|
756
|
+
proto_member = protocol.children.find do |p|
|
757
|
+
p.name == ext_member.name && p.type == ext_member.type
|
758
|
+
end
|
759
|
+
|
760
|
+
# Extension-only method, keep.
|
761
|
+
unless proto_member
|
762
|
+
ext_member.from_protocol_extension = true
|
763
|
+
next true
|
625
764
|
end
|
626
|
-
|
627
|
-
|
628
|
-
|
765
|
+
|
766
|
+
# Default impl but constrained, mark and keep.
|
767
|
+
if ext.constrained_extension?
|
768
|
+
ext_member.default_impl_abstract = ext_member.abstract
|
769
|
+
ext_member.abstract = nil
|
770
|
+
next true
|
629
771
|
end
|
772
|
+
|
773
|
+
# Default impl for all users, merge.
|
774
|
+
proto_member.default_impl_abstract = ext_member.abstract
|
775
|
+
next false
|
630
776
|
end
|
631
777
|
end
|
632
778
|
end
|
633
779
|
|
634
|
-
#
|
635
|
-
#
|
636
|
-
|
637
|
-
|
780
|
+
# Mark children merged from categories with the name of category
|
781
|
+
# (unless they already have a mark)
|
782
|
+
def self.merge_objc_declaration_marks(typedecl, extensions)
|
783
|
+
return unless typedecl.type.objc_class?
|
638
784
|
extensions.each do |ext|
|
639
|
-
ext.
|
640
|
-
|
641
|
-
end
|
785
|
+
_, category_name = ext.objc_category_name
|
786
|
+
ext.children.each { |c| c.mark.name ||= category_name }
|
642
787
|
end
|
643
788
|
end
|
644
789
|
|
645
|
-
#
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
end
|
654
|
-
else
|
655
|
-
# If the Swift extension has a mark and the first child doesn't
|
656
|
-
# then copy the mark contents down so it still shows up.
|
657
|
-
extensions.each do |ext|
|
658
|
-
child = ext.children.first
|
659
|
-
if child && child.mark.empty?
|
660
|
-
child.mark.copy(ext.mark)
|
661
|
-
end
|
790
|
+
# For each extension to be merged, move any MARK from the extension
|
791
|
+
# declaration down to the extension contents so it still shows up.
|
792
|
+
def self.move_merged_extension_marks(decls)
|
793
|
+
return unless to_be_merged = decls[1..-1]
|
794
|
+
to_be_merged.each do |ext|
|
795
|
+
child = ext.children.first
|
796
|
+
if child && child.mark.empty?
|
797
|
+
child.mark.copy(ext.mark)
|
662
798
|
end
|
663
799
|
end
|
664
800
|
end
|
665
801
|
|
802
|
+
# Merge useful information added by extensions into the main
|
803
|
+
# declaration: public protocol conformances and, for top-level extensions,
|
804
|
+
# further conditional extensions of the same type.
|
805
|
+
def self.merge_code_declaration(decls)
|
806
|
+
first = decls.first
|
807
|
+
|
808
|
+
declarations = decls[1..-1].select do |decl|
|
809
|
+
decl.type.swift_extension? &&
|
810
|
+
(decl.other_inherited_types?(@inaccessible_protocols) ||
|
811
|
+
(first.type.swift_extension? && decl.constrained_extension?))
|
812
|
+
end.map(&:declaration)
|
813
|
+
|
814
|
+
unless declarations.empty?
|
815
|
+
first.declaration = declarations.prepend(first.declaration).uniq.join
|
816
|
+
end
|
817
|
+
end
|
818
|
+
|
666
819
|
# Apply filtering based on the "included" and "excluded" flags.
|
667
820
|
def self.filter_files(json)
|
668
821
|
json = filter_included_files(json) if Config.instance.included_files.any?
|
@@ -818,19 +971,18 @@ module Jazzy
|
|
818
971
|
@min_acl = min_acl
|
819
972
|
@skip_undocumented = skip_undocumented
|
820
973
|
@stats = Stats.new
|
821
|
-
|
974
|
+
@inaccessible_protocols = []
|
975
|
+
sourcekitten_json = filter_files(JSON.parse(sourcekitten_output).flatten)
|
822
976
|
docs = make_source_declarations(sourcekitten_json).concat inject_docs
|
823
977
|
docs = expand_extensions(docs)
|
824
978
|
docs = deduplicate_declarations(docs)
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
# than min_acl
|
830
|
-
docs = docs.reject { |doc| doc.type.swift_enum_element? }
|
831
|
-
end
|
979
|
+
docs = reject_objc_types(docs)
|
980
|
+
# Remove top-level enum cases because it means they have an ACL lower
|
981
|
+
# than min_acl
|
982
|
+
docs = docs.reject { |doc| doc.type.swift_enum_element? }
|
832
983
|
ungrouped_docs = docs
|
833
984
|
docs = group_docs(docs)
|
985
|
+
merge_consecutive_marks(docs)
|
834
986
|
make_doc_urls(docs)
|
835
987
|
autolink(docs, ungrouped_docs)
|
836
988
|
[docs, @stats]
|