Note widget: better markdown rendering in read mode
This commit is contained in:
parent
5a3be88e5f
commit
2a8d4e3445
@ -1,36 +1,32 @@
|
||||
package de.mm20.launcher2.ui.component.markdown
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.OffsetMapping
|
||||
import androidx.compose.ui.text.input.TransformedText
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.intellij.markdown.MarkdownElementTypes
|
||||
import org.intellij.markdown.MarkdownTokenTypes
|
||||
import org.intellij.markdown.ast.ASTNode
|
||||
import org.intellij.markdown.flavours.space.SFMFlavourDescriptor
|
||||
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
|
||||
import org.intellij.markdown.parser.MarkdownParser
|
||||
import kotlin.math.min
|
||||
|
||||
@Composable
|
||||
fun MarkdownEditor(
|
||||
@ -41,44 +37,60 @@ fun MarkdownEditor(
|
||||
val typography = MaterialTheme.typography
|
||||
val delimiterColor = MaterialTheme.colorScheme.secondary
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
var focused by remember { mutableStateOf(false) }
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
BackHandler(
|
||||
enabled = focused
|
||||
) {
|
||||
focusManager.clearFocus()
|
||||
focused = false
|
||||
}
|
||||
|
||||
if (focused) {
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier,
|
||||
textStyle = MaterialTheme.typography.bodyMedium.copy(
|
||||
color = LocalContentColor.current,
|
||||
),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
visualTransformation = remember(
|
||||
typography,
|
||||
focused,
|
||||
delimiterColor
|
||||
) { MarkdownTransformation(typography, focused, delimiterColor) },
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
BasicTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier.focusRequester(focusRequester),
|
||||
textStyle = MaterialTheme.typography.bodyMedium.copy(
|
||||
color = LocalContentColor.current,
|
||||
),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
visualTransformation = remember(
|
||||
typography,
|
||||
delimiterColor
|
||||
) { MarkdownTransformation(typography, delimiterColor) },
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
|
||||
} else {
|
||||
MarkdownText(
|
||||
value,
|
||||
modifier = modifier.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
focused = true
|
||||
},
|
||||
onTextChange = onValueChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MarkdownTransformation(
|
||||
private val typography: Typography,
|
||||
renderDelimiters: Boolean,
|
||||
delimiterColor: Color,
|
||||
) : VisualTransformation {
|
||||
|
||||
private val parser = MarkdownParser(SFMFlavourDescriptor())
|
||||
private val parser = MarkdownParser(GFMFlavourDescriptor())
|
||||
private val delimiterStyle = SpanStyle(
|
||||
color = delimiterColor,
|
||||
fontSize = if (renderDelimiters) TextUnit.Unspecified else 0.sp
|
||||
)
|
||||
|
||||
override fun filter(text: AnnotatedString): TransformedText {
|
||||
@ -92,128 +104,3 @@ class MarkdownTransformation(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AnnotatedString.Builder.applyStyles(
|
||||
node: ASTNode,
|
||||
typography: Typography,
|
||||
delimiterStyle: SpanStyle
|
||||
) {
|
||||
when (node.type) {
|
||||
MarkdownElementTypes.STRONG -> {
|
||||
addStyle(
|
||||
SpanStyle(fontWeight = FontWeight.Bold),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.EMPH -> {
|
||||
addStyle(
|
||||
SpanStyle(fontStyle = FontStyle.Italic),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.CODE_SPAN -> {
|
||||
addStyle(
|
||||
SpanStyle(fontFamily = FontFamily.Monospace),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_1 -> {
|
||||
addStyle(
|
||||
typography.headlineLarge.toSpanStyle(),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
addStyle(
|
||||
typography.headlineLarge.toParagraphStyle(),
|
||||
node.startOffset,
|
||||
min(node.endOffset + 1, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_2 -> {
|
||||
addStyle(
|
||||
typography.headlineMedium.toSpanStyle(),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
addStyle(
|
||||
typography.headlineMedium.toParagraphStyle(),
|
||||
node.startOffset,
|
||||
min(node.endOffset + 1, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_3 -> {
|
||||
addStyle(
|
||||
typography.headlineSmall.toSpanStyle(),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
addStyle(
|
||||
typography.headlineSmall.toParagraphStyle(),
|
||||
node.startOffset,
|
||||
min(node.endOffset + 1, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_4 -> {
|
||||
addStyle(
|
||||
typography.titleLarge.toSpanStyle(),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
addStyle(
|
||||
typography.titleLarge.toParagraphStyle(),
|
||||
node.startOffset,
|
||||
min(node.endOffset + 1, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_5 -> {
|
||||
addStyle(
|
||||
typography.titleMedium.toSpanStyle(),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
addStyle(
|
||||
typography.titleMedium.toParagraphStyle(),
|
||||
node.startOffset,
|
||||
min(node.endOffset + 1, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_6 -> {
|
||||
addStyle(
|
||||
typography.titleSmall.toSpanStyle(),
|
||||
node.startOffset,
|
||||
node.endOffset
|
||||
)
|
||||
addStyle(
|
||||
typography.titleSmall.toParagraphStyle(),
|
||||
node.startOffset,
|
||||
min(node.endOffset + 1, length)
|
||||
)
|
||||
}
|
||||
}
|
||||
for (child in node.children) {
|
||||
applyStyles(child, typography, delimiterStyle)
|
||||
}
|
||||
|
||||
if (node.children.isEmpty() && node.type != MarkdownTokenTypes.TEXT
|
||||
&& node.children.isEmpty() && node.type != MarkdownTokenTypes.LIST_BULLET
|
||||
&& node.children.isEmpty() && node.type != MarkdownTokenTypes.LIST_NUMBER
|
||||
&& node.children.isEmpty() && node.type != MarkdownTokenTypes.WHITE_SPACE
|
||||
) {
|
||||
addStyle(
|
||||
delimiterStyle,
|
||||
node.startOffset,
|
||||
node.endOffset,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,282 @@
|
||||
package de.mm20.launcher2.ui.component.markdown
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.intellij.markdown.MarkdownElementTypes
|
||||
import org.intellij.markdown.MarkdownTokenTypes
|
||||
import org.intellij.markdown.ast.ASTNode
|
||||
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
|
||||
import org.intellij.markdown.flavours.gfm.GFMTokenTypes
|
||||
import org.intellij.markdown.parser.MarkdownParser
|
||||
|
||||
@Composable
|
||||
fun MarkdownText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
onTextChange: (String) -> Unit,
|
||||
) {
|
||||
val parsed = remember(text) {
|
||||
MarkdownParser(GFMFlavourDescriptor()).buildMarkdownTreeFromString(text)
|
||||
}
|
||||
MarkdownText(parsed, text, modifier, onTextChange)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MarkdownText(
|
||||
rootNode: ASTNode, text: String, modifier: Modifier = Modifier,
|
||||
onTextChange: (String) -> Unit,
|
||||
) {
|
||||
ProvideTextStyle(MaterialTheme.typography.bodyMedium) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
for (child in rootNode.children) {
|
||||
MarkdownNode(child, text, onTextChange)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MarkdownNode(
|
||||
node: ASTNode, text: String,
|
||||
onTextChange: (String) -> Unit,
|
||||
) {
|
||||
when (node.type) {
|
||||
MarkdownTokenTypes.TEXT -> TextNode(node, text)
|
||||
MarkdownTokenTypes.CODE_FENCE_CONTENT -> TextNode(node, text)
|
||||
MarkdownTokenTypes.CODE_LINE -> TextNode(node, text)
|
||||
MarkdownElementTypes.PARAGRAPH -> ParagraphNode(node, text)
|
||||
MarkdownElementTypes.ATX_1 -> AtxNode(node, text, 1, onTextChange)
|
||||
MarkdownElementTypes.ATX_2 -> AtxNode(node, text, 2, onTextChange)
|
||||
MarkdownElementTypes.ATX_3 -> AtxNode(node, text, 3, onTextChange)
|
||||
MarkdownElementTypes.ATX_4 -> AtxNode(node, text, 4, onTextChange)
|
||||
MarkdownElementTypes.ATX_5 -> AtxNode(node, text, 5, onTextChange)
|
||||
MarkdownElementTypes.ATX_6 -> AtxNode(node, text, 6, onTextChange)
|
||||
MarkdownTokenTypes.HORIZONTAL_RULE -> Divider(modifier = Modifier.padding(vertical = 4.dp))
|
||||
MarkdownElementTypes.UNORDERED_LIST -> UnorderedListNode(node, text, onTextChange)
|
||||
MarkdownElementTypes.ORDERED_LIST -> OrderedListNode(node, text, onTextChange)
|
||||
MarkdownElementTypes.BLOCK_QUOTE -> BlockQuoteNode(node, text, onTextChange)
|
||||
MarkdownElementTypes.CODE_BLOCK -> CodeBlockNode(node, text, onTextChange)
|
||||
MarkdownElementTypes.CODE_FENCE -> CodeBlockNode(node, text, onTextChange)
|
||||
|
||||
else -> {
|
||||
ChildNodes(node, text, onTextChange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChildNodes(node: ASTNode, text: String, onTextChange: (String) -> Unit) {
|
||||
for (child in node.children) {
|
||||
MarkdownNode(child, text, onTextChange)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AtxNode(node: ASTNode, text: String, level: Int, onTextChange: (String) -> Unit) {
|
||||
ProvideTextStyle(
|
||||
when (level) {
|
||||
1 -> MaterialTheme.typography.headlineLarge
|
||||
2 -> MaterialTheme.typography.headlineMedium
|
||||
3 -> MaterialTheme.typography.headlineSmall
|
||||
4 -> MaterialTheme.typography.titleLarge
|
||||
5 -> MaterialTheme.typography.titleMedium
|
||||
else -> MaterialTheme.typography.titleSmall
|
||||
}
|
||||
) {
|
||||
ChildNodes(node, text, onTextChange)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextNode(node: ASTNode, text: String) {
|
||||
val start = node.startOffset
|
||||
val end = node.endOffset
|
||||
val substring = text.substring(start, end)
|
||||
Text(
|
||||
text = substring,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ParagraphNode(node: ASTNode, text: String) {
|
||||
val start = node.startOffset
|
||||
val end = node.endOffset
|
||||
val substring = text.substring(start, end)
|
||||
val typography = MaterialTheme.typography
|
||||
Text(
|
||||
text = buildAnnotatedString {
|
||||
append(substring)
|
||||
applyStyles(node, typography, SpanStyle(fontSize = 0.sp), node.startOffset)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UnorderedListNode(node: ASTNode, text: String, onTextChange: (String) -> Unit) {
|
||||
var counter = 1
|
||||
for (child in node.children) {
|
||||
if (child.type == MarkdownElementTypes.LIST_ITEM) {
|
||||
ListItemNode(child, counter, text, onTextChange)
|
||||
counter++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OrderedListNode(node: ASTNode, text: String, onTextChange: (String) -> Unit) {
|
||||
var counter = 1
|
||||
for (child in node.children) {
|
||||
if (child.type == MarkdownElementTypes.LIST_ITEM) {
|
||||
ListItemNode(child, counter, text, onTextChange)
|
||||
counter++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ListItemNode(
|
||||
node: ASTNode,
|
||||
index: Int,
|
||||
text: String,
|
||||
onTextChange: (String) -> Unit
|
||||
) {
|
||||
|
||||
Row {
|
||||
val checkbox = node.children.find { it.type == GFMTokenTypes.CHECK_BOX }
|
||||
if (checkbox != null) {
|
||||
CheckboxNode(checkbox, text, onTextChange)
|
||||
} else {
|
||||
val ordinal = node.children.find { it.type == MarkdownTokenTypes.LIST_NUMBER }
|
||||
if (ordinal != null) {
|
||||
Text(
|
||||
text = "${index}.",
|
||||
modifier = Modifier
|
||||
.width(24.dp)
|
||||
.padding(end = 4.dp),
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "•",
|
||||
modifier = Modifier
|
||||
.width(24.dp)
|
||||
.padding(end = 4.dp),
|
||||
textAlign = TextAlign.End,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(top = if (checkbox != null) 4.dp else 0.dp)
|
||||
) {
|
||||
ChildNodes(node, text, onTextChange)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BlockQuoteNode(node: ASTNode, text: String, onTextChange: (String) -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.height(IntrinsicSize.Min)
|
||||
.clip(MaterialTheme.shapes.extraSmall)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(8.dp)
|
||||
.fillMaxHeight()
|
||||
.background(MaterialTheme.colorScheme.primary)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
|
||||
) {
|
||||
ChildNodes(node, text, onTextChange)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CodeBlockNode(node: ASTNode, text: String, onTextChange: (String) -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp)
|
||||
.clip(MaterialTheme.shapes.extraSmall)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
|
||||
) {
|
||||
ProvideTextStyle(
|
||||
LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
|
||||
) {
|
||||
ChildNodes(node, text, onTextChange)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CheckboxNode(node: ASTNode, text: String, onTextChange: (String) -> Unit = {}) {
|
||||
val checkbox = text.substring(node.startOffset, node.endOffset - 1)
|
||||
val checked = checkbox == "[x]"
|
||||
|
||||
|
||||
Checkbox(
|
||||
checked = checked, onCheckedChange = {
|
||||
val newCheckbox = if (it) "[x]" else "[ ]"
|
||||
val newText = text.replaceRange(node.startOffset, node.endOffset - 1, newCheckbox)
|
||||
onTextChange(newText)
|
||||
}, modifier = Modifier
|
||||
.padding(top = 4.dp, bottom = 4.dp, end = 8.dp)
|
||||
.requiredSize(18.dp)
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
package de.mm20.launcher2.ui.component.markdown
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import org.intellij.markdown.MarkdownElementTypes
|
||||
import org.intellij.markdown.MarkdownTokenTypes
|
||||
import org.intellij.markdown.ast.ASTNode
|
||||
import kotlin.math.min
|
||||
|
||||
fun AnnotatedString.Builder.applyStyles(
|
||||
node: ASTNode,
|
||||
typography: Typography,
|
||||
delimiterStyle: SpanStyle,
|
||||
rootOffset: Int = 0,
|
||||
) {
|
||||
require(node.startOffset >= rootOffset) {
|
||||
"Node start offset ${node.startOffset} is smaller than root offset $rootOffset"
|
||||
}
|
||||
when (node.type) {
|
||||
MarkdownElementTypes.STRONG -> {
|
||||
addStyle(
|
||||
SpanStyle(fontWeight = FontWeight.Bold),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.EMPH -> {
|
||||
addStyle(
|
||||
SpanStyle(fontStyle = FontStyle.Italic),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.CODE_SPAN -> {
|
||||
addStyle(
|
||||
SpanStyle(fontFamily = FontFamily.Monospace),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
}
|
||||
MarkdownElementTypes.ATX_1 -> {
|
||||
addStyle(
|
||||
typography.headlineLarge.toSpanStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
addStyle(
|
||||
typography.headlineLarge.toParagraphStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
min(node.endOffset + 1 - rootOffset, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_2 -> {
|
||||
addStyle(
|
||||
typography.headlineMedium.toSpanStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
addStyle(
|
||||
typography.headlineMedium.toParagraphStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
min(node.endOffset + 1 - rootOffset, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_3 -> {
|
||||
addStyle(
|
||||
typography.headlineSmall.toSpanStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
addStyle(
|
||||
typography.headlineSmall.toParagraphStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
min(node.endOffset + 1 - rootOffset, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_4 -> {
|
||||
addStyle(
|
||||
typography.titleLarge.toSpanStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
addStyle(
|
||||
typography.titleLarge.toParagraphStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
min(node.endOffset + 1 - rootOffset, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_5 -> {
|
||||
addStyle(
|
||||
typography.titleMedium.toSpanStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
addStyle(
|
||||
typography.titleMedium.toParagraphStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
min(node.endOffset + 1 - rootOffset, length)
|
||||
)
|
||||
}
|
||||
|
||||
MarkdownElementTypes.ATX_6 -> {
|
||||
addStyle(
|
||||
typography.titleSmall.toSpanStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
addStyle(
|
||||
typography.titleSmall.toParagraphStyle(),
|
||||
node.startOffset - rootOffset,
|
||||
min(node.endOffset + 1 - rootOffset, length)
|
||||
)
|
||||
}
|
||||
}
|
||||
for (child in node.children) {
|
||||
applyStyles(child, typography, delimiterStyle, rootOffset)
|
||||
}
|
||||
|
||||
if (node.children.isEmpty() &&
|
||||
node.type != MarkdownTokenTypes.TEXT &&
|
||||
node.type != MarkdownTokenTypes.WHITE_SPACE &&
|
||||
node.type != MarkdownTokenTypes.CODE_FENCE_CONTENT &&
|
||||
node.type != MarkdownTokenTypes.CODE_LINE
|
||||
) {
|
||||
addStyle(
|
||||
delimiterStyle,
|
||||
node.startOffset - rootOffset,
|
||||
node.endOffset - rootOffset,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user