jazzy 0.13.5 → 0.14.1
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 +139 -24
- data/CHANGELOG.md +105 -0
- data/CONTRIBUTING.md +5 -5
- data/Dangerfile +11 -8
- data/Gemfile +3 -1
- data/Gemfile.lock +101 -77
- data/README.md +81 -13
- data/Rakefile +13 -12
- 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/config.rb +125 -72
- data/lib/jazzy/doc.rb +3 -1
- data/lib/jazzy/doc_builder.rb +79 -84
- 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 +17 -3
- data/lib/jazzy/jazzy_markdown.rb +112 -31
- data/lib/jazzy/podspec_documenter.rb +14 -16
- data/lib/jazzy/search_builder.rb +4 -3
- data/lib/jazzy/source_declaration/access_control_level.rb +7 -5
- data/lib/jazzy/source_declaration/type.rb +29 -3
- data/lib/jazzy/source_declaration.rb +18 -3
- 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 +157 -86
- data/lib/jazzy/stats.rb +14 -3
- 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 +200 -0
- data/lib/jazzy/symbol_graph/relationship.rb +48 -0
- data/lib/jazzy/symbol_graph/sym_node.rb +176 -0
- data/lib/jazzy/symbol_graph/symbol.rb +248 -0
- data/lib/jazzy/symbol_graph.rb +95 -0
- data/lib/jazzy/themes/apple/assets/css/highlight.css.scss +63 -59
- data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +26 -2
- data/lib/jazzy/themes/apple/assets/js/jazzy.js +4 -0
- data/lib/jazzy/themes/apple/assets/js/jazzy.search.js +4 -0
- data/lib/jazzy/themes/apple/assets/js/jquery.min.js +2 -2
- data/lib/jazzy/themes/apple/assets/js/lunr.min.js +3 -3
- data/lib/jazzy/themes/apple/templates/doc.mustache +4 -5
- data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
- data/lib/jazzy/themes/apple/templates/header.mustache +6 -6
- data/lib/jazzy/themes/apple/templates/task.mustache +6 -11
- 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 +4 -5
- data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
- data/lib/jazzy/themes/fullwidth/templates/header.mustache +8 -8
- data/lib/jazzy/themes/fullwidth/templates/task.mustache +6 -11
- data/lib/jazzy/themes/jony/assets/css/highlight.css.scss +63 -59
- data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +26 -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 +4 -5
- data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
- data/lib/jazzy/themes/jony/templates/header.mustache +6 -6
- data/lib/jazzy/themes/jony/templates/task.mustache +6 -11
- data/lib/jazzy.rb +2 -0
- data/spec/integration_spec.rb +59 -45
- data/spec/spec_helper/pre_flight.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- metadata +39 -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/sourcekitten.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'pathname'
|
3
5
|
require 'shellwords'
|
@@ -12,7 +14,7 @@ require 'jazzy/source_declaration'
|
|
12
14
|
require 'jazzy/source_mark'
|
13
15
|
require 'jazzy/stats'
|
14
16
|
|
15
|
-
ELIDED_AUTOLINK_TOKEN = '36f8f5912051ae747ef441d6511ca4cb'
|
17
|
+
ELIDED_AUTOLINK_TOKEN = '36f8f5912051ae747ef441d6511ca4cb'
|
16
18
|
|
17
19
|
def autolink_regex(middle_regex, after_highlight)
|
18
20
|
start_tag_re, end_tag_re =
|
@@ -29,16 +31,15 @@ class String
|
|
29
31
|
gsub(autolink_regex(middle_regex, after_highlight)) do
|
30
32
|
original = Regexp.last_match(0)
|
31
33
|
start_tag, raw_name, end_tag = Regexp.last_match.captures
|
32
|
-
link_target = yield(CGI.unescape_html(raw_name))
|
34
|
+
link_target, display_name = yield(CGI.unescape_html(raw_name))
|
33
35
|
|
34
36
|
if link_target &&
|
35
37
|
!link_target.type.extension? &&
|
36
38
|
link_target.url &&
|
37
39
|
link_target.url != doc_url.split('#').first && # Don't link to parent
|
38
40
|
link_target.url != doc_url # Don't link to self
|
39
|
-
start_tag
|
40
|
-
"
|
41
|
-
raw_name + '</a>' + end_tag
|
41
|
+
"#{start_tag}<a href=\"#{ELIDED_AUTOLINK_TOKEN}#{link_target.url}\">" \
|
42
|
+
"#{CGI.escape_html(display_name)}</a>#{end_tag}"
|
42
43
|
else
|
43
44
|
original
|
44
45
|
end
|
@@ -74,8 +75,8 @@ module Jazzy
|
|
74
75
|
children = category['children'].flat_map do |name|
|
75
76
|
docs_with_name, docs = docs.partition { |doc| doc.name == name }
|
76
77
|
if docs_with_name.empty?
|
77
|
-
|
78
|
-
|
78
|
+
warn 'WARNING: No documented top-level declarations match ' \
|
79
|
+
"name \"#{name}\" specified in categories file"
|
79
80
|
end
|
80
81
|
docs_with_name
|
81
82
|
end
|
@@ -129,7 +130,7 @@ module Jazzy
|
|
129
130
|
def self.merge_consecutive_marks(docs)
|
130
131
|
prev_mark = nil
|
131
132
|
docs.each do |doc|
|
132
|
-
if prev_mark
|
133
|
+
if prev_mark&.can_merge?(doc.mark)
|
133
134
|
doc.mark = prev_mark
|
134
135
|
end
|
135
136
|
prev_mark = doc.mark
|
@@ -141,9 +142,9 @@ module Jazzy
|
|
141
142
|
unsafe_filename = doc.docs_filename
|
142
143
|
sanitzation_enabled = Config.instance.use_safe_filenames
|
143
144
|
if sanitzation_enabled && !doc.type.name_controlled_manually?
|
144
|
-
|
145
|
+
CGI.escape(unsafe_filename).gsub('_', '%5F').tr('%', '_')
|
145
146
|
else
|
146
|
-
|
147
|
+
unsafe_filename
|
147
148
|
end
|
148
149
|
end
|
149
150
|
|
@@ -162,11 +163,11 @@ module Jazzy
|
|
162
163
|
# Don't create HTML page for this doc if it doesn't have children
|
163
164
|
# Instead, make its link a hash-link on its parent's page
|
164
165
|
if doc.typename == '<<error type>>'
|
165
|
-
warn
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
166
|
+
warn "A compile error prevented #{doc.fully_qualified_name} " \
|
167
|
+
'from receiving a unique USR. Documentation may be ' \
|
168
|
+
'incomplete. Please check for compile errors by running ' \
|
169
|
+
'`xcodebuild` or `swift build` with arguments ' \
|
170
|
+
"`#{Config.instance.build_tool_arguments.shelljoin}`."
|
170
171
|
end
|
171
172
|
id = doc.usr
|
172
173
|
unless id
|
@@ -174,11 +175,11 @@ module Jazzy
|
|
174
175
|
warn "`#{id}` has no USR. First make sure all modules used in " \
|
175
176
|
'your project have been imported. If all used modules are ' \
|
176
177
|
'imported, please report this problem by filing an issue at ' \
|
177
|
-
'https://github.com/realm/jazzy/issues along with your
|
178
|
-
'project. If this token is declared in an `#if` block,
|
179
|
-
'ignore this message.'
|
178
|
+
'https://github.com/realm/jazzy/issues along with your ' \
|
179
|
+
'Xcode project. If this token is declared in an `#if` block, ' \
|
180
|
+
'please ignore this message.'
|
180
181
|
end
|
181
|
-
doc.url = doc.parent_in_docs.url
|
182
|
+
doc.url = "#{doc.parent_in_docs.url}#/#{id}"
|
182
183
|
end
|
183
184
|
end
|
184
185
|
end
|
@@ -189,6 +190,7 @@ module Jazzy
|
|
189
190
|
# Declarations under outer namespace type (Structures, Classes, etc.)
|
190
191
|
def self.subdir_for_doc(doc)
|
191
192
|
return [] if doc.type.markdown?
|
193
|
+
|
192
194
|
top_level_decl = doc.namespace_path.first
|
193
195
|
if top_level_decl.type.name
|
194
196
|
[top_level_decl.type.plural_url_name] +
|
@@ -258,6 +260,7 @@ module Jazzy
|
|
258
260
|
unless xcode = XCInvoke::Xcode.find_swift_version(swift_version)
|
259
261
|
raise "Unable to find an Xcode with swift version #{swift_version}."
|
260
262
|
end
|
263
|
+
|
261
264
|
env = xcode.as_env
|
262
265
|
else
|
263
266
|
env = ENV
|
@@ -269,22 +272,25 @@ module Jazzy
|
|
269
272
|
|
270
273
|
def self.make_default_doc_info(declaration)
|
271
274
|
# @todo: Fix these
|
272
|
-
declaration.line = nil
|
273
|
-
declaration.column = nil
|
274
275
|
declaration.abstract = ''
|
275
276
|
declaration.parameters = []
|
276
277
|
declaration.children = []
|
277
278
|
end
|
278
279
|
|
280
|
+
def self.attribute?(doc, attr_name)
|
281
|
+
doc['key.attributes']&.find do |attribute|
|
282
|
+
attribute['key.attribute'] == "source.decl.attribute.#{attr_name}"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
279
286
|
def self.availability_attribute?(doc)
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
287
|
+
attribute?(doc, 'available')
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.spi_attribute?(doc)
|
291
|
+
attribute?(doc, '_spi')
|
284
292
|
end
|
285
293
|
|
286
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
287
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
288
294
|
def self.should_document?(doc)
|
289
295
|
return false if doc['key.doc.comment'].to_s.include?(':nodoc:')
|
290
296
|
|
@@ -302,10 +308,31 @@ module Jazzy
|
|
302
308
|
return false
|
303
309
|
end
|
304
310
|
|
305
|
-
#
|
311
|
+
# Only document @_spi declarations in some scenarios
|
312
|
+
return false unless should_document_spi?(doc)
|
313
|
+
|
314
|
+
# Don't document declarations excluded by the min_acl setting
|
315
|
+
if type.swift_extension?
|
316
|
+
should_document_swift_extension?(doc)
|
317
|
+
else
|
318
|
+
should_document_acl?(type, doc)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Check visibility: SPI
|
323
|
+
def self.should_document_spi?(doc)
|
324
|
+
spi_ok = @min_acl < SourceDeclaration::AccessControlLevel.public ||
|
325
|
+
Config.instance.include_spi_declarations ||
|
326
|
+
(!spi_attribute?(doc) && !doc['key.symgraph_spi'])
|
327
|
+
|
328
|
+
@stats.add_spi_skipped unless spi_ok
|
329
|
+
spi_ok
|
330
|
+
end
|
331
|
+
|
332
|
+
# Check visibility: access control
|
333
|
+
def self.should_document_acl?(type, doc)
|
334
|
+
# Include all enum elements for now, can't tell their ACL.
|
306
335
|
return true if type.swift_enum_element?
|
307
|
-
# Document extensions if they might have parts covered by the ACL.
|
308
|
-
return should_document_swift_extension?(doc) if type.swift_extension?
|
309
336
|
|
310
337
|
acl_ok = SourceDeclaration::AccessControlLevel.from_doc(doc) >= @min_acl
|
311
338
|
unless acl_ok
|
@@ -314,9 +341,9 @@ module Jazzy
|
|
314
341
|
end
|
315
342
|
acl_ok
|
316
343
|
end
|
317
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
318
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
319
344
|
|
345
|
+
# Document extensions if they add protocol conformances, or have any
|
346
|
+
# member that needs to be documented.
|
320
347
|
def self.should_document_swift_extension?(doc)
|
321
348
|
doc['key.inheritedtypes'] ||
|
322
349
|
Array(doc['key.substructure']).any? do |subdoc|
|
@@ -325,19 +352,19 @@ module Jazzy
|
|
325
352
|
end
|
326
353
|
end
|
327
354
|
|
328
|
-
|
329
|
-
|
330
|
-
|
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 && !declaration.modulename
|
331
359
|
end
|
332
360
|
|
333
361
|
def self.process_undocumented_token(doc, declaration)
|
334
362
|
make_default_doc_info(declaration)
|
335
363
|
|
336
|
-
|
337
|
-
|
338
|
-
if !declaration.swift? || should_mark_undocumented(filepath)
|
364
|
+
if !declaration.swift? || should_mark_undocumented(declaration)
|
339
365
|
@stats.add_undocumented(declaration)
|
340
366
|
return nil if @skip_undocumented
|
367
|
+
|
341
368
|
declaration.abstract = undocumented_abstract
|
342
369
|
else
|
343
370
|
declaration.abstract = Markdown.render(doc['key.doc.comment'] || '',
|
@@ -405,7 +432,7 @@ module Jazzy
|
|
405
432
|
def self.xml_to_text(xml)
|
406
433
|
document = REXML::Document.new(xml)
|
407
434
|
REXML::XPath.match(document.root, '//text()').map(&:value).join
|
408
|
-
rescue
|
435
|
+
rescue StandardError
|
409
436
|
''
|
410
437
|
end
|
411
438
|
|
@@ -491,11 +518,18 @@ module Jazzy
|
|
491
518
|
end
|
492
519
|
|
493
520
|
# @available attrs only in compiler 'interface' style
|
494
|
-
|
521
|
+
extract_availability(doc['key.doc.declaration'] || '')
|
522
|
+
.concat(extract_attributes(annotated_decl_attrs))
|
523
|
+
.push(decl)
|
524
|
+
.join("\n")
|
525
|
+
end
|
495
526
|
|
496
|
-
|
497
|
-
|
498
|
-
|
527
|
+
# Exclude non-async routines that accept async closures
|
528
|
+
def self.swift_async?(fully_annotated_decl)
|
529
|
+
document = REXML::Document.new(fully_annotated_decl)
|
530
|
+
!document.elements['/*/syntaxtype.keyword[text()="async"]'].nil?
|
531
|
+
rescue StandardError
|
532
|
+
nil
|
499
533
|
end
|
500
534
|
|
501
535
|
# Strip default property attributes because libclang
|
@@ -511,12 +545,14 @@ module Jazzy
|
|
511
545
|
attrs = Regexp.last_match[1].split(',').map(&:strip) - DEFAULT_ATTRIBUTES
|
512
546
|
attrs_text = attrs.empty? ? '' : " (#{attrs.join(', ')})"
|
513
547
|
|
514
|
-
declaration
|
515
|
-
|
548
|
+
declaration
|
549
|
+
.sub(/(?<=@property)\s+\(.*?\)/, attrs_text)
|
550
|
+
.gsub(/\s+/, ' ')
|
516
551
|
end
|
517
552
|
|
518
553
|
def self.make_substructure(doc, declaration)
|
519
554
|
return [] unless subdocs = doc['key.substructure']
|
555
|
+
|
520
556
|
make_source_declarations(subdocs,
|
521
557
|
declaration,
|
522
558
|
declaration.mark_for_children)
|
@@ -537,7 +573,9 @@ module Jazzy
|
|
537
573
|
end
|
538
574
|
declaration = SourceDeclaration.new
|
539
575
|
declaration.parent_in_code = parent
|
540
|
-
declaration.type =
|
576
|
+
declaration.type =
|
577
|
+
SourceDeclaration::Type.new(doc['key.kind'],
|
578
|
+
doc['key.fully_annotated_decl'])
|
541
579
|
declaration.typename = doc['key.typename']
|
542
580
|
declaration.objc_name = doc['key.name']
|
543
581
|
documented_name = if Config.instance.hide_objc? && doc['key.swift_name']
|
@@ -559,8 +597,8 @@ module Jazzy
|
|
559
597
|
|
560
598
|
unless declaration.type.name
|
561
599
|
raise 'Please file an issue at ' \
|
562
|
-
|
563
|
-
|
600
|
+
'https://github.com/realm/jazzy/issues about adding support ' \
|
601
|
+
"for `#{declaration.type.kind}`."
|
564
602
|
end
|
565
603
|
|
566
604
|
declaration.file = Pathname(doc['key.filepath']) if doc['key.filepath']
|
@@ -571,8 +609,8 @@ module Jazzy
|
|
571
609
|
declaration.mark = current_mark
|
572
610
|
declaration.access_control_level =
|
573
611
|
SourceDeclaration::AccessControlLevel.from_doc(doc)
|
574
|
-
declaration.line = doc['key.doc.line']
|
575
|
-
declaration.column = doc['key.doc.column']
|
612
|
+
declaration.line = doc['key.doc.line'] || doc['key.line']
|
613
|
+
declaration.column = doc['key.doc.column'] || doc['key.column']
|
576
614
|
declaration.start_line = doc['key.parsed_scope.start']
|
577
615
|
declaration.end_line = doc['key.parsed_scope.end']
|
578
616
|
declaration.deprecated = doc['key.always_deprecated']
|
@@ -582,12 +620,19 @@ module Jazzy
|
|
582
620
|
inherited_types = doc['key.inheritedtypes'] || []
|
583
621
|
declaration.inherited_types =
|
584
622
|
inherited_types.map { |type| type['key.name'] }.compact
|
623
|
+
declaration.async =
|
624
|
+
doc['key.symgraph_async'] ||
|
625
|
+
if xml_declaration = doc['key.fully_annotated_decl']
|
626
|
+
swift_async?(xml_declaration)
|
627
|
+
end
|
585
628
|
|
586
629
|
next unless make_doc_info(doc, declaration)
|
630
|
+
|
587
631
|
declaration.children = make_substructure(doc, declaration)
|
588
632
|
next if declaration.type.extension? &&
|
589
633
|
declaration.children.empty? &&
|
590
634
|
!declaration.inherited_types?
|
635
|
+
|
591
636
|
declarations << declaration
|
592
637
|
end
|
593
638
|
declarations
|
@@ -599,6 +644,7 @@ module Jazzy
|
|
599
644
|
def self.find_generic_requirements(parsed_declaration)
|
600
645
|
parsed_declaration =~ /\bwhere\s+(.*)$/m
|
601
646
|
return nil unless Regexp.last_match
|
647
|
+
|
602
648
|
Regexp.last_match[1].gsub(/\s+/, ' ')
|
603
649
|
end
|
604
650
|
|
@@ -620,6 +666,7 @@ module Jazzy
|
|
620
666
|
|
621
667
|
def self.expand_extension(extension, name_parts, decls)
|
622
668
|
return extension if name_parts.empty?
|
669
|
+
|
623
670
|
name = name_parts.shift
|
624
671
|
candidates = decls.select { |decl| decl.name == name }
|
625
672
|
SourceDeclaration.new.tap do |decl|
|
@@ -645,8 +692,8 @@ module Jazzy
|
|
645
692
|
# Merges redundant declarations when documenting podspecs.
|
646
693
|
def self.deduplicate_declarations(declarations)
|
647
694
|
duplicate_groups = declarations
|
648
|
-
|
649
|
-
|
695
|
+
.group_by { |d| deduplication_key(d, declarations) }
|
696
|
+
.values
|
650
697
|
|
651
698
|
duplicate_groups.flat_map do |group|
|
652
699
|
# Put extended type (if present) before extensions
|
@@ -689,15 +736,17 @@ module Jazzy
|
|
689
736
|
end
|
690
737
|
|
691
738
|
# rubocop:disable Metrics/MethodLength
|
739
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
692
740
|
# Merges all of the given types and extensions into a single document.
|
693
741
|
def self.merge_declarations(decls)
|
694
742
|
extensions, typedecls = decls.partition { |d| d.type.extension? }
|
695
743
|
|
696
744
|
if typedecls.size > 1
|
745
|
+
info = typedecls
|
746
|
+
.map { |t| "#{t.type.name.downcase} #{t.name}" }
|
747
|
+
.join(', ')
|
697
748
|
warn 'Found conflicting type declarations with the same name, which ' \
|
698
|
-
|
699
|
-
typedecls.map { |t| "#{t.type.name.downcase} #{t.name}" }
|
700
|
-
.join(', ')
|
749
|
+
"may indicate a build issue or a bug in Jazzy: #{info}"
|
701
750
|
end
|
702
751
|
typedecl = typedecls.first
|
703
752
|
|
@@ -713,13 +762,14 @@ module Jazzy
|
|
713
762
|
end
|
714
763
|
|
715
764
|
# Keep type-aliases separate from any extensions
|
716
|
-
if typedecl
|
765
|
+
if typedecl&.type&.swift_typealias?
|
717
766
|
[merge_type_and_extensions(typedecls, []),
|
718
767
|
merge_type_and_extensions([], extensions)]
|
719
768
|
else
|
720
769
|
merge_type_and_extensions(typedecls, extensions)
|
721
770
|
end
|
722
771
|
end
|
772
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
723
773
|
# rubocop:enable Metrics/MethodLength
|
724
774
|
|
725
775
|
def self.merge_type_and_extensions(typedecls, extensions)
|
@@ -784,7 +834,9 @@ module Jazzy
|
|
784
834
|
extensions.each do |ext|
|
785
835
|
ext.children = ext.children.select do |ext_member|
|
786
836
|
proto_member = protocol.children.find do |p|
|
787
|
-
p.name == ext_member.name &&
|
837
|
+
p.name == ext_member.name &&
|
838
|
+
p.type == ext_member.type &&
|
839
|
+
p.async == ext_member.async
|
788
840
|
end
|
789
841
|
|
790
842
|
# Extension-only method, keep.
|
@@ -811,6 +863,7 @@ module Jazzy
|
|
811
863
|
# (unless they already have a mark)
|
812
864
|
def self.merge_objc_declaration_marks(typedecl, extensions)
|
813
865
|
return unless typedecl.type.objc_class?
|
866
|
+
|
814
867
|
extensions.each do |ext|
|
815
868
|
_, category_name = ext.objc_category_name
|
816
869
|
ext.children.each { |c| c.mark.name ||= category_name }
|
@@ -820,7 +873,8 @@ module Jazzy
|
|
820
873
|
# For each extension to be merged, move any MARK from the extension
|
821
874
|
# declaration down to the extension contents so it still shows up.
|
822
875
|
def self.move_merged_extension_marks(decls)
|
823
|
-
return unless to_be_merged = decls[1
|
876
|
+
return unless to_be_merged = decls[1..]
|
877
|
+
|
824
878
|
to_be_merged.each do |ext|
|
825
879
|
child = ext.children.first
|
826
880
|
if child && child.mark.empty?
|
@@ -835,7 +889,7 @@ module Jazzy
|
|
835
889
|
def self.merge_code_declaration(decls)
|
836
890
|
first = decls.first
|
837
891
|
|
838
|
-
declarations = decls[1
|
892
|
+
declarations = decls[1..].select do |decl|
|
839
893
|
decl.type.swift_extension? &&
|
840
894
|
(decl.other_inherited_types?(@inaccessible_protocols) ||
|
841
895
|
(first.type.swift_extension? && decl.constrained_extension?))
|
@@ -880,9 +934,11 @@ module Jazzy
|
|
880
934
|
|
881
935
|
def self.name_match(name_part, docs)
|
882
936
|
return nil unless name_part
|
937
|
+
|
883
938
|
wildcard_expansion = Regexp.escape(name_part)
|
884
|
-
|
885
|
-
|
939
|
+
.gsub('\.\.\.', '[^)]*')
|
940
|
+
.gsub(/<.*>/, '')
|
941
|
+
|
886
942
|
whole_name_pat = /\A#{wildcard_expansion}\Z/
|
887
943
|
docs.find do |doc|
|
888
944
|
whole_name_pat =~ doc.name
|
@@ -912,19 +968,26 @@ module Jazzy
|
|
912
968
|
# - method signatures after they've been processed by the highlighter
|
913
969
|
#
|
914
970
|
# The `after_highlight` flag is used to differentiate between the two modes.
|
915
|
-
|
971
|
+
#
|
972
|
+
# DocC link format - follow Xcode and don't display slash-separated parts.
|
973
|
+
# rubocop:disable Metrics/MethodLength
|
974
|
+
def self.autolink_text(text, doc, root_decls, after_highlight: false)
|
916
975
|
text.autolink_block(doc.url, '[^\s]+', after_highlight) do |raw_name|
|
917
|
-
|
918
|
-
|
919
|
-
|
976
|
+
sym_name =
|
977
|
+
(raw_name[/^<doc:(.*)>$/, 1] || raw_name).sub(/(?<!^)-.+$/, '')
|
978
|
+
|
979
|
+
parts = sym_name
|
980
|
+
.sub(/^@/, '') # ignore for custom attribute ref
|
981
|
+
.split(%r{(?<!\.)[/.](?!\.)}) # dot or slash, but not '...'
|
982
|
+
.reject(&:empty?)
|
920
983
|
|
921
984
|
# First dot-separated component can match any ancestor or top-level doc
|
922
985
|
first_part = parts.shift
|
923
986
|
name_root = ancestor_name_match(first_part, doc) ||
|
924
987
|
name_match(first_part, root_decls)
|
925
988
|
|
926
|
-
# Traverse children via
|
927
|
-
name_traversal(parts, name_root)
|
989
|
+
# Traverse children via subsequent components, if any
|
990
|
+
[name_traversal(parts, name_root), sym_name.sub(%r{^.*/}, '')]
|
928
991
|
end.autolink_block(doc.url, '[+-]\[\w+(?: ?\(\w+\))? [\w:]+\]',
|
929
992
|
after_highlight) do |raw_name|
|
930
993
|
match = raw_name.match(/([+-])\[(\w+(?: ?\(\w+\))?) ([\w:]+)\]/)
|
@@ -936,42 +999,50 @@ module Jazzy
|
|
936
999
|
|
937
1000
|
if name_root
|
938
1001
|
# Look up the verb in the subject’s children
|
939
|
-
name_match(match[1] + match[3], name_root.children)
|
1002
|
+
[name_match(match[1] + match[3], name_root.children), raw_name]
|
940
1003
|
end
|
941
1004
|
end.autolink_block(doc.url, '[+-]\w[\w:]*', after_highlight) do |raw_name|
|
942
|
-
name_match(raw_name, doc.children)
|
1005
|
+
[name_match(raw_name, doc.children), raw_name]
|
943
1006
|
end
|
944
1007
|
end
|
1008
|
+
# rubocop:enable Metrics/MethodLength
|
945
1009
|
|
946
1010
|
AUTOLINK_TEXT_FIELDS = %w[return
|
947
1011
|
abstract
|
948
1012
|
unavailable_message
|
949
1013
|
deprecation_message].freeze
|
950
1014
|
|
1015
|
+
def self.autolink_text_fields(doc, root_decls)
|
1016
|
+
AUTOLINK_TEXT_FIELDS.each do |field|
|
1017
|
+
if text = doc.send(field)
|
1018
|
+
doc.send(field + '=', autolink_text(text, doc, root_decls))
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
(doc.parameters || []).each do |param|
|
1023
|
+
param[:discussion] =
|
1024
|
+
autolink_text(param[:discussion], doc, root_decls)
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
951
1028
|
AUTOLINK_HIGHLIGHT_FIELDS = %w[declaration
|
952
1029
|
other_language_declaration].freeze
|
953
1030
|
|
1031
|
+
def self.autolink_highlight_fields(doc, root_decls)
|
1032
|
+
AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
|
1033
|
+
if text = doc.send(field)
|
1034
|
+
doc.send(field + '=',
|
1035
|
+
autolink_text(text, doc, root_decls, after_highlight: true))
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
end
|
1039
|
+
|
954
1040
|
def self.autolink(docs, root_decls)
|
955
1041
|
@autolink_root_decls = root_decls
|
956
1042
|
docs.each do |doc|
|
957
1043
|
doc.children = autolink(doc.children, root_decls)
|
958
|
-
|
959
|
-
|
960
|
-
if text = doc.send(field)
|
961
|
-
doc.send(field + '=', autolink_text(text, doc, root_decls))
|
962
|
-
end
|
963
|
-
end
|
964
|
-
|
965
|
-
AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
|
966
|
-
if text = doc.send(field)
|
967
|
-
doc.send(field + '=', autolink_text(text, doc, root_decls, true))
|
968
|
-
end
|
969
|
-
end
|
970
|
-
|
971
|
-
(doc.parameters || []).each do |param|
|
972
|
-
param[:discussion] =
|
973
|
-
autolink_text(param[:discussion], doc, root_decls)
|
974
|
-
end
|
1044
|
+
autolink_text_fields(doc, root_decls)
|
1045
|
+
autolink_highlight_fields(doc, root_decls)
|
975
1046
|
end
|
976
1047
|
end
|
977
1048
|
|
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, :spi_skipped, :undocumented_decls
|
8
9
|
|
9
10
|
def add_documented
|
10
11
|
@documented += 1
|
@@ -14,6 +15,10 @@ module Jazzy
|
|
14
15
|
@acl_skipped += 1
|
15
16
|
end
|
16
17
|
|
18
|
+
def add_spi_skipped
|
19
|
+
@spi_skipped += 1
|
20
|
+
end
|
21
|
+
|
17
22
|
def add_undocumented(decl)
|
18
23
|
@undocumented_decls << decl
|
19
24
|
end
|
@@ -31,7 +36,7 @@ module Jazzy
|
|
31
36
|
end
|
32
37
|
|
33
38
|
def initialize
|
34
|
-
@documented = @acl_skipped = 0
|
39
|
+
@documented = @acl_skipped = @spi_skipped = 0
|
35
40
|
@undocumented_decls = []
|
36
41
|
end
|
37
42
|
|
@@ -53,10 +58,16 @@ module Jazzy
|
|
53
58
|
"#{symbol_or_symbols(acl_skipped)} " \
|
54
59
|
'(use `--min-acl` to specify a different minimum ACL)'
|
55
60
|
end
|
61
|
+
|
62
|
+
if spi_skipped > 0
|
63
|
+
puts "skipped #{spi_skipped} SPI #{symbol_or_symbols(spi_skipped)} " \
|
64
|
+
'(use `--include-spi-declarations` to include these)'
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
68
|
def doc_coverage
|
59
69
|
return 0 if acl_included == 0
|
70
|
+
|
60
71
|
(100 * documented) / acl_included
|
61
72
|
end
|
62
73
|
|
@@ -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
|