jazzy 0.13.7 → 0.14.2

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Tests.yml +6 -6
  3. data/.rubocop.yml +155 -24
  4. data/CHANGELOG.md +91 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Dangerfile +11 -8
  7. data/Gemfile +3 -1
  8. data/Gemfile.lock +85 -64
  9. data/ObjectiveC.md +208 -0
  10. data/README.md +63 -33
  11. data/Rakefile +18 -15
  12. data/bin/jazzy +3 -2
  13. data/bin/sourcekitten +0 -0
  14. data/jazzy.gemspec +9 -6
  15. data/lib/jazzy/config.rb +135 -69
  16. data/lib/jazzy/doc.rb +3 -1
  17. data/lib/jazzy/doc_builder.rb +72 -83
  18. data/lib/jazzy/docset_builder.rb +3 -1
  19. data/lib/jazzy/documentation_generator.rb +6 -2
  20. data/lib/jazzy/executable.rb +3 -0
  21. data/lib/jazzy/extensions/bitbucket/img/bitbucket.svg +11 -0
  22. data/lib/jazzy/{themes/apple/assets → extensions/github}/img/gh.png +0 -0
  23. data/lib/jazzy/extensions/gitlab/img/gitlab.svg +23 -0
  24. data/lib/jazzy/gem_version.rb +3 -1
  25. data/lib/jazzy/highlighter.rb +5 -3
  26. data/lib/jazzy/jazzy_markdown.rb +75 -32
  27. data/lib/jazzy/podspec_documenter.rb +14 -16
  28. data/lib/jazzy/search_builder.rb +5 -6
  29. data/lib/jazzy/source_declaration/access_control_level.rb +7 -5
  30. data/lib/jazzy/source_declaration/type.rb +29 -3
  31. data/lib/jazzy/source_declaration.rb +22 -5
  32. data/lib/jazzy/source_document.rb +8 -5
  33. data/lib/jazzy/source_host.rb +111 -0
  34. data/lib/jazzy/source_mark.rb +8 -6
  35. data/lib/jazzy/source_module.rb +6 -6
  36. data/lib/jazzy/sourcekitten.rb +155 -81
  37. data/lib/jazzy/stats.rb +14 -3
  38. data/lib/jazzy/symbol_graph/constraint.rb +5 -1
  39. data/lib/jazzy/symbol_graph/ext_node.rb +3 -1
  40. data/lib/jazzy/symbol_graph/graph.rb +19 -12
  41. data/lib/jazzy/symbol_graph/relationship.rb +9 -0
  42. data/lib/jazzy/symbol_graph/sym_node.rb +25 -7
  43. data/lib/jazzy/symbol_graph/symbol.rb +54 -25
  44. data/lib/jazzy/symbol_graph.rb +43 -32
  45. data/lib/jazzy/themes/apple/assets/css/highlight.css.scss +63 -59
  46. data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +5 -1
  47. data/lib/jazzy/themes/apple/assets/js/jazzy.js +4 -0
  48. data/lib/jazzy/themes/apple/assets/js/jazzy.search.js +4 -0
  49. data/lib/jazzy/themes/apple/templates/doc.mustache +4 -5
  50. data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
  51. data/lib/jazzy/themes/apple/templates/header.mustache +6 -6
  52. data/lib/jazzy/themes/apple/templates/task.mustache +6 -11
  53. data/lib/jazzy/themes/fullwidth/assets/css/highlight.css.scss +63 -59
  54. data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +6 -2
  55. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.js +4 -0
  56. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.search.js +4 -0
  57. data/lib/jazzy/themes/fullwidth/templates/doc.mustache +4 -5
  58. data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
  59. data/lib/jazzy/themes/fullwidth/templates/header.mustache +8 -8
  60. data/lib/jazzy/themes/fullwidth/templates/task.mustache +6 -11
  61. data/lib/jazzy/themes/jony/assets/css/highlight.css.scss +63 -59
  62. data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +5 -1
  63. data/lib/jazzy/themes/jony/assets/js/jazzy.js +4 -0
  64. data/lib/jazzy/themes/jony/templates/doc.mustache +4 -5
  65. data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
  66. data/lib/jazzy/themes/jony/templates/header.mustache +6 -6
  67. data/lib/jazzy/themes/jony/templates/task.mustache +6 -11
  68. data/lib/jazzy.rb +2 -0
  69. data/spec/integration_spec.rb +46 -42
  70. data/spec/spec_helper/pre_flight.rb +2 -0
  71. data/spec/spec_helper.rb +3 -1
  72. metadata +32 -16
  73. data/lib/jazzy/themes/fullwidth/assets/img/gh.png +0 -0
  74. data/lib/jazzy/themes/jony/assets/img/gh.png +0 -0
  75. 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|
