diff --git a/Generator/Sources/CodeWriters/IndentedTextOutputStream.swift b/Generator/Sources/CodeWriters/IndentedTextOutputStream.swift index 52d781cf..7b90299c 100644 --- a/Generator/Sources/CodeWriters/IndentedTextOutputStream.swift +++ b/Generator/Sources/CodeWriters/IndentedTextOutputStream.swift @@ -75,8 +75,8 @@ public class IndentedTextOutputStream: TextOutputStream { lineEnd == str.startIndex ? "" : String(str[str.startIndex.." sourceFileWriter.writeTypeAlias( - visibility: SwiftProjection.toVisibility(interface.visibility), - name: try projection.toTypeName(interface), - typeParameters: interface.genericParams.map { $0.name }, + documentation: projection.getDocumentationComment(interfaceDefinition), + visibility: SwiftProjection.toVisibility(interfaceDefinition.visibility), + name: try projection.toTypeName(interfaceDefinition), + typeParameters: interfaceDefinition.genericParams.map { $0.name }, target: .identifier( protocolModifier: .existential, - name: try projection.toProtocolName(interface), - genericArgs: interface.genericParams.map { .identifier(name: $0.name) })) + name: try projection.toProtocolName(interfaceDefinition), + genericArgs: interfaceDefinition.genericParams.map { .identifier(name: $0.name) })) } internal func writeInterfaceProjection(_ interfaceDefinition: InterfaceDefinition, genericArgs: [TypeNode]?) throws { diff --git a/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter+structs.swift b/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter+structs.swift index f69e7186..6f48b20a 100644 --- a/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter+structs.swift +++ b/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter+structs.swift @@ -5,6 +5,7 @@ import WindowsMetadata extension SwiftAssemblyModuleFileWriter { internal func writeStruct(_ structDefinition: StructDefinition) throws { try sourceFileWriter.writeStruct( + documentation: projection.getDocumentationComment(structDefinition), visibility: SwiftProjection.toVisibility(structDefinition.visibility), name: try projection.toTypeName(structDefinition), typeParameters: structDefinition.genericParams.map { $0.name }, @@ -20,8 +21,10 @@ extension SwiftAssemblyModuleFileWriter { for field in structDefinition.fields { assert(field.isInstance && !field.isInitOnly && field.isPublic) - let type = try projection.toType(field.type) - writer.writeStoredProperty(visibility: .public, declarator: .var, name: projection.toMemberName(field), type: type) + try writer.writeStoredProperty( + documentation: projection.getDocumentationComment(field), + visibility: .public, declarator: .var, name: projection.toMemberName(field), + type: projection.toType(field.type)) } } diff --git a/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter.swift b/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter.swift index 7dd31e16..d9de1557 100644 --- a/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter.swift +++ b/Generator/Sources/ProjectionGenerator/SwiftAssemblyModuleFileWriter.swift @@ -31,9 +31,7 @@ public struct SwiftAssemblyModuleFileWriter { case let interfaceDefinition as InterfaceDefinition: try writeInterface(interfaceDefinition) case _ as ClassDefinition: - // Skip until we can generate interface members correctly - // try writeClass(classDefinition) - break + break // Unified with the projection for now case let structDefinition as StructDefinition: try writeStruct(structDefinition) case let enumDefinition as EnumDefinition: diff --git a/Generator/Sources/ProjectionGenerator/SwiftProjection+conversion.swift b/Generator/Sources/ProjectionGenerator/SwiftProjection+conversion.swift index 92f64241..dfab115a 100644 --- a/Generator/Sources/ProjectionGenerator/SwiftProjection+conversion.swift +++ b/Generator/Sources/ProjectionGenerator/SwiftProjection+conversion.swift @@ -13,11 +13,6 @@ extension SwiftProjection { } } - static func toDocumentationComment(_ documentation: MemberDocumentation) -> SwiftDocumentationComment { - // TODO: Convert doc comments - return SwiftDocumentationComment() - } - // Windows.Foundation.Collections to WindowsFoundationCollections public static func toCompactNamespace(_ namespace: String) -> String { namespace.replacing(".", with: "") diff --git a/Generator/Sources/ProjectionGenerator/SwiftProjection+documentation.swift b/Generator/Sources/ProjectionGenerator/SwiftProjection+documentation.swift new file mode 100644 index 00000000..07cf7465 --- /dev/null +++ b/Generator/Sources/ProjectionGenerator/SwiftProjection+documentation.swift @@ -0,0 +1,111 @@ +import CodeWriters +import DotNetMetadata +import DotNetXMLDocs + +extension SwiftProjection { + internal func getDocumentationComment(_ typeDefinition: TypeDefinition) -> SwiftDocumentationComment? { + guard let documentation = assembliesToModules[typeDefinition.assembly]?.documentation else { return nil } + return documentation.members[.type(fullName: typeDefinition.fullName)].map { toDocumentationComment($0) } + } + + internal func getDocumentationComment(_ member: Member) throws -> SwiftDocumentationComment? { + guard let documentation = assembliesToModules[member.definingType.assembly]?.documentation else { return nil } + + let memberKey: MemberDocumentationKey + switch member { + case let field as Field: + memberKey = .field(declaringType: field.definingType.fullName, name: field.name) + case let event as Event: + memberKey = .event(declaringType: event.definingType.fullName, name: event.name) + case let property as Property: + guard try (property.getter?.arity ?? 0) == 0 else { return nil } + memberKey = .property(declaringType: property.definingType.fullName, name: property.name) + case let method as Method: + memberKey = try .method(declaringType: method.definingType.fullName, name: method.name, + params: method.params.map { try .init(type: toParamType($0.type), isByRef: $0.isByRef) }) + default: + return nil + } + + return documentation.members[memberKey].map { toDocumentationComment($0) } + } +} + +fileprivate func toParamType(_ type: TypeNode) -> MemberDocumentationKey.ParamType { + switch type { + case .bound(let type): + return .bound( + fullName: type.definition.fullName, + genericArgs: type.genericArgs.map { toParamType($0) }) + case .array(of: let elementType): + return .array(of: toParamType(elementType)) + case .pointer(to: let pointeeType): + return .pointer(to: toParamType(pointeeType!)) // TODO: Handle void* + case .genericParam(let param): + return .genericArg(index: param.index, kind: param is GenericTypeParam ? .type : .method) + } +} + +fileprivate func toDocumentationComment(_ documentation: MemberDocumentation) -> SwiftDocumentationComment { + var swift = SwiftDocumentationComment() + if let summary = documentation.summary { + swift.summary = toBlocks(summary) + } + for param in documentation.params { + swift.parameters.append(SwiftDocumentationComment.Param(name: param.key, description: toSpans(param.value))) + } + if let returns = documentation.returns { + swift.returns = toSpans(returns) + } + return swift +} + +fileprivate func toBlocks(_ node: DocumentationTextNode) -> [SwiftDocumentationComment.Block] { + var blocks = [SwiftDocumentationComment.Block]() + appendBlocks(node, to: &blocks) + return blocks +} + +fileprivate func toSpans(_ node: DocumentationTextNode) -> [SwiftDocumentationComment.Span] { + var spans = [SwiftDocumentationComment.Span]() + appendSpans(node, to: &spans) + return spans +} + +fileprivate func appendSpans(_ node: DocumentationTextNode, to spans: inout [SwiftDocumentationComment.Span]) { + switch node { + case .plain(let text): + spans.append(.text(text)) + case .sequence(let nodes): + for node in nodes { appendSpans(node, to: &spans) } + case .codeSpan(let code): + spans.append(.code(code)) + default: + break // TODO: Support all node types + } +} + +fileprivate func appendBlocks(_ node: DocumentationTextNode, to blocks: inout [SwiftDocumentationComment.Block]) { + func appendSpan(_ span: SwiftDocumentationComment.Span) { + if case .paragraph(let paragraph) = blocks.last { + blocks[blocks.count - 1] = .paragraph(paragraph + [span]) + } + else { + blocks.append(.paragraph([span])) + } + } + + switch node { + case .plain(let text): + appendSpan(.text(text)) + case .sequence(let nodes): + for node in nodes { appendBlocks(node, to: &blocks) } + case .paragraph(let body): + blocks.append(.paragraph([])) + appendBlocks(body, to: &blocks) + case .codeSpan(let code): + appendSpan(.code(code)) + default: + break // TODO: Support all node types + } +} diff --git a/Generator/Sources/ProjectionGenerator/SwiftProjection.swift b/Generator/Sources/ProjectionGenerator/SwiftProjection.swift index f561e6c8..3999c8b1 100644 --- a/Generator/Sources/ProjectionGenerator/SwiftProjection.swift +++ b/Generator/Sources/ProjectionGenerator/SwiftProjection.swift @@ -25,32 +25,4 @@ public class SwiftProjection { public func getModule(_ assembly: Assembly) -> Module? { assembliesToModules[assembly]?.module } - - internal func getDocumentation(_ assembly: Assembly) -> AssemblyDocumentation? { - assembliesToModules[assembly]?.documentation - } - - internal func getDocumentation(_ typeDefinition: TypeDefinition) -> MemberDocumentation? { - guard let documentationFile = getDocumentation(typeDefinition.assembly) else { return nil } - return documentationFile.members[.type(fullName: typeDefinition.fullName)] - } - - internal func getDocumentation(_ member: Member) throws -> MemberDocumentation? { - guard let documentationFile = getDocumentation(member.definingType.assembly) else { return nil } - - let memberKey: MemberDocumentationKey - switch member { - case let field as Field: - memberKey = .field(declaringType: field.definingType.fullName, name: field.name) - case let event as Event: - memberKey = .event(declaringType: event.definingType.fullName, name: event.name) - case let property as Property: - guard try (property.getter?.arity ?? 0) == 0 else { return nil } - memberKey = .event(declaringType: property.definingType.fullName, name: property.name) - default: - return nil - } - - return documentationFile.members[memberKey] - } } \ No newline at end of file diff --git a/Generator/Sources/SwiftWinRT/EntryPoint.swift b/Generator/Sources/SwiftWinRT/EntryPoint.swift index 10ebc49a..7fa29936 100644 --- a/Generator/Sources/SwiftWinRT/EntryPoint.swift +++ b/Generator/Sources/SwiftWinRT/EntryPoint.swift @@ -157,7 +157,7 @@ struct EntryPoint: ParsableCommand { } else if let languageCode { let languageNestedDocsPath = "\(assemblyDirectoryPath)\\\(languageCode)\\\(assemblyFileNameWithoutExtension).xml" - if FileManager.default.fileExists(atPath: sideBySideDocsPath) { + if FileManager.default.fileExists(atPath: languageNestedDocsPath) { docs = try AssemblyDocumentation(readingFileAtPath: languageNestedDocsPath) } }