@@ -188,11 +188,6 @@ fun DiffView(diff: String, modifier: Modifier = Modifier, saveKey: String? = nul
188 // `@@` range text (rare but possible) are still distinct toggles. The KDoc promised this
188 // `@@` range text (rare but possible) are still distinct toggles. The KDoc promised this
189 // affordance; it's now implemented.
189 // affordance; it's now implemented.
190 val collapsedHunks = remember { mutableStateOf(emptySet<Int>()) }
190 val collapsedHunks = remember { mutableStateOf(emptySet<Int>()) }
191− // Resolve a syntax for highlighting from the first file header (if any). Memoized so a
192− // streaming re-parse doesn't re-resolve per token.
193− val syntax = remember(lines) {
194− lines.firstOrNull { it is DiffLine.FileHeader }?.let { syntaxForHeader(it.text) }
196 val palette = rememberHighlightPalette()
191 val palette = rememberHighlightPalette()
@@ -229,7 +224,7 @@ fun DiffView(diff: String, modifier: Modifier = Modifier, saveKey: String? = nul
231 Column(modifier = Modifier.horizontalScroll(hScrollState)) {
226 Column(modifier = Modifier.horizontalScroll(hScrollState)) {
232− rows.forEach { row -> RenderDiffRow(row, syntax, palette, collapsedHunks) }
227+ rows.forEach { row -> RenderDiffRow(row, palette, collapsedHunks) }
234 DiffExpandFooter(lines = lines, expanded = expanded, onToggle = { expanded = !expanded })
229 DiffExpandFooter(lines = lines, expanded = expanded, onToggle = { expanded = !expanded })
@@ -255,9 +250,6 @@ fun LazyDiffView(diff: String, modifier: Modifier = Modifier, saveKey: String? =
255 val hScrollState = rememberScrollState()
250 val hScrollState = rememberScrollState()
256 var expanded by rememberSaveable(saveKey ?: diff) { mutableStateOf(false) }
251 var expanded by rememberSaveable(saveKey ?: diff) { mutableStateOf(false) }
257 val collapsedHunks = remember { mutableStateOf(emptySet<Int>()) }
252 val collapsedHunks = remember { mutableStateOf(emptySet<Int>()) }
258− val syntax = remember(lines) {
259− lines.firstOrNull { it is DiffLine.FileHeader }?.let { syntaxForHeader(it.text) }
261 val palette = rememberHighlightPalette()
253 val palette = rememberHighlightPalette()
262 val lazyListState = androidx.compose.foundation.lazy.rememberLazyListState()
254 val lazyListState = androidx.compose.foundation.lazy.rememberLazyListState()
@@ -298,7 +290,7 @@ fun LazyDiffView(diff: String, modifier: Modifier = Modifier, saveKey: String? =
299 key = { i, row -> "${row.kind::class.simpleName}:$i:${row.text.hashCode()}" },
291 key = { i, row -> "${row.kind::class.simpleName}:$i:${row.text.hashCode()}" },
300 ) { _, row ->
292 ) { _, row ->
301− RenderDiffRow(row, syntax, palette, collapsedHunks)
293+ RenderDiffRow(row, palette, collapsedHunks)
304 DiffExpandFooter(lines = lines, expanded = expanded, onToggle = { expanded = !expanded })
296 DiffExpandFooter(lines = lines, expanded = expanded, onToggle = { expanded = !expanded })
@@ -406,6 +398,10 @@ private data class DiffRowItem(
406 val oldLine: Int?,
398 val oldLine: Int?,
407 val newLine: Int?,
399 val newLine: Int?,
408 val hunkIndex: Int?,
400 val hunkIndex: Int?,
401+ // The syntax resolved from the file header this row belongs to. A multi-file diff switches
402+ // language at each FileHeader; carrying syntax per row (instead of one for the whole diff)
403+ // ensures a later file's hunk is highlighted with its own language, not the first file's.
404+ val syntax: FileSyntax?,
411/** Walk [visibleLines] advancing the old/new line counters per hunk, producing a list of
407/** Walk [visibleLines] advancing the old/new line counters per hunk, producing a list of
@@ -426,6 +422,9 @@ private fun buildDiffRows(
426 var oldLine = 0
422 var oldLine = 0
427 var newLine = 0
423 var newLine = 0
428 var hunkIndex = -1
424 var hunkIndex = -1
425+ // Track the current file's syntax as we walk the list, re-resolving at each FileHeader so
426+ // a multi-file diff highlights each hunk with its own language instead of the first file's.
427+ var currentSyntax: FileSyntax? = null
429 val hunkRegex = Regex("""@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@""")
428 val hunkRegex = Regex("""@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@""")
430 return visibleLines.map { line ->
429 return visibleLines.map { line ->
431 when (line) {
430 when (line) {
@@ -436,21 +435,24 @@ private fun buildDiffRows(
436 oldLine = m.groupValues[1].toInt()
435 oldLine = m.groupValues[1].toInt()
437 newLine = m.groupValues[2].toInt()
436 newLine = m.groupValues[2].toInt()
439− DiffRowItem(line, line.text, Color.Transparent, tertiary, null, null, hunkIndex)
438+ DiffRowItem(line, line.text, Color.Transparent, tertiary, null, null, hunkIndex, currentSyntax)
440+ is DiffLine.FileHeader -> {
441+ currentSyntax = syntaxForHeader(line.text)
442+ DiffRowItem(line, line.text, Color.Transparent, onSurface, null, null, null, currentSyntax)
441− is DiffLine.FileHeader -> DiffRowItem(line, line.text, Color.Transparent, onSurface, null, null, null)
442− is DiffLine.Meta -> DiffRowItem(line, line.text, Color.Transparent, onSurfaceVariant, null, null, null)
444+ is DiffLine.Meta -> DiffRowItem(line, line.text, Color.Transparent, onSurfaceVariant, null, null, null, currentSyntax)
443 is DiffLine.Add -> {
445 is DiffLine.Add -> {
444 val n = newLine++
446 val n = newLine++
445− DiffRowItem(line, line.text, addColor, addText, null, n, hunkIndex)
447+ DiffRowItem(line, line.text, addColor, addText, null, n, hunkIndex, currentSyntax)
447 is DiffLine.Remove -> {
449 is DiffLine.Remove -> {
448 val o = oldLine++
450 val o = oldLine++
449− DiffRowItem(line, line.text, removeColor, removeText, o, null, hunkIndex)
451+ DiffRowItem(line, line.text, removeColor, removeText, o, null, hunkIndex, currentSyntax)
451 is DiffLine.Context -> {
453 is DiffLine.Context -> {
452 val o = oldLine++; val n = newLine++
454 val o = oldLine++; val n = newLine++
453− DiffRowItem(line, line.text, Color.Transparent, onSurface, o, n, hunkIndex)
455+ DiffRowItem(line, line.text, Color.Transparent, onSurface, o, n, hunkIndex, currentSyntax)
@@ -462,7 +464,6 @@ private fun buildDiffRows(
462@Composable
464@Composable
463private fun RenderDiffRow(
465private fun RenderDiffRow(
464 row: DiffRowItem,
466 row: DiffRowItem,
465− syntax: FileSyntax?,
466 palette: HighlightPalette,
467 palette: HighlightPalette,
467 collapsedHunks: androidx.compose.runtime.MutableState<Set<Int>>,
468 collapsedHunks: androidx.compose.runtime.MutableState<Set<Int>>,
@@ -528,9 +529,9 @@ private fun RenderDiffRow(
528 color = MaterialTheme.colorScheme.onSurfaceVariant,
529 color = MaterialTheme.colorScheme.onSurfaceVariant,
529 modifier = Modifier.padding(vertical = 1.dp),
530 modifier = Modifier.padding(vertical = 1.dp),
531− is DiffLine.Add -> DiffRow(row.text, "+", row.bg, row.textColor, oldLine = row.oldLine, newLine = row.newLine, syntax = syntax, palette = palette)
532+ is DiffLine.Add -> DiffRow(row.text, "+", row.bg, row.textColor, oldLine = row.oldLine, newLine = row.newLine, syntax = row.syntax, palette = palette)
532− is DiffLine.Remove -> DiffRow(row.text, "-", row.bg, row.textColor, oldLine = row.oldLine, newLine = row.newLine, syntax = syntax, palette = palette)
533+ is DiffLine.Remove -> DiffRow(row.text, "-", row.bg, row.textColor, oldLine = row.oldLine, newLine = row.newLine, syntax = row.syntax, palette = palette)
533− is DiffLine.Context -> DiffRow(row.text, " ", row.bg, row.textColor, oldLine = row.oldLine, newLine = row.newLine, syntax = syntax, palette = palette)
534+ is DiffLine.Context -> DiffRow(row.text, " ", row.bg, row.textColor, oldLine = row.oldLine, newLine = row.newLine, syntax = row.syntax, palette = palette)