jazzy 0.14.0 → 0.14.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Tests.yml +5 -5
  3. data/.rubocop.yml +62 -2
  4. data/CHANGELOG.md +75 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile.lock +76 -66
  7. data/ObjectiveC.md +208 -0
  8. data/README.md +42 -24
  9. data/Rakefile +5 -3
  10. data/bin/sourcekitten +0 -0
  11. data/jazzy.gemspec +2 -1
  12. data/js/package-lock.json +61 -12
  13. data/lib/jazzy/config.rb +54 -35
  14. data/lib/jazzy/doc_builder.rb +9 -2
  15. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.ttf +0 -0
  16. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff +0 -0
  17. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  18. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  19. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  20. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  21. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  22. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  23. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  24. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  25. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  26. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  27. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  28. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  29. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  30. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.ttf +0 -0
  31. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff +0 -0
  32. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Bold.woff2 +0 -0
  33. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  34. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  35. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  36. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.ttf +0 -0
  37. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff +0 -0
  38. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Italic.woff2 +0 -0
  39. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.ttf +0 -0
  40. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff +0 -0
  41. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Main-Regular.woff2 +0 -0
  42. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  43. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  44. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  45. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.ttf +0 -0
  46. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff +0 -0
  47. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Math-Italic.woff2 +0 -0
  48. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  49. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  50. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  51. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  52. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  53. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  54. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  55. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  56. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  57. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.ttf +0 -0
  58. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff +0 -0
  59. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Script-Regular.woff2 +0 -0
  60. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.ttf +0 -0
  61. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff +0 -0
  62. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  63. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.ttf +0 -0
  64. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff +0 -0
  65. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  66. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.ttf +0 -0
  67. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff +0 -0
  68. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  69. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.ttf +0 -0
  70. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff +0 -0
  71. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  72. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  73. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  74. data/lib/jazzy/extensions/katex/css/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  75. data/lib/jazzy/extensions/katex/css/katex.min.css +1 -1
  76. data/lib/jazzy/extensions/katex/js/katex.min.js +1 -1
  77. data/lib/jazzy/gem_version.rb +1 -1
  78. data/lib/jazzy/jazzy_markdown.rb +13 -3
  79. data/lib/jazzy/search_builder.rb +3 -5
  80. data/lib/jazzy/source_declaration/type.rb +26 -2
  81. data/lib/jazzy/source_declaration.rb +13 -2
  82. data/lib/jazzy/sourcekitten.rb +58 -10
  83. data/lib/jazzy/stats.rb +11 -2
  84. data/lib/jazzy/symbol_graph/graph.rb +7 -2
  85. data/lib/jazzy/symbol_graph/relationship.rb +6 -0
  86. data/lib/jazzy/symbol_graph/sym_node.rb +14 -1
  87. data/lib/jazzy/symbol_graph/symbol.rb +36 -10
  88. data/lib/jazzy/symbol_graph.rb +30 -19
  89. data/lib/jazzy/themes/apple/assets/js/jquery.min.js +2 -2
  90. data/lib/jazzy/themes/apple/templates/doc.mustache +1 -2
  91. data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
  92. data/lib/jazzy/themes/apple/templates/header.mustache +4 -4
  93. data/lib/jazzy/themes/apple/templates/task.mustache +3 -8
  94. data/lib/jazzy/themes/fullwidth/assets/js/jquery.min.js +2 -2
  95. data/lib/jazzy/themes/fullwidth/templates/doc.mustache +1 -2
  96. data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
  97. data/lib/jazzy/themes/fullwidth/templates/header.mustache +4 -4
  98. data/lib/jazzy/themes/fullwidth/templates/task.mustache +3 -8
  99. data/lib/jazzy/themes/jony/assets/js/jquery.min.js +2 -2
  100. data/lib/jazzy/themes/jony/templates/doc.mustache +1 -2
  101. data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
  102. data/lib/jazzy/themes/jony/templates/header.mustache +2 -2
  103. data/lib/jazzy/themes/jony/templates/task.mustache +3 -8
  104. data/spec/integration_spec.rb +18 -13
  105. metadata +8 -6
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jazzy
4
- VERSION = '0.14.0' unless defined? Jazzy::VERSION
4
+ VERSION = '0.14.3' unless defined? Jazzy::VERSION
5
5
  end
@@ -40,7 +40,8 @@ module Jazzy
40
40
  mapped = map_footnote(num)
