Skip to content

Commit d491147

Browse files
store ordered list start index from cmark (#22)
1 parent 69f31f8 commit d491147

File tree

8 files changed

+101
-34
lines changed

8 files changed

+101
-34
lines changed

Sources/Markdown/Base/RawMarkup.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ enum RawMarkupData: Equatable {
2424
case thematicBreak
2525
case htmlBlock(String)
2626
case listItem(checkbox: Checkbox?)
27-
case orderedList
27+
case orderedList(startIndex: UInt = 1)
2828
case unorderedList
2929
case paragraph
3030
case blockDirective(name: String, nameLocation: SourceLocation?, arguments: DirectiveArgumentText)
@@ -228,8 +228,8 @@ final class RawMarkup: ManagedBuffer<RawMarkupHeader, RawMarkup> {
228228
return .create(data: .listItem(checkbox: checkbox), parsedRange: parsedRange, children: children)
229229
}
230230

231-
static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {
232-
return .create(data: .orderedList, parsedRange: parsedRange, children: children)
231+
static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], startIndex: UInt = 1) -> RawMarkup {
232+
return .create(data: .orderedList(startIndex: startIndex), parsedRange: parsedRange, children: children)
233233
}
234234

235235
static func unorderedList(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup {

Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ public extension OrderedList {
3232
try! self.init(.orderedList(parsedRange: nil, items.map { $0.raw.markup }))
3333
}
3434

35+
/// The starting index for the list.
36+
///
37+
/// The default starting index in CommonMark is 1. In this case, clients may use the default
38+
/// ordered-list start index of their desired rendering format. For example, when rendering to
39+
/// HTML, clients may omit the `start` attribute of the rendered list when this returns 1.
40+
var startIndex: UInt {
41+
get {
42+
guard case let .orderedList(start) = _data.raw.markup.data else {
43+
fatalError("\(self) markup wrapped unexpected \(_data.raw)")
44+
}
45+
return start
46+
}
47+
set {
48+
guard startIndex != newValue else {
49+
return
50+
}
51+
_data = _data.replacingSelf(.orderedList(parsedRange: nil, _data.raw.markup.copyChildren(), startIndex: newValue))
52+
}
53+
}
54+
3555
// MARK: Visitation
3656

3757
func accept<V: MarkupVisitor>(_ visitor: inout V) -> V.Result {

Sources/Markdown/Parser/CommonMarkConverter.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ struct MarkupParser {
299299
case CMARK_BULLET_LIST:
300300
return MarkupConversion(state: childConversion.state.next(), result: .unorderedList(parsedRange: parsedRange, childConversion.result))
301301
case CMARK_ORDERED_LIST:
302-
return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result))
302+
let cmarkStart = UInt(cmark_node_get_list_start(state.node))
303+
return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, startIndex: cmarkStart))
303304
default:
304305
fatalError("cmark reported a list node but said its list type is CMARK_NO_LIST?")
305306
}

