Generated
+1
@@ -2805,6 +2805,7 @@ dependencies = [
|
||||
"tree-sitter-html",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript",
|
||||
"ui2",
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace2",
|
||||
|
||||
@@ -44,6 +44,7 @@ snippet = { path = "../snippet" }
|
||||
sum_tree = { path = "../sum_tree" }
|
||||
text = { package="text2", path = "../text2" }
|
||||
theme = { package="theme2", path = "../theme2" }
|
||||
ui = { package = "ui2", path = "../ui2" }
|
||||
util = { path = "../util" }
|
||||
sqlez = { path = "../sqlez" }
|
||||
workspace = { package = "workspace2", path = "../workspace2" }
|
||||
|
||||
+291
-348
@@ -39,10 +39,12 @@ use futures::FutureExt;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use git::diff_hunk_to_display;
|
||||
use gpui::{
|
||||
action, actions, point, px, relative, rems, size, AnyElement, AppContext, BackgroundExecutor,
|
||||
Bounds, ClipboardItem, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures,
|
||||
FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, Subscription,
|
||||
Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
action, actions, div, point, px, relative, rems, size, uniform_list, AnyElement, AppContext,
|
||||
AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
|
||||
DispatchContext, EventEmitter, FocusHandle, FontFeatures, FontStyle, FontWeight,
|
||||
HighlightStyle, Hsla, InputHandler, Model, MouseButton, ParentElement, Pixels, Render,
|
||||
StatelessInteractive, Styled, Subscription, Task, TextStyle, UniformListScrollHandle, View,
|
||||
ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use highlight_matching_bracket::refresh_matching_bracket_highlights;
|
||||
use hover_popover::{hide_hover, HoverState};
|
||||
@@ -67,7 +69,7 @@ pub use multi_buffer::{
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use project::{FormatTrigger, Location, Project};
|
||||
use project::{FormatTrigger, Location, Project, ProjectTransaction};
|
||||
use rand::prelude::*;
|
||||
use rpc::proto::*;
|
||||
use scroll::{
|
||||
@@ -95,6 +97,7 @@ use text::{OffsetUtf16, Rope};
|
||||
use theme::{
|
||||
ActiveTheme, DiagnosticStyle, PlayerColor, SyntaxTheme, Theme, ThemeColors, ThemeSettings,
|
||||
};
|
||||
use ui::IconButton;
|
||||
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
item::ItemEvent, searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace,
|
||||
@@ -384,26 +387,6 @@ actions!(
|
||||
UnfoldLines,
|
||||
);
|
||||
|
||||
// impl_actions!(
|
||||
// editor,
|
||||
// [
|
||||
// SelectNext,
|
||||
// SelectPrevious,
|
||||
// SelectAllMatches,
|
||||
// SelectToBeginningOfLine,
|
||||
// SelectToEndOfLine,
|
||||
// ToggleCodeActions,
|
||||
// MovePageUp,
|
||||
// MovePageDown,
|
||||
// ConfirmCompletion,
|
||||
// ConfirmCodeAction,
|
||||
// ToggleComments,
|
||||
// FoldAt,
|
||||
// UnfoldAt,
|
||||
// GutterHover
|
||||
// ]
|
||||
// );
|
||||
|
||||
enum DocumentHighlightRead {}
|
||||
enum DocumentHighlightWrite {}
|
||||
enum InputComposition {}
|
||||
@@ -919,15 +902,14 @@ impl ContextMenu {
|
||||
fn render(
|
||||
&self,
|
||||
cursor_position: DisplayPoint,
|
||||
style: EditorStyle,
|
||||
style: &EditorStyle,
|
||||
workspace: Option<WeakView<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> (DisplayPoint, AnyElement<Editor>) {
|
||||
todo!()
|
||||
// match self {
|
||||
// ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)),
|
||||
// ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
|
||||
// }
|
||||
match self {
|
||||
ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)),
|
||||
ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,29 +922,13 @@ struct CompletionsMenu {
|
||||
match_candidates: Arc<[StringMatchCandidate]>,
|
||||
matches: Arc<[StringMatch]>,
|
||||
selected_item: usize,
|
||||
list: UniformListState,
|
||||
}
|
||||
|
||||
// todo!(this is fake)
|
||||
#[derive(Clone, Default)]
|
||||
struct UniformListState;
|
||||
|
||||
// todo!(this is fake)
|
||||
impl UniformListState {
|
||||
pub fn scroll_to(&mut self, target: ScrollTarget) {}
|
||||
}
|
||||
|
||||
// todo!(this is somewhat fake)
|
||||
#[derive(Debug)]
|
||||
pub enum ScrollTarget {
|
||||
Show(usize),
|
||||
Center(usize),
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
}
|
||||
|
||||
impl CompletionsMenu {
|
||||
fn select_first(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
|
||||
self.selected_item = 0;
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
self.attempt_resolve_selected_completion_documentation(project, cx);
|
||||
cx.notify();
|
||||
}
|
||||
@@ -973,7 +939,7 @@ impl CompletionsMenu {
|
||||
} else {
|
||||
self.selected_item = self.matches.len() - 1;
|
||||
}
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
self.attempt_resolve_selected_completion_documentation(project, cx);
|
||||
cx.notify();
|
||||
}
|
||||
@@ -984,14 +950,14 @@ impl CompletionsMenu {
|
||||
} else {
|
||||
self.selected_item = 0;
|
||||
}
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
self.attempt_resolve_selected_completion_documentation(project, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_last(&mut self, project: Option<&Model<Project>>, cx: &mut ViewContext<Editor>) {
|
||||
self.selected_item = self.matches.len() - 1;
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
self.attempt_resolve_selected_completion_documentation(project, cx);
|
||||
cx.notify();
|
||||
}
|
||||
@@ -1252,13 +1218,13 @@ impl CompletionsMenu {
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
style: EditorStyle,
|
||||
style: &EditorStyle,
|
||||
workspace: Option<WeakView<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
) -> AnyElement<Editor> {
|
||||
todo!("old implementation below")
|
||||
}
|
||||
// ) -> AnyElement<Editor> {
|
||||
|
||||
// enum CompletionTag {}
|
||||
|
||||
// let settings = EditorSettings>(cx);
|
||||
@@ -1527,14 +1493,14 @@ struct CodeActionsMenu {
|
||||
actions: Arc<[CodeAction]>,
|
||||
buffer: Model<Buffer>,
|
||||
selected_item: usize,
|
||||
list: UniformListState,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
deployed_from_indicator: bool,
|
||||
}
|
||||
|
||||
impl CodeActionsMenu {
|
||||
fn select_first(&mut self, cx: &mut ViewContext<Editor>) {
|
||||
self.selected_item = 0;
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
@@ -1544,7 +1510,7 @@ impl CodeActionsMenu {
|
||||
} else {
|
||||
self.selected_item = self.actions.len() - 1;
|
||||
}
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1554,13 +1520,13 @@ impl CodeActionsMenu {
|
||||
} else {
|
||||
self.selected_item = 0;
|
||||
}
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn select_last(&mut self, cx: &mut ViewContext<Editor>) {
|
||||
self.selected_item = self.actions.len() - 1;
|
||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||
self.scroll_handle.scroll_to_item(self.selected_item);
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
@@ -1571,83 +1537,69 @@ impl CodeActionsMenu {
|
||||
fn render(
|
||||
&self,
|
||||
mut cursor_position: DisplayPoint,
|
||||
style: EditorStyle,
|
||||
style: &EditorStyle,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> (DisplayPoint, AnyElement<Editor>) {
|
||||
todo!("old version below")
|
||||
let actions = self.actions.clone();
|
||||
let selected_item = self.selected_item;
|
||||
let element = uniform_list(
|
||||
"code_actions_menu",
|
||||
self.actions.len(),
|
||||
move |editor, range, cx| {
|
||||
actions[range.clone()]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(ix, action)| {
|
||||
let item_ix = range.start + ix;
|
||||
let selected = selected_item == item_ix;
|
||||
let colors = cx.theme().colors();
|
||||
div()
|
||||
.px_2()
|
||||
.text_color(colors.text)
|
||||
.when(selected, |style| {
|
||||
style
|
||||
.bg(colors.element_active)
|
||||
.text_color(colors.text_accent)
|
||||
})
|
||||
.hover(|style| {
|
||||
style
|
||||
.bg(colors.element_hover)
|
||||
.text_color(colors.text_accent)
|
||||
})
|
||||
.on_mouse_down(MouseButton::Left, move |editor: &mut Editor, _, cx| {
|
||||
cx.stop_propagation();
|
||||
editor
|
||||
.confirm_code_action(
|
||||
&ConfirmCodeAction {
|
||||
item_ix: Some(item_ix),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
})
|
||||
.child(action.lsp_action.title.clone())
|
||||
})
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.bg(cx.theme().colors().element_background)
|
||||
.px_2()
|
||||
.py_1()
|
||||
.with_width_from_item(
|
||||
self.actions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|(_, action)| action.lsp_action.title.chars().count())
|
||||
.map(|(ix, _)| ix),
|
||||
)
|
||||
.render();
|
||||
|
||||
if self.deployed_from_indicator {
|
||||
*cursor_position.column_mut() = 0;
|
||||
}
|
||||
|
||||
(cursor_position, element)
|
||||
}
|
||||
// enum ActionTag {}
|
||||
|
||||
// let container_style = style.autocomplete.container;
|
||||
// let actions = self.actions.clone();
|
||||
// let selected_item = self.selected_item;
|
||||
// let element = UniformList::new(
|
||||
// self.list.clone(),
|
||||
// actions.len(),
|
||||
// cx,
|
||||
// move |_, range, items, cx| {
|
||||
// let start_ix = range.start;
|
||||
// for (ix, action) in actions[range].iter().enumerate() {
|
||||
// let item_ix = start_ix + ix;
|
||||
// items.push(
|
||||
// MouseEventHandler::new::<ActionTag, _>(item_ix, cx, |state, _| {
|
||||
// let item_style = if item_ix == selected_item {
|
||||
// style.autocomplete.selected_item
|
||||
// } else if state.hovered() {
|
||||
// style.autocomplete.hovered_item
|
||||
// } else {
|
||||
// style.autocomplete.item
|
||||
// };
|
||||
|
||||
// Text::new(action.lsp_action.title.clone(), style.text.clone())
|
||||
// .with_soft_wrap(false)
|
||||
// .contained()
|
||||
// .with_style(item_style)
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .on_down(MouseButton::Left, move |_, this, cx| {
|
||||
// let workspace = this
|
||||
// .workspace
|
||||
// .as_ref()
|
||||
// .and_then(|(workspace, _)| workspace.upgrade(cx));
|
||||
// cx.window_context().defer(move |cx| {
|
||||
// if let Some(workspace) = workspace {
|
||||
// workspace.update(cx, |workspace, cx| {
|
||||
// if let Some(task) = Editor::confirm_code_action(
|
||||
// workspace,
|
||||
// &ConfirmCodeAction {
|
||||
// item_ix: Some(item_ix),
|
||||
// },
|
||||
// cx,
|
||||
// ) {
|
||||
// task.detach_and_log_err(cx);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// })
|
||||
// .into_any(),
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
// .with_width_from_item(
|
||||
// self.actions
|
||||
// .iter()
|
||||
// .enumerate()
|
||||
// .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
|
||||
// .map(|(ix, _)| ix),
|
||||
// )
|
||||
// .contained()
|
||||
// .with_style(container_style)
|
||||
// .into_any();
|
||||
|
||||
// if self.deployed_from_indicator {
|
||||
// *cursor_position.column_mut() = 0;
|
||||
// }
|
||||
|
||||
// (cursor_position, element)
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct CopilotState {
|
||||
@@ -3660,7 +3612,7 @@ impl Editor {
|
||||
completions: Arc::new(RwLock::new(completions.into())),
|
||||
matches: Vec::new().into(),
|
||||
selected_item: 0,
|
||||
list: Default::default(),
|
||||
scroll_handle: UniformListScrollHandle::new(),
|
||||
};
|
||||
menu.filter(query.as_deref(), cx.background_executor().clone())
|
||||
.await;
|
||||
@@ -3846,156 +3798,161 @@ impl Editor {
|
||||
// }))
|
||||
// }
|
||||
|
||||
// pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
|
||||
// let mut context_menu = self.context_menu.write();
|
||||
// if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
|
||||
// *context_menu = None;
|
||||
// cx.notify();
|
||||
// return;
|
||||
// }
|
||||
// drop(context_menu);
|
||||
pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
|
||||
let mut context_menu = self.context_menu.write();
|
||||
if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) {
|
||||
*context_menu = None;
|
||||
cx.notify();
|
||||
return;
|
||||
}
|
||||
drop(context_menu);
|
||||
|
||||
// let deployed_from_indicator = action.deployed_from_indicator;
|
||||
// let mut task = self.code_actions_task.take();
|
||||
// cx.spawn(|this, mut cx| async move {
|
||||
// while let Some(prev_task) = task {
|
||||
// prev_task.await;
|
||||
// task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
|
||||
// }
|
||||
let deployed_from_indicator = action.deployed_from_indicator;
|
||||
let mut task = self.code_actions_task.take();
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
while let Some(prev_task) = task {
|
||||
prev_task.await;
|
||||
task = this.update(&mut cx, |this, _| this.code_actions_task.take())?;
|
||||
}
|
||||
|
||||
// this.update(&mut cx, |this, cx| {
|
||||
// if this.focused {
|
||||
// if let Some((buffer, actions)) = this.available_code_actions.clone() {
|
||||
// this.completion_tasks.clear();
|
||||
// this.discard_copilot_suggestion(cx);
|
||||
// *this.context_menu.write() =
|
||||
// Some(ContextMenu::CodeActions(CodeActionsMenu {
|
||||
// buffer,
|
||||
// actions,
|
||||
// selected_item: Default::default(),
|
||||
// list: Default::default(),
|
||||
// deployed_from_indicator,
|
||||
// }));
|
||||
// }
|
||||
// }
|
||||
// })?;
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if this.focus_handle.is_focused(cx) {
|
||||
if let Some((buffer, actions)) = this.available_code_actions.clone() {
|
||||
this.completion_tasks.clear();
|
||||
this.discard_copilot_suggestion(cx);
|
||||
*this.context_menu.write() =
|
||||
Some(ContextMenu::CodeActions(CodeActionsMenu {
|
||||
buffer,
|
||||
actions,
|
||||
selected_item: Default::default(),
|
||||
scroll_handle: UniformListScrollHandle::default(),
|
||||
deployed_from_indicator,
|
||||
}));
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
// Ok::<_, anyhow::Error>(())
|
||||
// })
|
||||
// .detach_and_log_err(cx);
|
||||
// }
|
||||
Ok::<_, anyhow::Error>(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
// pub fn confirm_code_action(
|
||||
// workspace: &mut Workspace,
|
||||
// action: &ConfirmCodeAction,
|
||||
// cx: &mut ViewContext<Workspace>,
|
||||
// ) -> Option<Task<Result<()>>> {
|
||||
// let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
|
||||
// let actions_menu = if let ContextMenu::CodeActions(menu) =
|
||||
// editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
|
||||
// {
|
||||
// menu
|
||||
// } else {
|
||||
// return None;
|
||||
// };
|
||||
// let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
|
||||
// let action = actions_menu.actions.get(action_ix)?.clone();
|
||||
// let title = action.lsp_action.title.clone();
|
||||
// let buffer = actions_menu.buffer;
|
||||
pub fn confirm_code_action(
|
||||
&mut self,
|
||||
action: &ConfirmCodeAction,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
let actions_menu = if let ContextMenu::CodeActions(menu) = self.hide_context_menu(cx)? {
|
||||
menu
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
|
||||
let action = actions_menu.actions.get(action_ix)?.clone();
|
||||
let title = action.lsp_action.title.clone();
|
||||
let buffer = actions_menu.buffer;
|
||||
let workspace = self.workspace()?;
|
||||
|
||||
// let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
|
||||
// project.apply_code_action(buffer, action, true, cx)
|
||||
// });
|
||||
// let editor = editor.downgrade();
|
||||
// Some(cx.spawn(|workspace, cx| async move {
|
||||
// let project_transaction = apply_code_actions.await?;
|
||||
// Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
|
||||
// }))
|
||||
// }
|
||||
let apply_code_actions = workspace
|
||||
.read(cx)
|
||||
.project()
|
||||
.clone()
|
||||
.update(cx, |project, cx| {
|
||||
project.apply_code_action(buffer, action, true, cx)
|
||||
});
|
||||
let workspace = workspace.downgrade();
|
||||
Some(cx.spawn(|editor, cx| async move {
|
||||
let project_transaction = apply_code_actions.await?;
|
||||
Self::open_project_transaction(&editor, workspace, project_transaction, title, cx).await
|
||||
}))
|
||||
}
|
||||
|
||||
// async fn open_project_transaction(
|
||||
// this: &WeakViewHandle<Editor
|
||||
// workspace: WeakViewHandle<Workspace
|
||||
// transaction: ProjectTransaction,
|
||||
// title: String,
|
||||
// mut cx: AsyncAppContext,
|
||||
// ) -> Result<()> {
|
||||
// let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx))?;
|
||||
async fn open_project_transaction(
|
||||
this: &WeakView<Editor>,
|
||||
workspace: WeakView<Workspace>,
|
||||
transaction: ProjectTransaction,
|
||||
title: String,
|
||||
mut cx: AsyncWindowContext,
|
||||
) -> Result<()> {
|
||||
let replica_id = this.update(&mut cx, |this, cx| this.replica_id(cx))?;
|
||||
|
||||
// let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
|
||||
// entries.sort_unstable_by_key(|(buffer, _)| {
|
||||
// buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
|
||||
// });
|
||||
let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
|
||||
cx.update(|_, cx| {
|
||||
entries.sort_unstable_by_key(|(buffer, _)| {
|
||||
buffer.read(cx).file().map(|f| f.path().clone())
|
||||
});
|
||||
})?;
|
||||
|
||||
// // If the project transaction's edits are all contained within this editor, then
|
||||
// // avoid opening a new editor to display them.
|
||||
// If the project transaction's edits are all contained within this editor, then
|
||||
// avoid opening a new editor to display them.
|
||||
|
||||
// if let Some((buffer, transaction)) = entries.first() {
|
||||
// if entries.len() == 1 {
|
||||
// let excerpt = this.read_with(&cx, |editor, cx| {
|
||||
// editor
|
||||
// .buffer()
|
||||
// .read(cx)
|
||||
// .excerpt_containing(editor.selections.newest_anchor().head(), cx)
|
||||
// })?;
|
||||
// if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
|
||||
// if excerpted_buffer == *buffer {
|
||||
// let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
|
||||
// let excerpt_range = excerpt_range.to_offset(buffer);
|
||||
// buffer
|
||||
// .edited_ranges_for_transaction::<usize>(transaction)
|
||||
// .all(|range| {
|
||||
// excerpt_range.start <= range.start
|
||||
// && excerpt_range.end >= range.end
|
||||
// })
|
||||
// });
|
||||
if let Some((buffer, transaction)) = entries.first() {
|
||||
if entries.len() == 1 {
|
||||
let excerpt = this.update(&mut cx, |editor, cx| {
|
||||
editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.excerpt_containing(editor.selections.newest_anchor().head(), cx)
|
||||
})?;
|
||||
if let Some((_, excerpted_buffer, excerpt_range)) = excerpt {
|
||||
if excerpted_buffer == *buffer {
|
||||
let all_edits_within_excerpt = buffer.read_with(&cx, |buffer, _| {
|
||||
let excerpt_range = excerpt_range.to_offset(buffer);
|
||||
buffer
|
||||
.edited_ranges_for_transaction::<usize>(transaction)
|
||||
.all(|range| {
|
||||
excerpt_range.start <= range.start
|
||||
&& excerpt_range.end >= range.end
|
||||
})
|
||||
})?;
|
||||
|
||||
// if all_edits_within_excerpt {
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// return Ok(());
|
||||
// }
|
||||
if all_edits_within_excerpt {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// let mut ranges_to_highlight = Vec::new();
|
||||
// let excerpt_buffer = cx.build_model(|cx| {
|
||||
// let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
|
||||
// for (buffer_handle, transaction) in &entries {
|
||||
// let buffer = buffer_handle.read(cx);
|
||||
// ranges_to_highlight.extend(
|
||||
// multibuffer.push_excerpts_with_context_lines(
|
||||
// buffer_handle.clone(),
|
||||
// buffer
|
||||
// .edited_ranges_for_transaction::<usize>(transaction)
|
||||
// .collect(),
|
||||
// 1,
|
||||
// cx,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
|
||||
// multibuffer
|
||||
// });
|
||||
let mut ranges_to_highlight = Vec::new();
|
||||
let excerpt_buffer = cx.build_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
|
||||
for (buffer_handle, transaction) in &entries {
|
||||
let buffer = buffer_handle.read(cx);
|
||||
ranges_to_highlight.extend(
|
||||
multibuffer.push_excerpts_with_context_lines(
|
||||
buffer_handle.clone(),
|
||||
buffer
|
||||
.edited_ranges_for_transaction::<usize>(transaction)
|
||||
.collect(),
|
||||
1,
|
||||
cx,
|
||||
),
|
||||
);
|
||||
}
|
||||
multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)), cx);
|
||||
multibuffer
|
||||
})?;
|
||||
|
||||
// workspace.update(&mut cx, |workspace, cx| {
|
||||
// let project = workspace.project().clone();
|
||||
// let editor =
|
||||
// cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
|
||||
// workspace.add_item(Box::new(editor.clone()), cx);
|
||||
// editor.update(cx, |editor, cx| {
|
||||
// editor.highlight_background::<Self>(
|
||||
// ranges_to_highlight,
|
||||
// |theme| theme.editor.highlighted_line_background,
|
||||
// cx,
|
||||
// );
|
||||
// });
|
||||
// })?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let project = workspace.project().clone();
|
||||
let editor =
|
||||
cx.build_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
|
||||
workspace.add_item(Box::new(editor.clone()), cx);
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.highlight_background::<Self>(
|
||||
ranges_to_highlight,
|
||||
|theme| theme.editor_highlighted_line_background,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
})?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
|
||||
let project = self.project.clone()?;
|
||||
@@ -4390,41 +4347,29 @@ impl Editor {
|
||||
self.discard_copilot_suggestion(cx);
|
||||
}
|
||||
|
||||
// pub fn render_code_actions_indicator(
|
||||
// &self,
|
||||
// style: &EditorStyle,
|
||||
// is_active: bool,
|
||||
// cx: &mut ViewContext<Self>,
|
||||
// ) -> Option<AnyElement<Self>> {
|
||||
// if self.available_code_actions.is_some() {
|
||||
// enum CodeActions {}
|
||||
// Some(
|
||||
// MouseEventHandler::new::<CodeActions, _>(0, cx, |state, _| {
|
||||
// Svg::new("icons/bolt.svg").with_color(
|
||||
// style
|
||||
// .code_actions
|
||||
// .indicator
|
||||
// .in_state(is_active)
|
||||
// .style_for(state)
|
||||
// .color,
|
||||
// )
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .with_padding(Padding::uniform(3.))
|
||||
// .on_down(MouseButton::Left, |_, this, cx| {
|
||||
// this.toggle_code_actions(
|
||||
// &ToggleCodeActions {
|
||||
// deployed_from_indicator: true,
|
||||
// },
|
||||
// cx,
|
||||
// );
|
||||
// })
|
||||
// .into_any(),
|
||||
// )
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
pub fn render_code_actions_indicator(
|
||||
&self,
|
||||
style: &EditorStyle,
|
||||
is_active: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<AnyElement<Self>> {
|
||||
if self.available_code_actions.is_some() {
|
||||
Some(
|
||||
IconButton::new("code_actions_indicator", ui::Icon::Bolt)
|
||||
.on_click(|editor: &mut Editor, cx| {
|
||||
editor.toggle_code_actions(
|
||||
&ToggleCodeActions {
|
||||
deployed_from_indicator: true,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.render(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn render_fold_indicators(
|
||||
// &self,
|
||||
@@ -4491,29 +4436,27 @@ impl Editor {
|
||||
// }
|
||||
|
||||
pub fn context_menu_visible(&self) -> bool {
|
||||
false
|
||||
// todo!("context menu")
|
||||
// self.context_menu
|
||||
// .read()
|
||||
// .as_ref()
|
||||
// .map_or(false, |menu| menu.visible())
|
||||
self.context_menu
|
||||
.read()
|
||||
.as_ref()
|
||||
.map_or(false, |menu| menu.visible())
|
||||
}
|
||||
|
||||
// pub fn render_context_menu(
|
||||
// &self,
|
||||
// cursor_position: DisplayPoint,
|
||||
// style: EditorStyle,
|
||||
// cx: &mut ViewContext<Editor>,
|
||||
// ) -> Option<(DisplayPoint, AnyElement<Editor>)> {
|
||||
// self.context_menu.read().as_ref().map(|menu| {
|
||||
// menu.render(
|
||||
// cursor_position,
|
||||
// style,
|
||||
// self.workspace.as_ref().map(|(w, _)| w.clone()),
|
||||
// cx,
|
||||
// )
|
||||
// })
|
||||
// }
|
||||
pub fn render_context_menu(
|
||||
&self,
|
||||
cursor_position: DisplayPoint,
|
||||
style: &EditorStyle,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Option<(DisplayPoint, AnyElement<Editor>)> {
|
||||
self.context_menu.read().as_ref().map(|menu| {
|
||||
menu.render(
|
||||
cursor_position,
|
||||
style,
|
||||
self.workspace.as_ref().map(|(w, _)| w.clone()),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
|
||||
cx.notify();
|
||||
@@ -5954,29 +5897,29 @@ impl Editor {
|
||||
});
|
||||
}
|
||||
|
||||
// pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
|
||||
// if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
// context_menu.select_first(self.project.as_ref(), cx);
|
||||
// }
|
||||
// }
|
||||
pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
|
||||
if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
context_menu.select_first(self.project.as_ref(), cx);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
|
||||
// if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
// context_menu.select_prev(self.project.as_ref(), cx);
|
||||
// }
|
||||
// }
|
||||
pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
|
||||
if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
context_menu.select_prev(self.project.as_ref(), cx);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
|
||||
// if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
// context_menu.select_next(self.project.as_ref(), cx);
|
||||
// }
|
||||
// }
|
||||
pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
|
||||
if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
context_menu.select_next(self.project.as_ref(), cx);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
|
||||
// if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
// context_menu.select_last(self.project.as_ref(), cx);
|
||||
// }
|
||||
// }
|
||||
pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
|
||||
if let Some(context_menu) = self.context_menu.write().as_mut() {
|
||||
context_menu.select_last(self.project.as_ref(), cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_to_previous_word_start(
|
||||
&mut self,
|
||||
|
||||
+107
-96
@@ -15,7 +15,7 @@ use crate::{
|
||||
use anyhow::Result;
|
||||
use collections::{BTreeMap, HashMap};
|
||||
use gpui::{
|
||||
black, hsla, point, px, relative, size, transparent_black, Action, AnyElement,
|
||||
black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace,
|
||||
BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchContext, DispatchPhase,
|
||||
Edges, Element, ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla,
|
||||
InputHandler, KeyDownEvent, KeyListener, KeyMatch, Line, LineLayout, Modifiers, MouseButton,
|
||||
@@ -447,7 +447,7 @@ impl EditorElement {
|
||||
fn paint_gutter(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
layout: &LayoutState,
|
||||
layout: &mut LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
@@ -495,14 +495,21 @@ impl EditorElement {
|
||||
// }
|
||||
// }
|
||||
|
||||
// todo!("code actions indicator")
|
||||
// if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
|
||||
// let mut x = 0.;
|
||||
// let mut y = *row as f32 * line_height - scroll_top;
|
||||
// x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x) / 2.;
|
||||
// y += (line_height - indicator.size().y) / 2.;
|
||||
// indicator.paint(bounds.origin + point(x, y), visible_bounds, editor, cx);
|
||||
// }
|
||||
if let Some(indicator) = layout.code_actions_indicator.as_mut() {
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height),
|
||||
);
|
||||
let indicator_size = indicator.element.measure(available_space, editor, cx);
|
||||
let mut x = Pixels::ZERO;
|
||||
let mut y = indicator.row as f32 * line_height - scroll_top;
|
||||
// Center indicator.
|
||||
x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
|
||||
y += (line_height - indicator_size.height) / 2.;
|
||||
indicator
|
||||
.element
|
||||
.draw(bounds.origin + point(x, y), available_space, editor, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_diff_hunks(
|
||||
@@ -596,7 +603,7 @@ impl EditorElement {
|
||||
fn paint_text(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
layout: &LayoutState,
|
||||
layout: &mut LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
@@ -787,48 +794,46 @@ impl EditorElement {
|
||||
)
|
||||
}
|
||||
|
||||
cx.stack(0, |cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
for cursor in cursors {
|
||||
cursor.paint(content_origin, cx);
|
||||
}
|
||||
});
|
||||
// cx.scene().push_layer(Some(bounds));
|
||||
|
||||
// cx.scene().pop_layer();
|
||||
if let Some((position, context_menu)) = layout.context_menu.as_mut() {
|
||||
cx.with_z_index(1, |cx| {
|
||||
let line_height = self.style.text.line_height_in_pixels(cx.rem_size());
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(
|
||||
(12. * line_height).min((bounds.size.height - line_height) / 2.),
|
||||
),
|
||||
);
|
||||
let context_menu_size = context_menu.measure(available_space, editor, cx);
|
||||
|
||||
// if let Some((position, context_menu)) = layout.context_menu.as_mut() {
|
||||
// cx.scene().push_stacking_context(None, None);
|
||||
// let cursor_row_layout =
|
||||
// &layout.position_map.line_layouts[(position.row() - start_row) as usize].line;
|
||||
// let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
|
||||
// let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
|
||||
// let mut list_origin = content_origin + point(x, y);
|
||||
// let list_width = context_menu.size().x;
|
||||
// let list_height = context_menu.size().y;
|
||||
let cursor_row_layout = &layout.position_map.line_layouts
|
||||
[(position.row() - start_row) as usize]
|
||||
.line;
|
||||
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
|
||||
let y =
|
||||
(position.row() + 1) as f32 * layout.position_map.line_height - scroll_top;
|
||||
let mut list_origin = content_origin + point(x, y);
|
||||
let list_width = context_menu_size.width;
|
||||
let list_height = context_menu_size.height;
|
||||
|
||||
// // Snap the right edge of the list to the right edge of the window if
|
||||
// // its horizontal bounds overflow.
|
||||
// if list_origin.x + list_width > cx.window_size().x {
|
||||
// list_origin.set_x((cx.window_size().x - list_width).max(0.));
|
||||
// }
|
||||
// Snap the right edge of the list to the right edge of the window if
|
||||
// its horizontal bounds overflow.
|
||||
if list_origin.x + list_width > cx.viewport_size().width {
|
||||
list_origin.x = (cx.viewport_size().width - list_width).max(Pixels::ZERO);
|
||||
}
|
||||
|
||||
// if list_origin.y + list_height > bounds.max_y {
|
||||
// list_origin
|
||||
// .set_y(list_origin.y - layout.position_map.line_height - list_height);
|
||||
// }
|
||||
if list_origin.y + list_height > bounds.lower_right().y {
|
||||
list_origin.y -= layout.position_map.line_height - list_height;
|
||||
}
|
||||
|
||||
// context_menu.paint(
|
||||
// list_origin,
|
||||
// Bounds::<Pixels>::from_points(
|
||||
// gpui::Point::<Pixels>::zero(),
|
||||
// point(f32::MAX, f32::MAX),
|
||||
// ), // Let content bleed outside of editor
|
||||
// editor,
|
||||
// cx,
|
||||
// );
|
||||
|
||||
// cx.scene().pop_stacking_context();
|
||||
// }
|
||||
context_menu.draw(list_origin, available_space, editor, cx);
|
||||
})
|
||||
}
|
||||
|
||||
// if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() {
|
||||
// cx.scene().push_stacking_context(None, None);
|
||||
@@ -1774,26 +1779,28 @@ impl EditorElement {
|
||||
snapshot = editor.snapshot(cx);
|
||||
}
|
||||
|
||||
// todo!("context menu")
|
||||
// let mut context_menu = None;
|
||||
// let mut code_actions_indicator = None;
|
||||
// if let Some(newest_selection_head) = newest_selection_head {
|
||||
// if (start_row..end_row).contains(&newest_selection_head.row()) {
|
||||
// if editor.context_menu_visible() {
|
||||
// context_menu =
|
||||
// editor.render_context_menu(newest_selection_head, style.clone(), cx);
|
||||
// }
|
||||
let mut context_menu = None;
|
||||
let mut code_actions_indicator = None;
|
||||
if let Some(newest_selection_head) = newest_selection_head {
|
||||
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
||||
if editor.context_menu_visible() {
|
||||
context_menu =
|
||||
editor.render_context_menu(newest_selection_head, &self.style, cx);
|
||||
}
|
||||
|
||||
// let active = matches!(
|
||||
// editor.context_menu.read().as_ref(),
|
||||
// Some(crate::ContextMenu::CodeActions(_))
|
||||
// );
|
||||
let active = matches!(
|
||||
editor.context_menu.read().as_ref(),
|
||||
Some(crate::ContextMenu::CodeActions(_))
|
||||
);
|
||||
|
||||
// code_actions_indicator = editor
|
||||
// .render_code_actions_indicator(&style, active, cx)
|
||||
// .map(|indicator| (newest_selection_head.row(), indicator));
|
||||
// }
|
||||
// }
|
||||
code_actions_indicator = editor
|
||||
.render_code_actions_indicator(&style, active, cx)
|
||||
.map(|element| CodeActionsIndicator {
|
||||
row: newest_selection_head.row(),
|
||||
element,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let visible_rows = start_row..start_row + line_layouts.len() as u32;
|
||||
// todo!("hover")
|
||||
@@ -1831,18 +1838,6 @@ impl EditorElement {
|
||||
// );
|
||||
// }
|
||||
|
||||
// todo!("code actions")
|
||||
// if let Some((_, indicator)) = code_actions_indicator.as_mut() {
|
||||
// indicator.layout(
|
||||
// SizeConstraint::strict_along(
|
||||
// Axis::Vertical,
|
||||
// line_height * style.code_actions.vertical_scale,
|
||||
// ),
|
||||
// editor,
|
||||
// cx,
|
||||
// );
|
||||
// }
|
||||
|
||||
// todo!("fold indicators")
|
||||
// for fold_indicator in fold_indicators.iter_mut() {
|
||||
// if let Some(indicator) = fold_indicator.as_mut() {
|
||||
@@ -1941,8 +1936,8 @@ impl EditorElement {
|
||||
display_hunks,
|
||||
// blocks,
|
||||
selections,
|
||||
// context_menu,
|
||||
// code_actions_indicator,
|
||||
context_menu,
|
||||
code_actions_indicator,
|
||||
// fold_indicators,
|
||||
tab_invisible,
|
||||
space_invisible,
|
||||
@@ -2493,7 +2488,7 @@ impl Element<Editor> for EditorElement {
|
||||
element_state: &mut Self::ElementState,
|
||||
cx: &mut gpui::ViewContext<Editor>,
|
||||
) {
|
||||
let layout = self.compute_layout(editor, cx, bounds);
|
||||
let mut layout = self.compute_layout(editor, cx, bounds);
|
||||
let gutter_bounds = Bounds {
|
||||
origin: bounds.origin,
|
||||
size: layout.gutter_size,
|
||||
@@ -2503,21 +2498,24 @@ impl Element<Editor> for EditorElement {
|
||||
size: layout.text_size,
|
||||
};
|
||||
|
||||
cx.with_content_mask(ContentMask { bounds }, |cx| {
|
||||
self.paint_mouse_listeners(
|
||||
bounds,
|
||||
gutter_bounds,
|
||||
text_bounds,
|
||||
&layout.position_map,
|
||||
cx,
|
||||
);
|
||||
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
|
||||
if layout.gutter_size.width > Pixels::ZERO {
|
||||
self.paint_gutter(gutter_bounds, &layout, editor, cx);
|
||||
}
|
||||
self.paint_text(text_bounds, &layout, editor, cx);
|
||||
let input_handler = ElementInputHandler::new(bounds, cx);
|
||||
cx.handle_input(&editor.focus_handle, input_handler);
|
||||
// We call with_z_index to establish a new stacking context.
|
||||
cx.with_z_index(0, |cx| {
|
||||
cx.with_content_mask(ContentMask { bounds }, |cx| {
|
||||
self.paint_mouse_listeners(
|
||||
bounds,
|
||||
gutter_bounds,
|
||||
text_bounds,
|
||||
&layout.position_map,
|
||||
cx,
|
||||
);
|
||||
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
|
||||
if layout.gutter_size.width > Pixels::ZERO {
|
||||
self.paint_gutter(gutter_bounds, &mut layout, editor, cx);
|
||||
}
|
||||
self.paint_text(text_bounds, &mut layout, editor, cx);
|
||||
let input_handler = ElementInputHandler::new(bounds, cx);
|
||||
cx.handle_input(&editor.focus_handle, input_handler);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3143,14 +3141,19 @@ pub struct LayoutState {
|
||||
show_scrollbars: bool,
|
||||
is_singleton: bool,
|
||||
max_row: u32,
|
||||
// context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
|
||||
// code_actions_indicator: Option<(u32, AnyElement<Editor>)>,
|
||||
context_menu: Option<(DisplayPoint, AnyElement<Editor>)>,
|
||||
code_actions_indicator: Option<CodeActionsIndicator>,
|
||||
// hover_popovers: Option<(DisplayPoint, Vec<AnyElement<Editor>>)>,
|
||||
// fold_indicators: Vec<Option<AnyElement<Editor>>>,
|
||||
tab_invisible: Line,
|
||||
space_invisible: Line,
|
||||
}
|
||||
|
||||
struct CodeActionsIndicator {
|
||||
row: u32,
|
||||
element: AnyElement<Editor>,
|
||||
}
|
||||
|
||||
struct PositionMap {
|
||||
size: Size<Pixels>,
|
||||
line_height: Pixels,
|
||||
@@ -4123,7 +4126,7 @@ fn build_key_listeners(
|
||||
build_action_listener(Editor::unfold_at),
|
||||
build_action_listener(Editor::fold_selected_ranges),
|
||||
build_action_listener(Editor::show_completions),
|
||||
// build_action_listener(Editor::toggle_code_actions), todo!()
|
||||
build_action_listener(Editor::toggle_code_actions),
|
||||
// build_action_listener(Editor::open_excerpts), todo!()
|
||||
build_action_listener(Editor::toggle_soft_wrap),
|
||||
build_action_listener(Editor::toggle_inlay_hints),
|
||||
@@ -4139,13 +4142,21 @@ fn build_key_listeners(
|
||||
build_action_listener(Editor::restart_language_server),
|
||||
build_action_listener(Editor::show_character_palette),
|
||||
// build_action_listener(Editor::confirm_completion), todo!()
|
||||
// build_action_listener(Editor::confirm_code_action), todo!()
|
||||
build_action_listener(|editor, action, cx| {
|
||||
editor
|
||||
.confirm_code_action(action, cx)
|
||||
.map(|task| task.detach_and_log_err(cx));
|
||||
}),
|
||||
// build_action_listener(Editor::rename), todo!()
|
||||
// build_action_listener(Editor::confirm_rename), todo!()
|
||||
// build_action_listener(Editor::find_all_references), todo!()
|
||||
build_action_listener(Editor::next_copilot_suggestion),
|
||||
build_action_listener(Editor::previous_copilot_suggestion),
|
||||
build_action_listener(Editor::copilot_suggest),
|
||||
build_action_listener(Editor::context_menu_first),
|
||||
build_action_listener(Editor::context_menu_prev),
|
||||
build_action_listener(Editor::context_menu_next),
|
||||
build_action_listener(Editor::context_menu_last),
|
||||
build_key_listener(
|
||||
move |editor, key_down: &KeyDownEvent, dispatch_context, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble {
|
||||
|
||||
+109
-2
@@ -1,4 +1,6 @@
|
||||
use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, ViewContext};
|
||||
use crate::{
|
||||
AvailableSpace, BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, Size, ViewContext,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub(crate) use smallvec::SmallVec;
|
||||
use std::{any::Any, mem};
|
||||
@@ -61,6 +63,19 @@ trait ElementObject<V> {
|
||||
fn initialize(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
|
||||
fn layout(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) -> LayoutId;
|
||||
fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>);
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels>;
|
||||
fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
);
|
||||
}
|
||||
|
||||
struct RenderedElement<V: 'static, E: Element<V>> {
|
||||
@@ -79,6 +94,11 @@ enum ElementRenderPhase<V> {
|
||||
layout_id: LayoutId,
|
||||
frame_state: Option<V>,
|
||||
},
|
||||
LayoutComputed {
|
||||
layout_id: LayoutId,
|
||||
available_space: Size<AvailableSpace>,
|
||||
frame_state: Option<V>,
|
||||
},
|
||||
Painted,
|
||||
}
|
||||
|
||||
@@ -135,7 +155,9 @@ where
|
||||
}
|
||||
}
|
||||
ElementRenderPhase::Start => panic!("must call initialize before layout"),
|
||||
ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::Painted => {
|
||||
ElementRenderPhase::LayoutRequested { .. }
|
||||
| ElementRenderPhase::LayoutComputed { .. }
|
||||
| ElementRenderPhase::Painted => {
|
||||
panic!("element rendered twice")
|
||||
}
|
||||
};
|
||||
@@ -152,6 +174,11 @@ where
|
||||
ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
mut frame_state,
|
||||
}
|
||||
| ElementRenderPhase::LayoutComputed {
|
||||
layout_id,
|
||||
mut frame_state,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
if let Some(id) = self.element.id() {
|
||||
@@ -171,6 +198,65 @@ where
|
||||
_ => panic!("must call layout before paint"),
|
||||
};
|
||||
}
|
||||
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
if matches!(&self.phase, ElementRenderPhase::Start) {
|
||||
self.initialize(view_state, cx);
|
||||
}
|
||||
|
||||
if matches!(&self.phase, ElementRenderPhase::Initialized { .. }) {
|
||||
self.layout(view_state, cx);
|
||||
}
|
||||
|
||||
let layout_id = match &mut self.phase {
|
||||
ElementRenderPhase::LayoutRequested {
|
||||
layout_id,
|
||||
frame_state,
|
||||
} => {
|
||||
cx.compute_layout(*layout_id, available_space);
|
||||
let layout_id = *layout_id;
|
||||
self.phase = ElementRenderPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space,
|
||||
frame_state: frame_state.take(),
|
||||
};
|
||||
layout_id
|
||||
}
|
||||
ElementRenderPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space: prev_available_space,
|
||||
..
|
||||
} => {
|
||||
if available_space != *prev_available_space {
|
||||
cx.compute_layout(*layout_id, available_space);
|
||||
*prev_available_space = available_space;
|
||||
}
|
||||
*layout_id
|
||||
}
|
||||
_ => panic!("cannot measure after painting"),
|
||||
};
|
||||
|
||||
cx.layout_bounds(layout_id).size
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
mut origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.measure(available_space, view_state, cx);
|
||||
// Ignore the element offset when drawing this element, as the origin is already specified
|
||||
// in absolute terms.
|
||||
origin -= cx.element_offset();
|
||||
cx.with_element_offset(Some(origin), |cx| self.paint(view_state, cx))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
|
||||
@@ -196,6 +282,27 @@ impl<V> AnyElement<V> {
|
||||
pub fn paint(&mut self, view_state: &mut V, cx: &mut ViewContext<V>) {
|
||||
self.0.paint(view_state, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout within the given available space to determine its size.
|
||||
pub fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
self.0.measure(available_space, view_state, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout in the available space, then paints it at the given origin.
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
view_state: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.0.draw(origin, available_space, view_state, cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Component<V> {
|
||||
|
||||
@@ -101,7 +101,12 @@ impl<V: 'static> Element<V> for Text<V> {
|
||||
.map(|line| line.wrap_count() + 1)
|
||||
.sum::<usize>();
|
||||
let size = Size {
|
||||
width: lines.iter().map(|line| line.layout.width).max().unwrap(),
|
||||
width: lines
|
||||
.iter()
|
||||
.map(|line| line.layout.width)
|
||||
.max()
|
||||
.unwrap()
|
||||
.ceil(),
|
||||
height: line_height * line_count,
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ where
|
||||
id: id.clone(),
|
||||
style,
|
||||
item_count,
|
||||
item_to_measure_index: 0,
|
||||
render_items: Box::new(move |view, visible_range, cx| {
|
||||
f(view, visible_range, cx)
|
||||
.into_iter()
|
||||
@@ -45,6 +46,7 @@ pub struct UniformList<V: 'static> {
|
||||
id: ElementId,
|
||||
style: StyleRefinement,
|
||||
item_count: usize,
|
||||
item_to_measure_index: usize,
|
||||
render_items: Box<
|
||||
dyn for<'a> Fn(
|
||||
&'a mut V,
|
||||
@@ -56,7 +58,7 @@ pub struct UniformList<V: 'static> {
|
||||
scroll_handle: Option<UniformListScrollHandle>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct UniformListScrollHandle(Arc<Mutex<Option<ScrollHandleState>>>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -112,7 +114,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::ElementState {
|
||||
element_state.unwrap_or_else(|| {
|
||||
let item_size = self.measure_first_item(view_state, None, cx);
|
||||
let item_size = self.measure_item(view_state, None, cx);
|
||||
UniformListState {
|
||||
interactive: InteractiveElementState::default(),
|
||||
item_size,
|
||||
@@ -158,7 +160,6 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let style = self.computed_style();
|
||||
style.paint(bounds, cx);
|
||||
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
@@ -170,10 +171,12 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
);
|
||||
|
||||
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
|
||||
style.paint(bounds, cx);
|
||||
|
||||
let content_size;
|
||||
if self.item_count > 0 {
|
||||
let item_height = self
|
||||
.measure_first_item(view_state, Some(padded_bounds.size.width), cx)
|
||||
.measure_item(view_state, Some(padded_bounds.size.width), cx)
|
||||
.height;
|
||||
if let Some(scroll_handle) = self.scroll_handle.clone() {
|
||||
scroll_handle.0.lock().replace(ScrollHandleState {
|
||||
@@ -207,19 +210,13 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
|
||||
cx.with_z_index(1, |cx| {
|
||||
for (item, ix) in items.iter_mut().zip(visible_range) {
|
||||
item.initialize(view_state, cx);
|
||||
|
||||
let layout_id = item.layout(view_state, cx);
|
||||
cx.compute_layout(
|
||||
layout_id,
|
||||
Size {
|
||||
width: AvailableSpace::Definite(bounds.size.width),
|
||||
height: AvailableSpace::Definite(item_height),
|
||||
},
|
||||
);
|
||||
let offset =
|
||||
let item_origin =
|
||||
padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
|
||||
cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx))
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_bounds.size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
item.draw(item_origin, available_space, view_state, cx);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -245,27 +242,31 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
||||
}
|
||||
|
||||
impl<V> UniformList<V> {
|
||||
fn measure_first_item(
|
||||
pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
|
||||
self.item_to_measure_index = item_index.unwrap_or(0);
|
||||
self
|
||||
}
|
||||
|
||||
fn measure_item(
|
||||
&self,
|
||||
view_state: &mut V,
|
||||
list_width: Option<Pixels>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Size<Pixels> {
|
||||
let mut items = (self.render_items)(view_state, 0..1, cx);
|
||||
debug_assert!(items.len() == 1);
|
||||
if self.item_count == 0 {
|
||||
return Size::default();
|
||||
}
|
||||
|
||||
let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
|
||||
let mut items = (self.render_items)(view_state, item_ix..item_ix + 1, cx);
|
||||
let mut item_to_measure = items.pop().unwrap();
|
||||
item_to_measure.initialize(view_state, cx);
|
||||
let layout_id = item_to_measure.layout(view_state, cx);
|
||||
cx.compute_layout(
|
||||
layout_id,
|
||||
Size {
|
||||
width: list_width.map_or(AvailableSpace::MinContent, |width| {
|
||||
AvailableSpace::Definite(width)
|
||||
}),
|
||||
height: AvailableSpace::MinContent,
|
||||
},
|
||||
let available_space = size(
|
||||
list_width.map_or(AvailableSpace::MinContent, |width| {
|
||||
AvailableSpace::Definite(width)
|
||||
}),
|
||||
AvailableSpace::MinContent,
|
||||
);
|
||||
cx.layout_bounds(layout_id).size
|
||||
item_to_measure.measure(available_space, view_state, cx)
|
||||
}
|
||||
|
||||
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
|
||||
|
||||
@@ -785,6 +785,10 @@ impl Pixels {
|
||||
Self(self.0.round())
|
||||
}
|
||||
|
||||
pub fn ceil(&self) -> Self {
|
||||
Self(self.0.ceil())
|
||||
}
|
||||
|
||||
pub fn scale(&self, factor: f32) -> ScaledPixels {
|
||||
ScaledPixels(self.0 * factor)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::{AbsoluteLength, Bounds, DefiniteLength, Edges, Length, Pixels, Point, Size, Style};
|
||||
use collections::HashMap;
|
||||
use collections::{HashMap, HashSet};
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::Debug;
|
||||
use taffy::{
|
||||
geometry::{Point as TaffyPoint, Rect as TaffyRect, Size as TaffySize},
|
||||
@@ -12,6 +13,7 @@ pub struct TaffyLayoutEngine {
|
||||
taffy: Taffy,
|
||||
children_to_parents: HashMap<LayoutId, LayoutId>,
|
||||
absolute_layout_bounds: HashMap<LayoutId, Bounds<Pixels>>,
|
||||
computed_layouts: HashSet<LayoutId>,
|
||||
}
|
||||
|
||||
static EXPECT_MESSAGE: &'static str =
|
||||
@@ -23,9 +25,17 @@ impl TaffyLayoutEngine {
|
||||
taffy: Taffy::new(),
|
||||
children_to_parents: HashMap::default(),
|
||||
absolute_layout_bounds: HashMap::default(),
|
||||
computed_layouts: HashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.taffy.clear();
|
||||
self.children_to_parents.clear();
|
||||
self.absolute_layout_bounds.clear();
|
||||
self.computed_layouts.clear();
|
||||
}
|
||||
|
||||
pub fn request_layout(
|
||||
&mut self,
|
||||
style: &Style,
|
||||
@@ -115,6 +125,7 @@ impl TaffyLayoutEngine {
|
||||
}
|
||||
|
||||
pub fn compute_layout(&mut self, id: LayoutId, available_space: Size<AvailableSpace>) {
|
||||
// Leaving this here until we have a better instrumentation approach.
|
||||
// println!("Laying out {} children", self.count_all_children(id)?);
|
||||
// println!("Max layout depth: {}", self.max_depth(0, id)?);
|
||||
|
||||
@@ -124,6 +135,22 @@ impl TaffyLayoutEngine {
|
||||
// println!("N{} --> N{}", u64::from(a), u64::from(b));
|
||||
// }
|
||||
// println!("");
|
||||
//
|
||||
|
||||
if !self.computed_layouts.insert(id) {
|
||||
let mut stack = SmallVec::<[LayoutId; 64]>::new();
|
||||
stack.push(id);
|
||||
while let Some(id) = stack.pop() {
|
||||
self.absolute_layout_bounds.remove(&id);
|
||||
stack.extend(
|
||||
self.taffy
|
||||
.children(id.into())
|
||||
.expect(EXPECT_MESSAGE)
|
||||
.into_iter()
|
||||
.map(Into::into),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// let started_at = std::time::Instant::now();
|
||||
self.taffy
|
||||
@@ -397,7 +424,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
|
||||
pub enum AvailableSpace {
|
||||
/// The amount of space available is the specified number of pixels
|
||||
Definite(Pixels),
|
||||
|
||||
@@ -206,7 +206,7 @@ pub struct Window {
|
||||
display_id: DisplayId,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
rem_size: Pixels,
|
||||
content_size: Size<Pixels>,
|
||||
viewport_size: Size<Pixels>,
|
||||
pub(crate) layout_engine: TaffyLayoutEngine,
|
||||
pub(crate) root_view: Option<AnyView>,
|
||||
pub(crate) element_id_stack: GlobalElementId,
|
||||
@@ -305,7 +305,7 @@ impl Window {
|
||||
display_id,
|
||||
sprite_atlas,
|
||||
rem_size: px(16.),
|
||||
content_size,
|
||||
viewport_size: content_size,
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
root_view: None,
|
||||
element_id_stack: GlobalElementId::default(),
|
||||
@@ -630,7 +630,7 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
fn window_bounds_changed(&mut self) {
|
||||
self.window.scale_factor = self.window.platform_window.scale_factor();
|
||||
self.window.content_size = self.window.platform_window.content_size();
|
||||
self.window.viewport_size = self.window.platform_window.content_size();
|
||||
self.window.bounds = self.window.platform_window.bounds();
|
||||
self.window.display_id = self.window.platform_window.display().id();
|
||||
self.window.dirty = true;
|
||||
@@ -645,6 +645,10 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.bounds
|
||||
}
|
||||
|
||||
pub fn viewport_size(&self) -> Size<Pixels> {
|
||||
self.window.viewport_size
|
||||
}
|
||||
|
||||
pub fn is_window_active(&self) -> bool {
|
||||
self.window.active
|
||||
}
|
||||
@@ -738,7 +742,7 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
/// Called during painting to invoke the given closure in a new stacking context. The given
|
||||
/// z-index is interpreted relative to the previous call to `stack`.
|
||||
pub fn stack<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||
self.window.current_frame.z_index_stack.push(z_index);
|
||||
let result = f(self);
|
||||
self.window.current_frame.z_index_stack.pop();
|
||||
@@ -1036,13 +1040,13 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
self.start_frame();
|
||||
|
||||
self.stack(0, |cx| {
|
||||
let available_space = cx.window.content_size.map(Into::into);
|
||||
self.with_z_index(0, |cx| {
|
||||
let available_space = cx.window.viewport_size.map(Into::into);
|
||||
root_view.draw(available_space, cx);
|
||||
});
|
||||
|
||||
if let Some(active_drag) = self.app.active_drag.take() {
|
||||
self.stack(1, |cx| {
|
||||
self.with_z_index(1, |cx| {
|
||||
let offset = cx.mouse_position() - active_drag.cursor_offset;
|
||||
cx.with_element_offset(Some(offset), |cx| {
|
||||
let available_space =
|
||||
@@ -1052,7 +1056,7 @@ impl<'a> WindowContext<'a> {
|
||||
});
|
||||
});
|
||||
} else if let Some(active_tooltip) = self.app.active_tooltip.take() {
|
||||
self.stack(1, |cx| {
|
||||
self.with_z_index(1, |cx| {
|
||||
cx.with_element_offset(Some(active_tooltip.cursor_offset), |cx| {
|
||||
let available_space =
|
||||
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
|
||||
@@ -1101,6 +1105,8 @@ impl<'a> WindowContext<'a> {
|
||||
self.text_system().start_frame();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.layout_engine.clear();
|
||||
|
||||
mem::swap(&mut window.previous_frame, &mut window.current_frame);
|
||||
let frame = &mut window.current_frame;
|
||||
frame.element_states.clear();
|
||||
@@ -1750,7 +1756,7 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
|
||||
.unwrap_or_else(|| ContentMask {
|
||||
bounds: Bounds {
|
||||
origin: Point::default(),
|
||||
size: self.window().content_size,
|
||||
size: self.window().viewport_size,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ impl<V: 'static> IconButton<V> {
|
||||
|
||||
if let Some(click_handler) = self.handlers.click.clone() {
|
||||
button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
|
||||
cx.stop_propagation();
|
||||
click_handler(state, cx);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user