41
41
  "\n<li><div class='footnote-def' id=\"fn#{mapped}\">" +
42
42
  text.sub(%r{(?=</p>)},
43
- "&nbsp;<a href=\"#fnref#{mapped}\">&#8617;</a></div></li>")
43
+ "&nbsp;<a href=\"#fnref#{mapped}\">&#8617;</a>") +
44
+ '</div></li>'
44
45
  end
45
46
  end
46
47
 
@@ -66,7 +67,9 @@ module Jazzy
66
67
  def codespan(text)
67
68
  case text
68
69
  when /^\$\$(.*)\$\$$/m
69
- o = ["<div class='math m-block'>", Regexp.last_match[1], '</div>']
70
+ o = ["</p><div class='math m-block'>",
71
+ Regexp.last_match[1],
72
+ '</div><p>']
70
73
  Markdown.has_math = true
71
74
  when /^\$(.*)\$$/m
72
75
  o = ["<span class='math m-inline'>", Regexp.last_match[1], '</span>']
@@ -258,12 +261,19 @@ module Jazzy
258
261
  @markdown ||= Redcarpet::Markdown.new(renderer, REDCARPET_OPTIONS)
259
262
  end
260
263
 
264
+ # Produces <p>-delimited block content
261
265
  def self.render(markdown_text, default_language = nil)
262
266
  renderer.reset
263
267
  renderer.default_language = default_language
264
268
  markdown.render(markdown_text)
265
269
  end
266
270
 
271
+ # Produces <span>-delimited inline content
272
+ def self.render_inline(markdown_text, default_language = nil)
273
+ render(markdown_text, default_language)
274
+ .sub(%r{^<p>(.*)</p>$}, '<span>\1</span>')
275
+ end
276
+
267
277
  def self.rendered_returns
268
278
  renderer.returns
269
279
  end
@@ -275,7 +285,7 @@ module Jazzy
275
285
  class JazzyCopyright < Redcarpet::Render::HTML
276
286
  def link(link, _title, content)
277
287
  %(<a class="link" href="#{link}" target="_blank" \
278
- rel="noopener" rel="external">#{content}</a>)
288
+ rel="external noopener">#{content}</a>)
279
289
  end
280
290
  end
281
291
 
@@ -6,17 +6,15 @@ module Jazzy
6
6
  decls = source_module.all_declarations.select do |d|
7
7
  d.type && d.name && !d.name.empty?
8
8
  end
9
- index = decls.map do |d|
9
+ index = decls.to_h do |d|
10
10
  [d.url,
11
11
  {
12
12
  name: d.name,
13
13
  abstract: d.abstract && d.abstract.split(/\n/).map(&:strip).first,
14
- parent_name: d.parent_in_code && d.parent_in_code.name,
14
+ parent_name: d.parent_in_code&.name,
15
15
  }.reject { |_, v| v.nil? || v.empty? }]
16
- end.to_h
17
- File.open(File.join(output_dir, 'search.json'), 'w') do |f|
18
- f.write(index.to_json)
19
16
  end
17
+ File.write(File.join(output_dir, 'search.json'), index.to_json)
20
18
  end
21
19
  end
22
20
  end
@@ -12,11 +12,24 @@ module Jazzy
12
12
 
13
13
  attr_reader :kind
14
14
 
15
- def initialize(kind)
15
+ def initialize(kind, declaration = nil)
16
+ kind = fixup_kind(kind, declaration) if declaration
16
17
  @kind = kind
17
18
  @type = TYPES[kind]
18
19
  end
19
20
 
21
+ # Improve kind from full declaration
22
+ def fixup_kind(kind, declaration)
23
+ if kind == 'source.lang.swift.decl.class' &&
24
+ declaration.include?(
25
+ '<syntaxtype.keyword>actor</syntaxtype.keyword>',
26
+ )
27
+ 'source.lang.swift.decl.actor'
28
+ else
29
+ kind
30
+ end
31
+ end
32
+
20
33
  def dash_type
21
34
  @type && @type[:dash]
22
35
  end
@@ -115,7 +128,8 @@ module Jazzy
115
128
  end
116
129
 
117
130
  def swift_extensible?
118
- kind =~ /^source\.lang\.swift\.decl\.(class|struct|protocol|enum)$/
131
+ kind =~
132
+ /^source\.lang\.swift\.decl\.(class|struct|protocol|enum|actor)$/
119
133
  end
120
134
 
121
135
  def swift_protocol?
@@ -255,6 +269,11 @@ module Jazzy
255
269
  dash: 'Struct',
256
270
  global: true,
257
271
  }.freeze,
