Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Hide markdown support behind a feature flag (`--enable-markdown`) - still process explicitly passed Markdown files - remove some PoC simplifications and workarounds - misc refactors
- Loading branch information
Showing
18 changed files
with
728 additions
and
414 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
modules/build/src/main/scala/scala/build/internal/markdown/MarkdownCodeBlockUtil.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package scala.build.internal.markdown | ||
|
||
import scala.collection.mutable | ||
|
||
/** A util for handling of code blocks in Markdown. */ | ||
object MarkdownCodeBlockUtil { | ||
private val allowIndentedFence: Boolean = false | ||
|
||
/** Representation for a code block contained in Markdown | ||
* | ||
* @param info | ||
* a list of tags tied to a given code block | ||
* @param body | ||
* the code block content | ||
* @param startLine | ||
* starting line on which the code block was defined | ||
* @param endLine | ||
* end line on which the code block was closed | ||
*/ | ||
case class MarkdownFence( | ||
info: Seq[String], | ||
body: String, | ||
startLine: Int, // start of fenced body EXCLUDING backticks | ||
endLine: Int // same as above | ||
) { | ||
override def toString: String = | ||
s"Fence[$info, lines $startLine-$endLine]{${body.replace("\n", "\\n")}}" | ||
|
||
/** @return | ||
* `true` if this snippet should be ignored, `false` otherwise | ||
*/ | ||
def shouldIgnore: Boolean = info.head != "scala" || info.contains("ignore") | ||
|
||
/** @return | ||
* `true` if this snippet should have its scope reset, `false` otherwise | ||
*/ | ||
def resetScope: Boolean = info.contains("reset") | ||
|
||
/** @return | ||
* `true` if this snippet is a test snippet, `false` otherwise | ||
*/ | ||
def isTest: Boolean = info.contains("test") | ||
|
||
/** @return | ||
* `true` if this snippet is a raw snippet, `false` otherwise | ||
*/ | ||
def isRaw: Boolean = info.contains("raw") | ||
} | ||
|
||
private case class StartedFence( | ||
info: String, | ||
tickStartLine: Int, // fence start INCLUDING backticks | ||
backticks: String, | ||
indent: Int | ||
) | ||
|
||
/** Closes started code-fence | ||
* | ||
* @param started | ||
* [[StartedFence]] representing this code-fence's start | ||
* @param tickEndLine | ||
* number of the line where closing backticks are | ||
* @param lines | ||
* input file sliced into lines | ||
* @return | ||
* [[MarkdownFence]] representing whole closed code-fence | ||
*/ | ||
private def closeFence( | ||
started: StartedFence, | ||
tickEndLine: Int, | ||
lines: Array[String] | ||
): MarkdownFence = { | ||
val start: Int = started.tickStartLine + 1 | ||
val bodyLines: Array[String] = lines.slice(start, tickEndLine) | ||
MarkdownFence( | ||
started.info.split("\\s+").toList, // strip info by whitespaces | ||
bodyLines.tail.foldLeft(bodyLines.head)((body, line) => body.:++("\n" + line)), | ||
start, // snippet has to begin in the new line | ||
tickEndLine - 1 // ending backticks have to be placed below the snippet | ||
) | ||
} | ||
|
||
/** Finds all code snippets in given input | ||
* | ||
* @param md | ||
* Markdown file in a `String` format | ||
* @return | ||
* list of all found snippets | ||
*/ | ||
def findFences(md: String): Seq[MarkdownFence] = { | ||
var startedFenceOpt: Option[StartedFence] = None | ||
val fences = mutable.ListBuffer.empty[MarkdownFence] | ||
val lines: Array[String] = md.split("\n\r?") | ||
for (i <- lines.indices) { | ||
val line = lines(i) | ||
startedFenceOpt match { | ||
case Some(s) => | ||
val start: Int = line.indexOf(s.backticks) | ||
if (start == s.indent && line.forall(c => c == '`' || c.isWhitespace)) { | ||
fences += closeFence(s, i, lines) | ||
startedFenceOpt = None | ||
} | ||
case None => | ||
val start: Int = line.indexOf("```") | ||
if (start == 0 || (start > 0 && allowIndentedFence)) { // doesn't allow snippet indent | ||
val fence = line.substring(start) | ||
val backticks: String = fence.takeWhile(_ == '`') | ||
val info: String = fence.substring(backticks.length) | ||
startedFenceOpt = Some(StartedFence(info, i, backticks, start)) | ||
} | ||
} | ||
} | ||
startedFenceOpt match { // snippet can be ended with EOF | ||
case Some(s) => | ||
fences += closeFence(s, lines.length, lines) | ||
startedFenceOpt = None | ||
case None => | ||
} | ||
|
||
fences.toList.filter(fence => !fence.shouldIgnore) | ||
} | ||
} |
Oops, something went wrong.