コードブロックに対応
This commit is contained in:
parent
4c23bdc1ad
commit
985df8ed82
|
@ -8,6 +8,9 @@ class Lexer {
|
||||||
val lines = PeekableStringIterator(input.lines())
|
val lines = PeekableStringIterator(input.lines())
|
||||||
|
|
||||||
var inQuote = false
|
var inQuote = false
|
||||||
|
var inCode = false
|
||||||
|
|
||||||
|
val codeBuffer = StringBuilder()
|
||||||
|
|
||||||
line@ while (lines.hasNext()) {
|
line@ while (lines.hasNext()) {
|
||||||
|
|
||||||
|
@ -20,6 +23,68 @@ class Lexer {
|
||||||
char@ while (iterator.hasNext()) {
|
char@ while (iterator.hasNext()) {
|
||||||
val next = iterator.next()
|
val next = iterator.next()
|
||||||
when {
|
when {
|
||||||
|
next == '`' || next == '`' -> {
|
||||||
|
//todo ````` のようなやつが来たときのことを考える
|
||||||
|
if (iterator.peekOrNull() == next) {
|
||||||
|
val codeBlockBuilder = StringBuilder()
|
||||||
|
codeBlockBuilder.append(next)
|
||||||
|
codeBlockBuilder.append(iterator.next())
|
||||||
|
if (iterator.peekOrNull() == next) {
|
||||||
|
codeBlockBuilder.append(iterator.next())
|
||||||
|
if (iterator.peekOrNull() == next) {
|
||||||
|
tokens.add(Text(codeBlockBuilder.toString()))
|
||||||
|
} else {
|
||||||
|
if (inCode) {
|
||||||
|
inCode = false
|
||||||
|
tokens.add(CodeBlock(codeBuffer.toString().trimStart('\n').trimEnd('\n')))
|
||||||
|
codeBuffer.clear()
|
||||||
|
} else {
|
||||||
|
inCode = true
|
||||||
|
var inFilename = false
|
||||||
|
val language = StringBuilder()
|
||||||
|
val filename = StringBuilder()
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
codeBlock@ while (iterator.hasNext()) {
|
||||||
|
val nextLanguage = iterator.next()
|
||||||
|
if ((nextLanguage == ':' || nextLanguage == ':') && !inFilename) {
|
||||||
|
inFilename = true
|
||||||
|
continue@codeBlock
|
||||||
|
}
|
||||||
|
if (inFilename) {
|
||||||
|
filename.append(nextLanguage)
|
||||||
|
} else {
|
||||||
|
language.append(nextLanguage)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
tokens.add(CodeBlockLanguage(language.toString(), filename.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (iterator.peekOrNull() == null) {
|
||||||
|
tokens.add(Text(codeBlockBuilder.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
val codeBuilder = StringBuilder()
|
||||||
|
while (iterator.hasNext() && iterator.peekOrNull() != next) {
|
||||||
|
codeBuilder.append(iterator.next())
|
||||||
|
}
|
||||||
|
if (iterator.hasNext() && iterator.next() == next) { //インラインコードブロックかと思ったら違った
|
||||||
|
tokens.add(InlineCodeBlock(codeBuilder.toString()))
|
||||||
|
} else {
|
||||||
|
tokens.add(Text(codeBuilder.insert(0, next).toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inCode -> {
|
||||||
|
codeBuffer.append(next)
|
||||||
|
}
|
||||||
|
|
||||||
next == '#' || next == '#' -> header(iterator, tokens)
|
next == '#' || next == '#' -> header(iterator, tokens)
|
||||||
(next == '>' || next == '>') && !inQuote -> {
|
(next == '>' || next == '>') && !inQuote -> {
|
||||||
inQuote = true
|
inQuote = true
|
||||||
|
@ -58,6 +123,7 @@ class Lexer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val lastToken = tokens.lastOrNull()
|
val lastToken = tokens.lastOrNull()
|
||||||
if (lastToken is Text) {
|
if (lastToken is Text) {
|
||||||
|
@ -69,7 +135,12 @@ class Lexer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens.add(Break(1))
|
|
||||||
|
if (inCode) {
|
||||||
|
codeBuffer.append("\n")
|
||||||
|
} else {
|
||||||
|
tokens.add(Break(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
inQuote = false
|
inQuote = false
|
||||||
}
|
}
|
||||||
|
@ -137,9 +208,10 @@ class Lexer {
|
||||||
while (iterator.hasNext() && iterator.peekOrNull() != doubleQuotation && iterator.peekOrNull() != ')') {
|
while (iterator.hasNext() && iterator.peekOrNull() != doubleQuotation && iterator.peekOrNull() != ')') {
|
||||||
titleBuilder.append(iterator.next())
|
titleBuilder.append(iterator.next())
|
||||||
}
|
}
|
||||||
if (iterator.peekOrNull() == '"') {
|
if (iterator.peekOrNull() == doubleQuotation) {
|
||||||
iterator.next()
|
iterator.next()
|
||||||
}
|
}
|
||||||
|
tokens.add(UrlTitle(titleBuilder.toString()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,7 @@ data object ParenthesesEnd : Token()
|
||||||
data class Url(var url: String) : Token()
|
data class Url(var url: String) : Token()
|
||||||
data class Asterisk(var count: Int, var char: Char) : Token()
|
data class Asterisk(var count: Int, var char: Char) : Token()
|
||||||
data object Exclamation : Token()
|
data object Exclamation : Token()
|
||||||
data class UrlTitle(val title: String) : Token()
|
data class UrlTitle(val title: String) : Token()
|
||||||
|
data class InlineCodeBlock(val text: String) : Token()
|
||||||
|
data class CodeBlock(val text: String) : Token()
|
||||||
|
data class CodeBlockLanguage(val language: String, val filename: String) : Token()
|
|
@ -608,4 +608,156 @@ class LexerTest {
|
||||||
), actual
|
), actual
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun urlとタイトル() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex("[alt](https://example.com \"example\")")
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
SquareBracketStart,
|
||||||
|
Text("alt"),
|
||||||
|
SquareBracketEnd,
|
||||||
|
ParenthesesStart,
|
||||||
|
Url("https://example.com"),
|
||||||
|
UrlTitle("example"),
|
||||||
|
ParenthesesEnd
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun urlとタイトル全角() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex("[alt](https://example.com \"example)")
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
SquareBracketStart,
|
||||||
|
Text("alt"),
|
||||||
|
SquareBracketEnd,
|
||||||
|
ParenthesesStart,
|
||||||
|
Url("https://example.com"),
|
||||||
|
UrlTitle("example"),
|
||||||
|
ParenthesesEnd
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun インラインコードブロック() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex("`code`")
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
InlineCodeBlock("code"),
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun インラインコードブロック2() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex("aiueo`code`abcd")
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
Text("aiueo"),
|
||||||
|
InlineCodeBlock("code"),
|
||||||
|
Text("abcd"),
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun コードブロック() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex(
|
||||||
|
"""```
|
||||||
|
|code
|
||||||
|
|```
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
CodeBlock("code"),
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun 言語指定付きコードブロック() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex(
|
||||||
|
"""```hoge
|
||||||
|
|code
|
||||||
|
|```
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
CodeBlockLanguage("hoge", ""),
|
||||||
|
CodeBlock("code"),
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun ファイル名と言語コードブロック() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex(
|
||||||
|
"""```hoge:fuga
|
||||||
|
|code
|
||||||
|
|```
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
CodeBlockLanguage("hoge", "fuga"),
|
||||||
|
CodeBlock("code"),
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun コードブロックかと思ったら違った() {
|
||||||
|
val lexer = Lexer()
|
||||||
|
|
||||||
|
val actual = lexer.lex("````aiueo")
|
||||||
|
|
||||||
|
println(actual)
|
||||||
|
|
||||||
|
assertContentEquals(
|
||||||
|
listOf(
|
||||||
|
Text("```"), Text("`aiueo")
|
||||||
|
), actual
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue