2024-01-17 23:48:37 +00:00
|
|
|
use editor::{scroll::Autoscroll, Anchor, Editor, ExcerptId};
|
2023-06-09 21:55:46 +00:00
|
|
|
use gpui::{
|
2024-01-03 18:52:40 +00:00
|
|
|
actions, canvas, div, rems, uniform_list, AnyElement, AppContext, AvailableSpace, Div,
|
|
|
|
EventEmitter, FocusHandle, FocusableView, Hsla, InteractiveElement, IntoElement, Model,
|
2024-01-10 11:44:08 +00:00
|
|
|
MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, Styled,
|
2024-01-03 18:52:40 +00:00
|
|
|
UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
|
2023-06-09 21:55:46 +00:00
|
|
|
};
|
2024-01-09 18:53:57 +00:00
|
|
|
use language::{Buffer, OwnedSyntaxLayer};
|
2024-01-03 18:52:40 +00:00
|
|
|
use std::{mem, ops::Range};
|
2024-01-10 11:44:08 +00:00
|
|
|
use theme::ActiveTheme;
|
2023-06-12 22:32:16 +00:00
|
|
|
use tree_sitter::{Node, TreeCursor};
|
2024-01-15 16:34:06 +00:00
|
|
|
use ui::{h_flex, popover_menu, ButtonLike, Color, ContextMenu, Label, LabelCommon, PopoverMenu};
|
2023-06-09 21:55:46 +00:00
|
|
|
use workspace::{
|
|
|
|
item::{Item, ItemHandle},
|
2024-01-03 18:52:40 +00:00
|
|
|
SplitDirection, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
2023-06-09 21:55:46 +00:00
|
|
|
};
|
|
|
|
|
2023-06-12 22:54:36 +00:00
|
|
|
actions!(debug, [OpenSyntaxTreeView]);
|
2023-06-09 21:55:46 +00:00
|
|
|
|
|
|
|
pub fn init(cx: &mut AppContext) {
|
2024-01-03 18:52:40 +00:00
|
|
|
cx.observe_new_views(|workspace: &mut Workspace, _| {
|
|
|
|
workspace.register_action(|workspace, _: &OpenSyntaxTreeView, cx| {
|
2023-06-12 19:18:01 +00:00
|
|
|
let active_item = workspace.active_item(cx);
|
|
|
|
let workspace_handle = workspace.weak_handle();
|
|
|
|
let syntax_tree_view =
|
2024-01-03 18:52:40 +00:00
|
|
|
cx.new_view(|cx| SyntaxTreeView::new(workspace_handle, active_item, cx));
|
|
|
|
workspace.split_item(SplitDirection::Right, Box::new(syntax_tree_view), cx)
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.detach();
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SyntaxTreeView {
|
2024-01-03 18:52:40 +00:00
|
|
|
workspace_handle: WeakView<Workspace>,
|
2023-06-09 23:11:29 +00:00
|
|
|
editor: Option<EditorState>,
|
2024-01-03 18:52:40 +00:00
|
|
|
list_scroll_handle: UniformListScrollHandle,
|
2023-06-09 23:11:29 +00:00
|
|
|
selected_descendant_ix: Option<usize>,
|
|
|
|
hovered_descendant_ix: Option<usize>,
|
2024-01-03 18:52:40 +00:00
|
|
|
focus_handle: FocusHandle,
|
2023-06-09 23:11:29 +00:00
|
|
|
}
|
|
|
|
|
2023-06-12 19:18:01 +00:00
|
|
|
pub struct SyntaxTreeToolbarItemView {
|
2024-01-03 18:52:40 +00:00
|
|
|
tree_view: Option<View<SyntaxTreeView>>,
|
2023-06-12 19:18:01 +00:00
|
|
|
subscription: Option<gpui::Subscription>,
|
|
|
|
}
|
|
|
|
|
2023-06-09 23:11:29 +00:00
|
|
|
struct EditorState {
|
2024-01-03 18:52:40 +00:00
|
|
|
editor: View<Editor>,
|
2023-06-09 23:11:29 +00:00
|
|
|
active_buffer: Option<BufferState>,
|
|
|
|
_subscription: gpui::Subscription,
|
|
|
|
}
|
|
|
|
|
2023-06-12 19:18:01 +00:00
|
|
|
#[derive(Clone)]
|
2023-06-09 23:11:29 +00:00
|
|
|
struct BufferState {
|
2024-01-03 18:52:40 +00:00
|
|
|
buffer: Model<Buffer>,
|
2023-06-09 23:11:29 +00:00
|
|
|
excerpt_id: ExcerptId,
|
2024-01-09 18:53:57 +00:00
|
|
|
active_layer: Option<OwnedSyntaxLayer>,
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SyntaxTreeView {
|
2023-06-12 19:18:01 +00:00
|
|
|
pub fn new(
|
2024-01-03 18:52:40 +00:00
|
|
|
workspace_handle: WeakView<Workspace>,
|
2023-06-12 19:18:01 +00:00
|
|
|
active_item: Option<Box<dyn ItemHandle>>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) -> Self {
|
2023-06-09 21:55:46 +00:00
|
|
|
let mut this = Self {
|
2023-06-12 19:18:01 +00:00
|
|
|
workspace_handle: workspace_handle.clone(),
|
2024-01-03 18:52:40 +00:00
|
|
|
list_scroll_handle: UniformListScrollHandle::new(),
|
2023-06-09 21:55:46 +00:00
|
|
|
editor: None,
|
2023-06-09 23:11:29 +00:00
|
|
|
hovered_descendant_ix: None,
|
|
|
|
selected_descendant_ix: None,
|
2024-01-03 18:52:40 +00:00
|
|
|
focus_handle: cx.focus_handle(),
|
2023-06-09 21:55:46 +00:00
|
|
|
};
|
|
|
|
|
2023-06-12 19:18:01 +00:00
|
|
|
this.workspace_updated(active_item, cx);
|
2023-06-09 21:55:46 +00:00
|
|
|
cx.observe(
|
2024-01-03 18:52:40 +00:00
|
|
|
&workspace_handle.upgrade().unwrap(),
|
2023-06-09 21:55:46 +00:00
|
|
|
|this, workspace, cx| {
|
|
|
|
this.workspace_updated(workspace.read(cx).active_item(cx), cx);
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.detach();
|
|
|
|
|
|
|
|
this
|
|
|
|
}
|
|
|
|
|
|
|
|
fn workspace_updated(
|
|
|
|
&mut self,
|
|
|
|
active_item: Option<Box<dyn ItemHandle>>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
) {
|
|
|
|
if let Some(item) = active_item {
|
2024-01-03 18:52:40 +00:00
|
|
|
if item.item_id() != cx.entity_id() {
|
2023-06-09 21:55:46 +00:00
|
|
|
if let Some(editor) = item.act_as::<Editor>(cx) {
|
|
|
|
self.set_editor(editor, cx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
fn set_editor(&mut self, editor: View<Editor>, cx: &mut ViewContext<Self>) {
|
2023-06-09 23:11:29 +00:00
|
|
|
if let Some(state) = &self.editor {
|
|
|
|
if state.editor == editor {
|
2023-06-09 21:55:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
editor.update(cx, |editor, cx| {
|
2023-06-09 23:11:29 +00:00
|
|
|
editor.clear_background_highlights::<Self>(cx)
|
2023-06-09 21:55:46 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-06-09 23:11:29 +00:00
|
|
|
let subscription = cx.subscribe(&editor, |this, _, event, cx| {
|
2023-06-12 19:18:01 +00:00
|
|
|
let did_reparse = match event {
|
2024-01-03 18:52:40 +00:00
|
|
|
editor::EditorEvent::Reparsed => true,
|
|
|
|
editor::EditorEvent::SelectionsChanged { .. } => false,
|
2023-06-09 21:55:46 +00:00
|
|
|
_ => return,
|
|
|
|
};
|
2023-06-12 19:18:01 +00:00
|
|
|
this.editor_updated(did_reparse, cx);
|
2023-06-09 21:55:46 +00:00
|
|
|
});
|
|
|
|
|
2023-06-09 23:11:29 +00:00
|
|
|
self.editor = Some(EditorState {
|
|
|
|
editor,
|
|
|
|
_subscription: subscription,
|
|
|
|
active_buffer: None,
|
|
|
|
});
|
|
|
|
self.editor_updated(true, cx);
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
|
2023-06-12 19:18:01 +00:00
|
|
|
fn editor_updated(&mut self, did_reparse: bool, cx: &mut ViewContext<Self>) -> Option<()> {
|
2023-06-09 23:11:29 +00:00
|
|
|
// Find which excerpt the cursor is in, and the position within that excerpted buffer.
|
|
|
|
let editor_state = self.editor.as_mut()?;
|
|
|
|
let editor = &editor_state.editor.read(cx);
|
|
|
|
let selection_range = editor.selections.last::<usize>(cx).range();
|
|
|
|
let multibuffer = editor.buffer().read(cx);
|
|
|
|
let (buffer, range, excerpt_id) = multibuffer
|
|
|
|
.range_to_buffer_ranges(selection_range, cx)
|
|
|
|
.pop()?;
|
|
|
|
|
|
|
|
// 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,
|
2023-06-09 21:55:46 +00:00
|
|
|
});
|
2023-06-12 19:18:01 +00:00
|
|
|
let mut prev_layer = None;
|
|
|
|
if did_reparse {
|
|
|
|
prev_layer = buffer_state.active_layer.take();
|
|
|
|
}
|
|
|
|
if buffer_state.buffer != buffer || buffer_state.excerpt_id != buffer_state.excerpt_id {
|
2023-06-09 23:11:29 +00:00
|
|
|
buffer_state.buffer = buffer.clone();
|
|
|
|
buffer_state.excerpt_id = excerpt_id;
|
|
|
|
buffer_state.active_layer = None;
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
|
2023-06-09 23:11:29 +00:00
|
|
|
let layer = match &mut buffer_state.active_layer {
|
|
|
|
Some(layer) => layer,
|
|
|
|
None => {
|
2023-06-12 19:18:01 +00:00
|
|
|
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())
|
2023-06-09 23:11:29 +00:00
|
|
|
}
|
|
|
|
};
|
2023-06-12 19:18:01 +00:00
|
|
|
|
|
|
|
// Within the active layer, find the syntax node under the cursor,
|
|
|
|
// and scroll to it.
|
2023-06-09 23:11:29 +00:00
|
|
|
let mut cursor = layer.node().walk();
|
|
|
|
while cursor.goto_first_child_for_byte(range.start).is_some() {
|
|
|
|
if !range.is_empty() && cursor.node().end_byte() == range.start {
|
|
|
|
cursor.goto_next_sibling();
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-09 23:11:29 +00:00
|
|
|
|
|
|
|
// Ascend to the smallest ancestor that contains the range.
|
|
|
|
loop {
|
|
|
|
let node_range = cursor.node().byte_range();
|
|
|
|
if node_range.start <= range.start && node_range.end >= range.end {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if !cursor.goto_parent() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let descendant_ix = cursor.descendant_index();
|
|
|
|
self.selected_descendant_ix = Some(descendant_ix);
|
2024-01-03 18:52:40 +00:00
|
|
|
self.list_scroll_handle.scroll_to_item(descendant_ix);
|
2023-06-09 23:11:29 +00:00
|
|
|
|
|
|
|
cx.notify();
|
|
|
|
Some(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_editor_with_range_for_descendant_ix(
|
2023-06-09 21:55:46 +00:00
|
|
|
&self,
|
2023-06-09 23:11:29 +00:00
|
|
|
descendant_ix: usize,
|
2023-06-09 21:55:46 +00:00
|
|
|
cx: &mut ViewContext<Self>,
|
|
|
|
mut f: impl FnMut(&mut Editor, Range<Anchor>, &mut ViewContext<Editor>),
|
2023-06-09 23:11:29 +00:00
|
|
|
) -> 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();
|
2023-06-09 21:55:46 +00:00
|
|
|
let range = node.byte_range();
|
|
|
|
|
2023-06-09 23:11:29 +00:00
|
|
|
// 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_in_excerpt(excerpt_id, range.start)
|
|
|
|
..multibuffer.anchor_in_excerpt(excerpt_id, range.end);
|
|
|
|
|
|
|
|
// Update the editor with the anchor range.
|
|
|
|
editor_state.editor.update(cx, |editor, cx| {
|
|
|
|
f(editor, range, cx);
|
2023-06-09 21:55:46 +00:00
|
|
|
});
|
2023-06-09 23:11:29 +00:00
|
|
|
Some(())
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
2023-06-12 19:18:01 +00:00
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
fn render_node(cursor: &TreeCursor, depth: u32, selected: bool, cx: &AppContext) -> Div {
|
|
|
|
let colors = cx.theme().colors();
|
2024-01-15 16:34:06 +00:00
|
|
|
let mut row = h_flex();
|
2023-06-12 22:32:16 +00:00
|
|
|
if let Some(field_name) = cursor.field_name() {
|
2024-01-03 18:52:40 +00:00
|
|
|
row = row.children([Label::new(field_name).color(Color::Info), Label::new(": ")]);
|
2023-06-12 22:32:16 +00:00
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
let node = cursor.node();
|
2023-06-12 22:32:16 +00:00
|
|
|
return row
|
2024-01-03 18:52:40 +00:00
|
|
|
.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(),
|
2023-06-12 19:18:01 +00:00
|
|
|
)
|
2024-01-03 18:52:40 +00:00
|
|
|
.text_bg(if selected {
|
|
|
|
colors.element_selected
|
2023-06-12 19:18:01 +00:00
|
|
|
} else {
|
2024-01-03 18:52:40 +00:00
|
|
|
Hsla::default()
|
2023-06-12 19:18:01 +00:00
|
|
|
})
|
2024-01-03 18:52:40 +00:00
|
|
|
.pl(rems(depth as f32))
|
|
|
|
.hover(|style| style.bg(colors.element_hover));
|
2023-06-12 19:18:01 +00:00
|
|
|
}
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
impl Render for SyntaxTreeView {
|
|
|
|
fn render(&mut self, cx: &mut gpui::ViewContext<'_, Self>) -> impl IntoElement {
|
|
|
|
let mut rendered = div().flex_1();
|
|
|
|
|
2023-06-09 23:11:29 +00:00
|
|
|
if let Some(layer) = self
|
|
|
|
.editor
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|editor| editor.active_buffer.as_ref())
|
|
|
|
.and_then(|buffer| buffer.active_layer.as_ref())
|
|
|
|
{
|
2023-06-09 21:55:46 +00:00
|
|
|
let layer = layer.clone();
|
2024-01-03 18:52:40 +00:00
|
|
|
let list = uniform_list(
|
|
|
|
cx.view().clone(),
|
|
|
|
"SyntaxTreeView",
|
|
|
|
layer.node().descendant_count(),
|
|
|
|
move |this, range, cx| {
|
|
|
|
let mut items = Vec::new();
|
|
|
|
let mut cursor = layer.node().walk();
|
2024-03-02 21:31:47 +00:00
|
|
|
let mut descendant_ix = range.start;
|
2024-01-03 18:52:40 +00:00
|
|
|
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 {
|
2024-01-10 11:44:08 +00:00
|
|
|
items.push(
|
|
|
|
Self::render_node(
|
|
|
|
&cursor,
|
|
|
|
depth,
|
|
|
|
Some(descendant_ix) == this.selected_descendant_ix,
|
|
|
|
cx,
|
|
|
|
)
|
|
|
|
.on_mouse_down(
|
|
|
|
MouseButton::Left,
|
|
|
|
cx.listener(move |tree_view, _: &MouseDownEvent, cx| {
|
|
|
|
tree_view.update_editor_with_range_for_descendant_ix(
|
|
|
|
descendant_ix,
|
|
|
|
cx,
|
|
|
|
|editor, mut range, cx| {
|
|
|
|
// Put the cursor at the beginning of the node.
|
|
|
|
mem::swap(&mut range.start, &mut range.end);
|
|
|
|
|
|
|
|
editor.change_selections(
|
|
|
|
Some(Autoscroll::newest()),
|
|
|
|
cx,
|
|
|
|
|selections| {
|
|
|
|
selections.select_ranges(vec![range]);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.on_mouse_move(cx.listener(
|
|
|
|
move |tree_view, _: &MouseMoveEvent, 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, cx, |editor, range, cx| {
|
|
|
|
editor.clear_background_highlights::<Self>(cx);
|
|
|
|
editor.highlight_background::<Self>(
|
|
|
|
vec![range],
|
|
|
|
|theme| theme.editor_document_highlight_write_background,
|
|
|
|
cx,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
cx.notify();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)),
|
|
|
|
);
|
2024-01-03 18:52:40 +00:00
|
|
|
descendant_ix += 1;
|
|
|
|
if cursor.goto_first_child() {
|
|
|
|
depth += 1;
|
2023-06-09 21:55:46 +00:00
|
|
|
} else {
|
2024-01-03 18:52:40 +00:00
|
|
|
visited_children = true;
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-03 18:52:40 +00:00
|
|
|
}
|
|
|
|
items
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.size_full()
|
|
|
|
.track_scroll(self.list_scroll_handle.clone())
|
|
|
|
.text_bg(cx.theme().colors().background);
|
|
|
|
|
|
|
|
rendered = rendered.child(
|
|
|
|
canvas(move |bounds, cx| {
|
|
|
|
list.into_any_element().draw(
|
|
|
|
bounds.origin,
|
|
|
|
bounds.size.map(AvailableSpace::Definite),
|
|
|
|
cx,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.size_full(),
|
|
|
|
);
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
rendered
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EventEmitter<()> for SyntaxTreeView {}
|
|
|
|
|
|
|
|
impl FocusableView for SyntaxTreeView {
|
|
|
|
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
|
|
|
|
self.focus_handle.clone()
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Item for SyntaxTreeView {
|
2024-01-03 18:52:40 +00:00
|
|
|
type Event = ();
|
|
|
|
|
|
|
|
fn to_item_events(_: &Self::Event, _: impl FnMut(workspace::item::ItemEvent)) {}
|
|
|
|
|
2024-01-04 22:25:11 +00:00
|
|
|
fn tab_content(&self, _: Option<usize>, selected: bool, _: &WindowContext<'_>) -> AnyElement {
|
|
|
|
Label::new("Syntax Tree")
|
|
|
|
.color(if selected {
|
|
|
|
Color::Default
|
|
|
|
} else {
|
|
|
|
Color::Muted
|
|
|
|
})
|
|
|
|
.into_any_element()
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|
2023-06-12 19:18:01 +00:00
|
|
|
|
2024-01-15 21:26:04 +00:00
|
|
|
fn telemetry_event_text(&self) -> Option<&'static str> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2023-06-12 19:18:01 +00:00
|
|
|
fn clone_on_split(
|
|
|
|
&self,
|
2024-01-03 18:52:40 +00:00
|
|
|
_: workspace::WorkspaceId,
|
2023-06-12 19:18:01 +00:00
|
|
|
cx: &mut ViewContext<Self>,
|
2024-01-03 18:52:40 +00:00
|
|
|
) -> Option<View<Self>>
|
2023-06-12 19:18:01 +00:00
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
2024-01-03 18:52:40 +00:00
|
|
|
Some(cx.new_view(|cx| {
|
|
|
|
let mut clone = Self::new(self.workspace_handle.clone(), None, cx);
|
|
|
|
if let Some(editor) = &self.editor {
|
|
|
|
clone.set_editor(editor.editor.clone(), cx)
|
|
|
|
}
|
|
|
|
clone
|
|
|
|
}))
|
2023-06-12 19:18:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SyntaxTreeToolbarItemView {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
tree_view: None,
|
|
|
|
subscription: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
fn render_menu(&mut self, cx: &mut ViewContext<'_, Self>) -> Option<PopoverMenu<ContextMenu>> {
|
2023-06-12 19:18:01 +00:00
|
|
|
let tree_view = self.tree_view.as_ref()?;
|
|
|
|
let tree_view = tree_view.read(cx);
|
|
|
|
|
|
|
|
let editor_state = tree_view.editor.as_ref()?;
|
|
|
|
let buffer_state = editor_state.active_buffer.as_ref()?;
|
|
|
|
let active_layer = buffer_state.active_layer.clone()?;
|
|
|
|
let active_buffer = buffer_state.buffer.read(cx).snapshot();
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
let view = cx.view().clone();
|
2023-06-12 19:18:01 +00:00
|
|
|
Some(
|
2024-01-03 18:52:40 +00:00
|
|
|
popover_menu("Syntax Tree")
|
|
|
|
.trigger(Self::render_header(&active_layer))
|
|
|
|
.menu(move |cx| {
|
|
|
|
ContextMenu::build(cx, |mut menu, cx| {
|
|
|
|
for (layer_ix, layer) in active_buffer.syntax_layers().enumerate() {
|
|
|
|
menu = menu.entry(
|
|
|
|
format!(
|
|
|
|
"{} {}",
|
|
|
|
layer.language.name(),
|
|
|
|
format_node_range(layer.node())
|
|
|
|
),
|
|
|
|
None,
|
|
|
|
cx.handler_for(&view, move |view, cx| {
|
|
|
|
view.select_layer(layer_ix, cx);
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
menu
|
|
|
|
})
|
|
|
|
.into()
|
|
|
|
}),
|
2023-06-12 19:18:01 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn select_layer(&mut self, layer_ix: usize, cx: &mut ViewContext<Self>) -> Option<()> {
|
|
|
|
let tree_view = self.tree_view.as_ref()?;
|
|
|
|
tree_view.update(cx, |view, cx| {
|
|
|
|
let editor_state = view.editor.as_mut()?;
|
|
|
|
let buffer_state = editor_state.active_buffer.as_mut()?;
|
|
|
|
let snapshot = buffer_state.buffer.read(cx).snapshot();
|
|
|
|
let layer = snapshot.syntax_layers().nth(layer_ix)?;
|
|
|
|
buffer_state.active_layer = Some(layer.to_owned());
|
|
|
|
view.selected_descendant_ix = None;
|
|
|
|
cx.notify();
|
2024-01-03 18:52:40 +00:00
|
|
|
view.focus_handle.focus(cx);
|
2023-06-12 19:18:01 +00:00
|
|
|
Some(())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-01-09 18:53:57 +00:00
|
|
|
fn render_header(active_layer: &OwnedSyntaxLayer) -> ButtonLike {
|
2024-01-03 18:52:40 +00:00
|
|
|
ButtonLike::new("syntax tree header")
|
|
|
|
.child(Label::new(active_layer.language.name()))
|
|
|
|
.child(Label::new(format_node_range(active_layer.node())))
|
2023-06-12 19:18:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn format_node_range(node: Node) -> String {
|
|
|
|
let start = node.start_position();
|
|
|
|
let end = node.end_position();
|
|
|
|
format!(
|
|
|
|
"[{}:{} - {}:{}]",
|
|
|
|
start.row + 1,
|
|
|
|
start.column + 1,
|
|
|
|
end.row + 1,
|
|
|
|
end.column + 1,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
impl Render for SyntaxTreeToolbarItemView {
|
|
|
|
fn render(&mut self, cx: &mut ViewContext<'_, Self>) -> impl IntoElement {
|
2023-06-12 19:18:01 +00:00
|
|
|
self.render_menu(cx)
|
2024-01-03 18:52:40 +00:00
|
|
|
.unwrap_or_else(|| popover_menu("Empty Syntax Tree"))
|
2023-06-12 19:18:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 18:52:40 +00:00
|
|
|
impl EventEmitter<ToolbarItemEvent> for SyntaxTreeToolbarItemView {}
|
|
|
|
|
2023-06-12 19:18:01 +00:00
|
|
|
impl ToolbarItemView for SyntaxTreeToolbarItemView {
|
|
|
|
fn set_active_pane_item(
|
|
|
|
&mut self,
|
|
|
|
active_pane_item: Option<&dyn ItemHandle>,
|
|
|
|
cx: &mut ViewContext<Self>,
|
2024-01-03 18:52:40 +00:00
|
|
|
) -> ToolbarItemLocation {
|
2023-06-12 19:18:01 +00:00
|
|
|
if let Some(item) = active_pane_item {
|
|
|
|
if let Some(view) = item.downcast::<SyntaxTreeView>() {
|
|
|
|
self.tree_view = Some(view.clone());
|
|
|
|
self.subscription = Some(cx.observe(&view, |_, _, cx| cx.notify()));
|
2024-01-03 18:52:40 +00:00
|
|
|
return ToolbarItemLocation::PrimaryLeft;
|
2023-06-12 19:18:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
self.tree_view = None;
|
|
|
|
self.subscription = None;
|
|
|
|
ToolbarItemLocation::Hidden
|
|
|
|
}
|
2023-06-09 21:55:46 +00:00
|
|
|
}
|