272
+ 'sourcekitten.source.lang.objc.decl.union' => {
273
+ jazzy: 'Union',
274
+ dash: 'Union',
275
+ global: true,
276
+ }.freeze,
258
277
  'sourcekitten.source.lang.objc.decl.field' => {
259
278
  jazzy: 'Field',
260
279
  dash: 'Field',
@@ -269,6 +288,11 @@ module Jazzy
269
288
  }.freeze,
270
289
 
271
290
  # Swift
291
+ 'source.lang.swift.decl.actor' => {
292
+ jazzy: 'Actor',
293
+ dash: 'Actor',
294
+ global: true,
295
+ }.freeze,
272
296
  'source.lang.swift.decl.function.accessor.address' => {
273
297
  jazzy: 'Addressor',
274
298
  dash: 'Function',
@@ -138,6 +138,7 @@ module Jazzy
138
138
  attr_accessor :unavailable_message
139
139
  attr_accessor :generic_requirements
140
140
  attr_accessor :inherited_types
141
+ attr_accessor :async
141
142
 
142
143
  def usage_discouraged?
143
144
  unavailable || deprecated
@@ -182,10 +183,20 @@ module Jazzy
182
183
  inherited_types.any? { |t| !unwanted.include?(t) }
183
184
  end
184
185
 
185
- # SourceKit only sets modulename for imported modules
186
+ # Pre-Swift 5.6: SourceKit only sets modulename for imported modules
187
+ # Swift 5.6+: modulename is always set
186
188
  def type_from_doc_module?
187
189
  !type.extension? ||
188
- (swift? && usr && modulename.nil?)
190
+ (swift? && usr &&
191
+ (modulename.nil? || modulename == Config.instance.module_name))
192
+ end
193
+
194
+ # Info text for contents page by collapsed item name
195
+ def declaration_note
196
+ notes = [default_impl_abstract ? 'default implementation' : nil,
197
+ from_protocol_extension ? 'extension method' : nil,
198
+ async ? 'asynchronous' : nil].compact
199
+ notes.join(', ').humanize unless notes.empty?
189
200
  end
190
201
 
191
202
  def alternative_abstract
@@ -277,12 +277,18 @@ module Jazzy
277
277
  declaration.children = []
278
278
  end
279
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
+
280
286
  def self.availability_attribute?(doc)
281
- return false unless doc['key.attributes']
287
+ attribute?(doc, 'available')
288
+ end
282
289
 
283
- !doc['key.attributes'].select do |attribute|
284
- attribute.values.first == 'source.decl.attribute.available'
285
- end.empty?
290
+ def self.spi_attribute?(doc)
291
+ attribute?(doc, '_spi')
286
292
  end
287
293
 
288
294
  def self.should_document?(doc)
@@ -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
@@ -315,6 +342,8 @@ module Jazzy
315
342
  acl_ok
316
343
  end
317
344
 
345
+ # Document extensions if they add protocol conformances, or have any
346
+ # member that needs to be documented.
318
347
  def self.should_document_swift_extension?(doc)
319
348
  doc['key.inheritedtypes'] ||
320
349
  Array(doc['key.substructure']).any? do |subdoc|
@@ -326,7 +355,9 @@ module Jazzy
326
355
  # Call things undocumented if they were compiled properly
327
356
  # and came from our module.
328
357
  def self.should_mark_undocumented(declaration)
329
- declaration.usr && !declaration.modulename
358
+ declaration.usr &&
359
+ (declaration.modulename.nil? ||
360
+ declaration.modulename == Config.instance.module_name)
330
361
  end
331
362
 
332
363
  def self.process_undocumented_token(doc, declaration)
@@ -495,6 +526,14 @@ module Jazzy
495
526
  .join("\n")
496
527
  end
497
528
 
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
535
+ end
536
+
498
537
  # Strip default property attributes because libclang
499
538
  # adds them all, even if absent in the original source code.
500
539
  DEFAULT_ATTRIBUTES = %w[atomic readwrite assign unsafe_unretained].freeze
@@ -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']
@@ -581,6 +622,11 @@ 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)
586
632
 
@@ -790,7 +836,9 @@ module Jazzy
790
836
  extensions.each do |ext|
