Skip to content

Instantly share code, notes, and snippets.

@Nohus
Created August 4, 2023 11:36
Show Gist options
  • Save Nohus/bac151623a287777208f8d66b4731497 to your computer and use it in GitHub Desktop.
Save Nohus/bac151623a287777208f8d66b4731497 to your computer and use it in GitHub Desktop.
private fun parseMarkdown(
markdown: String,
typography: Typography,
headingColor: Color
): AnnotatedString {
val parser = Parser.builder().build()
val document = parser.parse(markdown)
val annotatedString = buildAnnotatedString {
visitMarkdownNode(document, typography, headingColor)
}
return annotatedString.trim() as AnnotatedString
}
private fun AnnotatedString.Builder.visitMarkdownNode(
node: Node,
typography: Typography,
headingColor: Color
) {
when (node) {
is Heading -> {
val style = when (node.level) {
in 1..3 -> typography.titleLarge
4 -> typography.titleMedium
5 -> typography.bodySmall
else -> typography.bodySmall
}
withStyle(style.toParagraphStyle()) {
withStyle(style.toSpanStyle().copy(color = headingColor)) {
visitChildren(node, typography, headingColor)
appendLine()
}
}
}
is Paragraph -> {
if (node.parents.any { it is BulletList || it is OrderedList }) {
visitChildren(node, typography, headingColor)
} else {
withStyle(typography.bodyLarge.toParagraphStyle()) {
visitChildren(node, typography, headingColor)
appendLine()
}
}
}
is Emphasis -> {
withStyle(SpanStyle(fontStyle = FontStyle.Italic)) {
visitChildren(node, typography, headingColor)
}
}
is StrongEmphasis -> {
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
visitChildren(node, typography, headingColor)
}
}
is Text -> {
append(node.literal)
visitChildren(node, typography, headingColor)
}
is SoftLineBreak -> {
append(" ")
}
is HardLineBreak -> {
appendLine()
}
is ThematicBreak -> {
withStyle(ParagraphStyle(textAlign = TextAlign.Center)) {
withStyle(SpanStyle(letterSpacing = 0.sp)) {
appendLine("─".repeat(10))
}
}
}
is OrderedList -> {
withStyle(ParagraphStyle(textIndent = TextIndent(firstLine = 10.sp, restLine = 20.sp))) {
visitChildren(node, typography, headingColor)
}
}
is BulletList -> {
withStyle(ParagraphStyle(textIndent = TextIndent(firstLine = 10.sp, restLine = 20.sp))) {
visitChildren(node, typography, headingColor)
}
}
is ListItem -> {
if (node.parents.any { it is BulletList }) {
append("• ")
} else if (node.parents.any { it is OrderedList }) {
val startNumber = (node.parents.first { it is OrderedList } as OrderedList).startNumber
val index = startNumber + node.previousSiblings.filterIsInstance<ListItem>().size
append("$index. ")
}
visitChildren(node, typography, headingColor)
appendLine()
}
is Document -> {
visitChildren(node, typography, headingColor)
}
else -> {
Log.e("MarkdownText", "Traversing unhandled node: $node")
visitChildren(node, typography, headingColor)
}
}
}
private fun AnnotatedString.Builder.visitChildren(
node: Node,
typography: Typography,
headingColor: Color
) {
var child = node.firstChild
while (child != null) {
visitMarkdownNode(child, typography, headingColor)
child = child.next
}
}
private val Node.parents: List<Node> get() {
val list = mutableListOf<Node>()
var current = this
while (true) {
current = current.parent ?: return list
list += current
}
}
private val Node.previousSiblings: List<Node> get() {
val list = mutableListOf<Node>()
var current = this
while (true) {
current = current.previous ?: return list
list += current
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment