From c2afb348bb0d3dd60d336aa312b5fcedfb31b966 Mon Sep 17 00:00:00 2001 From: Alex Waters Date: Mon, 1 May 2017 14:00:31 +0930 Subject: Fix Markdown list spacing, ordering, and erroneous new lines --- .../main/kotlin/Formats/MarkdownFormatService.kt | 27 ++++++++----- core/src/main/kotlin/Kotlin/ContentBuilder.kt | 5 ++- core/src/main/kotlin/Markdown/MarkdownProcessor.kt | 2 + core/src/test/kotlin/format/MarkdownFormatTest.kt | 8 ++++ core/testdata/format/javadocOrderedList.md | 4 +- core/testdata/format/nestedLists.kt | 31 ++++++++++++++ core/testdata/format/nestedLists.md | 43 ++++++++++++++++++++ core/testdata/format/unorderedLists.kt | 36 +++++++++++++++++ core/testdata/format/unorderedLists.md | 47 ++++++++++++++++++++++ 9 files changed, 191 insertions(+), 12 deletions(-) create mode 100644 core/testdata/format/nestedLists.kt create mode 100644 core/testdata/format/nestedLists.md create mode 100644 core/testdata/format/unorderedLists.kt create mode 100644 core/testdata/format/unorderedLists.md diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt index b9c9c04f..f7c17401 100644 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ b/core/src/main/kotlin/Formats/MarkdownFormatService.kt @@ -10,6 +10,13 @@ enum class ListKind { Unordered } +private class ListState(val kind: ListKind, var size: Int = 1) { + fun getTagAndIncrement() = when (kind) { + ListKind.Ordered -> "${size++}. " + else -> "* " + } +} + private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator() open class MarkdownOutputBuilder(to: StringBuilder, @@ -20,7 +27,7 @@ open class MarkdownOutputBuilder(to: StringBuilder, impliedPlatforms: List) : StructuredOutputBuilder(to, location, locationService, languageService, extension, impliedPlatforms) { - private val listKindStack = Stack() + private val listStack = ArrayDeque() protected var inTableCell = false protected var inCodeBlock = false private var lastTableCellStart = -1 @@ -34,7 +41,7 @@ open class MarkdownOutputBuilder(to: StringBuilder, } private fun ensureNewline() { - if (inTableCell && listKindStack.isEmpty()) { + if (inTableCell && listStack.isEmpty()) { if (to.length != lastTableCellStart && !to.endsWith("
")) { to.append("
") } @@ -101,22 +108,22 @@ open class MarkdownOutputBuilder(to: StringBuilder, } override fun appendUnorderedList(body: () -> Unit) { - listKindStack.push(ListKind.Unordered) + listStack.push(ListState(ListKind.Unordered)) body() - listKindStack.pop() + listStack.pop() ensureNewline() } override fun appendOrderedList(body: () -> Unit) { - listKindStack.push(ListKind.Ordered) + listStack.push(ListState(ListKind.Ordered)) body() - listKindStack.pop() + listStack.pop() ensureNewline() } override fun appendListItem(body: () -> Unit) { ensureNewline() - to.append(if (listKindStack.peek() == ListKind.Unordered) "* " else "1. ") + to.append(listStack.peek()?.getTagAndIncrement()) body() ensureNewline() } @@ -151,8 +158,10 @@ open class MarkdownOutputBuilder(to: StringBuilder, if (inTableCell) { ensureNewline() body() - } - else { + } else if (listStack.isNotEmpty()) { + body() + ensureNewline() + } else { ensureParagraph() body() ensureParagraph() diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt index c124821e..a244a48e 100644 --- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt +++ b/core/src/main/kotlin/Kotlin/ContentBuilder.kt @@ -96,7 +96,9 @@ fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (Stri } } MarkdownTokenTypes.EOL -> { - if (keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) { + if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || + // Keep extra blank lines when processing lists (affects Markdown formatting) + (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { parent.append(ContentText(node.text)) } } @@ -156,6 +158,7 @@ fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (Stri private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH }.joinToString("") { it.text } private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode +private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: (String) -> ContentBlock) { val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt index d1d40dd4..2c8f7a73 100644 --- a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt +++ b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt @@ -14,6 +14,8 @@ class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: S val text: String get() = node.getTextInNode(markdown).toString() fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } + val previous get() = parent?.children?.getOrNull(parent.children.indexOf(this) - 1) + override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString() } diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt index 999d739b..217bfd09 100644 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ b/core/src/test/kotlin/format/MarkdownFormatTest.kt @@ -356,6 +356,14 @@ class MarkdownFormatTest { verifyMarkdownNode("tokensInHeaders") } + @Test fun unorderedLists() { + verifyMarkdownNode("unorderedLists") + } + + @Test fun nestedLists() { + verifyMarkdownNode("nestedLists") + } + private fun buildMultiplePlatforms(path: String): DocumentationModule { val module = DocumentationModule("test") val options = DocumentationOptions("", "html", generateIndexPages = false) diff --git a/core/testdata/format/javadocOrderedList.md b/core/testdata/format/javadocOrderedList.md index 00aa1bc4..f18ee96a 100644 --- a/core/testdata/format/javadocOrderedList.md +++ b/core/testdata/format/javadocOrderedList.md @@ -5,13 +5,13 @@ `open class Bar` 1. Rinse - 1. Repeat + 2. Repeat ### Constructors | [<init>](test/-bar/-init-) | `Bar()`
1. Rinse - 1. Repeat + 2. Repeat
| diff --git a/core/testdata/format/nestedLists.kt b/core/testdata/format/nestedLists.kt new file mode 100644 index 00000000..83217f8a --- /dev/null +++ b/core/testdata/format/nestedLists.kt @@ -0,0 +1,31 @@ +/** + * Usage instructions: + * + * - __Rinse__ + * 1. Alter any rinse options _(optional)_ + * - Recommended to [Bar.useSoap] + * - Optionally apply [Bar.elbowGrease] for best results + * 2. [Bar.rinse] to begin rinse + * 1. Thus you should call [Bar.rinse] + * 2. *Then* call [Bar.repeat] + * - Don't forget to use: + * - Soap + * - Elbow Grease + * 3. Finally, adjust soap usage [Bar.useSoap] as needed + * 3. Repeat with [Bar.repeat] + * + * - __Repeat__ + * - Will use previously used rinse options + * - [Bar.rinse] must have been called once before + * - Can be repeated any number of times + * - Options include: + * - [Bar.useSoap] + * - [Bar.useElbowGrease] + */ +class Bar { + fun rinse() = Unit + fun repeat() = Unit + + var useSoap = false + var useElbowGrease = false +} diff --git a/core/testdata/format/nestedLists.md b/core/testdata/format/nestedLists.md new file mode 100644 index 00000000..dd151944 --- /dev/null +++ b/core/testdata/format/nestedLists.md @@ -0,0 +1,43 @@ +[test](test/index) / [Bar](test/-bar/index) + +# Bar + +`class Bar` + +Usage instructions: + +* **Rinse** + 1. Alter any rinse options *(optional)* + * Recommended to [Bar.useSoap](test/-bar/use-soap) + * Optionally apply [Bar.elbowGrease](#) for best results + 2. [Bar.rinse](test/-bar/rinse) to begin rinse + 1. Thus you should call [Bar.rinse](test/-bar/rinse) + 2. *Then* call [Bar.repeat](test/-bar/repeat) + * Don't forget to use: + * Soap + * Elbow Grease + 3. Finally, adjust soap usage [Bar.useSoap](test/-bar/use-soap) as needed + 3. Repeat with [Bar.repeat](test/-bar/repeat) + +* **Repeat** + * Will use previously used rinse options + * [Bar.rinse](test/-bar/rinse) must have been called once before + * Can be repeated any number of times + * Options include: + * [Bar.useSoap](test/-bar/use-soap) + * [Bar.useElbowGrease](test/-bar/use-elbow-grease) + +### Constructors + +| [<init>](test/-bar/-init-) | `Bar()`
Usage instructions: | + +### Properties + +| [useElbowGrease](test/-bar/use-elbow-grease) | `var useElbowGrease: Boolean` | +| [useSoap](test/-bar/use-soap) | `var useSoap: Boolean` | + +### Functions + +| [repeat](test/-bar/repeat) | `fun repeat(): Unit` | +| [rinse](test/-bar/rinse) | `fun rinse(): Unit` | + diff --git a/core/testdata/format/unorderedLists.kt b/core/testdata/format/unorderedLists.kt new file mode 100644 index 00000000..a594b89b --- /dev/null +++ b/core/testdata/format/unorderedLists.kt @@ -0,0 +1,36 @@ +/** + * Usage summary: + * + * - Rinse + * - Repeat + * + * Usage instructions: + * + * - [Bar.rinse] to rinse + * - Alter any rinse options _(optional)_ + * - To repeat; [Bar.repeat] + * - Can reconfigure options: + * - Soap + * - Elbow Grease + * - Bleach + * + * Rinse options: + * + * - [Bar.useSoap] + * - _recommended_ + * + * - [Bar.useElbowGrease] + * - _warning: requires effort_ + * + * - [Bar.useBleach] + * - __use with caution__ + * + */ +class Bar { + fun rinse() = Unit + fun repeat() = Unit + + var useSoap = false + var useElbowGrease = false + var useBleach = false +} diff --git a/core/testdata/format/unorderedLists.md b/core/testdata/format/unorderedLists.md new file mode 100644 index 00000000..a6b00b34 --- /dev/null +++ b/core/testdata/format/unorderedLists.md @@ -0,0 +1,47 @@ +[test](test/index) / [Bar](test/-bar/index) + +# Bar + +`class Bar` + +Usage summary: + +* Rinse +* Repeat + +Usage instructions: + +* [Bar.rinse](test/-bar/rinse) to rinse +* Alter any rinse options *(optional)* +* To repeat; [Bar.repeat](test/-bar/repeat) + * Can reconfigure options: + * Soap + * Elbow Grease + * Bleach + +Rinse options: + +* [Bar.useSoap](test/-bar/use-soap) + * *recommended* + +* [Bar.useElbowGrease](test/-bar/use-elbow-grease) + * *warning: requires effort* + +* [Bar.useBleach](test/-bar/use-bleach) + * **use with caution** + +### Constructors + +| [<init>](test/-bar/-init-) | `Bar()`
Usage summary: | + +### Properties + +| [useBleach](test/-bar/use-bleach) | `var useBleach: Boolean` | +| [useElbowGrease](test/-bar/use-elbow-grease) | `var useElbowGrease: Boolean` | +| [useSoap](test/-bar/use-soap) | `var useSoap: Boolean` | + +### Functions + +| [repeat](test/-bar/repeat) | `fun repeat(): Unit` | +| [rinse](test/-bar/rinse) | `fun rinse(): Unit` | + -- cgit