Skip to content

Commit

Permalink
Added infrastructure for supporting writing documentation comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle committed Oct 30, 2023
1 parent b4f26d6 commit a2f322b
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 80 deletions.
31 changes: 31 additions & 0 deletions Generator/Sources/CodeWriters/Swift/SwiftDocComment.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
public struct SwiftDocComment {
public var summary: [Block]?
public var parameters = [Param]()
public var returns: [Span]?

public init() {}

public enum Block: Hashable {
case paragraph([Span])
case code(String)
case list([Span])

public static func paragraph(_ span: Span) -> Block { .paragraph([span]) }
public static func paragraph(_ text: String) -> Block { .paragraph(.text(text)) }
}

public enum Span: Hashable {
case text(String)
case code(String)
}

public struct Param {
public var name: String
public var description: String

public init(name: String, description: String) {
self.name = name
self.description = description
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

extension SwiftSyntaxWriter {
internal func writeDocComment(_ docComment: SwiftDocComment) {
output.writeLine("/**")
if let summary = docComment.summary {
for block in summary { writeDocCommentBlock(block) }
}

for param in docComment.parameters {
output.write("- Parameter ")
output.write(param.name)
output.write(": ")
output.write(param.description)
output.endLine()
}

if let returns = docComment.returns {
output.write("- Returns: ")
for span in returns { writeDocCommentSpan(span) }
output.endLine()
}

output.writeLine("*/")
}

fileprivate func writeDocCommentBlock(_ block: SwiftDocComment.Block) {
switch block {
case .paragraph(let spans):
for span in spans { writeDocCommentSpan(span) }
output.endLine()
default:
return // TODO: Support more block types
}
}

fileprivate func writeDocCommentSpan(_ span: SwiftDocComment.Span) {
switch span {
case .text(let string): output.write(string)
default:
return // TODO: Support more inline types
}
}
}
15 changes: 14 additions & 1 deletion Generator/Sources/CodeWriters/Swift/SyntaxWriters/Members.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extension SwiftDeclarationWriter {
public func writeStoredProperty(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
setVisibility: SwiftVisibility = .implicit,
static: Bool = false,
Expand All @@ -13,6 +14,7 @@ extension SwiftDeclarationWriter {
precondition(initialValue == nil || initializer == nil)

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .withName("storedProperty"))
visibility.write(to: &output, trailingSpace: true)
if setVisibility != .implicit {
Expand All @@ -39,6 +41,7 @@ extension SwiftDeclarationWriter {
}

public func writeComputedProperty(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
static: Bool = false,
override: Bool = false,
Expand All @@ -51,6 +54,7 @@ extension SwiftDeclarationWriter {
precondition(set == nil || !`throws`)

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
visibility.write(to: &output, trailingSpace: true)
if `static` { output.write("static ") }
Expand Down Expand Up @@ -84,6 +88,7 @@ extension SwiftDeclarationWriter {
}

public func writeFunc(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
static: Bool = false,
override: Bool = false,
Expand All @@ -94,6 +99,7 @@ extension SwiftDeclarationWriter {
returnType: SwiftType? = nil,
body: (inout SwiftStatementWriter) throws -> Void) rethrows {

if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
writeFuncHeader(
visibility: visibility,
Expand All @@ -110,8 +116,13 @@ extension SwiftDeclarationWriter {
}
}

public func writeEnumCase(name: String, rawValue: String? = nil) {
public func writeEnumCase(
docComments: SwiftDocComment? = nil,
name: String,
rawValue: String? = nil) {

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .withName("case"))
output.write("case ")
SwiftIdentifier.write(name, to: &output)
Expand All @@ -125,6 +136,7 @@ extension SwiftDeclarationWriter {
}

public func writeInit(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
required: Bool = false,
convenience: Bool = false,
Expand All @@ -135,6 +147,7 @@ extension SwiftDeclarationWriter {
body: (inout SwiftStatementWriter) throws -> Void) rethrows {

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
visibility.write(to: &output, trailingSpace: true)
if `required` { output.write("required ") }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
extension SwiftSourceFileWriter {
public func writeProtocol(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
name: String,
typeParameters: [String] = [],
Expand All @@ -8,6 +9,7 @@ extension SwiftSourceFileWriter {
members: (SwiftProtocolBodyWriter) throws -> Void) rethrows {

var output = output
if let docComments = docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
visibility.write(to: &output, trailingSpace: true)
output.write("protocol ")
Expand Down Expand Up @@ -36,6 +38,7 @@ public struct SwiftProtocolBodyWriter: SwiftSyntaxWriter {
}

public func writeProperty(
docComments: SwiftDocComment? = nil,
static: Bool = false,
name: String,
type: SwiftType,
Expand All @@ -45,6 +48,7 @@ public struct SwiftProtocolBodyWriter: SwiftSyntaxWriter {
precondition(!set || !`throws`)

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .withName("protocolProperty"))
if `static` { output.write("static ") }
output.write("var ")
Expand All @@ -58,6 +62,7 @@ public struct SwiftProtocolBodyWriter: SwiftSyntaxWriter {
}

public func writeFunc(
docComments: SwiftDocComment? = nil,
isPropertySetter: Bool = false,
static: Bool = false,
name: String,
Expand All @@ -66,6 +71,7 @@ public struct SwiftProtocolBodyWriter: SwiftSyntaxWriter {
throws: Bool = false,
returnType: SwiftType? = nil) {

if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .withName(isPropertySetter ? "protocolProperty" : "protocolFunc"))
writeFuncHeader(
visibility: .implicit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public struct SwiftTypeDefinitionWriter: SwiftDeclarationWriter {

extension SwiftDeclarationWriter {
public func writeClass(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
final: Bool = false,
name: String,
Expand All @@ -13,6 +14,7 @@ extension SwiftDeclarationWriter {
definition: (SwiftTypeDefinitionWriter) throws -> Void) rethrows {

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
visibility.write(to: &output, trailingSpace: true)
if final { output.write("final ") }
Expand All @@ -26,13 +28,15 @@ extension SwiftDeclarationWriter {
}

public func writeStruct(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
name: String,
typeParameters: [String] = [],
protocolConformances: [SwiftType] = [],
definition: (SwiftTypeDefinitionWriter) throws -> Void) rethrows {

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
visibility.write(to: &output, trailingSpace: true)
output.write("struct ")
Expand All @@ -45,6 +49,7 @@ extension SwiftDeclarationWriter {
}

public func writeEnum(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
name: String,
typeParameters: [String] = [],
Expand All @@ -53,6 +58,7 @@ extension SwiftDeclarationWriter {
definition: (SwiftTypeDefinitionWriter) throws -> Void) rethrows {

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .never)
visibility.write(to: &output, trailingSpace: true)
output.write("enum ")
Expand All @@ -65,12 +71,14 @@ extension SwiftDeclarationWriter {
}

public func writeTypeAlias(
docComments: SwiftDocComment? = nil,
visibility: SwiftVisibility = .implicit,
name: String,
typeParameters: [String] = [],
target: SwiftType) {

var output = output
if let docComments { writeDocComment(docComments) }
output.beginLine(grouping: .withName("typealias"))
visibility.write(to: &output, trailingSpace: true)
output.write("typealias ")
Expand Down
79 changes: 79 additions & 0 deletions Generator/Sources/ProjectionGenerator/SwiftProjection+Module.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import DotNetMetadata
import DotNetXMLDocs

extension SwiftProjection {
public class Module {
public unowned let projection: SwiftProjection
public let shortName: String
public private(set) var typeDefinitionsByNamespace = [String: Set<TypeDefinition>]()
public private(set) var closedGenericTypesByDefinition = [TypeDefinition: [[TypeNode]]]()
private(set) var references: Set<Reference> = []

internal init(projection: SwiftProjection, shortName: String) {
self.projection = projection
self.shortName = shortName
}

public var assemblyModuleName: String { shortName + "Assembly" }

public func addAssembly(_ assembly: Assembly, documentation: DocumentationFile? = nil) {
projection.assembliesToModules[assembly] = AssemblyEntry(module: self, documentation: documentation)
}

public func hasTypeDefinition(_ type: TypeDefinition) -> Bool {
typeDefinitionsByNamespace[Module.getNamespaceOrEmpty(type)]?.contains(type) ?? false
}

public func addTypeDefinition(_ type: TypeDefinition) {
precondition(projection.getModule(type.assembly) === self)
typeDefinitionsByNamespace[Module.getNamespaceOrEmpty(type), default: Set()].insert(type)
}

public func addClosedGenericType(_ type: BoundType) {
precondition(!type.genericArgs.isEmpty && !type.isParameterized)
closedGenericTypesByDefinition[type.definition, default: []].append(type.genericArgs)
}

public func addReference(_ other: Module) {
references.insert(Reference(target: other))
}

internal func getName(_ typeDefinition: TypeDefinition, namespaced: Bool = true) throws -> String {
// Map: Namespace.TypeName
// To: Namespace_TypeName
// Map: Namespace.Subnamespace.TypeName/NestedTypeName
// To: NamespaceSubnamespace_TypeName_NestedTypeName
var result: String = ""
if let enclosingType = try typeDefinition.enclosingType {
result += try getName(enclosingType, namespaced: namespaced) + "_"
}
else if namespaced {
result += typeDefinition.namespace.flatMap { SwiftProjection.toCompactNamespace($0) + "_" } ?? ""
}

result += typeDefinition.nameWithoutGenericSuffix

return result
}

private static func getNamespaceOrEmpty(_ type: TypeDefinition) -> String {
var namespacedType = type
while namespacedType.namespace == nil, let enclosingType = try? namespacedType.enclosingType {
namespacedType = enclosingType
}
return namespacedType.namespace ?? ""
}

struct Reference: Hashable {
unowned var target: Module

func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(target))
}

static func == (lhs: Module.Reference, rhs: Module.Reference) -> Bool {
lhs.target === rhs.target
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DotNetMetadata
import CodeWriters
import DotNetMetadata
import DotNetXMLDocs

extension SwiftProjection {
static func toVisibility(_ visibility: DotNetMetadata.Visibility, inheritableClass: Bool = false) -> SwiftVisibility {
Expand All @@ -12,6 +13,11 @@ extension SwiftProjection {
}
}

static func toDocComments(_ entry: MemberEntry) -> SwiftDocComment {
// TODO: Convert doc comments
return SwiftDocComment()
}

// Windows.Foundation.Collections to WindowsFoundationCollections
public static func toCompactNamespace(_ namespace: String) -> String {
namespace.replacing(".", with: "")
Expand Down Expand Up @@ -39,17 +45,17 @@ extension SwiftProjection {
}
}

func toTypeName(_ type: TypeDefinition, namespaced: Bool = true) throws -> String {
try assembliesToModules[type.assembly]!.getName(type, namespaced: namespaced)
func toTypeName(_ typeDefinition: TypeDefinition, namespaced: Bool = true) throws -> String {
try getModule(typeDefinition.assembly)!.getName(typeDefinition, namespaced: namespaced)
}

func toProtocolName(_ type: InterfaceDefinition, namespaced: Bool = true) throws -> String {
try toTypeName(type, namespaced: namespaced) + "Protocol"
func toProtocolName(_ typeDefinition: InterfaceDefinition, namespaced: Bool = true) throws -> String {
try toTypeName(typeDefinition, namespaced: namespaced) + "Protocol"
}

public func toProjectionTypeName(_ type: TypeDefinition, namespaced: Bool = true) throws -> String {
var typeName = try toTypeName(type, namespaced: namespaced)
if type is InterfaceDefinition || type is DelegateDefinition {
public func toProjectionTypeName(_ typeDefinition: TypeDefinition, namespaced: Bool = true) throws -> String {
var typeName = try toTypeName(typeDefinition, namespaced: namespaced)
if typeDefinition is InterfaceDefinition || typeDefinition is DelegateDefinition {
// protocols and function pointers cannot serve as the projection class,
// so an accompanying type provides the ABIProjection conformance.
typeName += "Projection"
Expand Down
Loading

0 comments on commit a2f322b

Please sign in to comment.