Sources/Markdown/Walker/Walkers/MarkupFormatter.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ public struct MarkupFormatter: MarkupWalker {
496496
return nil
497497
}
498498
let numeral: UInt
499+
// FIXME: allow `orderedListNumerals` to defer to the user-authored starting index (#76, rdar://99970544)
499500
switch formattingOptions.orderedListNumerals {
500501
case let .allSame(n):
501502
numeral = n

Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,14 @@ struct MarkupTreeDumper: MarkupWalker {
189189
dump(heading, customDescription: "level: \(heading.level)")
190190
}
191191

192+
mutating func visitOrderedList(_ orderedList: OrderedList) {
193+
if orderedList.startIndex != 1 {
194+
dump(orderedList, customDescription: "startIndex: \(orderedList.startIndex)")
195+
} else {
196+
defaultVisit(orderedList)
197+
}
198+
}
199+
192200
mutating func visitCodeBlock(_ codeBlock: CodeBlock) {
193201
let lines = indentLiteralBlock(codeBlock.code, from: codeBlock, countLines: false)
194202
dump(codeBlock, customDescription: "language: \(codeBlock.language ?? "none")\n\(lines)")

Tests/MarkdownTests/Visitors/Everything.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
> BlockQuote
1414
15+
2. flour
16+
2. sugar
17+
1518
```swift
1619
func foo() {
1720
let x = 1

Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@ class MarkupFormatterSingleElementTests: XCTestCase {
144144
}
145145
}
146146

147+
func testPrintOrderedListCustomStart() {
148+
let options = MarkupFormatter.Options(orderedListNumerals: .allSame(2))
149+
do { // no checkbox
150+
let expected = "2. A list item."
151+
var renderedList = OrderedList(ListItem(Paragraph(Text("A list item."))))
152+
renderedList.startIndex = 2
153+
let printed = renderedList.format(options: options)
154+
XCTAssertEqual(expected, printed)
155+
}
156+
do { // unchecked
157+
let expected = "2. [ ] A list item."
158+
var renderedList = OrderedList(ListItem(checkbox: .unchecked,
159+
Paragraph(Text("A list item."))))
160+
renderedList.startIndex = 2
161+
let printed = renderedList.format(options: options)
162+
XCTAssertEqual(expected, printed)
163+
}
164+
do { // checked
165+
let expected = "2. [x] A list item."
166+
var renderedList = OrderedList(ListItem(checkbox: .checked,
167+
Paragraph(Text("A list item."))))
168+
renderedList.startIndex = 2
169+
let printed = renderedList.format(options: options)
170+
XCTAssertEqual(expected, printed)
171+
}
172+
}
173+
147174
func testPrintParagraph() {
148175
let expected = "A paragraph."
149176
let printed = Paragraph(Text("A paragraph.")).format()
@@ -426,13 +453,13 @@ class MarkupFormatterOptionsTests: XCTestCase {
426453
3. C
427454
"""
428455
let allSame = """
429-
0. A
430-
0. B
431-
0. C
456+
1. A
457+
1. B
458+
1. C
432459
"""
433460
do {
434461
let document = Document(parsing: incrementing)
435-
let printed = document.format(options: .init(orderedListNumerals: .allSame(0)))
462+
let printed = document.format(options: .init(orderedListNumerals: .allSame(1)))
436463
XCTAssertEqual(allSame, printed)
437464
}
438465

@@ -917,7 +944,7 @@ class MarkupFormatterLineSplittingTests: XCTestCase {
917944

918945
let expectedTreeDump = """
919946
Document
920-
└─ OrderedList
947+
└─ OrderedList startIndex: 1000
921948
└─ ListItem
922949
└─ Paragraph
923950
├─ Text "Really really"

Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import XCTest
1414
final class MarkupTreeDumperTests: XCTestCase {
1515
func testDumpEverything() {
1616
let expectedDump = """
17-
Document @1:1-39:90 Root #\(everythingDocument.raw.metadata.id.rootId) #0
17+
Document @1:1-42:90 Root #\(everythingDocument.raw.metadata.id.rootId) #0
1818
├─ Heading @1:1-1:9 #1 level: 1
1919
│ └─ Text @1:3-1:9 #2 "Header"
2020
├─ Paragraph @3:1-3:65 #3
@@ -55,37 +55,44 @@ final class MarkupTreeDumperTests: XCTestCase {
5555
├─ BlockQuote @13:1-13:13 #38
5656
│ └─ Paragraph @13:3-13:13 #39
5757
│ └─ Text @13:3-13:13 #40 "BlockQuote"
58-
├─ CodeBlock @15:1-19:4 #41 language: swift
58+
├─ OrderedList @15:1-17:1 #41 startIndex: 2
59+
│ ├─ ListItem @15:1-15:9 #42
60+
│ │ └─ Paragraph @15:4-15:9 #43
61+
│ │ └─ Text @15:4-15:9 #44 "flour"
62+
│ └─ ListItem @16:1-17:1 #45
63+
│ └─ Paragraph @16:4-16:9 #46
64+
│ └─ Text @16:4-16:9 #47 "sugar"
65+
├─ CodeBlock @18:1-22:4 #48 language: swift
5966
│ func foo() {
6067
│ let x = 1
6168
│ }
62-
├─ CodeBlock @21:5-22:1 #42 language: none
69+
├─ CodeBlock @24:5-25:1 #49 language: none
6370
│ // Is this real code? Or just fantasy?
64-
├─ Paragraph @23:1-23:31 #43
65-
│ ├─ Text @23:1-23:12 #44 "This is an "
66-
│ ├─ Link @23:12-23:30 #45 destination: "topic://autolink"
67-
│ │ └─ Text @23:13-23:29 #46 "topic://autolink"
68-
│ └─ Text @23:30-23:31 #47 "."
69-
├─ ThematicBreak @25:1-26:1 #48
70-
├─ HTMLBlock @27:1-29:5 #49
71+
├─ Paragraph @26:1-26:31 #50
72+
│ ├─ Text @26:1-26:12 #51 "This is an "
73+
│ ├─ Link @26:12-26:30 #52 destination: "topic://autolink"
74+
│ │ └─ Text @26:13-26:29 #53 "topic://autolink"
75+
│ └─ Text @26:30-26:31 #54 "."
76+
├─ ThematicBreak @28:1-29:1 #55
77+
├─ HTMLBlock @30:1-32:5 #56
7178
│ <a href="foo.png">
7279
│ An HTML Block.
7380
│ </a>
74-
├─ Paragraph @31:1-31:33 #50
75-
│ ├─ Text @31:1-31:14 #51 "This is some "
76-
│ ├─ InlineHTML @31:14-31:17 #52 <p>
77-
│ ├─ Text @31:17-31:28 #53 "inline html"
78-
│ ├─ InlineHTML @31:28-31:32 #54 </p>
79-
│ └─ Text @31:32-31:33 #55 "."
80-
├─ Paragraph @33:1-34:6 #56
81-
│ ├─ Text @33:1-33:7 #57 "line"
82-
│ ├─ LineBreak #58
83-
│ └─ Text @34:1-34:6 #59 "break"
84-
├─ Paragraph @36:1-37:6 #60
85-
│ ├─ Text @36:1-36:5 #61 "soft"
86-
│ ├─ SoftBreak #62
87-
│ └─ Text @37:1-37:6 #63 "break"
88-
└─ HTMLBlock @39:1-39:90 #64
81+
├─ Paragraph @34:1-34:33 #57
82+
│ ├─ Text @34:1-34:14 #58 "This is some "
83+
│ ├─ InlineHTML @34:14-34:17 #59 <p>
84+
│ ├─ Text @34:17-34:28 #60 "inline html"
85+
│ ├─ InlineHTML @34:28-34:32 #61 </p>
86+
│ └─ Text @34:32-34:33 #62 "."
87+
├─ Paragraph @36:1-37:6 #63
88+
│ ├─ Text @36:1-36:7 #64 "line"
89+
│ ├─ LineBreak #65
90+
│ └─ Text @37:1-37:6 #66 "break"
91+
├─ Paragraph @39:1-40:6 #67
92+
│ ├─ Text @39:1-39:5 #68 "soft"
93+
│ ├─ SoftBreak #69
94+
│ └─ Text @40:1-40:6 #70 "break"
95+
└─ HTMLBlock @42:1-42:90 #71
8996
<!-- Copyright (c) 2021 Apple Inc and the Swift Project authors. All Rights Reserved. -->
9097
"""
9198
print(everythingDocument.debugDescription(options: [.printEverything]))

0 commit comments

Comments
 (0)