Support clicking on a completion to confirm it

This commit is contained in:
Antonio Scandurra 2022-02-03 11:39:19 +01:00
parent 7865c32727
commit efcbf2714c
6 changed files with 71 additions and 34 deletions

View file

@ -23,6 +23,7 @@ use gpui::{
fonts::{self, HighlightStyle, TextStyle}, fonts::{self, HighlightStyle, TextStyle},
geometry::vector::{vec2f, Vector2F}, geometry::vector::{vec2f, Vector2F},
keymap::Binding, keymap::Binding,
platform::CursorStyle,
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
MutableAppContext, RenderContext, Task, View, ViewContext, WeakModelHandle, WeakViewHandle, MutableAppContext, RenderContext, Task, View, ViewContext, WeakModelHandle, WeakViewHandle,
}; };
@ -123,7 +124,7 @@ action!(FoldSelectedRanges);
action!(Scroll, Vector2F); action!(Scroll, Vector2F);
action!(Select, SelectPhase); action!(Select, SelectPhase);
action!(ShowCompletions); action!(ShowCompletions);
action!(ConfirmCompletion); action!(ConfirmCompletion, Option<usize>);
pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpener>>) { pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpener>>) {
path_openers.push(Box::new(items::BufferOpener)); path_openers.push(Box::new(items::BufferOpener));
@ -139,7 +140,11 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpene
Input("\n".into()), Input("\n".into()),
Some("Editor && mode == auto_height"), Some("Editor && mode == auto_height"),
), ),
Binding::new("enter", ConfirmCompletion, Some("Editor && completing")), Binding::new(
"enter",
ConfirmCompletion(None),
Some("Editor && completing"),
),
Binding::new("tab", Tab, Some("Editor")), Binding::new("tab", Tab, Some("Editor")),
Binding::new("shift-tab", Outdent, Some("Editor")), Binding::new("shift-tab", Outdent, Some("Editor")),
Binding::new("ctrl-shift-K", DeleteLine, Some("Editor")), Binding::new("ctrl-shift-K", DeleteLine, Some("Editor")),
@ -297,11 +302,13 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpene
cx.add_action(Editor::unfold); cx.add_action(Editor::unfold);
cx.add_action(Editor::fold_selected_ranges); cx.add_action(Editor::fold_selected_ranges);
cx.add_action(Editor::show_completions); cx.add_action(Editor::show_completions);
cx.add_action(|editor: &mut Editor, _: &ConfirmCompletion, cx| { cx.add_action(
if let Some(task) = editor.confirm_completion(cx) { |editor: &mut Editor, &ConfirmCompletion(ix): &ConfirmCompletion, cx| {
task.detach_and_log_err(cx); if let Some(task) = editor.confirm_completion(ix, cx) {
} task.detach_and_log_err(cx);
}); }
},
);
} }
trait SelectionExt { trait SelectionExt {
@ -1669,11 +1676,15 @@ impl Editor {
self.completion_state.take() self.completion_state.take()
} }
fn confirm_completion(&mut self, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> { fn confirm_completion(
&mut self,
completion_ix: Option<usize>,
cx: &mut ViewContext<Self>,
) -> Option<Task<Result<()>>> {
let completion_state = self.hide_completions(cx)?; let completion_state = self.hide_completions(cx)?;
let mat = completion_state let mat = completion_state
.matches .matches
.get(completion_state.selected_item)?; .get(completion_ix.unwrap_or(completion_state.selected_item))?;
let completion = completion_state.completions.get(mat.candidate_id)?; let completion = completion_state.completions.get(mat.candidate_id)?;
if completion.is_snippet() { if completion.is_snippet() {
@ -1702,6 +1713,8 @@ impl Editor {
} }
pub fn render_completions(&self, cx: &AppContext) -> Option<ElementBox> { pub fn render_completions(&self, cx: &AppContext) -> Option<ElementBox> {
enum CompletionTag {}
self.completion_state.as_ref().map(|state| { self.completion_state.as_ref().map(|state| {
let build_settings = self.build_settings.clone(); let build_settings = self.build_settings.clone();
let settings = build_settings(cx); let settings = build_settings(cx);
@ -1715,30 +1728,48 @@ impl Editor {
let settings = build_settings(cx); let settings = build_settings(cx);
let start_ix = range.start; let start_ix = range.start;
for (ix, mat) in matches[range].iter().enumerate() { for (ix, mat) in matches[range].iter().enumerate() {
let item_style = if start_ix + ix == selected_item {
settings.style.autocomplete.selected_item
} else {
settings.style.autocomplete.item
};
let completion = &completions[mat.candidate_id]; let completion = &completions[mat.candidate_id];
let item_ix = start_ix + ix;
items.push( items.push(
Text::new(completion.label.text.clone(), settings.style.text.clone()) MouseEventHandler::new::<CompletionTag, _, _, _>(
.with_soft_wrap(false) mat.candidate_id,
.with_highlights(combine_syntax_and_fuzzy_match_highlights( cx,
&completion.label.text, |state, _| {
settings.style.text.color.into(), let item_style = if item_ix == selected_item {
completion.label.runs.iter().filter_map( settings.style.autocomplete.selected_item
|(range, highlight_id)| { } else if state.hovered {
highlight_id settings.style.autocomplete.hovered_item
.style(&settings.style.syntax) } else {
.map(|style| (range.clone(), style)) settings.style.autocomplete.item
}, };
),
&mat.positions, Text::new(
)) completion.label.text.clone(),
.contained() settings.style.text.clone(),
.with_style(item_style) )
.boxed(), .with_soft_wrap(false)
.with_highlights(combine_syntax_and_fuzzy_match_highlights(
&completion.label.text,
settings.style.text.color.into(),
completion.label.runs.iter().filter_map(
|(range, highlight_id)| {
highlight_id
.style(&settings.style.syntax)
.map(|style| (range.clone(), style))
},
),
&mat.positions,
))
.contained()
.with_style(item_style)
.boxed()
},
)
.with_cursor_style(CursorStyle::PointingHand)
.on_mouse_down(move |cx| {
cx.dispatch_action(ConfirmCompletion(Some(item_ix)));
})
.boxed(),
); );
} }
}, },
@ -6952,7 +6983,7 @@ mod tests {
let apply_additional_edits = editor.update(&mut cx, |editor, cx| { let apply_additional_edits = editor.update(&mut cx, |editor, cx| {
editor.move_down(&MoveDown, cx); editor.move_down(&MoveDown, cx);
let apply_additional_edits = editor.confirm_completion(cx).unwrap(); let apply_additional_edits = editor.confirm_completion(None, cx).unwrap();
assert_eq!( assert_eq!(
editor.text(cx), editor.text(cx),
" "

View file

@ -328,6 +328,7 @@ pub struct AutocompleteStyle {
pub container: ContainerStyle, pub container: ContainerStyle,
pub item: ContainerStyle, pub item: ContainerStyle,
pub selected_item: ContainerStyle, pub selected_item: ContainerStyle,
pub hovered_item: ContainerStyle,
pub match_highlight: HighlightStyle, pub match_highlight: HighlightStyle,
} }

View file

@ -322,6 +322,10 @@ match_highlight = { color = "$editor.syntax.keyword.color", weight = "$editor.sy
[editor.autocomplete.selected_item] [editor.autocomplete.selected_item]
extends = "$editor.autocomplete.item" extends = "$editor.autocomplete.item"
background = "$state.selected"
[editor.autocomplete.hovered_item]
extends = "$editor.autocomplete.item"
background = "$state.hover" background = "$state.hover"
[project_diagnostics] [project_diagnostics]

View file

@ -40,6 +40,7 @@ bad = "#b7372e"
active_line = "#161313" active_line = "#161313"
highlighted_line = "#faca5033" highlighted_line = "#faca5033"
hover = "#00000033" hover = "#00000033"
selected = "#00000088"
[editor.syntax] [editor.syntax]
keyword = { color = "#0086c0", weight = "bold" } keyword = { color = "#0086c0", weight = "bold" }

View file

@ -40,6 +40,7 @@ bad = "#b7372e"
active_line = "#00000022" active_line = "#00000022"
highlighted_line = "#faca5033" highlighted_line = "#faca5033"
hover = "#00000033" hover = "#00000033"
selected = "#00000088"
[editor.syntax] [editor.syntax]
keyword = { color = "#0086c0", weight = "bold" } keyword = { color = "#0086c0", weight = "bold" }
@ -51,7 +52,6 @@ comment = "#6a9955"
property = "#4e94ce" property = "#4e94ce"
variant = "#4fc1ff" variant = "#4fc1ff"
constant = "#9cdcfe" constant = "#9cdcfe"
title = { color = "#9cdcfe", weight = "bold" } title = { color = "#9cdcfe", weight = "bold" }
emphasis = "#4ec9b0" emphasis = "#4ec9b0"
"emphasis.strong" = { color = "#4ec9b0", weight = "bold" } "emphasis.strong" = { color = "#4ec9b0", weight = "bold" }

View file

@ -40,6 +40,7 @@ bad = "#b7372e"
active_line = "#00000008" active_line = "#00000008"
highlighted_line = "#faca5033" highlighted_line = "#faca5033"
hover = "#0000000D" hover = "#0000000D"
selected = "#0000001c"
[editor.syntax] [editor.syntax]
keyword = { color = "#0000fa", weight = "bold" } keyword = { color = "#0000fa", weight = "bold" }
@ -51,7 +52,6 @@ comment = "#6a9955"
property = "#4e94ce" property = "#4e94ce"
variant = "#4fc1ff" variant = "#4fc1ff"
constant = "#5a9ccc" constant = "#5a9ccc"
title = { color = "#5a9ccc", weight = "bold" } title = { color = "#5a9ccc", weight = "bold" }
emphasis = "#267f29" emphasis = "#267f29"
"emphasis.strong" = { color = "#267f29", weight = "bold" } "emphasis.strong" = { color = "#267f29", weight = "bold" }