Skip to content

Commit

Permalink
Adding better feedback messages on rule errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
mattmook committed Apr 12, 2019
1 parent 431c471 commit 623dd1c
Show file tree
Hide file tree
Showing 53 changed files with 324 additions and 157 deletions.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-all.zip
16 changes: 8 additions & 8 deletions markdown/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ dependencies {
implementation(kotlin("compiler-embeddable"))
implementation(kotlin("script-util"))

api("com.vladsch.flexmark:flexmark-ext-tables:0.40.20")
api("com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.40.20")
api("com.vladsch.flexmark:flexmark-ext-autolink:0.40.20")
api("com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.40.20")
api("com.vladsch.flexmark:flexmark-ext-tables:0.42.0")
api("com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.42.0")
api("com.vladsch.flexmark:flexmark-ext-autolink:0.42.0")
api("com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.42.0")

testImplementation(kotlin("test"))
testImplementation(gradleTestKit())
testImplementation("junit:junit:4.12")
testImplementation("org.assertj:assertj-core:3.12.1")
testImplementation("org.mockito:mockito-core:2.24.5")
testImplementation("org.assertj:assertj-core:3.12.2")
testImplementation("org.mockito:mockito-core:2.27.0")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
testImplementation("com.flextrade.jfixture:jfixture:2.7.2")

testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.1")
testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.1")
testImplementation("org.spekframework.spek2:spek-dsl-jvm:2.0.2")
testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:2.0.2")
// spek requires kotlin-reflect, can be omitted if already in the classpath
testRuntimeOnly(kotlin("reflect"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.appmattus.markdown.rules

import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.dsl.RuleSetup
import com.appmattus.markdown.errors.ErrorReporter
import com.appmattus.markdown.processing.MarkdownDocument
import com.vladsch.flexmark.util.sequence.BasedSequence

/**
Expand Down Expand Up @@ -44,8 +44,6 @@ class BlanksAroundFencesRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Fenced code blocks should be surrounded by blank lines"

private val fenceRegEx = Regex("^(`{3,}|~{3,})")

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {
Expand All @@ -64,14 +62,25 @@ class BlanksAroundFencesRule(
fence = if (inCode) null else it.value
inCode = !inCode

if (surroundingLinesEmpty(inCode, lines, lineNum)) {
val description = when {
prefixLineNotEmpty(inCode, lines, lineNum) ->
"Fenced code blocks should be surrounded by blank lines. Missing blank above this line."
suffixLineNotEmpty(inCode, lines, lineNum) ->
"Fenced code blocks should be surrounded by blank lines. Missing blank below this line."
else -> null
}

if (description != null) {
errorReporter.reportError(line.startOffset, line.endOffset, description)
}
}
}
}
}

private fun surroundingLinesEmpty(inCode: Boolean, lines: List<BasedSequence>, lineNum: Int) =
inCode && lines[lineNum - 1].isNotEmpty() || (!inCode && lines[lineNum + 1].isNotEmpty())
private fun prefixLineNotEmpty(inCode: Boolean, lines: List<BasedSequence>, lineNum: Int) =
inCode && lines[lineNum - 1].isNotEmpty()

private fun suffixLineNotEmpty(inCode: Boolean, lines: List<BasedSequence>, lineNum: Int) =
!inCode && lines[lineNum + 1].isNotEmpty()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.appmattus.markdown.rules

import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.dsl.RuleSetup
import com.appmattus.markdown.errors.ErrorReporter
import com.appmattus.markdown.processing.MarkdownDocument
import com.vladsch.flexmark.ast.ListItem

/**
Expand Down Expand Up @@ -36,28 +36,28 @@ class BlanksAroundHeadersRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Headers should be surrounded by blank lines"

private val whitespaceRegex = Regex("\\s*")

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {
document.headings.filterNot { it.parent is ListItem }.forEach { heading ->

var isValid = true
if (heading.startLineNumber > 0) {
if (!document.lines[heading.startLineNumber - 1].matches(whitespaceRegex)) {
isValid = false
}
}
val prefixLineNotEmpty =
heading.startLineNumber > 0 && !document.lines[heading.startLineNumber - 1].matches(whitespaceRegex)

val suffixLineNotEmpty = heading.endLineNumber < document.lines.size - 2
&& !document.lines[heading.endLineNumber + 1].matches(whitespaceRegex)

//heading.startLineNumber
if (heading.endLineNumber < document.lines.size - 2) {
if (!document.lines[heading.endLineNumber + 1].matches(whitespaceRegex)) {
isValid = false
}
val description = when {
prefixLineNotEmpty && suffixLineNotEmpty ->
"Headers should be surrounded by blank lines. Missing blank above and below this header."
prefixLineNotEmpty ->
"Headers should be surrounded by blank lines. Missing blank above this header."
suffixLineNotEmpty ->
"Headers should be surrounded by blank lines. Missing blank below this header."
else -> null
}

if (!isValid) {
if (description != null) {
errorReporter.reportError(heading.startOffset, heading.endOffset, description)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.appmattus.markdown.rules

import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.dsl.RuleSetup
import com.appmattus.markdown.errors.ErrorReporter
import com.appmattus.markdown.processing.MarkdownDocument
import com.vladsch.flexmark.util.sequence.BasedSequence

/**
Expand Down Expand Up @@ -48,8 +48,6 @@ class BlanksAroundListsRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Lists should be surrounded by blank lines"

private val fenceRegEx = Regex("^(`{3,}|~{3,})")
private val listRegEx = Regex("^([*+\\-]|(\\d+\\.))\\s")
private val emptyRegEx = Regex("^(\\s|$)")
Expand All @@ -63,15 +61,35 @@ class BlanksAroundListsRule(

val lines = document.lines

var missingBlankAbove = false

lines.forEach { line ->
if (!inCode) {
val listMarker = listRegEx.find(line.trim())?.value

if (!inList && listMarker != null && !prevLine.contains(emptyRegEx)) {
errorReporter.reportError(line.startOffset, line.endOffset, description)
missingBlankAbove = true
} else if (inList && listMarker == null && !line.contains(emptyRegEx)) {

val description = if (missingBlankAbove) {
"Lists should be surrounded by blank lines. Missing blank above and below this line."
} else {
"Lists should be surrounded by blank lines. Missing blank below this line."
}

errorReporter.reportError(prevLine.startOffset, prevLine.endOffset, description)

missingBlankAbove = false
} else if (missingBlankAbove) {
errorReporter.reportError(
prevLine.startOffset,
prevLine.endOffset,
"Lists should be surrounded by blank lines. Missing blank above this line."
)

missingBlankAbove = false
}

inList = listMarker != null
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.appmattus.markdown.rules

import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.dsl.RuleSetup
import com.appmattus.markdown.errors.ErrorReporter
import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.rules.config.CodeBlockStyle
import com.vladsch.flexmark.ast.FencedCodeBlock
import com.vladsch.flexmark.ast.IndentedCodeBlock
Expand Down Expand Up @@ -39,8 +39,6 @@ class CodeBlockStyleRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Code block style"

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {
val codeBlocks = document.codeBlocks

Expand All @@ -59,6 +57,8 @@ class CodeBlockStyleRule(

codeBlocks.forEach {
if (!it.matchesStyle(mainStyle)) {
val description = "Code block expected in ${mainStyle.description()} style but is " +
"${it.styleDescription()}. Configuration: style=${style.description()}."
errorReporter.reportError(it.startOffset, it.endOffset, description)
}
}
Expand All @@ -71,4 +71,20 @@ class CodeBlockStyleRule(
else -> false
}
}

private fun CodeBlockStyle.description(): String {
return when (this) {
CodeBlockStyle.Consistent -> "Consistent"
CodeBlockStyle.Fenced -> "Fenced"
CodeBlockStyle.Indented -> "Indented"
}
}

private fun Block.styleDescription(): String {
return when (this) {
is FencedCodeBlock -> "Fenced"
is IndentedCodeBlock -> "Indented"
else -> throw IllegalStateException("Unknown block type")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.appmattus.markdown.rules

import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.dsl.RuleSetup
import com.appmattus.markdown.errors.ErrorReporter
import com.appmattus.markdown.processing.MarkdownDocument

/**
* # Dollar signs used before commands without showing output
Expand Down Expand Up @@ -40,7 +40,7 @@ class CommandsShowOutputRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Dollar signs used before commands without showing output"
private val description = "Remove dollar sign prefix in code blocks when not showing output."

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {
document.codeBlocks.forEach { block ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ class ConsistentHeaderStyleRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Header style"

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {

val headings = document.headings
Expand All @@ -59,14 +57,32 @@ class ConsistentHeaderStyleRule(

headings.forEach {
if (docStyle == HeaderStyle.SetextWithAtx) {
if (it.style() != HeaderStyle.Setext && !(it.style() == HeaderStyle.Atx && it.level > 2)) {
val expectedStyle = if (it.level > 2) HeaderStyle.Atx else HeaderStyle.Setext

if (it.style() != expectedStyle) {
val description = "Header expected in ${expectedStyle.description()} style but is " +
"${it.style().description()}. Configuration: style=${style.description()}."

errorReporter.reportError(it.startOffset, it.endOffset, description)
}
} else {
if (it.style() != docStyle) {
val description = "Header expected in ${docStyle.description()} style but is " +
"${it.style().description()}. Configuration: style=${style.description()}."

errorReporter.reportError(it.startOffset, it.endOffset, description)
}
}
}
}

private fun HeaderStyle.description(): String {
return when (this) {
HeaderStyle.Consistent -> "Consistent"
HeaderStyle.Atx -> "Atx"
HeaderStyle.AtxClosed -> "AtxClosed"
HeaderStyle.Setext -> "Setext"
HeaderStyle.SetextWithAtx -> "SetextWithAtx"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ class ConsistentTaskListMarkerStyleRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Task list item marker style"

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {

val doneMarkers = document.taskListItems.filter { it.isItemDoneMarker }
Expand All @@ -39,12 +37,23 @@ class ConsistentTaskListMarkerStyleRule(
return
}

val docStyle = if (style == TaskListItemMarkerStyle.Consistent) doneMarkers.first().style() else style
val docStyle = if (style == TaskListItemMarkerStyle.Consistent) doneMarkers.first().style()!! else style

doneMarkers.forEach {
if (it.style() != docStyle) {
val description = "Task list marker expected in ${docStyle.description()} style but is " +
"${it.style()?.description()}. Configuration: style=${style.description()}."

errorReporter.reportError(it.startOffset, it.endOffset, description)
}
}
}

private fun TaskListItemMarkerStyle.description(): String {
return when (this) {
TaskListItemMarkerStyle.Consistent -> "Consistent"
TaskListItemMarkerStyle.Uppercase -> "Uppercase [X]"
TaskListItemMarkerStyle.Lowercase -> "Lowercase [x]"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ class ConsistentUlStyleRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Unordered list style"

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {

val bullets = document.unorderedListItems
Expand All @@ -47,8 +45,20 @@ class ConsistentUlStyleRule(

bullets.forEach {
if (it.style() != docStyle) {
val description = "Unordered list item expected in ${docStyle.description()} style but is " +
"${it.style().description()}. Configuration: style=${style.description()}."

errorReporter.reportError(it.startOffset, it.endOffset, description)
}
}
}

private fun UnorderedListStyle.description(): String {
return when (this) {
UnorderedListStyle.Consistent -> "Consistent"
UnorderedListStyle.Asterisk -> "Asterisk '*'"
UnorderedListStyle.Plus -> "Plus '+'"
UnorderedListStyle.Dash -> "Dash '-'"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.appmattus.markdown.rules

import com.appmattus.markdown.processing.MarkdownDocument
import com.appmattus.markdown.dsl.RuleSetup
import com.appmattus.markdown.errors.ErrorReporter
import com.appmattus.markdown.processing.MarkdownDocument

/**
* # Fenced code blocks should have a language specified
Expand All @@ -27,7 +27,7 @@ class FencedCodeLanguageRule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "Fenced code blocks should have a language specified"
private val description = "Fenced code blocks should have a language specified, for example '```kotlin'."

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {
document.fencedCodeBlocks.forEach {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class FirstHeaderH1Rule(
override val config: RuleSetup.Builder.() -> Unit = {}
) : Rule() {

override val description = "First header should be a top level header"
private val description = "First header should be a level $level header. Configuration: level=$level."

override fun visitDocument(document: MarkdownDocument, errorReporter: ErrorReporter) {
document.headings.firstOrNull()?.let {
Expand Down
Loading

0 comments on commit 623dd1c

Please sign in to comment.