Added option to hide Prompt completions

pull/8339/head
ItsEthra 1 year ago
parent 2533876ad7
commit 25f2016cf0

@ -2098,7 +2098,8 @@ fn searcher(cx: &mut Context, direction: Direction) {
.iter()
.filter(|comp| comp.starts_with(input))
.map(|comp| (0.., std::borrow::Cow::Owned(comp.clone())))
.collect()
.collect::<Vec<_>>()
.into()
},
move |cx, regex, event| {
if event == PromptEvent::Validate {
@ -2290,7 +2291,8 @@ fn global_search(cx: &mut Context) {
.iter()
.filter(|comp| comp.starts_with(input))
.map(|comp| (0.., std::borrow::Cow::Owned(comp.clone())))
.collect()
.collect::<Vec<_>>()
.into()
},
move |cx, _, input, event| {
if event != PromptEvent::Validate {

@ -1,16 +1,18 @@
use helix_view::document::read_to_string;
use std::fmt::Write;
use std::io::BufReader;
use std::ops::Deref;
use crate::job::Job;
use crate::ui::CompletionResult;
use super::*;
use helix_core::fuzzy::fuzzy_match;
use helix_core::indent::MAX_INDENT;
use helix_core::{line_ending, shellwords::Shellwords};
use helix_view::document::{read_to_string, DEFAULT_LANGUAGE_NAME};
use helix_view::editor::{CloseError, ConfigEvent};
use helix_core::{encoding, line_ending, shellwords::Shellwords};
use helix_view::document::DEFAULT_LANGUAGE_NAME;
use helix_view::editor::{Action, CloseError, CommandHints, ConfigEvent};
use serde_json::Value;
use ui::completers::{self, Completer};
@ -3132,14 +3134,18 @@ pub(super) fn command_mode(cx: &mut Context) {
let words = shellwords.words();
if words.is_empty() || (words.len() == 1 && !shellwords.ends_with_whitespace()) {
fuzzy_match(
let mut result: CompletionResult = fuzzy_match(
input,
TYPABLE_COMMAND_LIST.iter().map(|command| command.name),
false,
)
.into_iter()
.map(|(name, _)| (0.., name.into()))
.collect()
.collect::<Vec<_>>()
.into();
result.show_popup = matches!(editor.config().command_hints, CommandHints::Always);
result
} else {
// Otherwise, use the command's completer and the last shellword
// as completion input.
@ -3151,11 +3157,12 @@ pub(super) fn command_mode(cx: &mut Context) {
let argument_number = argument_number_of(&shellwords);
if let Some(completer) = TYPABLE_COMMAND_MAP
let mut result = if let Some(completer) = TYPABLE_COMMAND_MAP
.get(&words[0] as &str)
.map(|tc| tc.completer_for_argument_number(argument_number))
{
completer(editor, word)
.completions
.into_iter()
.map(|(range, file)| {
let file = shellwords::escape(file);
@ -3165,10 +3172,18 @@ pub(super) fn command_mode(cx: &mut Context) {
let range = (range.start + offset)..;
(range, file)
})
.collect()
.collect::<Vec<_>>()
.into()
} else {
Vec::new()
}
CompletionResult::default()
};
result.show_popup = matches!(
editor.config().command_hints,
CommandHints::Always | CommandHints::OnlyArguments
);
result
}
}, // completion
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {

@ -23,7 +23,7 @@ pub use markdown::Markdown;
pub use menu::Menu;
pub use picker::{DynamicPicker, FileLocation, Picker};
pub use popup::Popup;
pub use prompt::{Prompt, PromptEvent};
pub use prompt::{CompletionResult, Prompt, PromptEvent};
pub use spinner::{ProgressSpinners, Spinner};
pub use text::Text;
@ -35,7 +35,7 @@ pub fn prompt(
cx: &mut crate::commands::Context,
prompt: std::borrow::Cow<'static, str>,
history_register: Option<char>,
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
completion_fn: impl FnMut(&Editor, &str) -> CompletionResult + 'static,
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
) {
let mut prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn);
@ -49,7 +49,7 @@ pub fn prompt_with_input(
prompt: std::borrow::Cow<'static, str>,
input: String,
history_register: Option<char>,
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
completion_fn: impl FnMut(&Editor, &str) -> CompletionResult + 'static,
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
) {
let prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn)
@ -61,7 +61,7 @@ pub fn regex_prompt(
cx: &mut crate::commands::Context,
prompt: std::borrow::Cow<'static, str>,
history_register: Option<char>,
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
completion_fn: impl FnMut(&Editor, &str) -> CompletionResult + 'static,
fun: impl Fn(&mut crate::compositor::Context, rope::Regex, PromptEvent) + 'static,
) {
raw_regex_prompt(
@ -72,11 +72,12 @@ pub fn regex_prompt(
move |cx, regex, _, event| fun(cx, regex, event),
);
}
pub fn raw_regex_prompt(
cx: &mut crate::commands::Context,
prompt: std::borrow::Cow<'static, str>,
history_register: Option<char>,
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
completion_fn: impl FnMut(&Editor, &str) -> CompletionResult + 'static,
fun: impl Fn(&mut crate::compositor::Context, rope::Regex, &str, PromptEvent) + 'static,
) {
let (view, doc) = current!(cx.editor);
@ -254,6 +255,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> Picker
}
pub mod completers {
use super::CompletionResult;
use crate::ui::prompt::Completion;
use helix_core::fuzzy::fuzzy_match;
use helix_core::syntax::LanguageServerFeature;
@ -263,13 +265,13 @@ pub mod completers {
use once_cell::sync::Lazy;
use std::borrow::Cow;
pub type Completer = fn(&Editor, &str) -> Vec<Completion>;
pub type Completer = fn(&Editor, &str) -> CompletionResult;
pub fn none(_editor: &Editor, _input: &str) -> Vec<Completion> {
Vec::new()
pub fn none(_editor: &Editor, _input: &str) -> CompletionResult {
CompletionResult::default()
}
pub fn buffer(editor: &Editor, input: &str) -> Vec<Completion> {
pub fn buffer(editor: &Editor, input: &str) -> CompletionResult {
let names = editor.documents.values().map(|doc| {
doc.relative_path()
.map(|p| p.display().to_string().into())
@ -279,10 +281,11 @@ pub mod completers {
fuzzy_match(input, names, true)
.into_iter()
.map(|(name, _)| ((0..), name))
.collect()
.collect::<Vec<Completion>>()
.into()
}
pub fn theme(_editor: &Editor, input: &str) -> Vec<Completion> {
pub fn theme(_editor: &Editor, input: &str) -> CompletionResult {
let mut names = theme::Loader::read_names(&helix_loader::config_dir().join("themes"));
for rt_dir in helix_loader::runtime_dirs() {
names.extend(theme::Loader::read_names(&rt_dir.join("themes")));
@ -295,7 +298,8 @@ pub mod completers {
fuzzy_match(input, names, false)
.into_iter()
.map(|(name, _)| ((0..), name.into()))
.collect()
.collect::<Vec<Completion>>()
.into()
}
/// Recursive function to get all keys from this value and add them to vec
@ -314,7 +318,7 @@ pub mod completers {
}
}
pub fn setting(_editor: &Editor, input: &str) -> Vec<Completion> {
pub fn setting(_editor: &Editor, input: &str) -> CompletionResult {
static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
let mut keys = Vec::new();
let json = serde_json::json!(Config::default());
@ -325,18 +329,19 @@ pub mod completers {
fuzzy_match(input, &*KEYS, false)
.into_iter()
.map(|(name, _)| ((0..), name.into()))
.collect()
.collect::<Vec<Completion>>()
.into()
}
pub fn filename(editor: &Editor, input: &str) -> Vec<Completion> {
filename_with_git_ignore(editor, input, true)
pub fn filename(editor: &Editor, input: &str) -> CompletionResult {
filename_with_git_ignore(editor, input, true).into()
}
pub fn filename_with_git_ignore(
editor: &Editor,
input: &str,
git_ignore: bool,
) -> Vec<Completion> {
) -> CompletionResult {
filename_impl(editor, input, git_ignore, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
@ -346,9 +351,10 @@ pub mod completers {
FileMatch::Accept
}
})
.into()
}
pub fn language(editor: &Editor, input: &str) -> Vec<Completion> {
pub fn language(editor: &Editor, input: &str) -> CompletionResult {
let text: String = "text".into();
let loader = editor.syn_loader.load();
@ -360,32 +366,34 @@ pub mod completers {
fuzzy_match(input, language_ids, false)
.into_iter()
.map(|(name, _)| ((0..), name.to_owned().into()))
.collect()
.collect::<Vec<Completion>>()
.into()
}
pub fn lsp_workspace_command(editor: &Editor, input: &str) -> Vec<Completion> {
pub fn lsp_workspace_command(editor: &Editor, input: &str) -> CompletionResult {
let Some(options) = doc!(editor)
.language_servers_with_feature(LanguageServerFeature::WorkspaceCommand)
.find_map(|ls| ls.capabilities().execute_command_provider.as_ref())
else {
return vec![];
return CompletionResult::default();
};
fuzzy_match(input, &options.commands, false)
.into_iter()
.map(|(name, _)| ((0..), name.to_owned().into()))
.collect()
.collect::<Vec<Completion>>()
.into()
}
pub fn directory(editor: &Editor, input: &str) -> Vec<Completion> {
directory_with_git_ignore(editor, input, true)
pub fn directory(editor: &Editor, input: &str) -> CompletionResult {
directory_with_git_ignore(editor, input, true).into()
}
pub fn directory_with_git_ignore(
editor: &Editor,
input: &str,
git_ignore: bool,
) -> Vec<Completion> {
) -> CompletionResult {
filename_impl(editor, input, git_ignore, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
@ -395,6 +403,7 @@ pub mod completers {
FileMatch::Reject
}
})
.into()
}
#[derive(Copy, Clone, PartialEq, Eq)]
@ -414,7 +423,7 @@ pub mod completers {
input: &str,
git_ignore: bool,
filter_fn: F,
) -> Vec<Completion>
) -> CompletionResult
where
F: Fn(&ignore::DirEntry) -> FileMatch,
{
@ -498,17 +507,18 @@ pub mod completers {
fuzzy_match(&file_name, files, true)
.into_iter()
.map(|(name, _)| (range.clone(), name))
.collect()
.collect::<Vec<Completion>>()
.into()
// TODO: complete to longest common match
} else {
let mut files: Vec<_> = files.map(|file| (end.clone(), file)).collect();
files.sort_unstable_by(|(_, path1), (_, path2)| path1.cmp(path2));
files
files.into()
}
}
pub fn register(editor: &Editor, input: &str) -> Vec<Completion> {
pub fn register(editor: &Editor, input: &str) -> CompletionResult {
let iter = editor
.registers
.iter_preview()
@ -519,6 +529,7 @@ pub mod completers {
fuzzy_match(input, iter, false)
.into_iter()
.map(|(name, _)| ((0..), name.into()))
.collect()
.collect::<Vec<Completion>>()
.into()
}
}

@ -19,10 +19,25 @@ use helix_view::{
type PromptCharHandler = Box<dyn Fn(&mut Prompt, char, &Context)>;
pub type Completion = (RangeFrom<usize>, Cow<'static, str>);
type CompletionFn = Box<dyn FnMut(&Editor, &str) -> Vec<Completion>>;
type CompletionFn = Box<dyn FnMut(&Editor, &str) -> CompletionResult>;
type CallbackFn = Box<dyn FnMut(&mut Context, &str, PromptEvent)>;
pub type DocFn = Box<dyn Fn(&str) -> Option<Cow<str>>>;
#[derive(Default)]
pub struct CompletionResult {
pub completions: Vec<Completion>,
pub show_popup: bool,
}
impl From<Vec<Completion>> for CompletionResult {
fn from(completions: Vec<Completion>) -> Self {
Self {
show_popup: true,
completions,
}
}
}
pub struct Prompt {
prompt: Cow<'static, str>,
line: String,
@ -34,6 +49,7 @@ pub struct Prompt {
completion_fn: CompletionFn,
callback_fn: CallbackFn,
pub doc_fn: DocFn,
pub show_popup: bool,
next_char_handler: Option<PromptCharHandler>,
language: Option<(&'static str, Arc<ArcSwap<syntax::Loader>>)>,
}
@ -72,7 +88,7 @@ impl Prompt {
pub fn new(
prompt: Cow<'static, str>,
history_register: Option<char>,
completion_fn: impl FnMut(&Editor, &str) -> Vec<Completion> + 'static,
completion_fn: impl FnMut(&Editor, &str) -> CompletionResult + 'static,
callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
) -> Self {
Self {
@ -88,6 +104,7 @@ impl Prompt {
doc_fn: Box::new(|_| None),
next_char_handler: None,
language: None,
show_popup: true,
}
}
@ -114,7 +131,13 @@ impl Prompt {
pub fn recalculate_completion(&mut self, editor: &Editor) {
self.exit_selection();
self.completion = (self.completion_fn)(editor, &self.line);
let CompletionResult {
completions,
show_popup,
} = (self.completion_fn)(editor, &self.line);
self.completion = completions;
self.show_popup = show_popup;
}
/// Compute the cursor position after applying movement
@ -367,14 +390,10 @@ impl Prompt {
const BASE_WIDTH: u16 = 30;
impl Prompt {
pub fn render_prompt(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
fn render_completion_hints(&self, area: Rect, surface: &mut Surface, cx: &mut Context) -> Rect {
let theme = &cx.editor.theme;
let prompt_color = theme.get("ui.text");
let completion_color = theme.get("ui.menu");
let selected_color = theme.get("ui.menu.selected");
let suggestion_color = theme.get("ui.text.inactive");
let background = theme.get("ui.background");
// completion
let max_len = self
.completion
@ -437,6 +456,21 @@ impl Prompt {
}
}
completion_area
}
pub fn render_prompt(&self, area: Rect, surface: &mut Surface, cx: &mut Context) {
let completion_area = if self.show_popup {
self.render_completion_hints(area, surface, cx)
} else {
Rect::new(area.x, area.height.saturating_sub(1), area.width, 0)
};
let theme = &cx.editor.theme;
let prompt_color = theme.get("ui.text");
let suggestion_color = theme.get("ui.text.inactive");
let background = theme.get("ui.background");
if let Some(doc) = (self.doc_fn)(&self.line) {
let mut text = ui::Text::new(doc.to_string());

@ -351,6 +351,7 @@ pub enum CommandHints {
/// Never show it
Never,
/// Show only for command's arguments
#[serde(rename = "only-args")]
OnlyArguments,
}

Loading…
Cancel
Save