use command_palette_hooks::CommandPaletteFilter;use editor::{Anchor, Editor, ExcerptId, MultiBufferOffset, SelectionEffects, scroll::Autoscroll};use gpui::{ App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, ScrollStrategy, SharedString, Styled, Task, UniformListScrollHandle, WeakEntity, Window, actions, div, rems, uniform_list,};use language::{Buffer, OwnedSyntaxLayer};use std::{any::TypeId, mem, ops::Range};use theme::ActiveTheme;use tree_sitter::{Node, TreeCursor};use ui::{ ButtonCommon, ButtonLike, Clickable, Color, ContextMenu, FluentBuilder as _, IconButton, IconName, Label, LabelCommon, LabelSize, PopoverMenu, StyledExt, Tooltip, WithScrollbar, h_flex, v_flex,};use workspace::{ Event as WorkspaceEvent, SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{Item, ItemHandle},}; actions!( dev, [ /// Opens the syntax tree view for the current file. OpenSyntaxTreeView, ]); actions!( syntax_tree_view, [ /// Update the syntax tree view to show the last focused file. UseActiveEditor ]); pub fn init(cx: &mut App) { let syntax_tree_actions = [TypeId::of::<UseActiveEditor>()]; CommandPaletteFilter::update_global(cx, |this, _| { this.hide_action_types(&syntax_tree_actions); }); cx.observe_new(move |workspace: &mut Workspace, _, _| { workspace.register_action(move |workspace, _: &OpenSyntaxTreeView, window, cx| { CommandPaletteFilter::update_global(cx, |this, _| { this.show_action_types(&syntax_tree_actions); }); let active_item = workspace.active_item(cx); let workspace_handle = workspace.weak_handle(); let syntax_tree_view = cx.new(|cx| { cx.on_release(move |view: &mut SyntaxTreeView, cx| { if view .workspace_handle .read_with(cx, |workspace, cx| { workspace.item_of_type::<SyntaxTreeView>(cx).is_none() }) .unwrap_or_default() { CommandPaletteFilter::update_global(cx, |this, _| { this.hide_action_types(&syntax_tree_actions); }); } }) .detach(); SyntaxTreeView::new(workspace_handle, active_item, window, cx) }); workspace.split_item( SplitDirection::Right, Box::new(syntax_tree_view), window, cx, ) }); workspace.register_action(|workspace, _: &UseActiveEditor, window, cx| { if let Some(tree_view) = workspace.item_of_type::<SyntaxTreeView>(cx) { tree_view.update(cx, |view, cx| { view.update_active_editor(&Default::default(), window, cx) }) } }); }) .detach();} pub struct SyntaxTreeView { workspace_handle: WeakEntity<Workspace>, editor: Option<EditorState>, list_scroll_handle: UniformListScrollHandle, /// The last active editor in the workspace. Note that this is specifically not the /// currently shown editor. last_active_editor: Option<Entity<Editor>>, selected_descendant_ix: Option<usize>, hovered_descendant_ix: Option<usize>, focus_handle: FocusHandle,} pub struct SyntaxTreeToolbarItemView { tree_view: Option<Entity<SyntaxTreeView>>, subscription: Option<gpui::Subscription>,} struct EditorState { editor: Entity<Editor>, active_buffer: Option<BufferState>, _subscription: gpui::Subscription,} impl EditorState { fn has_language(&self) -> bool { self.active_buffer .as_ref() .is_some_and(|buffer| buffer.active_layer.is_some()) }} #[derive(Clone)]struct BufferState { buffer: Entity<Buffer>, excerpt_id: ExcerptId, active_layer: Option<OwnedSyntaxLayer>,} impl SyntaxTreeView { 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, last_active_editor: None, hovered_descendant_ix: None, selected_descendant_ix: None, focus_handle: cx.focus_handle(), }; 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 Some(editor) = active_item .filter(|item| item.item_id() != cx.entity_id()) .and_then(|item| item.act_as::<Editor>(cx)) else { return; }; if let Some(editor_state) = self.editor.as_ref().filter(|state| state.has_language()) { self.last_active_editor = (editor_state.editor != editor).then_some(editor); } else { self.set_editor(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.editor = None; // Try activating the last active editor if there is one self.update_active_editor(&Default::default(), window, cx); cx.notify(); } } fn update_active_editor( &mut self, _: &UseActiveEditor, window: &mut Window, cx: &mut Context<Self>, ) { let Some(editor) = self.last_active_editor.take() else { return; }; self.set_editor(editor, window, cx); } 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; } editor.update(cx, |editor, cx| { editor.clear_background_highlights::<Self>(cx) }); } let subscription = cx.subscribe_in(&editor, window, |this, _, event, window, cx| { let did_reparse = match event { editor::EditorEvent::Reparsed(_) => true, editor::EditorEvent::SelectionsChanged { .. } => false, _ => return, }; this.editor_updated(did_reparse, window, cx); }); self.editor = Some(EditorState { editor, _subscription: subscription, active_buffer: None, }); self.editor_updated(true, window, cx); } fn editor_updated( &mut self, did_reparse: bool, window: &mut Window, cx: &mut Context<Self>, ) -> Option<()> { // Find which excerpt the cursor is in, and the position within that excerpted buffer. let editor_state = self.editor.as_mut()?; let snapshot = editor_state .editor .update(cx, |editor, cx| editor.snapshot(window, cx)); let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| { let selection_range = editor .selections .last::<MultiBufferOffset>(&editor.display_snapshot(cx)) .range(); let multi_buffer = editor.buffer().read(cx); let (buffer, range, excerpt_id) = snapshot .buffer_snapshot() .range_to_buffer_ranges(selection_range.start..=selection_range.end) .pop()?; let buffer = multi_buffer.buffer(buffer.remote_id()).unwrap(); Some((buffer, range, excerpt_id)) })?; // If the cursor has moved into a different excerpt, retrieve a new syntax layer // from that buffer. let buffer_state = editor_state .active_buffer .get_or_insert_with(|| BufferState { buffer: buffer.clone(), excerpt_id, active_layer: None, }); let mut prev_layer = None; if did_reparse { prev_layer = buffer_state.active_layer.take(); } if buffer_state.buffer != buffer || buffer_state.excerpt_id != excerpt_id { buffer_state.buffer = buffer.clone(); buffer_state.excerpt_id = excerpt_id; buffer_state.active_layer = None; } let layer = match &mut buffer_state.active_layer { Some(layer) => layer, None => { let snapshot = buffer.read(cx).snapshot(); let layer = if let Some(prev_layer) = prev_layer { let prev_range = prev_layer.node().byte_range(); snapshot .syntax_layers() .filter(|layer| layer.language == &prev_layer.language) .min_by_key(|layer| { let range = layer.node().byte_range(); ((range.start as i64) - (prev_range.start as i64)).abs() + ((range.end as i64) - (prev_range.end as i64)).abs() })? } else { snapshot.syntax_layers().next()? }; buffer_state.active_layer.insert(layer.to_owned()) } }; // Within the active layer, find the syntax node under the cursor, // and scroll to it. let mut cursor = layer.node().walk(); while cursor.goto_first_child_for_byte(range.start.0).is_some() { if !range.is_empty() && cursor.node().end_byte() == range.start.0 { cursor.goto_next_sibling(); } } // Ascend to the smallest ancestor that contains the range. loop { let node_range = cursor.node().byte_range(); if node_range.start <= range.start.0 && node_range.end >= range.end.0 { break; } if !cursor.goto_parent() { break; } } let descendant_ix = cursor.descendant_index(); self.selected_descendant_ix = Some(descendant_ix); self.list_scroll_handle .scroll_to_item(descendant_ix, ScrollStrategy::Center); cx.notify(); Some(()) } fn update_editor_with_range_for_descendant_ix( &self, descendant_ix: usize, window: &mut Window, cx: &mut Context<Self>, mut f: impl FnMut(&mut Editor, Range<Anchor>, usize, &mut Window, &mut Context<Editor>), ) -> Option<()> { let editor_state = self.editor.as_ref()?; let buffer_state = editor_state.active_buffer.as_ref()?; let layer = buffer_state.active_layer.as_ref()?; // Find the node. let mut cursor = layer.node().walk(); cursor.goto_descendant(descendant_ix); let node = cursor.node(); let range = node.byte_range(); // Build a text anchor range. let buffer = buffer_state.buffer.read(cx); let range = buffer.anchor_before(range.start)..buffer.anchor_after(range.end); // Build a multibuffer anchor range. let multibuffer = editor_state.editor.read(cx).buffer(); let multibuffer = multibuffer.read(cx).snapshot(cx); let excerpt_id = buffer_state.excerpt_id; let range = multibuffer.anchor_range_in_excerpt(excerpt_id, range)?; let key = cx.entity_id().as_u64() as usize; // Update the editor with the anchor range. editor_state.editor.update(cx, |editor, cx| { f(editor, range, key, window, cx); }); Some(()) } fn render_node(cursor: &TreeCursor, depth: u32, selected: bool, cx: &App) -> Div { let colors = cx.theme().colors(); let mut row = h_flex(); if let Some(field_name) = cursor.field_name() { row = row.children([Label::new(field_name).color(Color::Info), Label::new(": ")]); } let node = cursor.node(); row.child(if node.is_named() { Label::new(node.kind()).color(Color::Default) } else { Label::new(format!("\"{}\"", node.kind())).color(Color::Created) }) .child( div() .child(Label::new(format_node_range(node)).color(Color::Muted)) .pl_1(), ) .text_bg(if selected { colors.element_selected } else { Hsla::default() }) .pl(rems(depth as f32)) .hover(|style| style.bg(colors.element_hover)) } fn compute_items( &mut self, layer: &OwnedSyntaxLayer, range: Range<usize>, cx: &Context<Self>, ) -> Vec<Div> { let mut items = Vec::new(); let mut cursor = layer.node().walk(); let mut descendant_ix = range.start; cursor.goto_descendant(descendant_ix); let mut depth = cursor.depth(); let mut visited_children = false; while descendant_ix < range.end { if visited_children { if cursor.goto_next_sibling() { visited_children = false; } else if cursor.goto_parent() { depth -= 1; } else { break; } } else { items.push( Self::render_node( &cursor, depth, Some(descendant_ix) == self.selected_descendant_ix, cx, ) .on_mouse_down( MouseButton::Left, cx.listener(move |tree_view, _: &MouseDownEvent, window, cx| { tree_view.update_editor_with_range_for_descendant_ix( descendant_ix, window, cx, |editor, mut range, _, window, cx| { // Put the cursor at the beginning of the node. mem::swap(&mut range.start, &mut range.end); editor.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, cx, |selections| { selections.select_ranges([range]); }, ); }, ); }), ) .on_mouse_move(cx.listener( move |tree_view, _: &MouseMoveEvent, window, cx| { if tree_view.hovered_descendant_ix != Some(descendant_ix) { tree_view.hovered_descendant_ix = Some(descendant_ix); tree_view.update_editor_with_range_for_descendant_ix( descendant_ix, window, cx, |editor, range, key, _, cx| { Self::set_editor_highlights(editor, key, &[range], cx); }, ); cx.notify(); } }, )), ); descendant_ix += 1; if cursor.goto_first_child() { depth += 1; } else { visited_children = true; } } } items } fn set_editor_highlights( editor: &mut Editor, key: usize, ranges: &[Range<Anchor>], cx: &mut Context<Editor>, ) { editor.highlight_background_key::<Self>( key, ranges, |_, theme| theme.colors().editor_document_highlight_write_background, cx, ); } fn clear_editor_highlights(editor: &Entity<Editor>, cx: &mut Context<Self>) { let highlight_key = cx.entity_id().as_u64() as usize; editor.update(cx, |editor, cx| { editor.clear_background_highlights_key::<Self>(highlight_key, cx); }); }} Lines 1–500 of 754. Load more
use command_palette_hooks::CommandPaletteFilter; use editor::{Anchor, Editor, ExcerptId, MultiBufferOffset, SelectionEffects, scroll::Autoscroll}; use gpui::{ App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, ScrollStrategy, SharedString, Styled, Task, UniformListScrollHandle, WeakEntity, Window, actions, div, rems, uniform_list, }; use language::{Buffer, OwnedSyntaxLayer}; use std::{any::TypeId, mem, ops::Range}; use theme::ActiveTheme; use tree_sitter::{Node, TreeCursor}; use ui::{ ButtonCommon, ButtonLike, Clickable, Color, ContextMenu, FluentBuilder as _, IconButton, IconName, Label, LabelCommon, LabelSize, PopoverMenu, StyledExt, Tooltip, WithScrollbar, h_flex, v_flex, }; use workspace::{ Event as WorkspaceEvent, SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{Item, ItemHandle}, }; actions!( dev, [ /// Opens the syntax tree view for the current file. OpenSyntaxTreeView, ] ); actions!( syntax_tree_view, [ /// Update the syntax tree view to show the last focused file. UseActiveEditor ] ); pub fn init(cx: &mut App) { let syntax_tree_actions = [TypeId::of::<UseActiveEditor>()]; CommandPaletteFilter::update_global(cx, |this, _| { this.hide_action_types(&syntax_tree_actions); }); cx.observe_new(move |workspace: &mut Workspace, _, _| { workspace.register_action(move |workspace, _: &OpenSyntaxTreeView, window, cx| { CommandPaletteFilter::update_global(cx, |this, _| { this.show_action_types(&syntax_tree_actions); }); let active_item = workspace.active_item(cx); let workspace_handle = workspace.weak_handle(); let syntax_tree_view = cx.new(|cx| { cx.on_release(move |view: &mut SyntaxTreeView, cx| { if view .workspace_handle .read_with(cx, |workspace, cx| { workspace.item_of_type::<SyntaxTreeView>(cx).is_none() }) .unwrap_or_default() { CommandPaletteFilter::update_global(cx, |this, _| { this.hide_action_types(&syntax_tree_actions); }); } }) .detach(); SyntaxTreeView::new(workspace_handle, active_item, window, cx) }); workspace.split_item( SplitDirection::Right, Box::new(syntax_tree_view), window, cx, ) }); workspace.register_action(|workspace, _: &UseActiveEditor, window, cx| { if let Some(tree_view) = workspace.item_of_type::<SyntaxTreeView>(cx) { tree_view.update(cx, |view, cx| { view.update_active_editor(&Default::default(), window, cx) }) } }); }) .detach(); } pub struct SyntaxTreeView { workspace_handle: WeakEntity<Workspace>, editor: Option<EditorState>, list_scroll_handle: UniformListScrollHandle, /// The last active editor in the workspace. Note that this is specifically not the /// currently shown editor. last_active_editor: Option<Entity<Editor>>, selected_descendant_ix: Option<usize>, hovered_descendant_ix: Option<usize>, focus_handle: FocusHandle, } pub struct SyntaxTreeToolbarItemView { tree_view: Option<Entity<SyntaxTreeView>>, subscription: Option<gpui::Subscription>, } struct EditorState { editor: Entity<Editor>, active_buffer: Option<BufferState>, _subscription: gpui::Subscription, } impl EditorState { fn has_language(&self) -> bool { self.active_buffer .as_ref() .is_some_and(|buffer| buffer.active_layer.is_some()) } } #[derive(Clone)] struct BufferState { buffer: Entity<Buffer>, excerpt_id: ExcerptId, active_layer: Option<OwnedSyntaxLayer>, } impl SyntaxTreeView { 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, last_active_editor: None, hovered_descendant_ix: None, selected_descendant_ix: None, focus_handle: cx.focus_handle(), }; 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 Some(editor) = active_item .filter(|item| item.item_id() != cx.entity_id()) .and_then(|item| item.act_as::<Editor>(cx)) else { return; }; if let Some(editor_state) = self.editor.as_ref().filter(|state| state.has_language()) { self.last_active_editor = (editor_state.editor != editor).then_some(editor); } else { self.set_editor(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.editor = None; // Try activating the last active editor if there is one self.update_active_editor(&Default::default(), window, cx); cx.notify(); } } fn update_active_editor( &mut self, _: &UseActiveEditor, window: &mut Window, cx: &mut Context<Self>, ) { let Some(editor) = self.last_active_editor.take() else { return; }; self.set_editor(editor, window, cx); } 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; } editor.update(cx, |editor, cx| { editor.clear_background_highlights::<Self>(cx) }); } let subscription = cx.subscribe_in(&editor, window, |this, _, event, window, cx| { let did_reparse = match event { editor::EditorEvent::Reparsed(_) => true, editor::EditorEvent::SelectionsChanged { .. } => false, _ => return, }; this.editor_updated(did_reparse, window, cx); }); self.editor = Some(EditorState { editor, _subscription: subscription, active_buffer: None, }); self.editor_updated(true, window, cx); } fn editor_updated( &mut self, did_reparse: bool, window: &mut Window, cx: &mut Context<Self>, ) -> Option<()> { // Find which excerpt the cursor is in, and the position within that excerpted buffer. let editor_state = self.editor.as_mut()?; let snapshot = editor_state .editor .update(cx, |editor, cx| editor.snapshot(window, cx)); let (buffer, range, excerpt_id) = editor_state.editor.update(cx, |editor, cx| { let selection_range = editor .selections .last::<MultiBufferOffset>(&editor.display_snapshot(cx)) .range(); let multi_buffer = editor.buffer().read(cx); let (buffer, range, excerpt_id) = snapshot .buffer_snapshot() .range_to_buffer_ranges(selection_range.start..=selection_range.end) .pop()?; let buffer = multi_buffer.buffer(buffer.remote_id()).unwrap(); Some((buffer, range, excerpt_id)) })?; // If the cursor has moved into a different excerpt, retrieve a new syntax layer // from that buffer. let buffer_state = editor_state .active_buffer .get_or_insert_with(|| BufferState { buffer: buffer.clone(), excerpt_id, active_layer: None, }); let mut prev_layer = None; if did_reparse { prev_layer = buffer_state.active_layer.take(); } if buffer_state.buffer != buffer || buffer_state.excerpt_id != excerpt_id { buffer_state.buffer = buffer.clone(); buffer_state.excerpt_id = excerpt_id; buffer_state.active_layer = None; } let layer = match &mut buffer_state.active_layer { Some(layer) => layer, None => { let snapshot = buffer.read(cx).snapshot(); let layer = if let Some(prev_layer) = prev_layer { let prev_range = prev_layer.node().byte_range(); snapshot .syntax_layers() .filter(|layer| layer.language == &prev_layer.language) .min_by_key(|layer| { let range = layer.node().byte_range(); ((range.start as i64) - (prev_range.start as i64)).abs() + ((range.end as i64) - (prev_range.end as i64)).abs() })? } else { snapshot.syntax_layers().next()? }; buffer_state.active_layer.insert(layer.to_owned()) } }; // Within the active layer, find the syntax node under the cursor, // and scroll to it. let mut cursor = layer.node().walk(); while cursor.goto_first_child_for_byte(range.start.0).is_some() { if !range.is_empty() && cursor.node().end_byte() == range.start.0 { cursor.goto_next_sibling(); } } // Ascend to the smallest ancestor that contains the range. loop { let node_range = cursor.node().byte_range(); if node_range.start <= range.start.0 && node_range.end >= range.end.0 { break; } if !cursor.goto_parent() { break; } } let descendant_ix = cursor.descendant_index(); self.selected_descendant_ix = Some(descendant_ix); self.list_scroll_handle .scroll_to_item(descendant_ix, ScrollStrategy::Center); cx.notify(); Some(()) } fn update_editor_with_range_for_descendant_ix( &self, descendant_ix: usize, window: &mut Window, cx: &mut Context<Self>, mut f: impl FnMut(&mut Editor, Range<Anchor>, usize, &mut Window, &mut Context<Editor>), ) -> Option<()> { let editor_state = self.editor.as_ref()?; let buffer_state = editor_state.active_buffer.as_ref()?; let layer = buffer_state.active_layer.as_ref()?; // Find the node. let mut cursor = layer.node().walk(); cursor.goto_descendant(descendant_ix); let node = cursor.node(); let range = node.byte_range(); // Build a text anchor range. let buffer = buffer_state.buffer.read(cx); let range = buffer.anchor_before(range.start)..buffer.anchor_after(range.end); // Build a multibuffer anchor range. let multibuffer = editor_state.editor.read(cx).buffer(); let multibuffer = multibuffer.read(cx).snapshot(cx); let excerpt_id = buffer_state.excerpt_id; let range = multibuffer.anchor_range_in_excerpt(excerpt_id, range)?; let key = cx.entity_id().as_u64() as usize; // Update the editor with the anchor range. editor_state.editor.update(cx, |editor, cx| { f(editor, range, key, window, cx); }); Some(()) } fn render_node(cursor: &TreeCursor, depth: u32, selected: bool, cx: &App) -> Div { let colors = cx.theme().colors(); let mut row = h_flex(); if let Some(field_name) = cursor.field_name() { row = row.children([Label::new(field_name).color(Color::Info), Label::new(": ")]); } let node = cursor.node(); row.child(if node.is_named() { Label::new(node.kind()).color(Color::Default) } else { Label::new(format!("\"{}\"", node.kind())).color(Color::Created) }) .child( div() .child(Label::new(format_node_range(node)).color(Color::Muted)) .pl_1(), ) .text_bg(if selected { colors.element_selected } else { Hsla::default() }) .pl(rems(depth as f32)) .hover(|style| style.bg(colors.element_hover)) } fn compute_items( &mut self, layer: &OwnedSyntaxLayer, range: Range<usize>, cx: &Context<Self>, ) -> Vec<Div> { let mut items = Vec::new(); let mut cursor = layer.node().walk(); let mut descendant_ix = range.start; cursor.goto_descendant(descendant_ix); let mut depth = cursor.depth(); let mut visited_children = false; while descendant_ix < range.end { if visited_children { if cursor.goto_next_sibling() { visited_children = false; } else if cursor.goto_parent() { depth -= 1; } else { break; } } else { items.push( Self::render_node( &cursor, depth, Some(descendant_ix) == self.selected_descendant_ix, cx, ) .on_mouse_down( MouseButton::Left, cx.listener(move |tree_view, _: &MouseDownEvent, window, cx| { tree_view.update_editor_with_range_for_descendant_ix( descendant_ix, window, cx, |editor, mut range, _, window, cx| { // Put the cursor at the beginning of the node. mem::swap(&mut range.start, &mut range.end); editor.change_selections( SelectionEffects::scroll(Autoscroll::newest()), window, cx, |selections| { selections.select_ranges([range]); }, ); }, ); }), ) .on_mouse_move(cx.listener( move |tree_view, _: &MouseMoveEvent, window, cx| { if tree_view.hovered_descendant_ix != Some(descendant_ix) { tree_view.hovered_descendant_ix = Some(descendant_ix); tree_view.update_editor_with_range_for_descendant_ix( descendant_ix, window, cx, |editor, range, key, _, cx| { Self::set_editor_highlights(editor, key, &[range], cx); }, ); cx.notify(); } }, )), ); descendant_ix += 1; if cursor.goto_first_child() { depth += 1; } else { visited_children = true; } } } items } fn set_editor_highlights( editor: &mut Editor, key: usize, ranges: &[Range<Anchor>], cx: &mut Context<Editor>, ) { editor.highlight_background_key::<Self>( key, ranges, |_, theme| theme.colors().editor_document_highlight_write_background, cx, ); } fn clear_editor_highlights(editor: &Entity<Editor>, cx: &mut Context<Self>) { let highlight_key = cx.entity_id().as_u64() as usize; editor.update(cx, |editor, cx| { editor.clear_background_highlights_key::<Self>(highlight_key, cx); }); } }