@@ -328,7 +355,9 @@ module Jazzy
328
355
  # Call things undocumented if they were compiled properly
329
356
  # and came from our module.
330
357
  def self.should_mark_undocumented(declaration)
331
- declaration.usr && !declaration.modulename
358
+ declaration.usr &&
359
+ (declaration.modulename.nil? ||
360
+ declaration.modulename == Config.instance.module_name)
332
361
  end
333
362
 
334
363
  def self.process_undocumented_token(doc, declaration)
@@ -337,6 +366,7 @@ module Jazzy
337
366
  if !declaration.swift? || should_mark_undocumented(declaration)
338
367
  @stats.add_undocumented(declaration)
339
368
  return nil if @skip_undocumented
369
+
340
370
  declaration.abstract = undocumented_abstract
341
371
  else
342
372
  declaration.abstract = Markdown.render(doc['key.doc.comment'] || '',
@@ -404,7 +434,7 @@ module Jazzy
404
434
  def self.xml_to_text(xml)
405
435
  document = REXML::Document.new(xml)
406
436
  REXML::XPath.match(document.root, '//text()').map(&:value).join
407
- rescue
437
+ rescue StandardError
408
438
  ''
409
439
  end
410
440
 
@@ -490,11 +520,18 @@ module Jazzy
490
520
  end
491
521
 
492
522
  # @available attrs only in compiler 'interface' style
493
- available_attrs = extract_availability(doc['key.doc.declaration'] || '')
523
+ extract_availability(doc['key.doc.declaration'] || '')
524
+ .concat(extract_attributes(annotated_decl_attrs))
525
+ .push(decl)
526
+ .join("\n")
527
+ end
494
528
 
495
- available_attrs.concat(extract_attributes(annotated_decl_attrs))
496
- .push(decl)
497
- .join("\n")
529
+ # Exclude non-async routines that accept async closures
530
+ def self.swift_async?(fully_annotated_decl)
531
+ document = REXML::Document.new(fully_annotated_decl)
532
+ !document.elements['/*/syntaxtype.keyword[text()="async"]'].nil?
533
+ rescue StandardError
534
+ nil
498
535
  end
499
536
 
500
537
  # Strip default property attributes because libclang
@@ -510,12 +547,14 @@ module Jazzy
510
547
  attrs = Regexp.last_match[1].split(',').map(&:strip) - DEFAULT_ATTRIBUTES
511
548
  attrs_text = attrs.empty? ? '' : " (#{attrs.join(', ')})"
512
549
 
513
- declaration.sub(/(?<=@property)\s+\(.*?\)/, attrs_text)
514
- .gsub(/\s+/, ' ')
550
+ declaration
551
+ .sub(/(?<=@property)\s+\(.*?\)/, attrs_text)
552
+ .gsub(/\s+/, ' ')
515
553
  end
516
554
 
517
555
  def self.make_substructure(doc, declaration)
518
556
  return [] unless subdocs = doc['key.substructure']
557
+
519
558
  make_source_declarations(subdocs,
520
559
  declaration,
521
560
  declaration.mark_for_children)
@@ -536,7 +575,9 @@ module Jazzy
536
575
  end
537
576
  declaration = SourceDeclaration.new
538
577
  declaration.parent_in_code = parent
539
- declaration.type = SourceDeclaration::Type.new(doc['key.kind'])
578
+ declaration.type =
579
+ SourceDeclaration::Type.new(doc['key.kind'],
580
+ doc['key.fully_annotated_decl'])
540
581
  declaration.typename = doc['key.typename']
541
582
  declaration.objc_name = doc['key.name']
542
583
  documented_name = if Config.instance.hide_objc? && doc['key.swift_name']
@@ -558,8 +599,8 @@ module Jazzy
558
599
 
559
600
  unless declaration.type.name
560
601
  raise 'Please file an issue at ' \
561
- 'https://github.com/realm/jazzy/issues about adding support ' \
562
- "for `#{declaration.type.kind}`."
602
+ 'https://github.com/realm/jazzy/issues about adding support ' \
603
+ "for `#{declaration.type.kind}`."
563
604
  end
564
605
 
565
606
  declaration.file = Pathname(doc['key.filepath']) if doc['key.filepath']
@@ -570,8 +611,8 @@ module Jazzy
570
611
  declaration.mark = current_mark
571
612
  declaration.access_control_level =
572
613
  SourceDeclaration::AccessControlLevel.from_doc(doc)
573
- declaration.line = doc['key.doc.line']
574
- declaration.column = doc['key.doc.column']
614
+ declaration.line = doc['key.doc.line'] || doc['key.line']
615
+ declaration.column = doc['key.doc.column'] || doc['key.column']
575
616
  declaration.start_line = doc['key.parsed_scope.start']
576
617
  declaration.end_line = doc['key.parsed_scope.end']
577
618
  declaration.deprecated = doc['key.always_deprecated']
@@ -581,12 +622,19 @@ module Jazzy
581
622
  inherited_types = doc['key.inheritedtypes'] || []
582
623
  declaration.inherited_types =
583
624
  inherited_types.map { |type| type['key.name'] }.compact
625
+ declaration.async =
626
+ doc['key.symgraph_async'] ||
627
+ if xml_declaration = doc['key.fully_annotated_decl']
628
+ swift_async?(xml_declaration)
629
+ end
584
630
 
585
631
  next unless make_doc_info(doc, declaration)
632
+
586
633
  declaration.children = make_substructure(doc, declaration)
587
634
  next if declaration.type.extension? &&
588
635
  declaration.children.empty? &&
589
636
  !declaration.inherited_types?
637
+
590
638
  declarations << declaration
591
639
  end
592
640
  declarations
@@ -598,6 +646,7 @@ module Jazzy
598
646
  def self.find_generic_requirements(parsed_declaration)
599
647
  parsed_declaration =~ /\bwhere\s+(.*)$/m
600
648
  return nil unless Regexp.last_match
649
+
601
650
  Regexp.last_match[1].gsub(/\s+/, ' ')
602
651
  end
603
652
 
@@ -619,6 +668,7 @@ module Jazzy
619
668
 
620
669
  def self.expand_extension(extension, name_parts, decls)
621
670
  return extension if name_parts.empty?
671
+
622
672
  name = name_parts.shift
623
673
  candidates = decls.select { |decl| decl.name == name }
624
674
  SourceDeclaration.new.tap do |decl|
@@ -644,8 +694,8 @@ module Jazzy
644
694
  # Merges redundant declarations when documenting podspecs.
645
695
  def self.deduplicate_declarations(declarations)
646
696
  duplicate_groups = declarations
647
- .group_by { |d| deduplication_key(d, declarations) }
648
- .values
697
+ .group_by { |d| deduplication_key(d, declarations) }
698
+ .values
649
699
 
650
700
  duplicate_groups.flat_map do |group|
651
701
  # Put extended type (if present) before extensions
@@ -688,15 +738,17 @@ module Jazzy
688
738
  end
689
739
 
690
740
  # rubocop:disable Metrics/MethodLength
741
+ # rubocop:disable Metrics/PerceivedComplexity
691
742
  # Merges all of the given types and extensions into a single document.
692
743
  def self.merge_declarations(decls)
693
744
  extensions, typedecls = decls.partition { |d| d.type.extension? }
694
745
 
695
746
  if typedecls.size > 1
747
+ info = typedecls
748
+ .map { |t| "#{t.type.name.downcase} #{t.name}" }
749
+ .join(', ')
696
750
  warn 'Found conflicting type declarations with the same name, which ' \
697
- 'may indicate a build issue or a bug in Jazzy: ' +
698
- typedecls.map { |t| "#{t.type.name.downcase} #{t.name}" }
699
- .join(', ')
751
+ "may indicate a build issue or a bug in Jazzy: #{info}"
700
752
  end
701
753
  typedecl = typedecls.first
702
754
 
@@ -712,13 +764,14 @@ module Jazzy
712
764
  end
713
765
 
714
766
  # Keep type-aliases separate from any extensions
715
- if typedecl && typedecl.type.swift_typealias?
767
+ if typedecl&.type&.swift_typealias?
716
768
  [merge_type_and_extensions(typedecls, []),
717
769
  merge_type_and_extensions([], extensions)]
718
770
  else
719
771
  merge_type_and_extensions(typedecls, extensions)
720
772
  end
721
773
  end
774
+ # rubocop:enable Metrics/PerceivedComplexity
722
775
  # rubocop:enable Metrics/MethodLength
723
776
 
724
777
  def self.merge_type_and_extensions(typedecls, extensions)
@@ -783,7 +836,9 @@ module Jazzy
783
836
  extensions.each do |ext|
784
837
  ext.children = ext.children.select do |ext_member|
785
838
  proto_member = protocol.children.find do |p|
786
- p.name == ext_member.name && p.type == ext_member.type
839
+ p.name == ext_member.name &&
840
+ p.type == ext_member.type &&
841
+ p.async == ext_member.async
787
842
  end
788
843
 
789
844
  # Extension-only method, keep.
@@ -810,6 +865,7 @@ module Jazzy
810
865
  # (unless they already have a mark)
811
866
  def self.merge_objc_declaration_marks(typedecl, extensions)
812
867
  return unless typedecl.type.objc_class?
868
+
813
869
  extensions.each do |ext|
814
870
  _, category_name = ext.objc_category_name
815
871
  ext.children.each { |c| c.mark.name ||= category_name }
@@ -819,7 +875,8 @@ module Jazzy
819
875
  # For each extension to be merged, move any MARK from the extension
820
876
  # declaration down to the extension contents so it still shows up.
821
877
  def self.move_merged_extension_marks(decls)
822
- return unless to_be_merged = decls[1..-1]
878
+ return unless to_be_merged = decls[1..]
879
+
823
880
  to_be_merged.each do |ext|
824
881
  child = ext.children.first
825
882
  if child && child.mark.empty?
@@ -834,7 +891,7 @@ module Jazzy
834
891
  def self.merge_code_declaration(decls)
835
892
  first = decls.first
836
893
 
837
- declarations = decls[1..-1].select do |decl|
894
+ declarations = decls[1..].select do |decl|
838
895
  decl.type.swift_extension? &&
839
896
  (decl.other_inherited_types?(@inaccessible_protocols) ||
840
897
  (first.type.swift_extension? && decl.constrained_extension?))
@@ -879,9 +936,11 @@ module Jazzy
879
936
 
880
937
  def self.name_match(name_part, docs)
881
938
  return nil unless name_part
939
+
882
940
  wildcard_expansion = Regexp.escape(name_part)
883
- .gsub('\.\.\.', '[^)]*')
884
- .gsub(/<.*>/, '')
941
+ .gsub('\.\.\.', '[^)]*')
942
+ .gsub(/<.*>/, '')
943
+
885
944
  whole_name_pat = /\A#{wildcard_expansion}\Z/
886
945
  docs.find do |doc|
887
946
  whole_name_pat =~ doc.name
@@ -911,19 +970,26 @@ module Jazzy
911
970
  # - method signatures after they've been processed by the highlighter
912
971
  #
913
972
  # The `after_highlight` flag is used to differentiate between the two modes.
914
- def self.autolink_text(text, doc, root_decls, after_highlight = false)
973
+ #
974
+ # DocC link format - follow Xcode and don't display slash-separated parts.
975
+ # rubocop:disable Metrics/MethodLength
976
+ def self.autolink_text(text, doc, root_decls, after_highlight: false)
915
977
  text.autolink_block(doc.url, '[^\s]+', after_highlight) do |raw_name|
916
- parts = raw_name.sub(/^@/, '') # ignore for custom attribute ref
917
- .split(/(?<!\.)\.(?!\.)/) # dot with no neighboring dots
918
- .reject(&:empty?)
978
+ sym_name =
979
+ (raw_name[/^<doc:(.*)>$/, 1] || raw_name).sub(/(?<!^)-.+$/, '')
980
+
981
+ parts = sym_name
982
+ .sub(/^@/, '') # ignore for custom attribute ref
983
+ .split(%r{(?<!\.)[/.](?!\.)}) # dot or slash, but not '...'
984
+ .reject(&:empty?)
919
985
 
920
986
  # First dot-separated component can match any ancestor or top-level doc
921
987
  first_part = parts.shift
922
988
  name_root = ancestor_name_match(first_part, doc) ||
923
989
  name_match(first_part, root_decls)
924
990
 
925
- # Traverse children via subsequence components, if any
926
- name_traversal(parts, name_root)
991
+ # Traverse children via subsequent components, if any
992
+ [name_traversal(parts, name_root), sym_name.sub(%r{^.*/}, '')]
927
993
  end.autolink_block(doc.url, '[+-]\[\w+(?: ?\(\w+\))? [\w:]+\]',
928
994
  after_highlight) do |raw_name|
929
995
  match = raw_name.match(/([+-])\[(\w+(?: ?\(\w+\))?) ([\w:]+)\]/)
@@ -935,42 +1001,50 @@ module Jazzy
935
1001
 
936
1002
  if name_root
937
1003
  # Look up the verb in the subject’s children
938
- name_match(match[1] + match[3], name_root.children)
1004
+ [name_match(match[1] + match[3], name_root.children), raw_name]
939
1005
  end
940
1006
  end.autolink_block(doc.url, '[+-]\w[\w:]*', after_highlight) do |raw_name|
941
- name_match(raw_name, doc.children)
1007
+ [name_match(raw_name, doc.children), raw_name]
942
1008
  end
943
1009
  end
1010
+ # rubocop:enable Metrics/MethodLength
944
1011
 
945
1012
  AUTOLINK_TEXT_FIELDS = %w[return
946
1013
  abstract
947
1014
  unavailable_message
948
1015
  deprecation_message].freeze
949
1016
 
1017
+ def self.autolink_text_fields(doc, root_decls)
1018
+ AUTOLINK_TEXT_FIELDS.each do |field|
1019
+ if text = doc.send(field)
1020
+ doc.send(field + '=', autolink_text(text, doc, root_decls))
1021
+ end
1022
+ end
1023
+
1024
+ (doc.parameters || []).each do |param|
1025
+ param[:discussion] =
1026
+ autolink_text(param[:discussion], doc, root_decls)
1027
+ end
1028
+ end
1029
+
950
1030
  AUTOLINK_HIGHLIGHT_FIELDS = %w[declaration
951
1031
  other_language_declaration].freeze
952
1032
 
1033
+ def self.autolink_highlight_fields(doc, root_decls)
1034
+ AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
1035
+ if text = doc.send(field)
1036
+ doc.send(field + '=',
1037
+ autolink_text(text, doc, root_decls, after_highlight: true))
1038
+ end
1039
+ end
1040
+ end
1041
+
953
1042
  def self.autolink(docs, root_decls)
954
1043
  @autolink_root_decls = root_decls
955
1044
  docs.each do |doc|
956
1045
  doc.children = autolink(doc.children, root_decls)
957
-
958
- AUTOLINK_TEXT_FIELDS.each do |field|
959
- if text = doc.send(field)
960
- doc.send(field + '=', autolink_text(text, doc, root_decls))
961
- end
962
- end
963
-
964
- AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
965
- if text = doc.send(field)
966
- doc.send(field + '=', autolink_text(text, doc, root_decls, true))
967
- end
968
- end
969
-
970
- (doc.parameters || []).each do |param|
971
- param[:discussion] =
972
- autolink_text(param[:discussion], doc, root_decls)
973
- end
1046
+ autolink_text_fields(doc, root_decls)
1047
+ autolink_highlight_fields(doc, root_decls)
974
1048
  end
975
1049
  end
976
1050
 
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
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jazzy
2
4
  module SymbolGraph
3
5
  # Constraint is a tidied-up JSON object, used by both Symbol and
@@ -27,6 +29,7 @@ module Jazzy
27
29
  def self.new_hash(hash)
28
30
  kind = KIND_MAP[hash[:kind]]
29
31
  raise "Unknown constraint kind '#{kind}'" unless kind
32
+
30
33
  lhs = hash[:lhs].sub(/^Self\./, '')
31
34
  rhs = hash[:rhs].sub(/^Self\./, '')
32
35
  new(kind, lhs, rhs)
@@ -62,6 +65,7 @@ module Jazzy
62
65
  path_components.include?(hash[:rhs])
63
66
  next nil
64
67
  end
68
+
65
69
  Constraint.new_hash(hash)
66
70
  end.compact
67
71
  end
@@ -89,6 +93,6 @@ end
89
93
 
90
94
  class Array
91
95
  def to_where_clause
92
- empty? ? '' : ' where ' + map(&:to_swift).join(', ')
96
+ empty? ? '' : " where #{map(&:to_swift).join(', ')}"
93
97
  end
94
98
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jazzy
2
4
  module SymbolGraph
3
5
  # For extensions we need to track constraints of the extended type
@@ -68,7 +70,7 @@ module Jazzy
68
70
  def full_declaration
69
71
  decl = "extension #{name}"
70
72
  unless conformances.empty?
71
- decl += ' : ' + conformances.join(', ')
73
+ decl += " : #{conformances.join(', ')}"
72
74
  end
73
75
  decl + all_constraints.ext.to_where_clause
74
76
  end