791
837
  ext.children = ext.children.select do |ext_member|
792
838
  proto_member = protocol.children.find do |p|
793
- 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
794
842
  end
795
843
 
796
844
  # Extension-only method, keep.
data/lib/jazzy/stats.rb CHANGED
@@ -5,7 +5,7 @@ module Jazzy
5
5
  class Stats
6
6
  include Config::Mixin
7
7
 
8
- attr_reader :documented, :acl_skipped, :undocumented_decls
8
+ attr_reader :documented, :acl_skipped, :spi_skipped, :undocumented_decls
9
9
 
10
10
  def add_documented
11
11
  @documented += 1
@@ -15,6 +15,10 @@ module Jazzy
15
15
  @acl_skipped += 1
16
16
  end
17
17
 
18
+ def add_spi_skipped
19
+ @spi_skipped += 1
20
+ end
21
+
18
22
  def add_undocumented(decl)
19
23
  @undocumented_decls << decl
20
24
  end
@@ -32,7 +36,7 @@ module Jazzy
32
36
  end
33
37
 
34
38
  def initialize
35
- @documented = @acl_skipped = 0
39
+ @documented = @acl_skipped = @spi_skipped = 0
36
40
  @undocumented_decls = []
37
41
  end
38
42
 
@@ -54,6 +58,11 @@ module Jazzy
54
58
  "#{symbol_or_symbols(acl_skipped)} " \
55
59
  '(use `--min-acl` to specify a different minimum ACL)'
56
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
57
66
  end
58
67
 
59
68
  def doc_coverage
@@ -78,8 +78,13 @@ module Jazzy
78
78
 
79
79
  # Protocol conformance is redundant if it's unconditional
80
80
  # and already expressed in the type's declaration.
81
+ #
82
+ # Skip implementation-detail conformances.
81
83
  def redundant_conformance?(rel, type, protocol)
82
- type && rel.constraints.empty? && type.conformance?(protocol)
84
+ return false unless type
85
+
86
+ (rel.constraints.empty? && type.conformance?(protocol)) ||
87
+ (type.actor? && rel.actor_protocol?)
83
88
  end
84
89
 
85
90
  # source is a member/protocol requirement of target
@@ -123,7 +128,7 @@ module Jazzy
123
128
  (target_parent = target.parent) &&
124
129
  target_parent.is_a?(SymNode)
125
130
  # Could probably figure this out with demangle, but...
126
- warn "Can't resolve membership of default implementation "\
131
+ warn "Can't resolve membership of default implementation " \
127
132
  "#{source.symbol.usr}."
128
133
  source.unlisted = true
129
134
  return
@@ -22,6 +22,12 @@ module Jazzy
22
22
  kind == :defaultImplementationOf
23
23
  end
24
24
 
25
+ # Protocol conformances added by compiler to actor decls that
26
+ # users aren't interested in.
27
+ def actor_protocol?
28
+ %w[Actor Sendable].include?(target_fallback)
29
+ end
30
+
25
31
  def initialize(hash)
26
32
  kind = hash[:kind]
27
33
  unless KINDS.include?(kind)
@@ -25,6 +25,7 @@ module Jazzy
25
25
 
26
26
  # A SymNode is a node of the reconstructed syntax tree holding a symbol.
27
27
  # It can turn itself into SourceKit and helps decode extensions.
28
+ # rubocop:disable Metrics/ClassLength
28
29
  class SymNode < BaseNode
29
30
  attr_accessor :symbol
30
31
  attr_writer :override
@@ -61,6 +62,10 @@ module Jazzy
61
62
  symbol.kind.end_with?('protocol')
62
63
  end
63
64
 
65
+ def actor?
66
+ symbol.kind.end_with?('actor')
67
+ end
68
+
64
69
  def constraints
65
70
  symbol.constraints
66
71
  end
@@ -112,8 +117,13 @@ module Jazzy
112
117
  " : #{superclass_name}"
113
118
  end
114
119
 
120
+ # approximately...
121
+ def async?
122
+ symbol.declaration =~ /\basync\b[^)]*$/
123
+ end
124
+
115
125
  def full_declaration
116
- symbol.availability
126
+ symbol.attributes
117
127
  .append(symbol.declaration + inherits_clause + where_clause)
118
128
  .join("\n")
119
129
  end
@@ -130,6 +140,7 @@ module Jazzy
130
140
  'key.accessibility' => symbol.acl,
