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 {
fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
if self.visible() {
match self {
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 {
if self.visible() {
match self {
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 {
ContextMenu::Completions(menu) => menu.should_render(),
ContextMenu::CodeActions(menu) => menu.should_render(),
ContextMenu::Completions(menu) => menu.visible(),
ContextMenu::CodeActions(menu) => menu.visible(),
}
}
@ -525,7 +535,7 @@ impl CompletionsMenu {
cx.notify();
}
fn should_render(&self) -> bool {
fn visible(&self) -> bool {
!self.matches.is_empty()
}
@ -660,7 +670,7 @@ impl CodeActionsMenu {
}
}
fn should_render(&self) -> bool {
fn visible(&self) -> bool {
!self.actions.is_empty()
}
@ -1081,7 +1091,7 @@ impl Editor {
}
fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
self.hide_completions(cx);
self.hide_context_menu(cx);
match phase {
SelectPhase::Begin {
@ -1383,7 +1393,7 @@ impl Editor {
}
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;
}
@ -1711,7 +1721,7 @@ impl Editor {
{
self.show_completions(&ShowCompletions, cx);
} else {
self.hide_completions(cx);
self.hide_context_menu(cx);
}
}
}
@ -1907,10 +1917,8 @@ impl Editor {
}
this.completion_tasks.retain(|(id, _)| *id > menu.id);
if menu.matches.is_empty() {
this.hide_completions(cx);
} else if this.focused {
this.context_menu = Some(ContextMenu::Completions(menu));
if this.focused {
this.show_context_menu(ContextMenu::Completions(menu), cx);
}
cx.notify();
@ -1938,12 +1946,16 @@ impl Editor {
let actions = actions.await?;
if !actions.is_empty() {
this.update(&mut cx, |this, cx| {
this.context_menu = Some(ContextMenu::CodeActions(CodeActionsMenu {
if this.focused {
this.show_context_menu(
ContextMenu::CodeActions(CodeActionsMenu {
actions: actions.into(),
selected_item: 0,
list: UniformListState::default(),
}));
cx.notify();
}),
cx,
);
}
});
}
Ok::<_, anyhow::Error>(())
@ -1951,28 +1963,21 @@ impl Editor {
.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(
&mut self,
completion_ix: Option<usize>,
cx: &mut ViewContext<Self>,
) -> Option<Task<Result<()>>> {
let context_menu = self.hide_completions(cx)?;
let mat = context_menu
let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
menu
} else {
return None;
};
let mat = completions_menu
.matches
.get(completion_ix.unwrap_or(context_menu.selected_item))?;
let completion = context_menu.completions.get(mat.candidate_id)?;
.get(completion_ix.unwrap_or(completions_menu.selected_item))?;
let completion = completions_menu.completions.get(mat.candidate_id)?;
let snippet;
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
.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> {
@ -2053,6 +2058,20 @@ impl Editor {
.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(
&mut self,
insertion_ranges: &[Range<usize>],
@ -2859,9 +2878,10 @@ impl Editor {
pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
if let Some(context_menu) = self.context_menu.as_mut() {
context_menu.select_prev(cx);
if context_menu.select_prev(cx) {
return;
}
}
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate_action();
@ -2902,9 +2922,10 @@ impl Editor {
pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
if let Some(context_menu) = self.context_menu.as_mut() {
context_menu.select_next(cx);
if context_menu.select_next(cx) {
return;
}
}
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate_action();
@ -4215,7 +4236,7 @@ impl Editor {
.block(completion_menu.filter(query.as_deref(), cx.background().clone()));
self.show_completions(&ShowCompletions, cx);
} else {
self.hide_completions(cx);
self.hide_context_menu(cx);
}
}
@ -4767,7 +4788,7 @@ impl View for Editor {
self.show_local_cursors = false;
self.buffer
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));
self.hide_completions(cx);
self.hide_context_menu(cx);
cx.emit(Event::Blurred);
cx.notify();
}
@ -4780,7 +4801,7 @@ impl View for Editor {
EditorMode::Full => "full",
};
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

View file

@ -880,7 +880,7 @@ impl Element for EditorElement {
snapshot = view.snapshot(cx);
}
if view.should_render_context_menu() {
if view.showing_context_menu() {
let newest_selection_head = view
.newest_selection::<usize>(&snapshot.buffer_snapshot)
.head()

View file

@ -2468,7 +2468,7 @@ mod tests {
// Confirm a completion on the guest.
editor_b.next_notification(&cx_b).await;
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);
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
});