Generalize showing and hiding of context menus
We still cancel pending completions when hiding the context menu so its not perfectly general, but I think this is ok.
This commit is contained in:
parent
025e83c1ec
commit
ba99b01de6
3 changed files with 74 additions and 53 deletions
|
@ -469,24 +469,34 @@ enum ContextMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
|
fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
|
||||||
match self {
|
if self.visible() {
|
||||||
ContextMenu::Completions(menu) => menu.select_prev(cx),
|
match self {
|
||||||
ContextMenu::CodeActions(menu) => menu.select_prev(cx),
|
ContextMenu::Completions(menu) => menu.select_prev(cx),
|
||||||
|
ContextMenu::CodeActions(menu) => menu.select_prev(cx),
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
|
fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
|
||||||
match self {
|
if self.visible() {
|
||||||
ContextMenu::Completions(menu) => menu.select_next(cx),
|
match self {
|
||||||
ContextMenu::CodeActions(menu) => menu.select_next(cx),
|
ContextMenu::Completions(menu) => menu.select_next(cx),
|
||||||
|
ContextMenu::CodeActions(menu) => menu.select_next(cx),
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_render(&self) -> bool {
|
fn visible(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
ContextMenu::Completions(menu) => menu.should_render(),
|
ContextMenu::Completions(menu) => menu.visible(),
|
||||||
ContextMenu::CodeActions(menu) => menu.should_render(),
|
ContextMenu::CodeActions(menu) => menu.visible(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +535,7 @@ impl CompletionsMenu {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_render(&self) -> bool {
|
fn visible(&self) -> bool {
|
||||||
!self.matches.is_empty()
|
!self.matches.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -660,7 +670,7 @@ impl CodeActionsMenu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_render(&self) -> bool {
|
fn visible(&self) -> bool {
|
||||||
!self.actions.is_empty()
|
!self.actions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1081,7 +1091,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
|
fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
|
||||||
self.hide_completions(cx);
|
self.hide_context_menu(cx);
|
||||||
|
|
||||||
match phase {
|
match phase {
|
||||||
SelectPhase::Begin {
|
SelectPhase::Begin {
|
||||||
|
@ -1383,7 +1393,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
|
pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
|
||||||
if self.hide_completions(cx).is_some() {
|
if self.hide_context_menu(cx).is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1711,7 +1721,7 @@ impl Editor {
|
||||||
{
|
{
|
||||||
self.show_completions(&ShowCompletions, cx);
|
self.show_completions(&ShowCompletions, cx);
|
||||||
} else {
|
} else {
|
||||||
self.hide_completions(cx);
|
self.hide_context_menu(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1907,10 +1917,8 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.completion_tasks.retain(|(id, _)| *id > menu.id);
|
this.completion_tasks.retain(|(id, _)| *id > menu.id);
|
||||||
if menu.matches.is_empty() {
|
if this.focused {
|
||||||
this.hide_completions(cx);
|
this.show_context_menu(ContextMenu::Completions(menu), cx);
|
||||||
} else if this.focused {
|
|
||||||
this.context_menu = Some(ContextMenu::Completions(menu));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -1938,12 +1946,16 @@ impl Editor {
|
||||||
let actions = actions.await?;
|
let actions = actions.await?;
|
||||||
if !actions.is_empty() {
|
if !actions.is_empty() {
|
||||||
this.update(&mut cx, |this, cx| {
|
this.update(&mut cx, |this, cx| {
|
||||||
this.context_menu = Some(ContextMenu::CodeActions(CodeActionsMenu {
|
if this.focused {
|
||||||
actions: actions.into(),
|
this.show_context_menu(
|
||||||
selected_item: 0,
|
ContextMenu::CodeActions(CodeActionsMenu {
|
||||||
list: UniformListState::default(),
|
actions: actions.into(),
|
||||||
}));
|
selected_item: 0,
|
||||||
cx.notify();
|
list: UniformListState::default(),
|
||||||
|
}),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok::<_, anyhow::Error>(())
|
Ok::<_, anyhow::Error>(())
|
||||||
|
@ -1951,28 +1963,21 @@ impl Editor {
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hide_completions(&mut self, cx: &mut ViewContext<Self>) -> Option<CompletionsMenu> {
|
|
||||||
cx.notify();
|
|
||||||
self.completion_tasks.clear();
|
|
||||||
self.context_menu.take().and_then(|menu| {
|
|
||||||
if let ContextMenu::Completions(menu) = menu {
|
|
||||||
Some(menu)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn confirm_completion(
|
pub fn confirm_completion(
|
||||||
&mut self,
|
&mut self,
|
||||||
completion_ix: Option<usize>,
|
completion_ix: Option<usize>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<Task<Result<()>>> {
|
) -> Option<Task<Result<()>>> {
|
||||||
let context_menu = self.hide_completions(cx)?;
|
let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
|
||||||
let mat = context_menu
|
menu
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mat = completions_menu
|
||||||
.matches
|
.matches
|
||||||
.get(completion_ix.unwrap_or(context_menu.selected_item))?;
|
.get(completion_ix.unwrap_or(completions_menu.selected_item))?;
|
||||||
let completion = context_menu.completions.get(mat.candidate_id)?;
|
let completion = completions_menu.completions.get(mat.candidate_id)?;
|
||||||
|
|
||||||
let snippet;
|
let snippet;
|
||||||
let text;
|
let text;
|
||||||
|
@ -2041,10 +2046,10 @@ impl Editor {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_render_context_menu(&self) -> bool {
|
pub fn showing_context_menu(&self) -> bool {
|
||||||
self.context_menu
|
self.context_menu
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |menu| menu.should_render())
|
.map_or(false, |menu| menu.visible())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_context_menu(&self, cx: &AppContext) -> Option<ElementBox> {
|
pub fn render_context_menu(&self, cx: &AppContext) -> Option<ElementBox> {
|
||||||
|
@ -2053,6 +2058,20 @@ impl Editor {
|
||||||
.map(|menu| menu.render(self.build_settings.clone(), cx))
|
.map(|menu| menu.render(self.build_settings.clone(), cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
|
||||||
|
if !matches!(menu, ContextMenu::Completions(_)) {
|
||||||
|
self.completion_tasks.clear();
|
||||||
|
}
|
||||||
|
self.context_menu = Some(menu);
|
||||||
|
cx.notify()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
|
||||||
|
cx.notify();
|
||||||
|
self.completion_tasks.clear();
|
||||||
|
self.context_menu.take()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_snippet(
|
pub fn insert_snippet(
|
||||||
&mut self,
|
&mut self,
|
||||||
insertion_ranges: &[Range<usize>],
|
insertion_ranges: &[Range<usize>],
|
||||||
|
@ -2859,8 +2878,9 @@ impl Editor {
|
||||||
|
|
||||||
pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
|
pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(context_menu) = self.context_menu.as_mut() {
|
if let Some(context_menu) = self.context_menu.as_mut() {
|
||||||
context_menu.select_prev(cx);
|
if context_menu.select_prev(cx) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches!(self.mode, EditorMode::SingleLine) {
|
if matches!(self.mode, EditorMode::SingleLine) {
|
||||||
|
@ -2902,8 +2922,9 @@ impl Editor {
|
||||||
|
|
||||||
pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
|
pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(context_menu) = self.context_menu.as_mut() {
|
if let Some(context_menu) = self.context_menu.as_mut() {
|
||||||
context_menu.select_next(cx);
|
if context_menu.select_next(cx) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches!(self.mode, EditorMode::SingleLine) {
|
if matches!(self.mode, EditorMode::SingleLine) {
|
||||||
|
@ -4215,7 +4236,7 @@ impl Editor {
|
||||||
.block(completion_menu.filter(query.as_deref(), cx.background().clone()));
|
.block(completion_menu.filter(query.as_deref(), cx.background().clone()));
|
||||||
self.show_completions(&ShowCompletions, cx);
|
self.show_completions(&ShowCompletions, cx);
|
||||||
} else {
|
} else {
|
||||||
self.hide_completions(cx);
|
self.hide_context_menu(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4767,7 +4788,7 @@ impl View for Editor {
|
||||||
self.show_local_cursors = false;
|
self.show_local_cursors = false;
|
||||||
self.buffer
|
self.buffer
|
||||||
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));
|
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));
|
||||||
self.hide_completions(cx);
|
self.hide_context_menu(cx);
|
||||||
cx.emit(Event::Blurred);
|
cx.emit(Event::Blurred);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -4780,7 +4801,7 @@ impl View for Editor {
|
||||||
EditorMode::Full => "full",
|
EditorMode::Full => "full",
|
||||||
};
|
};
|
||||||
cx.map.insert("mode".into(), mode.into());
|
cx.map.insert("mode".into(), mode.into());
|
||||||
if self.context_menu.is_some() {
|
if matches!(self.context_menu.as_ref(), Some(ContextMenu::Completions(_))) {
|
||||||
cx.set.insert("completing".into());
|
cx.set.insert("completing".into());
|
||||||
}
|
}
|
||||||
cx
|
cx
|
||||||
|
|
|
@ -880,7 +880,7 @@ impl Element for EditorElement {
|
||||||
snapshot = view.snapshot(cx);
|
snapshot = view.snapshot(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if view.should_render_context_menu() {
|
if view.showing_context_menu() {
|
||||||
let newest_selection_head = view
|
let newest_selection_head = view
|
||||||
.newest_selection::<usize>(&snapshot.buffer_snapshot)
|
.newest_selection::<usize>(&snapshot.buffer_snapshot)
|
||||||
.head()
|
.head()
|
||||||
|
|
|
@ -2468,7 +2468,7 @@ mod tests {
|
||||||
// Confirm a completion on the guest.
|
// Confirm a completion on the guest.
|
||||||
editor_b.next_notification(&cx_b).await;
|
editor_b.next_notification(&cx_b).await;
|
||||||
editor_b.update(&mut cx_b, |editor, cx| {
|
editor_b.update(&mut cx_b, |editor, cx| {
|
||||||
assert!(editor.should_render_context_menu());
|
assert!(editor.showing_context_menu());
|
||||||
editor.confirm_completion(Some(0), cx);
|
editor.confirm_completion(Some(0), cx);
|
||||||
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
|
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue