From b54348ac3affc74cae0547b31c932b6be7b6d730 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Wed, 8 Dec 2021 14:07:54 -0700 Subject: [PATCH 1/8] store ordered list start index --- Sources/Markdown/Base/RawMarkup.swift | 6 +- .../Block Container Blocks/OrderedList.swift | 21 ++++++- .../Markdown/Parser/CommonMarkConverter.swift | 4 +- .../Walker/Walkers/MarkupFormatter.swift | 14 ++++- .../Walker/Walkers/MarkupTreeDumper.swift | 8 +++ .../Base/RawMarkupToMarkupTests.swift | 2 +- Tests/MarkdownTests/Visitors/Everything.md | 3 + .../Visitors/MarkupFormatterTests.swift | 36 ++++++++++-- .../Visitors/MarkupTreeDumperTests.swift | 57 +++++++++++-------- 9 files changed, 112 insertions(+), 39 deletions(-) diff --git a/Sources/Markdown/Base/RawMarkup.swift b/Sources/Markdown/Base/RawMarkup.swift index 3cd1f853..5e47e4e9 100644 --- a/Sources/Markdown/Base/RawMarkup.swift +++ b/Sources/Markdown/Base/RawMarkup.swift @@ -24,7 +24,7 @@ enum RawMarkupData: Equatable { case thematicBreak case htmlBlock(String) case listItem(checkbox: Checkbox?) - case orderedList + case orderedList(start: Int?) case unorderedList case paragraph case blockDirective(name: String, nameLocation: SourceLocation?, arguments: DirectiveArgumentText) @@ -228,8 +228,8 @@ final class RawMarkup: ManagedBuffer { return .create(data: .listItem(checkbox: checkbox), parsedRange: parsedRange, children: children) } - static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup { - return .create(data: .orderedList, parsedRange: parsedRange, children: children) + static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], start: Int?) -> RawMarkup { + return .create(data: .orderedList(start: start), parsedRange: parsedRange, children: children) } static func unorderedList(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup { diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index b3fe1884..5a976eac 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -29,7 +29,26 @@ public extension OrderedList { // MARK: ListItemContainer init(_ items: Items) where Items.Element == ListItem { - try! self.init(.orderedList(parsedRange: nil, items.map { $0.raw.markup })) + try! self.init(.orderedList(parsedRange: nil, items.map { $0.raw.markup }, start: nil)) + } + + /// The starting index for the list. + /// + /// If this is `nil`, the list will start at the default value of 1. + var start: Int? { + get { + guard case let .orderedList(start) = _data.raw.markup.data else { + fatalError("\(self) markup wrapped unexpected \(_data.raw)") + } + return start + } + set { + precondition(newValue ?? 1 > 0, "List start must be 1 or greater") + guard start != newValue else { + return + } + _data = _data.replacingSelf(.orderedList(parsedRange: nil, _data.raw.markup.copyChildren(), start: newValue)) + } } // MARK: Visitation diff --git a/Sources/Markdown/Parser/CommonMarkConverter.swift b/Sources/Markdown/Parser/CommonMarkConverter.swift index 2819f7c7..3168ab0e 100644 --- a/Sources/Markdown/Parser/CommonMarkConverter.swift +++ b/Sources/Markdown/Parser/CommonMarkConverter.swift @@ -299,7 +299,9 @@ struct MarkupParser { case CMARK_BULLET_LIST: return MarkupConversion(state: childConversion.state.next(), result: .unorderedList(parsedRange: parsedRange, childConversion.result)) case CMARK_ORDERED_LIST: - return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result)) + let cmarkStart = cmark_node_get_list_start(state.node) + let start: Int? = cmarkStart == 1 ? nil : Int(cmarkStart) + return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, start: start)) default: fatalError("cmark reported a list node but said its list type is CMARK_NO_LIST?") } diff --git a/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift b/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift index f961de98..8a14979d 100644 --- a/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift +++ b/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift @@ -492,15 +492,23 @@ public struct MarkupFormatter: MarkupWalker { /// Get the numeral prefix for a list item if its parent is an ordered list. func numeralPrefix(for listItem: ListItem) -> String? { - guard listItem.parent is OrderedList else { + guard let list = listItem.parent as? OrderedList else { return nil } let numeral: UInt switch formattingOptions.orderedListNumerals { case let .allSame(n): - numeral = n + if let origStart = list.start { + numeral = UInt(origStart) + } else { + numeral = n + } case let .incrementing(start): - numeral = start + UInt(listItem.indexInParent) + if let origStart = list.start { + numeral = UInt(origStart) + UInt(listItem.indexInParent) + } else { + numeral = start + UInt(listItem.indexInParent) + } } return "\(numeral). " } diff --git a/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift b/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift index 3b265f17..6c28ad84 100644 --- a/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift +++ b/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift @@ -189,6 +189,14 @@ struct MarkupTreeDumper: MarkupWalker { dump(heading, customDescription: "level: \(heading.level)") } + mutating func visitOrderedList(_ orderedList: OrderedList) { + if let start = orderedList.start { + dump(orderedList, customDescription: "start: \(start)") + } else { + defaultVisit(orderedList) + } + } + mutating func visitCodeBlock(_ codeBlock: CodeBlock) { let lines = indentLiteralBlock(codeBlock.code, from: codeBlock, countLines: false) dump(codeBlock, customDescription: "language: \(codeBlock.language ?? "none")\n\(lines)") diff --git a/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift b/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift index ebf9698c..db082db1 100644 --- a/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift +++ b/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift @@ -48,7 +48,7 @@ final class RawMarkupToMarkupTests: XCTestCase { } func testOrderedList() { - XCTAssertNoThrow(try OrderedList(.orderedList(parsedRange: nil, []))) + XCTAssertNoThrow(try OrderedList(.orderedList(parsedRange: nil, [], start: nil))) XCTAssertThrowsError(try OrderedList(.softBreak(parsedRange: nil))) } diff --git a/Tests/MarkdownTests/Visitors/Everything.md b/Tests/MarkdownTests/Visitors/Everything.md index bd810706..690fc6af 100644 --- a/Tests/MarkdownTests/Visitors/Everything.md +++ b/Tests/MarkdownTests/Visitors/Everything.md @@ -12,6 +12,9 @@ > BlockQuote +2. flour +2. sugar + ```swift func foo() { let x = 1 diff --git a/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift b/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift index f599f1e2..3ae84477 100644 --- a/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift +++ b/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift @@ -144,6 +144,32 @@ class MarkupFormatterSingleElementTests: XCTestCase { } } + func testPrintOrderedListCustomStart() { + do { // no checkbox + let expected = "2. A list item." + var renderedList = OrderedList(ListItem(Paragraph(Text("A list item.")))) + renderedList.start = 2 + let printed = renderedList.format() + XCTAssertEqual(expected, printed) + } + do { // unchecked + let expected = "2. [ ] A list item." + var renderedList = OrderedList(ListItem(checkbox: .unchecked, + Paragraph(Text("A list item.")))) + renderedList.start = 2 + let printed = renderedList.format() + XCTAssertEqual(expected, printed) + } + do { // checked + let expected = "2. [x] A list item." + var renderedList = OrderedList(ListItem(checkbox: .checked, + Paragraph(Text("A list item.")))) + renderedList.start = 2 + let printed = renderedList.format() + XCTAssertEqual(expected, printed) + } + } + func testPrintParagraph() { let expected = "A paragraph." let printed = Paragraph(Text("A paragraph.")).format() @@ -426,13 +452,13 @@ class MarkupFormatterOptionsTests: XCTestCase { 3. C """ let allSame = """ - 0. A - 0. B - 0. C + 1. A + 1. B + 1. C """ do { let document = Document(parsing: incrementing) - let printed = document.format(options: .init(orderedListNumerals: .allSame(0))) + let printed = document.format(options: .init(orderedListNumerals: .allSame(1))) XCTAssertEqual(allSame, printed) } @@ -917,7 +943,7 @@ class MarkupFormatterLineSplittingTests: XCTestCase { let expectedTreeDump = """ Document - └─ OrderedList + └─ OrderedList start: 1000 └─ ListItem └─ Paragraph ├─ Text "Really really" diff --git a/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift b/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift index edf0514f..a61c11f9 100644 --- a/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift +++ b/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift @@ -14,7 +14,7 @@ import XCTest final class MarkupTreeDumperTests: XCTestCase { func testDumpEverything() { let expectedDump = """ - Document @1:1-39:90 Root #\(everythingDocument.raw.metadata.id.rootId) #0 + Document @1:1-42:90 Root #\(everythingDocument.raw.metadata.id.rootId) #0 ├─ Heading @1:1-1:9 #1 level: 1 │ └─ Text @1:3-1:9 #2 "Header" ├─ Paragraph @3:1-3:65 #3 @@ -55,37 +55,44 @@ final class MarkupTreeDumperTests: XCTestCase { ├─ BlockQuote @13:1-13:13 #38 │ └─ Paragraph @13:3-13:13 #39 │ └─ Text @13:3-13:13 #40 "BlockQuote" - ├─ CodeBlock @15:1-19:4 #41 language: swift + ├─ OrderedList @15:1-17:1 #41 start: 2 + │ ├─ ListItem @15:1-15:9 #42 + │ │ └─ Paragraph @15:4-15:9 #43 + │ │ └─ Text @15:4-15:9 #44 "flour" + │ └─ ListItem @16:1-17:1 #45 + │ └─ Paragraph @16:4-16:9 #46 + │ └─ Text @16:4-16:9 #47 "sugar" + ├─ CodeBlock @18:1-22:4 #48 language: swift │ func foo() { │ let x = 1 │ } - ├─ CodeBlock @21:5-22:1 #42 language: none + ├─ CodeBlock @24:5-25:1 #49 language: none │ // Is this real code? Or just fantasy? - ├─ Paragraph @23:1-23:31 #43 - │ ├─ Text @23:1-23:12 #44 "This is an " - │ ├─ Link @23:12-23:30 #45 destination: "topic://autolink" - │ │ └─ Text @23:13-23:29 #46 "topic://autolink" - │ └─ Text @23:30-23:31 #47 "." - ├─ ThematicBreak @25:1-26:1 #48 - ├─ HTMLBlock @27:1-29:5 #49 + ├─ Paragraph @26:1-26:31 #50 + │ ├─ Text @26:1-26:12 #51 "This is an " + │ ├─ Link @26:12-26:30 #52 destination: "topic://autolink" + │ │ └─ Text @26:13-26:29 #53 "topic://autolink" + │ └─ Text @26:30-26:31 #54 "." + ├─ ThematicBreak @28:1-29:1 #55 + ├─ HTMLBlock @30:1-32:5 #56 │ │ An HTML Block. │ - ├─ Paragraph @31:1-31:33 #50 - │ ├─ Text @31:1-31:14 #51 "This is some " - │ ├─ InlineHTML @31:14-31:17 #52

- │ ├─ Text @31:17-31:28 #53 "inline html" - │ ├─ InlineHTML @31:28-31:32 #54

- │ └─ Text @31:32-31:33 #55 "." - ├─ Paragraph @33:1-34:6 #56 - │ ├─ Text @33:1-33:7 #57 "line" - │ ├─ LineBreak #58 - │ └─ Text @34:1-34:6 #59 "break" - ├─ Paragraph @36:1-37:6 #60 - │ ├─ Text @36:1-36:5 #61 "soft" - │ ├─ SoftBreak #62 - │ └─ Text @37:1-37:6 #63 "break" - └─ HTMLBlock @39:1-39:90 #64 + ├─ Paragraph @34:1-34:33 #57 + │ ├─ Text @34:1-34:14 #58 "This is some " + │ ├─ InlineHTML @34:14-34:17 #59

+ │ ├─ Text @34:17-34:28 #60 "inline html" + │ ├─ InlineHTML @34:28-34:32 #61

+ │ └─ Text @34:32-34:33 #62 "." + ├─ Paragraph @36:1-37:6 #63 + │ ├─ Text @36:1-36:7 #64 "line" + │ ├─ LineBreak #65 + │ └─ Text @37:1-37:6 #66 "break" + ├─ Paragraph @39:1-40:6 #67 + │ ├─ Text @39:1-39:5 #68 "soft" + │ ├─ SoftBreak #69 + │ └─ Text @40:1-40:6 #70 "break" + └─ HTMLBlock @42:1-42:90 #71 """ print(everythingDocument.debugDescription(options: [.printEverything])) From facf34e5bad1f31dd28603719f7cb961db9f3c96 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Tue, 21 Dec 2021 13:47:19 -0700 Subject: [PATCH 2/8] refactor: start is non-nullable, but defaults to 1 --- Sources/Markdown/Base/RawMarkup.swift | 4 ++-- .../Block Container Blocks/OrderedList.swift | 8 ++++---- Sources/Markdown/Parser/CommonMarkConverter.swift | 5 ++--- Sources/Markdown/Walker/Walkers/MarkupFormatter.swift | 10 +++++----- Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift | 4 ++-- Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Sources/Markdown/Base/RawMarkup.swift b/Sources/Markdown/Base/RawMarkup.swift index 5e47e4e9..3cd1c6eb 100644 --- a/Sources/Markdown/Base/RawMarkup.swift +++ b/Sources/Markdown/Base/RawMarkup.swift @@ -24,7 +24,7 @@ enum RawMarkupData: Equatable { case thematicBreak case htmlBlock(String) case listItem(checkbox: Checkbox?) - case orderedList(start: Int?) + case orderedList(start: Int = 1) case unorderedList case paragraph case blockDirective(name: String, nameLocation: SourceLocation?, arguments: DirectiveArgumentText) @@ -228,7 +228,7 @@ final class RawMarkup: ManagedBuffer { return .create(data: .listItem(checkbox: checkbox), parsedRange: parsedRange, children: children) } - static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], start: Int?) -> RawMarkup { + static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], start: Int = 1) -> RawMarkup { return .create(data: .orderedList(start: start), parsedRange: parsedRange, children: children) } diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index 5a976eac..1eaa1064 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -29,13 +29,13 @@ public extension OrderedList { // MARK: ListItemContainer init(_ items: Items) where Items.Element == ListItem { - try! self.init(.orderedList(parsedRange: nil, items.map { $0.raw.markup }, start: nil)) + try! self.init(.orderedList(parsedRange: nil, items.map { $0.raw.markup })) } /// The starting index for the list. /// - /// If this is `nil`, the list will start at the default value of 1. - var start: Int? { + /// The default starting index in CommonMark is 1. In this case, clients may use any desired index for this list. + var start: Int { get { guard case let .orderedList(start) = _data.raw.markup.data else { fatalError("\(self) markup wrapped unexpected \(_data.raw)") @@ -43,7 +43,7 @@ public extension OrderedList { return start } set { - precondition(newValue ?? 1 > 0, "List start must be 1 or greater") + precondition(newValue > 0, "List start must be 1 or greater") guard start != newValue else { return } diff --git a/Sources/Markdown/Parser/CommonMarkConverter.swift b/Sources/Markdown/Parser/CommonMarkConverter.swift index 3168ab0e..ac0bb4b2 100644 --- a/Sources/Markdown/Parser/CommonMarkConverter.swift +++ b/Sources/Markdown/Parser/CommonMarkConverter.swift @@ -299,9 +299,8 @@ struct MarkupParser { case CMARK_BULLET_LIST: return MarkupConversion(state: childConversion.state.next(), result: .unorderedList(parsedRange: parsedRange, childConversion.result)) case CMARK_ORDERED_LIST: - let cmarkStart = cmark_node_get_list_start(state.node) - let start: Int? = cmarkStart == 1 ? nil : Int(cmarkStart) - return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, start: start)) + let cmarkStart = Int(cmark_node_get_list_start(state.node)) + return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, start: cmarkStart)) default: fatalError("cmark reported a list node but said its list type is CMARK_NO_LIST?") } diff --git a/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift b/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift index 8a14979d..b5e2cef9 100644 --- a/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift +++ b/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift @@ -498,14 +498,14 @@ public struct MarkupFormatter: MarkupWalker { let numeral: UInt switch formattingOptions.orderedListNumerals { case let .allSame(n): - if let origStart = list.start { - numeral = UInt(origStart) - } else { + if list.start == 1 { numeral = n + } else { + numeral = UInt(list.start) } case let .incrementing(start): - if let origStart = list.start { - numeral = UInt(origStart) + UInt(listItem.indexInParent) + if list.start != 1 { + numeral = UInt(list.start) + UInt(listItem.indexInParent) } else { numeral = start + UInt(listItem.indexInParent) } diff --git a/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift b/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift index 6c28ad84..345d70ca 100644 --- a/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift +++ b/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift @@ -190,8 +190,8 @@ struct MarkupTreeDumper: MarkupWalker { } mutating func visitOrderedList(_ orderedList: OrderedList) { - if let start = orderedList.start { - dump(orderedList, customDescription: "start: \(start)") + if orderedList.start != 1 { + dump(orderedList, customDescription: "start: \(orderedList.start)") } else { defaultVisit(orderedList) } diff --git a/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift b/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift index db082db1..ebf9698c 100644 --- a/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift +++ b/Tests/MarkdownTests/Base/RawMarkupToMarkupTests.swift @@ -48,7 +48,7 @@ final class RawMarkupToMarkupTests: XCTestCase { } func testOrderedList() { - XCTAssertNoThrow(try OrderedList(.orderedList(parsedRange: nil, [], start: nil))) + XCTAssertNoThrow(try OrderedList(.orderedList(parsedRange: nil, []))) XCTAssertThrowsError(try OrderedList(.softBreak(parsedRange: nil))) } From 5e25b500847b134e1bca3d5bd3f798dc807fd592 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 11 Jul 2022 16:33:21 -0600 Subject: [PATCH 3/8] update OrderedList.start docs --- .../Block Nodes/Block Container Blocks/OrderedList.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index 1eaa1064..caf649eb 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -34,7 +34,9 @@ public extension OrderedList { /// The starting index for the list. /// - /// The default starting index in CommonMark is 1. In this case, clients may use any desired index for this list. + /// The default starting index in CommonMark is 1. In this case, clients may use the default + /// ordered-list start index of their desired rendering format. For example, when rendering to + /// HTML, clients may omit the `start` attribute of the rendered list when this returns 1. var start: Int { get { guard case let .orderedList(start) = _data.raw.markup.data else { From 28131345bc8b5f2bab32c3abc546e8f68f352d6e Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 11 Jul 2022 16:33:35 -0600 Subject: [PATCH 4/8] ordered lists can start with zero --- .../Block Nodes/Block Container Blocks/OrderedList.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index caf649eb..a99f5549 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -45,7 +45,6 @@ public extension OrderedList { return start } set { - precondition(newValue > 0, "List start must be 1 or greater") guard start != newValue else { return } From 48dd77e8449f4ae150c7bec89999f111cfad3c86 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 11 Jul 2022 16:47:52 -0600 Subject: [PATCH 5/8] use UInt for list start index instead of Int cmark will never parse a negative number as a list index, since the hyphen followed by a digit will not parse as a list marker --- Sources/Markdown/Base/RawMarkup.swift | 4 ++-- .../Block Nodes/Block Container Blocks/OrderedList.swift | 2 +- Sources/Markdown/Parser/CommonMarkConverter.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Markdown/Base/RawMarkup.swift b/Sources/Markdown/Base/RawMarkup.swift index 3cd1c6eb..c2a333e6 100644 --- a/Sources/Markdown/Base/RawMarkup.swift +++ b/Sources/Markdown/Base/RawMarkup.swift @@ -24,7 +24,7 @@ enum RawMarkupData: Equatable { case thematicBreak case htmlBlock(String) case listItem(checkbox: Checkbox?) - case orderedList(start: Int = 1) + case orderedList(start: UInt = 1) case unorderedList case paragraph case blockDirective(name: String, nameLocation: SourceLocation?, arguments: DirectiveArgumentText) @@ -228,7 +228,7 @@ final class RawMarkup: ManagedBuffer { return .create(data: .listItem(checkbox: checkbox), parsedRange: parsedRange, children: children) } - static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], start: Int = 1) -> RawMarkup { + static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], start: UInt = 1) -> RawMarkup { return .create(data: .orderedList(start: start), parsedRange: parsedRange, children: children) } diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index a99f5549..cf53ee60 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -37,7 +37,7 @@ public extension OrderedList { /// The default starting index in CommonMark is 1. In this case, clients may use the default /// ordered-list start index of their desired rendering format. For example, when rendering to /// HTML, clients may omit the `start` attribute of the rendered list when this returns 1. - var start: Int { + var start: UInt { get { guard case let .orderedList(start) = _data.raw.markup.data else { fatalError("\(self) markup wrapped unexpected \(_data.raw)") diff --git a/Sources/Markdown/Parser/CommonMarkConverter.swift b/Sources/Markdown/Parser/CommonMarkConverter.swift index ac0bb4b2..0a26099f 100644 --- a/Sources/Markdown/Parser/CommonMarkConverter.swift +++ b/Sources/Markdown/Parser/CommonMarkConverter.swift @@ -299,7 +299,7 @@ struct MarkupParser { case CMARK_BULLET_LIST: return MarkupConversion(state: childConversion.state.next(), result: .unorderedList(parsedRange: parsedRange, childConversion.result)) case CMARK_ORDERED_LIST: - let cmarkStart = Int(cmark_node_get_list_start(state.node)) + let cmarkStart = UInt(cmark_node_get_list_start(state.node)) return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, start: cmarkStart)) default: fatalError("cmark reported a list node but said its list type is CMARK_NO_LIST?") From cf58e795b62a9d1b3038efddbcd272861c566552 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 26 Sep 2022 15:07:49 -0600 Subject: [PATCH 6/8] review: use startIndex instead of start --- Sources/Markdown/Base/RawMarkup.swift | 6 +++--- .../Block Nodes/Block Container Blocks/OrderedList.swift | 2 +- Sources/Markdown/Parser/CommonMarkConverter.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Markdown/Base/RawMarkup.swift b/Sources/Markdown/Base/RawMarkup.swift index c2a333e6..c9051306 100644 --- a/Sources/Markdown/Base/RawMarkup.swift +++ b/Sources/Markdown/Base/RawMarkup.swift @@ -24,7 +24,7 @@ enum RawMarkupData: Equatable { case thematicBreak case htmlBlock(String) case listItem(checkbox: Checkbox?) - case orderedList(start: UInt = 1) + case orderedList(startIndex: UInt = 1) case unorderedList case paragraph case blockDirective(name: String, nameLocation: SourceLocation?, arguments: DirectiveArgumentText) @@ -228,8 +228,8 @@ final class RawMarkup: ManagedBuffer { return .create(data: .listItem(checkbox: checkbox), parsedRange: parsedRange, children: children) } - static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], start: UInt = 1) -> RawMarkup { - return .create(data: .orderedList(start: start), parsedRange: parsedRange, children: children) + static func orderedList(parsedRange: SourceRange?, _ children: [RawMarkup], startIndex: UInt = 1) -> RawMarkup { + return .create(data: .orderedList(startIndex: startIndex), parsedRange: parsedRange, children: children) } static func unorderedList(parsedRange: SourceRange?, _ children: [RawMarkup]) -> RawMarkup { diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index cf53ee60..5103aa27 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -48,7 +48,7 @@ public extension OrderedList { guard start != newValue else { return } - _data = _data.replacingSelf(.orderedList(parsedRange: nil, _data.raw.markup.copyChildren(), start: newValue)) + _data = _data.replacingSelf(.orderedList(parsedRange: nil, _data.raw.markup.copyChildren(), startIndex: newValue)) } } diff --git a/Sources/Markdown/Parser/CommonMarkConverter.swift b/Sources/Markdown/Parser/CommonMarkConverter.swift index 0a26099f..06782433 100644 --- a/Sources/Markdown/Parser/CommonMarkConverter.swift +++ b/Sources/Markdown/Parser/CommonMarkConverter.swift @@ -300,7 +300,7 @@ struct MarkupParser { return MarkupConversion(state: childConversion.state.next(), result: .unorderedList(parsedRange: parsedRange, childConversion.result)) case CMARK_ORDERED_LIST: let cmarkStart = UInt(cmark_node_get_list_start(state.node)) - return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, start: cmarkStart)) + return MarkupConversion(state: childConversion.state.next(), result: .orderedList(parsedRange: parsedRange, childConversion.result, startIndex: cmarkStart)) default: fatalError("cmark reported a list node but said its list type is CMARK_NO_LIST?") } From dbc1f77b55a11e26d08c4e7c5b6e65610f3d4800 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Mon, 26 Sep 2022 15:17:05 -0600 Subject: [PATCH 7/8] revert MarkupFormatter.numeralPrefix overriding format options --- .../Markdown/Walker/Walkers/MarkupFormatter.swift | 15 ++++----------- .../Visitors/MarkupFormatterTests.swift | 7 ++++--- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift b/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift index b5e2cef9..6c8307fc 100644 --- a/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift +++ b/Sources/Markdown/Walker/Walkers/MarkupFormatter.swift @@ -492,23 +492,16 @@ public struct MarkupFormatter: MarkupWalker { /// Get the numeral prefix for a list item if its parent is an ordered list. func numeralPrefix(for listItem: ListItem) -> String? { - guard let list = listItem.parent as? OrderedList else { + guard listItem.parent is OrderedList else { return nil } let numeral: UInt + // FIXME: allow `orderedListNumerals` to defer to the user-authored starting index (#76, rdar://99970544) switch formattingOptions.orderedListNumerals { case let .allSame(n): - if list.start == 1 { - numeral = n - } else { - numeral = UInt(list.start) - } + numeral = n case let .incrementing(start): - if list.start != 1 { - numeral = UInt(list.start) + UInt(listItem.indexInParent) - } else { - numeral = start + UInt(listItem.indexInParent) - } + numeral = start + UInt(listItem.indexInParent) } return "\(numeral). " } diff --git a/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift b/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift index 3ae84477..98da56b6 100644 --- a/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift +++ b/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift @@ -145,11 +145,12 @@ class MarkupFormatterSingleElementTests: XCTestCase { } func testPrintOrderedListCustomStart() { + let options = MarkupFormatter.Options(orderedListNumerals: .allSame(2)) do { // no checkbox let expected = "2. A list item." var renderedList = OrderedList(ListItem(Paragraph(Text("A list item.")))) renderedList.start = 2 - let printed = renderedList.format() + let printed = renderedList.format(options: options) XCTAssertEqual(expected, printed) } do { // unchecked @@ -157,7 +158,7 @@ class MarkupFormatterSingleElementTests: XCTestCase { var renderedList = OrderedList(ListItem(checkbox: .unchecked, Paragraph(Text("A list item.")))) renderedList.start = 2 - let printed = renderedList.format() + let printed = renderedList.format(options: options) XCTAssertEqual(expected, printed) } do { // checked @@ -165,7 +166,7 @@ class MarkupFormatterSingleElementTests: XCTestCase { var renderedList = OrderedList(ListItem(checkbox: .checked, Paragraph(Text("A list item.")))) renderedList.start = 2 - let printed = renderedList.format() + let printed = renderedList.format(options: options) XCTAssertEqual(expected, printed) } } From 06115ed58172b6dd2cdb8d95f06df64f39aaeb12 Mon Sep 17 00:00:00 2001 From: Victoria Mitchell Date: Tue, 27 Sep 2022 09:55:44 -0600 Subject: [PATCH 8/8] use "startIndex" instead of "start" on public API --- .../Block Nodes/Block Container Blocks/OrderedList.swift | 4 ++-- Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift | 4 ++-- Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift | 8 ++++---- Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift index 5103aa27..e52243b8 100644 --- a/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift +++ b/Sources/Markdown/Block Nodes/Block Container Blocks/OrderedList.swift @@ -37,7 +37,7 @@ public extension OrderedList { /// The default starting index in CommonMark is 1. In this case, clients may use the default /// ordered-list start index of their desired rendering format. For example, when rendering to /// HTML, clients may omit the `start` attribute of the rendered list when this returns 1. - var start: UInt { + var startIndex: UInt { get { guard case let .orderedList(start) = _data.raw.markup.data else { fatalError("\(self) markup wrapped unexpected \(_data.raw)") @@ -45,7 +45,7 @@ public extension OrderedList { return start } set { - guard start != newValue else { + guard startIndex != newValue else { return } _data = _data.replacingSelf(.orderedList(parsedRange: nil, _data.raw.markup.copyChildren(), startIndex: newValue)) diff --git a/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift b/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift index 345d70ca..2acdcd13 100644 --- a/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift +++ b/Sources/Markdown/Walker/Walkers/MarkupTreeDumper.swift @@ -190,8 +190,8 @@ struct MarkupTreeDumper: MarkupWalker { } mutating func visitOrderedList(_ orderedList: OrderedList) { - if orderedList.start != 1 { - dump(orderedList, customDescription: "start: \(orderedList.start)") + if orderedList.startIndex != 1 { + dump(orderedList, customDescription: "startIndex: \(orderedList.startIndex)") } else { defaultVisit(orderedList) } diff --git a/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift b/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift index 98da56b6..5dc30846 100644 --- a/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift +++ b/Tests/MarkdownTests/Visitors/MarkupFormatterTests.swift @@ -149,7 +149,7 @@ class MarkupFormatterSingleElementTests: XCTestCase { do { // no checkbox let expected = "2. A list item." var renderedList = OrderedList(ListItem(Paragraph(Text("A list item.")))) - renderedList.start = 2 + renderedList.startIndex = 2 let printed = renderedList.format(options: options) XCTAssertEqual(expected, printed) } @@ -157,7 +157,7 @@ class MarkupFormatterSingleElementTests: XCTestCase { let expected = "2. [ ] A list item." var renderedList = OrderedList(ListItem(checkbox: .unchecked, Paragraph(Text("A list item.")))) - renderedList.start = 2 + renderedList.startIndex = 2 let printed = renderedList.format(options: options) XCTAssertEqual(expected, printed) } @@ -165,7 +165,7 @@ class MarkupFormatterSingleElementTests: XCTestCase { let expected = "2. [x] A list item." var renderedList = OrderedList(ListItem(checkbox: .checked, Paragraph(Text("A list item.")))) - renderedList.start = 2 + renderedList.startIndex = 2 let printed = renderedList.format(options: options) XCTAssertEqual(expected, printed) } @@ -944,7 +944,7 @@ class MarkupFormatterLineSplittingTests: XCTestCase { let expectedTreeDump = """ Document - └─ OrderedList start: 1000 + └─ OrderedList startIndex: 1000 └─ ListItem └─ Paragraph ├─ Text "Really really" diff --git a/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift b/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift index a61c11f9..54de038d 100644 --- a/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift +++ b/Tests/MarkdownTests/Visitors/MarkupTreeDumperTests.swift @@ -55,7 +55,7 @@ final class MarkupTreeDumperTests: XCTestCase { ├─ BlockQuote @13:1-13:13 #38 │ └─ Paragraph @13:3-13:13 #39 │ └─ Text @13:3-13:13 #40 "BlockQuote" - ├─ OrderedList @15:1-17:1 #41 start: 2 + ├─ OrderedList @15:1-17:1 #41 startIndex: 2 │ ├─ ListItem @15:1-15:9 #42 │ │ └─ Paragraph @15:4-15:9 #43 │ │ └─ Text @15:4-15:9 #44 "flour"