Draw a fence plain while it is still being written
Warming covers a settled message, so it did not reach the block a reply is still writing: that one was re-lexed at every delta, on the composing thread. Measured streaming a two-hundred-line Kotlin fence -- 211 lexes, 13.7 seconds across the turn, the worst 177ms -- for colours on text being replaced as fast as they were computed. A live reply's last segment now draws its code plain and takes its colours when the block freezes, which for a finished fence is as soon as the next block starts. The settle-time lex then happens in warm, off the drawing thread. Same fixture after: one lex of 374ms in warm, draw phase 1.51ms to 1.01ms per frame while streaming, and the fence coloured on screen once the reply settles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ab6a797941
commit
b0b91ad28f
3 files changed
+71
-21
No files matched your search
@@ -49,16 +49,28 @@ import org.intellij.markdown.ast.getTextInNode
|
||||
* hands out the code and the language and leaves the drawing to the block it is given.
|
||||
*/
|
||||
@Composable
|
||||
fun CodeFence(content: String, node: ASTNode, style: TextStyle, replies: ParsedReplies) {
|
||||
fun CodeFence(
|
||||
content: String,
|
||||
node: ASTNode,
|
||||
style: TextStyle,
|
||||
replies: ParsedReplies,
|
||||
streaming: Boolean = false,
|
||||
) {
|
||||
val (code, language) = remember(content, node) { fenceContent(content, node) } ?: return
|
||||
CodeBlockText(code, language, style, replies)
|
||||
CodeBlockText(code, language, style, replies, streaming)
|
||||
}
|
||||
|
||||
/** An indented code block, which is a fence with no language word. */
|
||||
@Composable
|
||||
fun CodeBlock(content: String, node: ASTNode, style: TextStyle, replies: ParsedReplies) {
|
||||
fun CodeBlock(
|
||||
content: String,
|
||||
node: ASTNode,
|
||||
style: TextStyle,
|
||||
replies: ParsedReplies,
|
||||
streaming: Boolean = false,
|
||||
) {
|
||||
val (code, language) = remember(content, node) { fenceContent(content, node) } ?: return
|
||||
CodeBlockText(code, language, style, replies)
|
||||
CodeBlockText(code, language, style, replies, streaming)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +102,8 @@ fun fenceContent(content: String, node: ASTNode): Pair<String, SyntaxLanguage?>?
|
||||
}
|
||||
|
||||
/**
|
||||
* Plain while [streaming], coloured once the block is finished; see [MarkdownRoot].
|
||||
*
|
||||
* The renderer's own block, less what nothing here needs: the same background, corner, padding and
|
||||
* sideways scroll, without the shadow, the border and the empty pointer handler it also carried.
|
||||
* The vertical margin is the renderer's too, kept so a reply's fences sit where they always have.
|
||||
@@ -100,6 +114,7 @@ private fun CodeBlockText(
|
||||
language: SyntaxLanguage?,
|
||||
style: TextStyle,
|
||||
replies: ParsedReplies,
|
||||
streaming: Boolean,
|
||||
) {
|
||||
val colors = LocalMarkdownColors.current
|
||||
val dimens = LocalMarkdownDimens.current
|
||||
@@ -111,7 +126,9 @@ private fun CodeBlockText(
|
||||
.semantics { isTraversalGroup = true }
|
||||
) {
|
||||
BasicText(
|
||||
replies.highlighted(code, language),
|
||||
// No language while the block is still being written, which is what draws it plain;
|
||||
// see [MarkdownRoot]'s `streaming`.
|
||||
replies.highlighted(code, language.takeUnless { streaming }),
|
||||
style = style,
|
||||
modifier = Modifier.horizontalScroll(rememberScrollState()).padding(padding.codeBlock),
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user