131
141
  'key.parsed_decl' => declaration,
132
142
  'key.annotated_decl' => xml_declaration,
143
+ 'key.symgraph_async' => async?,
133
144
  }
134
145
  if docs = symbol.doc_comments
135
146
  hash['key.doc.comment'] = docs
@@ -147,6 +158,7 @@ module Jazzy
147
158
  unless children.empty?
148
159
  hash['key.substructure'] = children_to_sourcekit
149
160
  end
161
+ hash['key.symgraph_spi'] = true if symbol.spi
150
162
 
151
163
  hash
152
164
  end
@@ -159,5 +171,6 @@ module Jazzy
159
171
  symbol <=> other.symbol
160
172
  end
161
173
  end
174
+ # rubocop:enable Metrics/ClassLength
162
175
  end
163
176
  end
@@ -10,10 +10,11 @@ module Jazzy
10
10
  attr_accessor :declaration
11
11
  attr_accessor :kind
12
12
  attr_accessor :acl
13
+ attr_accessor :spi
13
14
  attr_accessor :location # can be nil, keys :filename :line :character
14
15
  attr_accessor :constraints # array, can be empty
15
16
  attr_accessor :doc_comments # can be nil
16
- attr_accessor :availability # array, can be empty
17
+ attr_accessor :attributes # array, can be empty
17
18
  attr_accessor :generic_type_params # set, can be empty
18
19
  attr_accessor :parameter_names # array, can be nil
19
20
 
@@ -24,13 +25,14 @@ module Jazzy
24
25
  def initialize(hash)
25
26
  self.usr = hash[:identifier][:precise]
26
27
  self.path_components = hash[:pathComponents]
27
- raw_decl = hash[:declarationFragments].map { |f| f[:spelling] }.join
28
- init_kind(hash[:kind][:identifier])
28
+ raw_decl, keywords = parse_decl_fragments(hash[:declarationFragments])
29
+ init_kind(hash[:kind][:identifier], keywords)
29
30
  init_declaration(raw_decl)
30
31
  if func_signature = hash[:functionSignature]
31
32
  init_func_signature(func_signature)
32
33
  end
33
34
  init_acl(hash[:accessLevel])
35
+ self.spi = hash[:spi]
34
36
  if location = hash[:location]
35
37
  init_location(location)
36
38
  end
@@ -38,10 +40,20 @@ module Jazzy
38
40
  if comments_hash = hash[:docComment]
39
41
  init_doc_comments(comments_hash)
40
42
  end
41
- init_availability(hash[:availability] || [])
43
+ init_attributes(hash[:availability] || [])
42
44
  init_generic_type_params(hash)
43
45
  end
44
46
 
47
+ def parse_decl_fragments(fragments)
48
+ decl = ''
49
+ keywords = Set.new
50
+ fragments.each do |frag|
51
+ decl += frag[:spelling]
52
+ keywords.add(frag[:spelling]) if frag[:kind] == 'keyword'
53
+ end
54
+ [decl, keywords]
55
+ end
56
+
45
57
  # Repair problems with SymbolGraph's declprinter
46
58
 
47
59
  def init_declaration(raw_decl)
@@ -87,17 +99,22 @@ module Jazzy
87
99
  'static.subscript' => 'function.subscript',
88
100
  'typealias' => 'typealias',
89
101
  'associatedtype' => 'associatedtype',
102
+ 'actor' => 'actor',
90
103
  }.freeze
91
104
 
92
105
  # We treat 'static var' differently to 'class var'
93
- def adjust_kind_for_declaration(kind)
94
- return kind unless declaration =~ /\bstatic\b/
106
+ # We treat actors as first-class entities
107
+ def adjust_kind_for_declaration(kind, keywords)
108
+ if kind == 'swift.class' && keywords.member?('actor')
109
+ return 'swift.actor'
110
+ end
111
+ return kind unless keywords.member?('static')
95
112
 
96
113
  kind.gsub(/type/, 'static')
97
114
  end
98
115
 
99
- def init_kind(kind)
100
- adjusted = adjust_kind_for_declaration(kind)
116
+ def init_kind(kind, keywords)
117
+ adjusted = adjust_kind_for_declaration(kind, keywords)
101
118
  sourcekit_kind = KIND_MAP[adjusted.sub('swift.', '')]
102
119
  raise "Unknown symbol kind '#{kind}'" unless sourcekit_kind
