SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/mfm/NodeParseEnv.kt

122 lines
3.5 KiB
Kotlin
Raw Normal View History

package jp.juggler.subwaytooter.mfm
import jp.juggler.util.data.firstNonNull
import java.util.regex.Pattern
class NodeParseEnv(
val useFunction: Boolean,
private val parentNode: Node,
val text: String,
start: Int,
val end: Int,
) {
private val childNodes = parentNode.childNodes
private val allowInside: HashSet<NodeType> =
mapAllowInside[parentNode.type] ?: hashSetOf()
// 直前のノードの終了位置
internal var lastEnd = start
// 注目中の位置
internal var pos: Int = 0
// 直前のードの終了位置から次のードの開始位置の手前までをresultに追加する
private fun closeText(endText: Int) {
val length = endText - lastEnd
if (length <= 0) return
val textInside = text.substring(lastEnd, endText)
childNodes.add(Node(NodeType.TEXT, arrayOf(textInside), null))
}
fun remainMatcher(pattern: Pattern) =
MatcherCache.matcher(pattern, text, pos, end)
fun parseInside() {
if (allowInside.isEmpty()) return
var i = lastEnd //スキャン中の位置
while (i < end) {
// 注目位置の文字に関連するパーサー
val lastParsers = nodeParserMap[text[i].code]
if (lastParsers == null) {
++i
continue
}
// パーサー用のパラメータを用意する
// 部分文字列のコストは高くないと信じたい
pos = i
val detected = lastParsers.firstNonNull {
val d = this.it()
if (d == null) {
null
} else {
val n = d.node
if (!allowInside.contains(d.node.type)) {
MisskeyMarkdownDecoder.log.w(
"not allowed : ${parentNode.type} => ${n.type} ${
text.substring(
d.start,
d.end
)
}"
)
null
} else {
d
}
}
}
if (detected == null) {
++i
continue
}
closeText(detected.start)
childNodes.add(detected.node)
i = detected.end
lastEnd = i
NodeParseEnv(
useFunction,
detected.node,
detected.textInside,
detected.startInside,
detected.endInside
).parseInside()
}
closeText(end)
}
fun makeDetected(
type: NodeType,
args: Array<String>,
start: Int,
end: Int,
textInside: String,
startInside: Int,
lengthInside: Int,
): NodeDetected {
val node = Node(type, args, parentNode)
if (MisskeyMarkdownDecoder.DEBUG) MisskeyMarkdownDecoder.log.d(
"NodeDetected: ${node.type} inside=${
textInside.substring(startInside, startInside + lengthInside)
}"
)
return NodeDetected(
node,
start,
end,
textInside,
startInside,
lengthInside
)
}
}