jazzy 0.13.5 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Tests.yml +52 -0
  3. data/.rubocop.yml +139 -24
  4. data/CHANGELOG.md +105 -0
  5. data/CONTRIBUTING.md +5 -5
  6. data/Dangerfile +11 -8
  7. data/Gemfile +3 -1
  8. data/Gemfile.lock +101 -77
  9. data/README.md +81 -13
  10. data/Rakefile +13 -12
  11. data/bin/jazzy +3 -2
  12. data/bin/sourcekitten +0 -0
  13. data/jazzy.gemspec +8 -6
  14. data/js/package-lock.json +30 -25
  15. data/js/package.json +3 -3
  16. data/lib/jazzy/config.rb +125 -72
  17. data/lib/jazzy/doc.rb +3 -1
  18. data/lib/jazzy/doc_builder.rb +79 -84
  19. data/lib/jazzy/docset_builder.rb +3 -1
  20. data/lib/jazzy/documentation_generator.rb +6 -2
  21. data/lib/jazzy/executable.rb +3 -0
  22. data/lib/jazzy/extensions/bitbucket/img/bitbucket.svg +11 -0
  23. data/lib/jazzy/{themes/apple/assets → extensions/github}/img/gh.png +0 -0
  24. data/lib/jazzy/extensions/gitlab/img/gitlab.svg +23 -0
  25. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.ttf +0 -0
  26. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff +0 -0
  27. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  28. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  29. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  30. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  31. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  32. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  33. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  34. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  35. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  36. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  37. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  38. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  39. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  40. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.ttf +0 -0
  41. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff +0 -0
  42. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff2 +0 -0
  43. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  44. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  45. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  46. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.ttf +0 -0
  47. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff +0 -0
  48. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff2 +0 -0
  49. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.ttf +0 -0
  50. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff +0 -0
  51. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff2 +0 -0
  52. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  53. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  54. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  55. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.ttf +0 -0
  56. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff +0 -0
  57. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff2 +0 -0
  58. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  59. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  60. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  61. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  62. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  63. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  64. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  65. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  66. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  67. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.ttf +0 -0
  68. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff +0 -0
  69. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff2 +0 -0
  70. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.ttf +0 -0
  71. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff +0 -0
  72. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  73. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.ttf +0 -0
  74. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff +0 -0
  75. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  76. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.ttf +0 -0
  77. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff +0 -0
  78. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  79. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.ttf +0 -0
  80. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff +0 -0
  81. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  82. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  83. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  84. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  85. data/lib/jazzy/extensions/katex/css/katex.min.css +1 -1
  86. data/lib/jazzy/extensions/katex/js/katex.min.js +1 -1
  87. data/lib/jazzy/gem_version.rb +3 -1
  88. data/lib/jazzy/highlighter.rb +17 -3
  89. data/lib/jazzy/jazzy_markdown.rb +112 -31
  90. data/lib/jazzy/podspec_documenter.rb +14 -16
  91. data/lib/jazzy/search_builder.rb +4 -3
  92. data/lib/jazzy/source_declaration/access_control_level.rb +7 -5
  93. data/lib/jazzy/source_declaration/type.rb +29 -3
  94. data/lib/jazzy/source_declaration.rb +18 -3
  95. data/lib/jazzy/source_document.rb +8 -5
  96. data/lib/jazzy/source_host.rb +111 -0
  97. data/lib/jazzy/source_mark.rb +8 -6
  98. data/lib/jazzy/source_module.rb +6 -6
  99. data/lib/jazzy/sourcekitten.rb +157 -86
  100. data/lib/jazzy/stats.rb +14 -3
  101. data/lib/jazzy/symbol_graph/constraint.rb +98 -0
  102. data/lib/jazzy/symbol_graph/ext_node.rb +116 -0
  103. data/lib/jazzy/symbol_graph/graph.rb +200 -0
  104. data/lib/jazzy/symbol_graph/relationship.rb +48 -0
  105. data/lib/jazzy/symbol_graph/sym_node.rb +176 -0
  106. data/lib/jazzy/symbol_graph/symbol.rb +248 -0
  107. data/lib/jazzy/symbol_graph.rb +95 -0
  108. data/lib/jazzy/themes/apple/assets/css/highlight.css.scss +63 -59
  109. data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +26 -2
  110. data/lib/jazzy/themes/apple/assets/js/jazzy.js +4 -0
  111. data/lib/jazzy/themes/apple/assets/js/jazzy.search.js +4 -0
  112. data/lib/jazzy/themes/apple/assets/js/jquery.min.js +2 -2
  113. data/lib/jazzy/themes/apple/assets/js/lunr.min.js +3 -3
  114. data/lib/jazzy/themes/apple/templates/doc.mustache +4 -5
  115. data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
  116. data/lib/jazzy/themes/apple/templates/header.mustache +6 -6
  117. data/lib/jazzy/themes/apple/templates/task.mustache +6 -11
  118. data/lib/jazzy/themes/fullwidth/assets/css/highlight.css.scss +63 -59
  119. data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +11 -2
  120. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.js +4 -0
  121. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.search.js +4 -0
  122. data/lib/jazzy/themes/fullwidth/assets/js/jquery.min.js +2 -2
  123. data/lib/jazzy/themes/fullwidth/assets/js/lunr.min.js +3 -3
  124. data/lib/jazzy/themes/fullwidth/templates/doc.mustache +4 -5
  125. data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
  126. data/lib/jazzy/themes/fullwidth/templates/header.mustache +8 -8
  127. data/lib/jazzy/themes/fullwidth/templates/task.mustache +6 -11
  128. data/lib/jazzy/themes/jony/assets/css/highlight.css.scss +63 -59
  129. data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +26 -2
  130. data/lib/jazzy/themes/jony/assets/js/jazzy.js +4 -0
  131. data/lib/jazzy/themes/jony/assets/js/jquery.min.js +2 -2
  132. data/lib/jazzy/themes/jony/templates/doc.mustache +4 -5
  133. data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
  134. data/lib/jazzy/themes/jony/templates/header.mustache +6 -6
  135. data/lib/jazzy/themes/jony/templates/task.mustache +6 -11
  136. data/lib/jazzy.rb +2 -0
  137. data/spec/integration_spec.rb +59 -45
  138. data/spec/spec_helper/pre_flight.rb +2 -0
  139. data/spec/spec_helper.rb +3 -1
  140. metadata +39 -19
  141. data/.circleci/config.yml +0 -83
  142. data/lib/jazzy/themes/fullwidth/assets/img/gh.png +0 -0
  143. data/lib/jazzy/themes/jony/assets/img/gh.png +0 -0
  144. data/spec/sourcekitten_spec.rb +0 -6
@@ -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'.freeze
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
- "<a href=\"#{ELIDED_AUTOLINK_TOKEN}#{link_target.url}\">" +
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
- STDERR.puts 'WARNING: No documented top-level declarations match ' \
78
- "name \"#{name}\" specified in categories file"
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 && prev_mark.can_merge?(doc.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
- return CGI.escape(unsafe_filename).gsub('_', '%5F').tr('%', '_')
145
+ CGI.escape(unsafe_filename).gsub('_', '%5F').tr('%', '_')
145
146
  else
146
- return unsafe_filename
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 'A compile error prevented ' + doc.fully_qualified_name +
166
- ' from receiving a unique USR. Documentation may be ' \
167
- 'incomplete. Please check for compile errors by running ' \
168
- '`xcodebuild` or `swift build` with arguments ' \
169
- "`#{Config.instance.build_tool_arguments.shelljoin}`."
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 Xcode ' \
178
- 'project. If this token is declared in an `#if` block, please ' \
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 + '#/' + id
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
- return false unless doc['key.attributes']
281
- !doc['key.attributes'].select do |attribute|
282
- attribute.values.first == 'source.decl.attribute.available'
283
- end.empty?
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
- # Document enum elements, since we can't tell their ACL.
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
- def self.should_mark_undocumented(filepath)
329
- source_directory = Config.instance.source_directory.to_s
330
- (filepath || '').start_with?(source_directory)
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
- filepath = doc['key.filepath']
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
- available_attrs = extract_availability(doc['key.doc.declaration'] || '')
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
- available_attrs.concat(extract_attributes(annotated_decl_attrs))
497
- .push(decl)
498
- .join("\n")
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.sub(/(?<=@property)\s+\(.*?\)/, attrs_text)
515
- .gsub(/\s+/, ' ')
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 = SourceDeclaration::Type.new(doc['key.kind'])
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
- 'https://github.com/realm/jazzy/issues about adding support ' \
563
- "for `#{declaration.type.kind}`."
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
- .group_by { |d| deduplication_key(d, declarations) }
649
- .values
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
- 'may indicate a build issue or a bug in Jazzy: ' +
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 && typedecl.type.swift_typealias?
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 && p.type == ext_member.type
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..-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..-1].select do |decl|
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
- .gsub('\.\.\.', '[^)]*')
885
- .gsub(/<.*>/, '')
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
- def self.autolink_text(text, doc, root_decls, after_highlight = false)
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
- parts = raw_name.sub(/^@/, '') # ignore for custom attribute ref
918
- .split(/(?<!\.)\.(?!\.)/) # dot with no neighboring dots
919
- .reject(&:empty?)
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 subsequence components, if any
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
- AUTOLINK_TEXT_FIELDS.each do |field|
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