From a7cc3fc663fa5dbd4c1f4451de542a3cf2ca64be Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Wed, 16 Jun 2021 13:31:30 +0100 Subject: [PATCH 01/11] Identify actors --- lib/jazzy/source_declaration/type.rb | 23 +++++++++++++++++++++-- lib/jazzy/sourcekitten.rb | 4 +++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/jazzy/source_declaration/type.rb b/lib/jazzy/source_declaration/type.rb index 076071dde..97ec49a79 100644 --- a/lib/jazzy/source_declaration/type.rb +++ b/lib/jazzy/source_declaration/type.rb @@ -12,11 +12,24 @@ def self.all attr_reader :kind - def initialize(kind) + def initialize(kind, declaration = nil) + kind = fixup_kind(kind, declaration) if declaration @kind = kind @type = TYPES[kind] end + # Improve kind from full declaration + def fixup_kind(kind, declaration) + if kind == 'source.lang.swift.decl.class' && + declaration.include?( + 'actor', + ) + 'source.lang.swift.decl.actor' + else + kind + end + end + def dash_type @type && @type[:dash] end @@ -115,7 +128,8 @@ def swift_extension? end def swift_extensible? - kind =~ /^source\.lang\.swift\.decl\.(class|struct|protocol|enum)$/ + kind =~ + /^source\.lang\.swift\.decl\.(class|struct|protocol|enum|actor)$/ end def swift_protocol? @@ -274,6 +288,11 @@ def ==(other) }.freeze, # Swift + 'source.lang.swift.decl.actor' => { + jazzy: 'Actor', + dash: 'Class', # have asked Dash for something else + global: true, + }.freeze, 'source.lang.swift.decl.function.accessor.address' => { jazzy: 'Addressor', dash: 'Function', diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index fa6d037c8..ad3f9490e 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -565,7 +565,9 @@ def self.make_source_declarations(docs, parent = nil, mark = SourceMark.new) end declaration = SourceDeclaration.new declaration.parent_in_code = parent - declaration.type = SourceDeclaration::Type.new(doc['key.kind']) + declaration.type = + SourceDeclaration::Type.new(doc['key.kind'], + doc['key.fully_annotated_decl']) declaration.typename = doc['key.typename'] declaration.objc_name = doc['key.name'] documented_name = if Config.instance.hide_objc? && doc['key.swift_name'] From 1ec4f95e5caf9014ca2b1945344cfc0d5a01b09b Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Sat, 19 Jun 2021 11:20:52 +0100 Subject: [PATCH 02/11] Identify async methods and properties --- lib/jazzy/doc_builder.rb | 2 ++ lib/jazzy/source_declaration.rb | 9 +++++++++ lib/jazzy/sourcekitten.rb | 11 +++++++++++ lib/jazzy/themes/apple/templates/task.mustache | 11 +++-------- lib/jazzy/themes/fullwidth/templates/task.mustache | 11 +++-------- lib/jazzy/themes/jony/templates/task.mustache | 11 +++-------- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/jazzy/doc_builder.rb b/lib/jazzy/doc_builder.rb index 5ca4d12f6..8de6a5d8d 100644 --- a/lib/jazzy/doc_builder.rb +++ b/lib/jazzy/doc_builder.rb @@ -370,6 +370,8 @@ def self.render_item(item, source_module) deprecation_message: item.deprecation_message, unavailable_message: item.unavailable_message, usage_discouraged: item.usage_discouraged?, + async: item.async, + declaration_note: item.declaration_note, } end # rubocop:enable Metrics/MethodLength diff --git a/lib/jazzy/source_declaration.rb b/lib/jazzy/source_declaration.rb index 828056052..f31a6b6ee 100644 --- a/lib/jazzy/source_declaration.rb +++ b/lib/jazzy/source_declaration.rb @@ -138,6 +138,7 @@ def display_other_language_declaration attr_accessor :unavailable_message attr_accessor :generic_requirements attr_accessor :inherited_types + attr_accessor :async def usage_discouraged? unavailable || deprecated @@ -188,6 +189,14 @@ def type_from_doc_module? (swift? && usr && modulename.nil?) end + # Info text for contents page by collapsed item name + def declaration_note + notes = [default_impl_abstract ? 'default implementation' : nil, + from_protocol_extension ? 'extension method' : nil, + async ? 'asynchronous' : nil].compact + notes.join(', ').humanize unless notes.empty? + end + def alternative_abstract if file = alternative_abstract_file Pathname(file).read diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index ad3f9490e..6b7c66060 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -524,6 +524,14 @@ def self.make_swift_declaration(doc, declaration) .join("\n") end + # Exclude non-async routines that accept async closures + def self.swift_async?(fully_annotated_decl) + document = REXML::Document.new(fully_annotated_decl) + !document.elements['/*/syntaxtype.keyword[text()="async"]'].nil? + rescue + nil + end + # Strip default property attributes because libclang # adds them all, even if absent in the original source code. DEFAULT_ATTRIBUTES = %w[atomic readwrite assign unsafe_unretained].freeze @@ -612,6 +620,9 @@ def self.make_source_declarations(docs, parent = nil, mark = SourceMark.new) inherited_types = doc['key.inheritedtypes'] || [] declaration.inherited_types = inherited_types.map { |type| type['key.name'] }.compact + if xml_declaration = doc['key.fully_annotated_decl'] + declaration.async = swift_async?(xml_declaration) + end next unless make_doc_info(doc, declaration) diff --git a/lib/jazzy/themes/apple/templates/task.mustache b/lib/jazzy/themes/apple/templates/task.mustache index ea738c009..1353dafd5 100644 --- a/lib/jazzy/themes/apple/templates/task.mustache +++ b/lib/jazzy/themes/apple/templates/task.mustache @@ -28,16 +28,11 @@ {{{name_html}}} {{/usage_discouraged}} - {{#default_impl_abstract}} + {{#declaration_note}} - Default implementation + {{.}} - {{/default_impl_abstract}} - {{#from_protocol_extension}} - - Extension method - - {{/from_protocol_extension}} + {{/declaration_note}}
diff --git a/lib/jazzy/themes/fullwidth/templates/task.mustache b/lib/jazzy/themes/fullwidth/templates/task.mustache index 456d7576a..a33f74f62 100644 --- a/lib/jazzy/themes/fullwidth/templates/task.mustache +++ b/lib/jazzy/themes/fullwidth/templates/task.mustache @@ -28,16 +28,11 @@ {{{name_html}}} {{/usage_discouraged}} - {{#default_impl_abstract}} + {{#declaration_note}} - Default implementation + {{.}} - {{/default_impl_abstract}} - {{#from_protocol_extension}} - - Extension method - - {{/from_protocol_extension}} + {{/declaration_note}}
diff --git a/lib/jazzy/themes/jony/templates/task.mustache b/lib/jazzy/themes/jony/templates/task.mustache index ea738c009..1353dafd5 100644 --- a/lib/jazzy/themes/jony/templates/task.mustache +++ b/lib/jazzy/themes/jony/templates/task.mustache @@ -28,16 +28,11 @@ {{{name_html}}} {{/usage_discouraged}} - {{#default_impl_abstract}} + {{#declaration_note}} - Default implementation + {{.}} - {{/default_impl_abstract}} - {{#from_protocol_extension}} - - Extension method - - {{/from_protocol_extension}} + {{/declaration_note}}
From e09404b030a4809c44de8059df1165b33a0ea13b Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Sat, 19 Jun 2021 12:52:34 +0100 Subject: [PATCH 03/11] Syntax highlighter workaround for 5.5 --- lib/jazzy/highlighter.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/jazzy/highlighter.rb b/lib/jazzy/highlighter.rb index 32bcae732..ace679bff 100644 --- a/lib/jazzy/highlighter.rb +++ b/lib/jazzy/highlighter.rb @@ -2,6 +2,18 @@ require 'rouge' +# While Rouge is downlevel (Rouge PR#1715 unmerged) +module Rouge + module Lexers + class Swift + prepend :root do + rule(/\b(?:async|await)\b/, Keyword) + rule(/\b(?:actor|nonisolated)\b/, Keyword::Declaration) + end + end + end +end + module Jazzy # This module helps highlight code module Highlighter From d54a97d4d7171fcab084c66f01ccf714fe3be56d Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Thu, 24 Jun 2021 11:10:24 +0100 Subject: [PATCH 04/11] Update Dash type for actor --- lib/jazzy/source_declaration/type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jazzy/source_declaration/type.rb b/lib/jazzy/source_declaration/type.rb index 97ec49a79..26833ebc2 100644 --- a/lib/jazzy/source_declaration/type.rb +++ b/lib/jazzy/source_declaration/type.rb @@ -290,7 +290,7 @@ def ==(other) # Swift 'source.lang.swift.decl.actor' => { jazzy: 'Actor', - dash: 'Class', # have asked Dash for something else + dash: 'Actor', global: true, }.freeze, 'source.lang.swift.decl.function.accessor.address' => { From 1a9ee3f596966643785b9c1d8245b8a5a75a4256 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Thu, 24 Jun 2021 12:22:19 +0100 Subject: [PATCH 05/11] Identify actors during symbolgraph import --- lib/jazzy/symbol_graph/graph.rb | 7 ++++++- lib/jazzy/symbol_graph/relationship.rb | 6 ++++++ lib/jazzy/symbol_graph/sym_node.rb | 4 ++++ lib/jazzy/symbol_graph/symbol.rb | 28 +++++++++++++++++++------- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/jazzy/symbol_graph/graph.rb b/lib/jazzy/symbol_graph/graph.rb index db98490e6..a738fb9c2 100644 --- a/lib/jazzy/symbol_graph/graph.rb +++ b/lib/jazzy/symbol_graph/graph.rb @@ -78,8 +78,13 @@ def rel_source_name(rel, source_node) # Protocol conformance is redundant if it's unconditional # and already expressed in the type's declaration. + # + # Skip implementation-detail conformances. def redundant_conformance?(rel, type, protocol) - type && rel.constraints.empty? && type.conformance?(protocol) + return false unless type + + (rel.constraints.empty? && type.conformance?(protocol)) || + (type.actor? && rel.actor_protocol?) end # source is a member/protocol requirement of target diff --git a/lib/jazzy/symbol_graph/relationship.rb b/lib/jazzy/symbol_graph/relationship.rb index 97959d140..a32a11b80 100644 --- a/lib/jazzy/symbol_graph/relationship.rb +++ b/lib/jazzy/symbol_graph/relationship.rb @@ -22,6 +22,12 @@ def default_implementation? kind == :defaultImplementationOf end + # Protocol conformances added by compiler to actor decls that + # users aren't interested in. + def actor_protocol? + %w[Actor Sendable].include?(target_fallback) + end + def initialize(hash) kind = hash[:kind] unless KINDS.include?(kind) diff --git a/lib/jazzy/symbol_graph/sym_node.rb b/lib/jazzy/symbol_graph/sym_node.rb index 65200b6c7..6c166f326 100644 --- a/lib/jazzy/symbol_graph/sym_node.rb +++ b/lib/jazzy/symbol_graph/sym_node.rb @@ -61,6 +61,10 @@ def protocol? symbol.kind.end_with?('protocol') end + def actor? + symbol.kind.end_with?('actor') + end + def constraints symbol.constraints end diff --git a/lib/jazzy/symbol_graph/symbol.rb b/lib/jazzy/symbol_graph/symbol.rb index a2bb66c7d..0244bc67a 100644 --- a/lib/jazzy/symbol_graph/symbol.rb +++ b/lib/jazzy/symbol_graph/symbol.rb @@ -25,8 +25,8 @@ def name def initialize(hash) self.usr = hash[:identifier][:precise] self.path_components = hash[:pathComponents] - raw_decl = hash[:declarationFragments].map { |f| f[:spelling] }.join - init_kind(hash[:kind][:identifier]) + raw_decl, keywords = parse_decl_fragments(hash[:declarationFragments]) + init_kind(hash[:kind][:identifier], keywords) init_declaration(raw_decl) if func_signature = hash[:functionSignature] init_func_signature(func_signature) @@ -44,6 +44,16 @@ def initialize(hash) init_generic_type_params(hash) end + def parse_decl_fragments(fragments) + decl = '' + keywords = Set.new + fragments.each do |frag| + decl += frag[:spelling] + keywords.add(frag[:spelling]) if frag[:kind] == 'keyword' + end + [decl, keywords] + end + # Repair problems with SymbolGraph's declprinter def init_declaration(raw_decl) @@ -89,17 +99,21 @@ def init_func_signature(func_signature) 'static.subscript' => 'function.subscript', 'typealias' => 'typealias', 'associatedtype' => 'associatedtype', + 'actor' => 'actor', }.freeze # We treat 'static var' differently to 'class var' - def adjust_kind_for_declaration(kind) - return kind unless declaration =~ /\bstatic\b/ - + # We treat actors as first-class entities + def adjust_kind_for_declaration(kind, keywords) + if kind == 'swift.class' && keywords.member?('actor') + return 'swift.actor' + end + return kind unless keywords.member?('static') kind.gsub(/type/, 'static') end - def init_kind(kind) - adjusted = adjust_kind_for_declaration(kind) + def init_kind(kind, keywords) + adjusted = adjust_kind_for_declaration(kind, keywords) sourcekit_kind = KIND_MAP[adjusted.sub('swift.', '')] raise "Unknown symbol kind '#{kind}'" unless sourcekit_kind From 4e40943f1a5487acff0b0a605d7bad22a0459bfd Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Mon, 28 Jun 2021 11:43:13 +0100 Subject: [PATCH 06/11] Identify async methods in symgraph import --- lib/jazzy/sourcekitten.rb | 8 +++++--- lib/jazzy/symbol_graph/sym_node.rb | 8 ++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index 6b7c66060..8f865f206 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -620,9 +620,11 @@ def self.make_source_declarations(docs, parent = nil, mark = SourceMark.new) inherited_types = doc['key.inheritedtypes'] || [] declaration.inherited_types = inherited_types.map { |type| type['key.name'] }.compact - if xml_declaration = doc['key.fully_annotated_decl'] - declaration.async = swift_async?(xml_declaration) - end + declaration.async = + doc['key.async'] || + if xml_declaration = doc['key.fully_annotated_decl'] + swift_async?(xml_declaration) + end next unless make_doc_info(doc, declaration) diff --git a/lib/jazzy/symbol_graph/sym_node.rb b/lib/jazzy/symbol_graph/sym_node.rb index 6c166f326..491744977 100644 --- a/lib/jazzy/symbol_graph/sym_node.rb +++ b/lib/jazzy/symbol_graph/sym_node.rb @@ -25,6 +25,7 @@ def children_to_sourcekit # A SymNode is a node of the reconstructed syntax tree holding a symbol. # It can turn itself into SourceKit and helps decode extensions. + # rubocop:disable Metrics/ClassLength class SymNode < BaseNode attr_accessor :symbol attr_writer :override @@ -116,6 +117,11 @@ def inherits_clause " : #{superclass_name}" end + # approximately... + def async? + symbol.declaration =~ /async[^\)]*$/ + end + def full_declaration symbol.attributes .append(symbol.declaration + inherits_clause + where_clause) @@ -134,6 +140,7 @@ def to_sourcekit 'key.accessibility' => symbol.acl, 'key.parsed_decl' => declaration, 'key.annotated_decl' => xml_declaration, + 'key.async' => async?, } if docs = symbol.doc_comments hash['key.doc.comment'] = docs @@ -164,5 +171,6 @@ def <=>(other) symbol <=> other.symbol end end + # rubocop:enable Metrics/ClassLength end end From 7b21eb42c041ca45908798bddbb053ce93dcb5c1 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Fri, 10 Sep 2021 10:52:44 +0100 Subject: [PATCH 07/11] Add `isolated` to syntax highlighting --- lib/jazzy/highlighter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jazzy/highlighter.rb b/lib/jazzy/highlighter.rb index ace679bff..e01cc30b2 100644 --- a/lib/jazzy/highlighter.rb +++ b/lib/jazzy/highlighter.rb @@ -7,7 +7,7 @@ module Rouge module Lexers class Swift prepend :root do - rule(/\b(?:async|await)\b/, Keyword) + rule(/\b(?:async|await|isolated)\b/, Keyword) rule(/\b(?:actor|nonisolated)\b/, Keyword::Declaration) end end From 041e347cc1df8746910550267c8aeb71f2f0d436 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Fri, 17 Sep 2021 12:10:15 +0100 Subject: [PATCH 08/11] Rebase --- CHANGELOG.md | 4 ++++ spec/integration_specs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edee4e26b..036ffb475 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ [Brian Osborn](https://github.com/bosborn) [John Fairhurst](https://github.com/johnfairh) +* Support Swift concurrency features: identify actors and asynchronous + methods. + [John Fairhurst](https://github.com/johnfairh) + ##### Bug Fixes * None. diff --git a/spec/integration_specs b/spec/integration_specs index 55663efa5..c28a0b31e 160000 --- a/spec/integration_specs +++ b/spec/integration_specs @@ -1 +1 @@ -Subproject commit 55663efa5dad0f3526c4b1cfa12a073c8c4704e1 +Subproject commit c28a0b31eb89969f59ea25f466f0c3cac7435d19 From 6dfb57277c7d48304b4829841dccf8a0cba6d441 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Fri, 17 Sep 2021 16:02:18 +0100 Subject: [PATCH 09/11] Rubocop tweaks --- lib/jazzy/sourcekitten.rb | 2 +- lib/jazzy/symbol_graph/sym_node.rb | 2 +- lib/jazzy/symbol_graph/symbol.rb | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index 8f865f206..d7c857655 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -528,7 +528,7 @@ def self.make_swift_declaration(doc, declaration) def self.swift_async?(fully_annotated_decl) document = REXML::Document.new(fully_annotated_decl) !document.elements['/*/syntaxtype.keyword[text()="async"]'].nil? - rescue + rescue StandardError nil end diff --git a/lib/jazzy/symbol_graph/sym_node.rb b/lib/jazzy/symbol_graph/sym_node.rb index 491744977..7272b7431 100644 --- a/lib/jazzy/symbol_graph/sym_node.rb +++ b/lib/jazzy/symbol_graph/sym_node.rb @@ -119,7 +119,7 @@ def inherits_clause # approximately... def async? - symbol.declaration =~ /async[^\)]*$/ + symbol.declaration =~ /\basync\b[^)]*$/ end def full_declaration diff --git a/lib/jazzy/symbol_graph/symbol.rb b/lib/jazzy/symbol_graph/symbol.rb index 0244bc67a..7429934c6 100644 --- a/lib/jazzy/symbol_graph/symbol.rb +++ b/lib/jazzy/symbol_graph/symbol.rb @@ -109,6 +109,7 @@ def adjust_kind_for_declaration(kind, keywords) return 'swift.actor' end return kind unless keywords.member?('static') + kind.gsub(/type/, 'static') end From 806a5fddb8f68a83e4d1875ac04e2f6e9044492b Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Sat, 18 Sep 2021 12:13:30 +0100 Subject: [PATCH 10/11] Tests and fixes --- lib/jazzy/sourcekitten.rb | 6 ++++-- lib/jazzy/symbol_graph/sym_node.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index d7c857655..8cbc45eda 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -621,7 +621,7 @@ def self.make_source_declarations(docs, parent = nil, mark = SourceMark.new) declaration.inherited_types = inherited_types.map { |type| type['key.name'] }.compact declaration.async = - doc['key.async'] || + doc['key.symgraph_async'] || if xml_declaration = doc['key.fully_annotated_decl'] swift_async?(xml_declaration) end @@ -834,7 +834,9 @@ def self.mark_and_merge_protocol_extensions(protocol, extensions) extensions.each do |ext| ext.children = ext.children.select do |ext_member| proto_member = protocol.children.find do |p| - p.name == ext_member.name && p.type == ext_member.type + p.name == ext_member.name && + p.type == ext_member.type && + p.async == ext_member.async end # Extension-only method, keep. diff --git a/lib/jazzy/symbol_graph/sym_node.rb b/lib/jazzy/symbol_graph/sym_node.rb index 7272b7431..4b307ca96 100644 --- a/lib/jazzy/symbol_graph/sym_node.rb +++ b/lib/jazzy/symbol_graph/sym_node.rb @@ -140,7 +140,7 @@ def to_sourcekit 'key.accessibility' => symbol.acl, 'key.parsed_decl' => declaration, 'key.annotated_decl' => xml_declaration, - 'key.async' => async?, + 'key.symgraph_async' => async?, } if docs = symbol.doc_comments hash['key.doc.comment'] = docs From 68d0e91478b529aeff89f3ceeaaf8fe62bd20084 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Mon, 27 Sep 2021 11:04:16 +0100 Subject: [PATCH 11/11] Fix ups --- CONTRIBUTING.md | 2 +- lib/jazzy/highlighter.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79ebdef2b..fb3158a26 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,7 +49,7 @@ git push You'll need push access to the integration specs repo to do this. You can request access from one of the maintainers when filing your PR. -You must have Xcode 12.5 installed to build the integration specs. +You must have Xcode 13 installed to build the integration specs. ## Making changes to SourceKitten diff --git a/lib/jazzy/highlighter.rb b/lib/jazzy/highlighter.rb index e01cc30b2..2320d98bf 100644 --- a/lib/jazzy/highlighter.rb +++ b/lib/jazzy/highlighter.rb @@ -2,7 +2,7 @@ require 'rouge' -# While Rouge is downlevel (Rouge PR#1715 unmerged) +# While Rouge is downlevel (Rouge PR#1715 unreleased) module Rouge module Lexers class Swift