103
120
 
@@ -159,8 +176,8 @@ module Jazzy
159
176
  # Availability
160
177
  # Re-encode this as Swift. Should really teach Jazzy about these,
161
178
  # could maybe then do something smarter here.
162
- def init_availability(avail_hash_list)
163
- self.availability = avail_hash_list.map do |avail|
179
+ def availability_attributes(avail_hash_list)
180
+ avail_hash_list.map do |avail|
164
181
  str = '@available('
165
182
  if avail[:isUnconditionallyDeprecated]
166
183
  str += '*, deprecated'
@@ -190,6 +207,15 @@ module Jazzy
190
207
  str
191
208
  end
192
209
 
210
+ def spi_attributes
211
+ spi ? ['@_spi(Unknown)'] : []
212
+ end
213
+
214
+ def init_attributes(avail_hash_list)
215
+ self.attributes =
216
+ availability_attributes(avail_hash_list) + spi_attributes
217
+ end
218
+
193
219
  # Sort order
194
220
  include Comparable
195
221
 
@@ -14,27 +14,24 @@ require 'jazzy/symbol_graph/ext_node'
14
14
 
15
15
  module Jazzy
16
16
  module SymbolGraph
17
- # Run `swift symbolgraph-extract` with configured args,
18
- # parse the results, and return as JSON in SourceKit[ten]
17
+ # Find swift symbol graph files, either having been passed
18
+ # in directly, or generated by running`swift symbolgraph-extract`
19
+ # with configured args.
20
+ # Then parse the results, and return as JSON in SourceKit[ten]
19
21
  # format.
20
22
  def self.build(config)
21
- Dir.mktmpdir do |tmp_dir|
22
- args = arguments(config, tmp_dir)
23
+ if config.symbolgraph_directory.nil?
24
+ Dir.mktmpdir do |tmp_dir|
25
+ args = arguments(config, tmp_dir)
23
26
 
24
- Executable.execute_command('swift',
25
- args.unshift('symbolgraph-extract'),
26
- true) # raise on error
27
+ Executable.execute_command('swift',
28
+ args.unshift('symbolgraph-extract'),
29
+ true) # raise on error
27
30
 
28
- Dir[tmp_dir + '/*.symbols.json'].map do |filename|
29
- # The @ part is for extensions in our module (before the @)
30
- # of types in another module (after the @).
31
- filename =~ /(.*?)(@(.*?))?\.symbols/
32
- module_name = Regexp.last_match[3] || Regexp.last_match[1]
33
- {
34
- filename =>
35
- Graph.new(File.read(filename), module_name).to_sourcekit,
36
- }
37
- end.to_json
31
+ parse_symbols(tmp_dir)
32
+ end
33
+ else
34
+ parse_symbols(config.symbolgraph_directory.to_s)
38
35
  end
39
36
  end
40
37
 
@@ -47,8 +44,8 @@ module Jazzy
47
44
  user_args = config.build_tool_arguments.join
48
45
 
49
46
  if user_args =~ /-(?:module-name|minimum-access-level|output-dir)/
50
- raise 'error: `--build-tool-arguments` for '\
51
- "`--swift-build-tool symbolgraph` can't use `-module`, "\
47
+ raise 'error: `--build-tool-arguments` for ' \
48
+ "`--swift-build-tool symbolgraph` can't use `-module`, " \
52
49
  '`-minimum-access-level`, or `-output-dir`.'
53
50
  end
54
51
 
@@ -69,6 +66,20 @@ module Jazzy
69
66
  args + config.build_tool_arguments
70
67
  end
71
68
 
69
+ # Parse the symbol files in the given directory
70
+ def self.parse_symbols(directory)
71
+ Dir[directory + '/*.symbols.json'].map do |filename|
72
+ # The @ part is for extensions in our module (before the @)
73
+ # of types in another module (after the @).
74
+ File.basename(filename) =~ /(.*?)(@(.*?))?\.symbols/
75
+ module_name = Regexp.last_match[3] || Regexp.last_match[1]
76
+ {
77
+ filename =>
78
+ Graph.new(File.read(filename), module_name).to_sourcekit,
79
+ }
80
+ end.to_json
81
+ end
82
+
72
83
  # Get the SDK path. On !darwin this just isn't needed.
73
84
  def self.sdk(config)
74
85
  `xcrun --show-sdk-path --sdk #{config.sdk}`.chomp