Skip to content

Commit 528d49c

Browse files
track inner indentation for doxygen commands (swiftlang#118)
rdar://107580214
1 parent ad0b81f commit 528d49c

File tree

3 files changed

+128
-6
lines changed

3 files changed

+128
-6
lines changed

Sources/Markdown/Markdown.docc/Markdown/DoxygenCommands.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,16 @@ This is the thing that is modified.
2929
}
3030
```
3131

32-
Doxygen commands are not parsed within code blocks or block directive content. No indentation
33-
adjustment is performed on paragraph arguments that span multiple lines, unlike with block directive
34-
content.
32+
Trailing lines in a command's description are allowed to be indented relative to the command. For
33+
example, the description below is parsed as a paragraph, not a code block:
34+
35+
```markdown
36+
\param thing
37+
The thing.
38+
This is the thing that is modified.
39+
```
40+
41+
Doxygen commands are not parsed within code blocks or block directive content.
3542

3643
## Topics
3744

Sources/Markdown/Parser/BlockDirectiveParser.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,26 @@ struct PendingDoxygenCommand {
241241

242242
var atLocation: SourceLocation
243243

244+
var atSignIndentation: Int
245+
244246
var nameLocation: SourceLocation
245247

248+
var innerIndentation: Int? = nil
249+
246250
var kind: CommandKind
247251

248252
var endLocation: SourceLocation
249253

254+
var indentationAdjustment: Int {
255+
innerIndentation ?? atSignIndentation
256+
}
257+
250258
mutating func addLine(_ line: TrimmedLine) {
251259
endLocation = SourceLocation(line: line.lineNumber ?? 0, column: line.untrimmedText.count + 1, source: line.source)
260+
261+
if innerIndentation == nil, line.location?.line != atLocation.line, !line.isEmptyOrAllWhitespace {
262+
innerIndentation = line.indentationColumnCount
263+
}
252264
}
253265
}
254266

@@ -655,8 +667,8 @@ private enum ParseContainer: CustomStringConvertible {
655667
return parent?.indentationAdjustment(under: nil) ?? 0
656668
case .blockDirective(let pendingBlockDirective, _):
657669
return pendingBlockDirective.indentationColumnCount
658-
case .doxygenCommand:
659-
return parent?.indentationAdjustment(under: nil) ?? 0
670+
case .doxygenCommand(let pendingCommand, _):
671+
return pendingCommand.indentationAdjustment
660672
}
661673
}
662674

@@ -842,6 +854,7 @@ struct ParseContainerStack {
842854
guard !isCodeFenceOrIndentedCodeBlock(on: line) else { return nil }
843855

844856
var remainder = line
857+
let indent = remainder.lexWhitespace()
845858
guard let at = remainder.lex(until: { ch in
846859
switch ch {
847860
case "@", "\\":
@@ -871,6 +884,7 @@ struct ParseContainerStack {
871884
remainder.lexWhitespace()
872885
var pendingCommand = PendingDoxygenCommand(
873886
atLocation: at.range!.lowerBound,
887+
atSignIndentation: indent?.text.count ?? 0,
874888
nameLocation: name.range!.lowerBound,
875889
kind: .param(name: paramName.text),
876890
endLocation: name.range!.upperBound)
@@ -879,6 +893,7 @@ struct ParseContainerStack {
879893
case "return", "returns", "result":
880894
var pendingCommand = PendingDoxygenCommand(
881895
atLocation: at.range!.lowerBound,
896+
atSignIndentation: indent?.text.count ?? 0,
882897
nameLocation: name.range!.lowerBound,
883898
kind: .returns,
884899
endLocation: name.range!.upperBound)

Tests/MarkdownTests/Parsing/DoxygenCommandParserTests.swift

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,65 @@ class DoxygenCommandParserTests: XCTestCase {
9191
XCTAssertEqual(document.debugDescription(), expectedDump)
9292
}
9393

94+
func testParseIndentedDescription() {
95+
let source = """
96+
@param thing
97+
The thing.
98+
"""
99+
100+
let document = Document(parsing: source, options: parseOptions)
101+
102+
let expectedDump = """
103+
Document
104+
└─ DoxygenParameter parameter: thing
105+
└─ Paragraph
106+
└─ Text "The thing."
107+
"""
108+
XCTAssertEqual(document.debugDescription(), expectedDump)
109+
}
110+
111+
func testParseMultilineIndentedDescription() {
112+
let source = """
113+
@param thing The thing.
114+
This is the thing that is messed with.
115+
"""
116+
117+
let document = Document(parsing: source, options: parseOptions)
118+
119+
let expectedDump = """
120+
Document
121+
└─ DoxygenParameter parameter: thing
122+
└─ Paragraph
123+
├─ Text "The thing."
124+
├─ SoftBreak
125+
└─ Text "This is the thing that is messed with."
126+
"""
127+
XCTAssertEqual(document.debugDescription(), expectedDump)
128+
}
129+
130+
func testParseWithIndentedAtSign() {
131+
let source = """
132+
Method description.
133+
134+
@param thing The thing.
135+
This is the thing that is messed with.
136+
"""
137+
138+
let document = Document(parsing: source, options: parseOptions)
139+
140+
let expectedDump = """
141+
Document
142+
├─ Paragraph
143+
│ └─ Text "Method description."
144+
└─ DoxygenParameter parameter: thing
145+
└─ Paragraph
146+
├─ Text "The thing."
147+
├─ SoftBreak
148+
└─ Text "This is the thing that is messed with."
149+
"""
150+
XCTAssertEqual(document.debugDescription(), expectedDump)
151+
}
152+
94153
func testBreakDescriptionWithBlankLine() {
95154
let source = """
96155
@param thing The thing.
@@ -188,7 +247,6 @@ class DoxygenCommandParserTests: XCTestCase {
188247

189248
let document = Document(parsing: source, options: parseOptions)
190249

191-
// FIXME: The source location for the first description line is wrong
192250
let expectedDump = """
193251
Document @1:1-2:39
194252
└─ DoxygenParameter @1:1-2:39 parameter: thing
@@ -200,6 +258,48 @@ class DoxygenCommandParserTests: XCTestCase {
200258
XCTAssertEqual(document.debugDescription(options: .printSourceLocations), expectedDump)
201259
}
202260

261+
func testSourceLocationsWithIndentation() {
262+
let source = """
263+
@param thing The thing.
264+
This is the thing that is messed with.
265+
"""
266+
267+
let document = Document(parsing: source, options: parseOptions)
268+
269+
let expectedDump = """
270+
Document @1:1-2:43
271+
└─ DoxygenParameter @1:1-2:43 parameter: thing
272+
└─ Paragraph @1:14-2:43
273+
├─ Text @1:14-1:24 "The thing."
274+
├─ SoftBreak
275+
└─ Text @2:5-2:43 "This is the thing that is messed with."
276+
"""
277+
XCTAssertEqual(document.debugDescription(options: .printSourceLocations), expectedDump)
278+
}
279+
280+
func testSourceLocationsWithIndentedAtSign() {
281+
let source = """
282+
Method description.
283+
284+
@param thing The thing.
285+
This is the thing that is messed with.
286+
"""
287+
288+
let document = Document(parsing: source, options: parseOptions)
289+
290+
let expectedDump = """
291+
Document @1:1-4:43
292+
├─ Paragraph @1:1-1:20
293+
│ └─ Text @1:1-1:20 "Method description."
294+
└─ DoxygenParameter @3:2-4:43 parameter: thing
295+
└─ Paragraph @3:15-4:43
296+
├─ Text @3:15-3:25 "The thing."
297+
├─ SoftBreak
298+
└─ Text @4:5-4:43 "This is the thing that is messed with."
299+
"""
300+
XCTAssertEqual(document.debugDescription(options: .printSourceLocations), expectedDump)
301+
}
302+
203303
func testDoesNotParseWithoutOption() {
204304
do {
205305
let source = """

0 commit comments

Comments
 (0)