Commit 385ad813bb
Unsigned
Layout: unified · split
gitbay/Views/Repos/MarkdownView.swift +13 −5
| @@ -65,9 +65,7 @@ struct MarkdownView: View { | ||
| 65 | 65 | blocks.append(.code(code.joined(separator: "\n"))) |
| 66 | 66 | continue |
| 67 | 67 | } |
| 68 | if trimmed.hasPrefix("#") { | |
| 69 | let level = trimmed.prefix(while: { $0 == "#" }).count | |
| 70 | let text = trimmed.drop(while: { $0 == "#" }).trimmingCharacters(in: .whitespaces) | |
| 68 | if let (level, text) = Self.atxHeading(trimmed) { | |
| 71 | 69 | blocks.append(.heading(level: min(level, 4), text: text)) |
| 72 | 70 | lines = lines.dropFirst() |
| 73 | 71 | continue |
| @@ -140,7 +138,7 @@ struct MarkdownView: View { | ||
| 140 | 138 | var paragraph: [String] = [] |
| 141 | 139 | while let next = lines.first { |
| 142 | 140 | let t = next.trimmingCharacters(in: .whitespaces) |
| 143 | if t.isEmpty || t.hasPrefix("#") || t.hasPrefix("```") || t.hasPrefix(">") | |
| 141 | if t.isEmpty || Self.atxHeading(t) != nil || t.hasPrefix("```") || t.hasPrefix(">") | |
| 144 | 142 | || t.hasPrefix("- ") || t.hasPrefix("* ") || Self.tableStart(lines) != nil { |
| 145 | 143 | break |
| 146 | 144 | } |
| @@ -152,12 +150,22 @@ struct MarkdownView: View { | ||
| 152 | 150 | return blocks |
| 153 | 151 | } |
| 154 | 152 | |
| 153 | /// An ATX heading: one to six `#` followed by a space, a tab or the end | |
| 154 | /// of the line, as in CommonMark. `#12 fixed this` is a paragraph. | |
| 155 | private static func atxHeading(_ line: String) -> (level: Int, text: String)? { | |
| 156 | let level = line.prefix(while: { $0 == "#" }).count | |
| 157 | guard (1...6).contains(level) else { return nil } | |
| 158 | let rest = line.dropFirst(level) | |
| 159 | guard rest.isEmpty || rest.first == " " || rest.first == "\t" else { return nil } | |
| 160 | return (level, rest.trimmingCharacters(in: .whitespaces)) | |
| 161 | } | |
| 162 | ||
| 155 | 163 | /// A line that wraps the list item above it: anything but a blank line or |
| 156 | 164 | /// the start of another block, indented or not, as in CommonMark. |
| 157 | 165 | private static func continuesItem(_ lines: ArraySlice<String>) -> Bool { |
| 158 | 166 | guard let line = lines.first?.trimmingCharacters(in: .whitespaces), !line.isEmpty |
| 159 | 167 | else { return false } |
| 160 | let startsBlock = line.hasPrefix("#") || line.hasPrefix("```") || line.hasPrefix(">") | |
| 168 | let startsBlock = atxHeading(line) != nil || line.hasPrefix("```") || line.hasPrefix(">") | |
| 161 | 169 | || line.hasPrefix("- ") || line.hasPrefix("* ") || line.hasPrefix("+ ") |
| 162 | 170 | || line == "---" || line == "***" || line == "___" |
| 163 | 171 | || line.range(of: #"^\d+\. "#, options: .regularExpression) != nil |
gitbayTests/MarkdownTests.swift +29
| @@ -175,3 +175,32 @@ struct MarkdownListTests { | ||
| 175 | 175 | #expect(parsed == [.bullet(["one"]), .heading(level: 2, text: "Next")]) |
| 176 | 176 | } |
| 177 | 177 | } |
| 178 | ||
| 179 | @MainActor | |
| 180 | struct MarkdownHeadingTests { | |
| 181 | ||
| 182 | private func blocks(_ markdown: String) -> [MarkdownView.Block] { | |
| 183 | MarkdownView(markdown: markdown).blocks | |
| 184 | } | |
| 185 | ||
| 186 | @Test func hashWithoutSpaceIsAParagraph() { | |
| 187 | #expect(blocks("#12 fixed this") == [.paragraph("#12 fixed this")]) | |
| 188 | } | |
| 189 | ||
| 190 | @Test func hashReferenceDoesNotEndAParagraph() { | |
| 191 | #expect(blocks("see\n#12 for context") == [.paragraph("see #12 for context")]) | |
| 192 | } | |
| 193 | ||
| 194 | @Test func hashReferenceContinuesAListItem() { | |
| 195 | #expect(blocks("- fixed in\n#12") == blocks("- fixed in #12")) | |
| 196 | } | |
| 197 | ||
| 198 | @Test func headingNeedsSpaceOrEndOfLine() { | |
| 199 | #expect(blocks("## Usage") == [.heading(level: 2, text: "Usage")]) | |
| 200 | #expect(blocks("#") == [.heading(level: 1, text: "")]) | |
| 201 | } | |
| 202 | ||
| 203 | @Test func sevenHashesIsAParagraph() { | |
| 204 | #expect(blocks("####### x") == [.paragraph("####### x")]) | |
| 205 | } | |
| 206 | } | |