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:
Nathan Sobo 2022-02-05 12:13:02 -07:00
parent 025e83c1ec
commit ba99b01de6
3 changed files with 74 additions and 53 deletions

View file

@ -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

View file

@ -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()

View file

@ -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() }");
}); });