use editor::{ Anchor, Editor, HighlightKey, MultiBufferSnapshot, SelectionEffects, ToPoint, scroll::Autoscroll,};use gpui::{ Action, App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable, HighlightStyle, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, ScrollStrategy, SharedString, Styled, Task, UniformListScrollHandle, WeakEntity, Window, actions, div, rems, uniform_list,};use language::{BufferId, Point, ToOffset};use menu::{SelectNext, SelectPrevious};use std::{mem, ops::Range, sync::Arc, time::Duration};use theme::ActiveTheme;use theme::SyntaxTheme;use ui::{ ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, WithScrollbar, prelude::*,}; use workspace::{ Event as WorkspaceEvent, SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{Item, ItemHandle},}; actions!( dev, [ /// Opens the highlights tree view for the current file. OpenHighlightsTreeView, ]); actions!( highlights_tree_view, [ /// Toggles showing text highlights. ToggleTextHighlights, /// Toggles showing semantic token highlights. ToggleSemanticTokens, /// Toggles showing syntax token highlights. ToggleSyntaxTokens, ]); pub fn init(cx: &mut App) { cx.observe_new(move |workspace: &mut Workspace, _, _| { workspace.register_action(move |workspace, _: &OpenHighlightsTreeView, window, cx| { let active_item = workspace.active_item(cx); let workspace_handle = workspace.weak_handle(); let highlights_tree_view = cx.new(|cx| HighlightsTreeView::new(workspace_handle, active_item, window, cx)); workspace.split_item( SplitDirection::Right, Box::new(highlights_tree_view), window, cx, ) }); }) .detach();} #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]pub enum HighlightCategory { Text(HighlightKey), SyntaxToken { capture_name: SharedString, theme_key: Option<SharedString>, }, SemanticToken { token_type: Option<SharedString>, token_modifiers: Option<SharedString>, theme_key: Option<SharedString>, },} impl HighlightCategory { fn label(&self) -> SharedString { match self { HighlightCategory::Text(key) => format!("text: {key:?}").into(), HighlightCategory::SyntaxToken { capture_name, theme_key: Some(theme_key), } => format!("syntax: {capture_name} \u{2192} {theme_key}").into(), HighlightCategory::SyntaxToken { capture_name, theme_key: None, } => format!("syntax: {capture_name}").into(), HighlightCategory::SemanticToken { token_type, token_modifiers, theme_key, } => { let label = match (token_type, token_modifiers) { (Some(token_type), Some(modifiers)) => { format!("semantic token: {token_type} [{modifiers}]") } (Some(token_type), None) => format!("semantic token: {token_type}"), (None, Some(modifiers)) => format!("semantic token [{modifiers}]"), (None, None) => "semantic token".to_string(), }; if let Some(theme_key) = theme_key { format!("{label} \u{2192} {theme_key}").into() } else { label.into() } } } }} #[derive(Debug, Clone)]struct HighlightEntry { range: Range<Anchor>, buffer_id: BufferId, buffer_point_range: Range<Point>, range_display: SharedString, style: HighlightStyle, category: HighlightCategory,} /// An item in the display list: either a separator between excerpts or a highlight entry.#[derive(Debug, Clone)]enum DisplayItem { ExcerptSeparator { label: SharedString, }, Entry { /// Index into `cached_entries`. entry_ix: usize, },} pub struct HighlightsTreeView { workspace_handle: WeakEntity<Workspace>, editor: Option<EditorState>, list_scroll_handle: UniformListScrollHandle, selected_item_ix: Option<usize>, hovered_item_ix: Option<usize>, focus_handle: FocusHandle, cached_entries: Vec<HighlightEntry>, display_items: Vec<DisplayItem>, is_singleton: bool, show_text_highlights: bool, show_syntax_tokens: bool, show_semantic_tokens: bool, skip_next_scroll: bool, refresh_task: Task<()>,} pub struct HighlightsTreeToolbarItemView { tree_view: Option<Entity<HighlightsTreeView>>, _subscription: Option<gpui::Subscription>, toggle_settings_handle: PopoverMenuHandle<ContextMenu>,} struct EditorState { editor: Entity<Editor>, _subscription: gpui::Subscription,} struct SemanticHighlightEntry { range: Range<Anchor>, style: HighlightStyle, category: HighlightCategory,} struct HighlightRefreshInput { multi_buffer_snapshot: MultiBufferSnapshot, text_highlights: Vec<(HighlightKey, Arc<(HighlightStyle, Vec<Range<Anchor>>)>)>, semantic_highlights: Vec<SemanticHighlightEntry>, syntax_theme: Arc<SyntaxTheme>,} impl HighlightsTreeView { pub fn new( workspace_handle: WeakEntity<Workspace>, active_item: Option<Box<dyn ItemHandle>>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let mut this = Self { workspace_handle: workspace_handle.clone(), list_scroll_handle: UniformListScrollHandle::new(), editor: None, hovered_item_ix: None, selected_item_ix: None, focus_handle: cx.focus_handle(), cached_entries: Vec::new(), display_items: Vec::new(), is_singleton: true, show_text_highlights: true, show_syntax_tokens: true, show_semantic_tokens: true, skip_next_scroll: false, refresh_task: Task::ready(()), }; this.handle_item_updated(active_item, window, cx); cx.subscribe_in( &workspace_handle.upgrade().unwrap(), window, move |this, workspace, event, window, cx| match event { WorkspaceEvent::ItemAdded { .. } | WorkspaceEvent::ActiveItemChanged => { this.handle_item_updated(workspace.read(cx).active_item(cx), window, cx) } WorkspaceEvent::ItemRemoved { item_id } => { this.handle_item_removed(item_id, window, cx); } _ => {} }, ) .detach(); this } fn handle_item_updated( &mut self, active_item: Option<Box<dyn ItemHandle>>, window: &mut Window, cx: &mut Context<Self>, ) { let active_editor = match active_item { Some(active_item) => { if active_item.item_id() == cx.entity_id() { return; } else { match active_item.downcast::<Editor>() { Some(active_editor) => active_editor, None => { self.clear(cx); return; } } } } None => { self.clear(cx); return; } }; let is_different_editor = self .editor .as_ref() .is_none_or(|state| state.editor != active_editor); if is_different_editor { self.set_editor(active_editor, window, cx); } } fn handle_item_removed( &mut self, item_id: &EntityId, _window: &mut Window, cx: &mut Context<Self>, ) { if self .editor .as_ref() .is_some_and(|state| state.editor.entity_id() == *item_id) { self.clear(cx); } } fn clear(&mut self, cx: &mut Context<Self>) { self.refresh_task = Task::ready(()); self.cached_entries.clear(); self.display_items.clear(); self.selected_item_ix = None; self.hovered_item_ix = None; if let Some(state) = self.editor.take() { Self::clear_editor_highlights(&state.editor, cx); } cx.notify(); } fn set_editor(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut Context<Self>) { if let Some(state) = &self.editor { if state.editor == editor { return; } Self::clear_editor_highlights(&state.editor, cx); } let subscription = cx.subscribe_in(&editor, window, |this, _, event, window, cx| match event { editor::EditorEvent::Reparsed(_) => this.schedule_refresh_highlights(window, cx), editor::EditorEvent::SelectionsChanged { .. } => { this.update_selection_from_editor(cx); } _ => return, }); self.editor = Some(EditorState { editor, _subscription: subscription, }); self.refresh_task = Task::ready(()); self.cached_entries.clear(); self.display_items.clear(); self.selected_item_ix = None; self.hovered_item_ix = None; self.schedule_refresh_highlights(window, cx); cx.notify(); } fn schedule_refresh_highlights(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.refresh_task = cx.spawn_in(window, async move |this, cx| { cx.background_executor() .timer(Duration::from_millis(30)) .await; let Some(input) = this .update(cx, |this, cx| this.highlight_refresh_input(cx)) .ok() .flatten() else { return; }; let new_highlights = cx .background_spawn(async move { build_highlight_entries(input) }) .await; this.update_in(cx, |this, _window, cx| { this.apply_highlight_refresh(new_highlights, cx); }) .ok(); }); } fn highlight_refresh_input(&self, cx: &mut Context<Self>) -> Option<HighlightRefreshInput> { let editor_state = self.editor.as_ref()?; let editor = editor_state.editor.read(cx); let display_map = editor.display_map.clone(); let project = editor.project().cloned()?; let multi_buffer = editor.buffer().clone(); let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); let syntax_theme = cx.theme().syntax().clone(); let (text_highlights, semantic_token_highlights) = display_map.update(cx, |display_map, _| { let text_highlights = display_map .all_text_highlights() .map(|(key, highlights)| (*key, highlights.clone())) .collect::<Vec<_>>(); let semantic_token_highlights = display_map .all_semantic_token_highlights() .map(|(buffer_id, (tokens, interner))| { (*buffer_id, tokens.clone(), interner.clone()) }) .collect::<Vec<_>>(); (text_highlights, semantic_token_highlights) }); let mut semantic_highlights = Vec::new(); project.read(cx).lsp_store().update(cx, |lsp_store, cx| { for (buffer_id, tokens, interner) in semantic_token_highlights { let language_name = multi_buffer .read(cx) .buffer(buffer_id) .and_then(|buffer| buffer.read(cx).language().map(|language| language.name())); for token in tokens.iter() { let Some(stylizer) = lsp_store.get_or_create_token_stylizer( token.server_id, language_name.as_ref(), cx, ) else { continue; }; let theme_key = stylizer .rules_for_token(token.token_type) .and_then(|rules| { rules .iter() .filter(|rule| { rule.token_modifiers.iter().all(|modifier| { stylizer.has_modifier(token.token_modifiers, modifier) }) }) .fold(None, |theme_key, rule| { rule.style .iter() .find(|style_name| { syntax_theme.style_for_name(style_name).is_some() }) .map(|style_name| SharedString::from(style_name.clone())) .or(theme_key) }) }); semantic_highlights.push(SemanticHighlightEntry { range: token.range.start..token.range.end, style: interner[token.style], category: HighlightCategory::SemanticToken { token_type: stylizer.token_type_name(token.token_type).cloned(), token_modifiers: stylizer .token_modifiers(token.token_modifiers) .map(SharedString::from), theme_key, }, }); } } }); Some(HighlightRefreshInput { multi_buffer_snapshot, text_highlights, semantic_highlights, syntax_theme, }) } fn apply_highlight_refresh( &mut self, new_highlights: Vec<HighlightEntry>, cx: &mut Context<Self>, ) { let Some(editor) = self.editor.as_ref().map(|state| state.editor.clone()) else { return; }; let editor = editor.read(cx); let cursor_position = editor.selections.newest_anchor().head(); let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx); self.is_singleton = multi_buffer_snapshot.is_singleton(); self.cached_entries = new_highlights; self.hovered_item_ix = None; self.rebuild_display_items(&multi_buffer_snapshot, cx); let should_scroll = !mem::take(&mut self.skip_next_scroll); self.update_selection_for_cursor(&cursor_position, &multi_buffer_snapshot, should_scroll); self.sync_editor_highlight_for_selected_entry(cx); cx.notify(); } fn rebuild_display_items(&mut self, snapshot: &MultiBufferSnapshot, cx: &App) { self.display_items.clear(); let mut last_range_end = None; for (entry_ix, entry) in self.cached_entries.iter().enumerate() { if !self.should_show_entry(entry) { continue; } if !self.is_singleton { let excerpt_changed = last_range_end.is_none_or(|anchor| { snapshot .excerpt_containing(anchor..entry.range.start) .is_none() }); if excerpt_changed { last_range_end = Some(entry.range.end); let label = excerpt_label_for(entry, snapshot, cx); self.display_items .push(DisplayItem::ExcerptSeparator { label }); } } self.display_items.push(DisplayItem::Entry { entry_ix }); } } fn should_show_entry(&self, entry: &HighlightEntry) -> bool { match entry.category { HighlightCategory::Text(_) => self.show_text_highlights, HighlightCategory::SyntaxToken { .. } => self.show_syntax_tokens, HighlightCategory::SemanticToken { .. } => self.show_semantic_tokens, } } fn update_selection_from_editor(&mut self, cx: &mut Context<Self>) { let Some(editor_state) = self.editor.as_ref() else { return; }; let (cursor_position, multi_buffer_snapshot) = { let editor = editor_state.editor.read(cx); ( editor.selections.newest_anchor().head(), editor.buffer().read(cx).snapshot(cx), ) }; let should_update_selection = !mem::take(&mut self.skip_next_scroll); if !should_update_selection { self.sync_editor_highlight_for_selected_entry(cx); return; } let should_notify =Lines 1–500 of 1337. Load more
use editor::{ Anchor, Editor, HighlightKey, MultiBufferSnapshot, SelectionEffects, ToPoint, scroll::Autoscroll, }; use gpui::{ Action, App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable, HighlightStyle, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, ScrollStrategy, SharedString, Styled, Task, UniformListScrollHandle, WeakEntity, Window, actions, div, rems, uniform_list, }; use language::{BufferId, Point, ToOffset}; use menu::{SelectNext, SelectPrevious}; use std::{mem, ops::Range, sync::Arc, time::Duration}; use theme::ActiveTheme; use theme::SyntaxTheme; use ui::{ ButtonLike, ContextMenu, PopoverMenu, PopoverMenuHandle, Tooltip, WithScrollbar, prelude::*, }; use workspace::{ Event as WorkspaceEvent, SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{Item, ItemHandle}, }; actions!( dev, [ /// Opens the highlights tree view for the current file. OpenHighlightsTreeView, ] ); actions!( highlights_tree_view, [ /// Toggles showing text highlights. ToggleTextHighlights, /// Toggles showing semantic token highlights. ToggleSemanticTokens, /// Toggles showing syntax token highlights. ToggleSyntaxTokens, ] ); pub fn init(cx: &mut App) { cx.observe_new(move |workspace: &mut Workspace, _, _| { workspace.register_action(move |workspace, _: &OpenHighlightsTreeView, window, cx| { let active_item = workspace.active_item(cx); let workspace_handle = workspace.weak_handle(); let highlights_tree_view = cx.new(|cx| HighlightsTreeView::new(workspace_handle, active_item, window, cx)); workspace.split_item( SplitDirection::Right, Box::new(highlights_tree_view), window, cx, ) }); }) .detach(); } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum HighlightCategory { Text(HighlightKey), SyntaxToken { capture_name: SharedString, theme_key: Option<SharedString>, }, SemanticToken { token_type: Option<SharedString>, token_modifiers: Option<SharedString>, theme_key: Option<SharedString>, }, } impl HighlightCategory { fn label(&self) -> SharedString { match self { HighlightCategory::Text(key) => format!("text: {key:?}").into(), HighlightCategory::SyntaxToken { capture_name, theme_key: Some(theme_key), } => format!("syntax: {capture_name} \u{2192} {theme_key}").into(), HighlightCategory::SyntaxToken { capture_name, theme_key: None, } => format!("syntax: {capture_name}").into(), HighlightCategory::SemanticToken { token_type, token_modifiers, theme_key, } => { let label = match (token_type, token_modifiers) { (Some(token_type), Some(modifiers)) => { format!("semantic token: {token_type} [{modifiers}]") } (Some(token_type), None) => format!("semantic token: {token_type}"), (None, Some(modifiers)) => format!("semantic token [{modifiers}]"), (None, None) => "semantic token".to_string(), }; if let Some(theme_key) = theme_key { format!("{label} \u{2192} {theme_key}").into() } else { label.into() } } } } } #[derive(Debug, Clone)] struct HighlightEntry { range: Range<Anchor>, buffer_id: BufferId, buffer_point_range: Range<Point>, range_display: SharedString, style: HighlightStyle, category: HighlightCategory, } /// An item in the display list: either a separator between excerpts or a highlight entry. #[derive(Debug, Clone)] enum DisplayItem { ExcerptSeparator { label: SharedString, }, Entry { /// Index into `cached_entries`. entry_ix: usize, }, } pub struct HighlightsTreeView { workspace_handle: WeakEntity<Workspace>, editor: Option<EditorState>, list_scroll_handle: UniformListScrollHandle, selected_item_ix: Option<usize>, hovered_item_ix: Option<usize>, focus_handle: FocusHandle, cached_entries: Vec<HighlightEntry>, display_items: Vec<DisplayItem>, is_singleton: bool, show_text_highlights: bool, show_syntax_tokens: bool, show_semantic_tokens: bool, skip_next_scroll: bool, refresh_task: Task<()>, } pub struct HighlightsTreeToolbarItemView { tree_view: Option<Entity<HighlightsTreeView>>, _subscription: Option<gpui::Subscription>, toggle_settings_handle: PopoverMenuHandle<ContextMenu>, } struct EditorState { editor: Entity<Editor>, _subscription: gpui::Subscription, } struct SemanticHighlightEntry { range: Range<Anchor>, style: HighlightStyle, category: HighlightCategory, } struct HighlightRefreshInput { multi_buffer_snapshot: MultiBufferSnapshot, text_highlights: Vec<(HighlightKey, Arc<(HighlightStyle, Vec<Range<Anchor>>)>)>, semantic_highlights: Vec<SemanticHighlightEntry>, syntax_theme: Arc<SyntaxTheme>, } impl HighlightsTreeView { pub fn new( workspace_handle: WeakEntity<Workspace>, active_item: Option<Box<dyn ItemHandle>>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let mut this = Self { workspace_handle: workspace_handle.clone(), list_scroll_handle: UniformListScrollHandle::new(), editor: None, hovered_item_ix: None, selected_item_ix: None, focus_handle: cx.focus_handle(), cached_entries: Vec::new(), display_items: Vec::new(), is_singleton: true, show_text_highlights: true, show_syntax_tokens: true, show_semantic_tokens: true, skip_next_scroll: false, refresh_task: Task::ready(()), }; this.handle_item_updated(active_item, window, cx); cx.subscribe_in( &workspace_handle.upgrade().unwrap(), window, move |this, workspace, event, window, cx| match event { WorkspaceEvent::ItemAdded { .. } | WorkspaceEvent::ActiveItemChanged => { this.handle_item_updated(workspace.read(cx).active_item(cx), window, cx) } WorkspaceEvent::ItemRemoved { item_id } => { this.handle_item_removed(item_id, window, cx); } _ => {} }, ) .detach(); this } fn handle_item_updated( &mut self, active_item: Option<Box<dyn ItemHandle>>, window: &mut Window, cx: &mut Context<Self>, ) { let active_editor = match active_item { Some(active_item) => { if active_item.item_id() == cx.entity_id() { return; } else { match active_item.downcast::<Editor>() { Some(active_editor) => active_editor, None => { self.clear(cx); return; } } } } None => { self.clear(cx); return; } }; let is_different_editor = self .editor .as_ref() .is_none_or(|state| state.editor != active_editor); if is_different_editor { self.set_editor(active_editor, window, cx); } } fn handle_item_removed( &mut self, item_id: &EntityId, _window: &mut Window, cx: &mut Context<Self>, ) { if self .editor .as_ref() .is_some_and(|state| state.editor.entity_id() == *item_id) { self.clear(cx); } } fn clear(&mut self, cx: &mut Context<Self>) { self.refresh_task = Task::ready(()); self.cached_entries.clear(); self.display_items.clear(); self.selected_item_ix = None; self.hovered_item_ix = None; if let Some(state) = self.editor.take() { Self::clear_editor_highlights(&state.editor, cx); } cx.notify(); } fn set_editor(&mut self, editor: Entity<Editor>, window: &mut Window, cx: &mut Context<Self>) { if let Some(state) = &self.editor { if state.editor == editor { return; } Self::clear_editor_highlights(&state.editor, cx); } let subscription = cx.subscribe_in(&editor, window, |this, _, event, window, cx| match event { editor::EditorEvent::Reparsed(_) => this.schedule_refresh_highlights(window, cx), editor::EditorEvent::SelectionsChanged { .. } => { this.update_selection_from_editor(cx); } _ => return, }); self.editor = Some(EditorState { editor, _subscription: subscription, }); self.refresh_task = Task::ready(()); self.cached_entries.clear(); self.display_items.clear(); self.selected_item_ix = None; self.hovered_item_ix = None; self.schedule_refresh_highlights(window, cx); cx.notify(); } fn schedule_refresh_highlights(&mut self, window: &mut Window, cx: &mut Context<Self>) { self.refresh_task = cx.spawn_in(window, async move |this, cx| { cx.background_executor() .timer(Duration::from_millis(30)) .await; let Some(input) = this .update(cx, |this, cx| this.highlight_refresh_input(cx)) .ok() .flatten() else { return; }; let new_highlights = cx .background_spawn(async move { build_highlight_entries(input) }) .await; this.update_in(cx, |this, _window, cx| { this.apply_highlight_refresh(new_highlights, cx); }) .ok(); }); } fn highlight_refresh_input(&self, cx: &mut Context<Self>) -> Option<HighlightRefreshInput> { let editor_state = self.editor.as_ref()?; let editor = editor_state.editor.read(cx); let display_map = editor.display_map.clone(); let project = editor.project().cloned()?; let multi_buffer = editor.buffer().clone(); let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); let syntax_theme = cx.theme().syntax().clone(); let (text_highlights, semantic_token_highlights) = display_map.update(cx, |display_map, _| { let text_highlights = display_map .all_text_highlights() .map(|(key, highlights)| (*key, highlights.clone())) .collect::<Vec<_>>(); let semantic_token_highlights = display_map .all_semantic_token_highlights() .map(|(buffer_id, (tokens, interner))| { (*buffer_id, tokens.clone(), interner.clone()) }) .collect::<Vec<_>>(); (text_highlights, semantic_token_highlights) }); let mut semantic_highlights = Vec::new(); project.read(cx).lsp_store().update(cx, |lsp_store, cx| { for (buffer_id, tokens, interner) in semantic_token_highlights { let language_name = multi_buffer .read(cx) .buffer(buffer_id) .and_then(|buffer| buffer.read(cx).language().map(|language| language.name())); for token in tokens.iter() { let Some(stylizer) = lsp_store.get_or_create_token_stylizer( token.server_id, language_name.as_ref(), cx, ) else { continue; }; let theme_key = stylizer .rules_for_token(token.token_type) .and_then(|rules| { rules .iter() .filter(|rule| { rule.token_modifiers.iter().all(|modifier| { stylizer.has_modifier(token.token_modifiers, modifier) }) }) .fold(None, |theme_key, rule| { rule.style .iter() .find(|style_name| { syntax_theme.style_for_name(style_name).is_some() }) .map(|style_name| SharedString::from(style_name.clone())) .or(theme_key) }) }); semantic_highlights.push(SemanticHighlightEntry { range: token.range.start..token.range.end, style: interner[token.style], category: HighlightCategory::SemanticToken { token_type: stylizer.token_type_name(token.token_type).cloned(), token_modifiers: stylizer .token_modifiers(token.token_modifiers) .map(SharedString::from), theme_key, }, }); } } }); Some(HighlightRefreshInput { multi_buffer_snapshot, text_highlights, semantic_highlights, syntax_theme, }) } fn apply_highlight_refresh( &mut self, new_highlights: Vec<HighlightEntry>, cx: &mut Context<Self>, ) { let Some(editor) = self.editor.as_ref().map(|state| state.editor.clone()) else { return; }; let editor = editor.read(cx); let cursor_position = editor.selections.newest_anchor().head(); let multi_buffer_snapshot = editor.buffer().read(cx).snapshot(cx); self.is_singleton = multi_buffer_snapshot.is_singleton(); self.cached_entries = new_highlights; self.hovered_item_ix = None; self.rebuild_display_items(&multi_buffer_snapshot, cx); let should_scroll = !mem::take(&mut self.skip_next_scroll); self.update_selection_for_cursor(&cursor_position, &multi_buffer_snapshot, should_scroll); self.sync_editor_highlight_for_selected_entry(cx); cx.notify(); } fn rebuild_display_items(&mut self, snapshot: &MultiBufferSnapshot, cx: &App) { self.display_items.clear(); let mut last_range_end = None; for (entry_ix, entry) in self.cached_entries.iter().enumerate() { if !self.should_show_entry(entry) { continue; } if !self.is_singleton { let excerpt_changed = last_range_end.is_none_or(|anchor| { snapshot .excerpt_containing(anchor..entry.range.start) .is_none() }); if excerpt_changed { last_range_end = Some(entry.range.end); let label = excerpt_label_for(entry, snapshot, cx); self.display_items .push(DisplayItem::ExcerptSeparator { label }); } } self.display_items.push(DisplayItem::Entry { entry_ix }); } } fn should_show_entry(&self, entry: &HighlightEntry) -> bool { match entry.category { HighlightCategory::Text(_) => self.show_text_highlights, HighlightCategory::SyntaxToken { .. } => self.show_syntax_tokens, HighlightCategory::SemanticToken { .. } => self.show_semantic_tokens, } } fn update_selection_from_editor(&mut self, cx: &mut Context<Self>) { let Some(editor_state) = self.editor.as_ref() else { return; }; let (cursor_position, multi_buffer_snapshot) = { let editor = editor_state.editor.read(cx); ( editor.selections.newest_anchor().head(), editor.buffer().read(cx).snapshot(cx), ) }; let should_update_selection = !mem::take(&mut self.skip_next_scroll); if !should_update_selection { self.sync_editor_highlight_for_selected_entry(cx); return; } let should_notify =