Fix shared element state across component instances

This commit is contained in:
2026-11-09 14:04:25 +07:00
parent f022f08947
commit 73f7dc31d4
20 changed files with 489 additions and 136 deletions
+7 -20
View File
@@ -13,7 +13,7 @@ use gpui::{
use gpui_native_core::scrollbar::{Scrollbar, ScrollbarHandle, ScrollbarVisibility};
use gpui_native_theme::{ActiveTheme as _, ColorTokens, Ramps};
use crate::highlight::{HighlightedCode, Language, Role, highlight_with_tree};
use crate::highlight::{HighlightedCode, Language, Role, highlight};
use crate::utils::{gutter_cell, run_style};
actions!(code_view, [Copy]);
@@ -53,8 +53,6 @@ pub struct CodeViewState {
source: SharedString,
language: Language,
code: HighlightedCode,
/// The parse tree of `source`, so updates reparse incrementally instead of from scratch.
tree: Option<tree_sitter::Tree>,
handle: UniformListScrollHandle,
/// The hovered line, driving the active-line wash and number emphasis.
active_line: Option<usize>,
@@ -74,14 +72,13 @@ impl CodeViewState {
S: Into<SharedString>,
{
let source = source.into();
let (code, tree) = highlight_with_tree(&source, language, None);
let code = highlight(&source, language);
Self {
id: id.into(),
source,
language,
code,
tree,
handle: UniformListScrollHandle::new(),
active_line: None,
selection: None,
@@ -98,18 +95,9 @@ impl CodeViewState {
{
let source = source.into();
// The old tree is only reusable when the grammar stays the same.
let old_tree = if language == self.language {
self.tree.as_ref()
} else {
None
};
let (code, tree) = highlight_with_tree(&source, language, old_tree);
self.code = highlight(&source, language);
self.source = source;
self.language = language;
self.code = code;
self.tree = tree;
self.selection = None;
self.handle.scroll_to_item(0, ScrollStrategy::Top);
@@ -134,9 +122,7 @@ impl CodeViewState {
lines.splice(line_ix..line_ix + 1, replacement);
self.source = (lines.join("\n") + if trailing_newline { "\n" } else { "" }).into();
let (code, tree) = highlight_with_tree(&self.source, self.language, self.tree.as_ref());
self.code = code;
self.tree = tree;
self.code = highlight(&self.source, self.language);
self.selection = None;
cx.notify();
}
@@ -348,7 +334,7 @@ impl Render for CodeViewState {
code.position_at(
*bounds,
event.position,
Pixels::ZERO,
ScrollbarHandle::offset(&code.handle).y,
line_height,
char_width,
text_x,
@@ -453,7 +439,8 @@ impl Render for CodeViewState {
)
.child(rows)
.child(
Scrollbar::vertical(self.handle.clone()).visibility(ScrollbarVisibility::Platform),
Scrollbar::vertical((self.id.clone(), "v-scrollbar"), self.handle.clone())
.visibility(ScrollbarVisibility::Platform),
)
.when_some(self.a11y_label.clone(), |view, label| {
view.aria_label(label)
+2 -4
View File
@@ -363,13 +363,11 @@ impl Render for DiffViewState {
})
.child(rows)
.child(
Scrollbar::vertical(self.handle.clone())
.key("v-scrollbar")
Scrollbar::vertical((self.id.clone(), "v-scrollbar"), self.handle.clone())
.visibility(ScrollbarVisibility::Platform),
)
.child(
Scrollbar::horizontal(self.handle.clone())
.key("h-scrollbar")
Scrollbar::horizontal((self.id.clone(), "h-scrollbar"), self.handle.clone())
.visibility(ScrollbarVisibility::Platform),
)
}
+5 -14
View File
@@ -67,17 +67,8 @@ pub struct HighlightedCode {
/// Highlights `source` with `language`'s tree-sitter grammar.
pub fn highlight(source: &str, language: Language) -> HighlightedCode {
highlight_with_tree(source, language, None).0
}
/// Highlights `source`, reusing `old_tree` so only the changed region is re-lexed.
pub fn highlight_with_tree(
source: &str,
language: Language,
old_tree: Option<&tree_sitter::Tree>,
) -> (HighlightedCode, Option<tree_sitter::Tree>) {
let Some((grammar, query_source)) = grammar_for(language) else {
return (plain(source), None);
return plain(source);
};
HIGHLIGHTERS.with(|cache| {
@@ -104,11 +95,11 @@ pub fn highlight_with_tree(
.as_mut();
let Some(highlighter) = highlighter else {
return (plain(source), None);
return plain(source);
};
let Some(tree) = highlighter.parser.parse(source, old_tree) else {
return (plain(source), None);
let Some(tree) = highlighter.parser.parse(source, None) else {
return plain(source);
};
// One role per byte; matches arrive in query order
@@ -134,7 +125,7 @@ pub fn highlight_with_tree(
}
}
(build_lines(source, &role_at), Some(tree))
build_lines(source, &role_at)
})
}
+4 -3
View File
@@ -18,7 +18,7 @@ use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag, T
use std::ops::Range;
use crate::highlight::{HighlightedCode, highlight_with_tree, plain};
use crate::highlight::{HighlightedCode, highlight, plain};
use crate::utils::{code_line, grammar_language};
/// Heading scale steps relative to the root font size, H1 first.
@@ -277,7 +277,8 @@ impl Render for MarkdownViewState {
.size_full()
.child(blocks)
.child(
Scrollbar::vertical(self.handle.clone()).visibility(ScrollbarVisibility::Platform),
Scrollbar::vertical((self.id.clone(), "v-scrollbar"), self.handle.clone())
.visibility(ScrollbarVisibility::Platform),
)
.when_some(self.a11y_label.clone(), |view, label| {
view.aria_label(label)
@@ -588,7 +589,7 @@ fn parse(source: &str, highlight_code: bool) -> Vec<MarkdownBlock> {
.then(|| language.as_deref().and_then(grammar_language))
.flatten()
{
Some(grammar) => highlight_with_tree(text, grammar, None).0,
Some(grammar) => highlight(text, grammar),
None => plain(text),
};
blocks.push(MarkdownBlock::Code {