Skip to content

Commit

Permalink
Added support for multiline documentation comments (#1293)
Browse files Browse the repository at this point in the history
* Added support for multiline documentation comments

* Checking `documentationComment` type for annotations
  • Loading branch information
art-divin committed Mar 17, 2024
1 parent 8333563 commit b701e94
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 31 deletions.
58 changes: 52 additions & 6 deletions SourceryFramework/Sources/Parsing/Utils/AnnotationsParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,13 @@ public struct AnnotationsParser {

for line in lines[0..<lineNumber-1].reversed() {
if line.type == .documentationComment {
documentation.append(line.content.trimmingCharacters(in: .whitespaces).trimmingPrefix("///").trimmingPrefix("/**").trimmingPrefix(" "))
var clearedLine = line.content.trimmingCharacters(in: .whitespaces)
clearedLine = clearedLine.trimmingPrefix("///")
clearedLine = clearedLine.trimmingPrefix("/**")
clearedLine = clearedLine.trimmingSuffix("*/")
clearedLine = clearedLine.trimmingPrefix(" ")
clearedLine = clearedLine.trimmingSuffix(" ")
documentation.append(clearedLine)
}
if line.type != .comment && line.type != .documentationComment && line.type != .macros && line.type != .propertyWrapper {
break
Expand All @@ -156,7 +162,6 @@ public struct AnnotationsParser {
return documentation.reversed()
}

func inlineFrom(line lineInfo: (line: Int, character: Int), stop: inout Bool) -> Annotations {
let sourceLine = lines[lineInfo.line - 1]
let utf8View = sourceLine.content.utf8
Expand Down Expand Up @@ -215,16 +220,57 @@ public struct AnnotationsParser {
private static func parse(contents: String) -> [Line] {
var annotationsBlock: Annotations?
var fileAnnotationsBlock = Annotations()

class MultilineCommentStack {
private var lines: [String] = []
var hasOpenedComment: Bool {
!lines.isEmpty && lines.last?.contains("*/") == false
}
func reset() {
lines.removeAll()
}
func push(_ line: String) {
lines.append(line)
}
func contains(_ line: String) -> Bool {
lines.contains(line)
}
}
let multilineCommentStack = MultilineCommentStack()
return StringView(contents).lines
.map { line in
let content = line.content.trimmingCharacters(in: .whitespaces)
var annotations = Annotations()
let isComment = content.hasPrefix("//") || content.hasPrefix("/*") || content.hasPrefix("*")
let isDocumentationComment = content.hasPrefix("///") || content.hasPrefix("/**")
var isComment = content.hasPrefix("//") || content.hasPrefix("/*") && !content.hasPrefix("/**") || content.hasPrefix("*") && !content.hasPrefix("*/")
let isClosingMultilineDocumentationComment = (content.contains("*/") && multilineCommentStack.hasOpenedComment)
let isOpeningMultilineDocumentationComment = content.hasPrefix("/**")
let isDocumentationComment = content.hasPrefix("///") || isOpeningMultilineDocumentationComment || isClosingMultilineDocumentationComment
let isPropertyWrapper = content.isPropertyWrapper
let isMacros = content.hasPrefix("#")
var type = Line.LineType.other
if isDocumentationComment {
if isOpeningMultilineDocumentationComment {
multilineCommentStack.push(content)
if content == "/**" {
// ignoring the actual token which indicates the start of a multiline comment
// but not stopping traversal of comments by setting the type to `comment`
type = .comment
isComment = true
} else {
type = .documentationComment
}
} else if isClosingMultilineDocumentationComment {
if content == "*/" {
// ignoring the actual token which indicates the start of a multiline comment
// but not stopping traversal of comments by setting the type to `comment`
type = .comment
isComment = true
} else {
type = .documentationComment
}
multilineCommentStack.reset()
} else if multilineCommentStack.hasOpenedComment {
type = .documentationComment
} else if isDocumentationComment {
type = .documentationComment
} else if isComment {
type = .comment
Expand All @@ -233,7 +279,7 @@ public struct AnnotationsParser {
} else if isMacros {
type = .macros
}
if isComment {
if isComment || (type == .documentationComment) {
switch searchForAnnotations(commentLine: content) {
case let .begin(items):
type = .blockStart
Expand Down
100 changes: 75 additions & 25 deletions SourceryTests/Parsing/FileParserSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,49 @@ class FileParserSpec: QuickSpec {
""", parseDocumentation: true))
.to(equal([expectedType]))
}

it("extracts documentation correctly if there is a directive and an attribute on preceeding line") {
let expectedType = Class(name: "Foo", accessLevel: .internal, isExtension: false, variables: [], inheritedTypes: ["TestProtocol"], attributes: ["MainActor": [Attribute(name: "MainActor")]])
expectedType.annotations["thirdLine"] = NSNumber(value: 4543)
expectedType.documentation = ["doc", "comment", "baz"]

expect(parse("""
/// doc
// sourcery: thirdLine = 4543
/// comment
// firstLine
///baz
#warning("a warning")
@MainActor
class Foo: TestProtocol { }
""", parseDocumentation: true))
.to(equal([expectedType]))
}

it("extracts documentation correctly if there is a directive and multiline comments") {
let expectedType = Class(name: "Foo", accessLevel: .internal, isExtension: false, variables: [], inheritedTypes: ["TestProtocol"], attributes: ["MainActor": [Attribute(name: "MainActor")]])
expectedType.annotations["thirdLine"] = NSNumber(value: 4543)
expectedType.documentation = ["doc", "This is a documentation comment", "This is another documentation comment", "This is another another documentation comment", "This is yet another documentation comment"]

let parsedType = parse("""
/// doc
// sourcery: thirdLine = 4543
/* This is not a documentation comment */
/** This is a documentation comment */
/**
This is another documentation comment
*/
/** This is another another documentation comment
*/
/**
This is yet another documentation comment */
#warning("a warning")
@MainActor
class Foo: TestProtocol { }
""", parseDocumentation: true)
expect(parsedType)
.to(equal([expectedType]))
}
}

context("given typealias") {
Expand Down Expand Up @@ -673,41 +716,48 @@ class FileParserSpec: QuickSpec {
context("given enum cases annotations") {

it("extracts cases with annotations properly") {
expect(parse("""
let parsedTypes = parse("""
enum Foo {
// sourcery:begin: block
// sourcery: first, second=\"value\"
case optionA(/* sourcery: first, second = \"value\" */Int)
// sourcery: third
case optionA(/* sourcery: first, third = \"value\" */Int)
// sourcery: fourth
case optionB
case optionC
// sourcery:end
}
"""))
.to(equal([
Enum(name: "Foo", cases: [
EnumCase(name: "optionA", associatedValues: [
AssociatedValue(name: nil, typeName: TypeName(name: "Int"), annotations: [
"first": NSNumber(value: true),
"second": "value" as NSString,
"block": NSNumber(value: true)
])
], annotations: [
"block": NSNumber(value: true),
"first": NSNumber(value: true),
"second": "value" as NSString
]
),
EnumCase(name: "optionB", annotations: [
"block": NSNumber(value: true),
"third": NSNumber(value: true)
]
),
EnumCase(name: "optionC", annotations: [
""")
let expectedTypes = [
Enum(name: "Foo", cases: [
EnumCase(name: "optionA", associatedValues: [
AssociatedValue(name: nil, typeName: TypeName(name: "Int"), annotations: [
"first": NSNumber(value: true),
"third": "value" as NSString,
"block": NSNumber(value: true)
])
], annotations: [
"block": NSNumber(value: true),
"first": NSNumber(value: true),
"second": "value" as NSString
]
),
EnumCase(name: "optionB", annotations: [
"block": NSNumber(value: true),
"fourth": NSNumber(value: true)
]
),
EnumCase(name: "optionC", annotations: [
"block": NSNumber(value: true)
])
]))
])
]

expect((parsedTypes.first! as! Enum).cases[0].annotations).to(equal((expectedTypes.first!).cases[0].annotations))
expect((parsedTypes.first! as! Enum).cases[1].annotations).to(equal((expectedTypes.first!).cases[1].annotations))
expect((parsedTypes.first! as! Enum).cases[2].annotations).to(equal((expectedTypes.first!).cases[2].annotations))

expect(parsedTypes)
.to(equal(expectedTypes))
}

it("extracts cases with inline annotations properly") {
Expand Down

0 comments on commit b701e94

Please sign in to comment.