aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src/ui
diff options
context:
space:
mode:
authorCole Helbling2021-11-20 21:03:39 +0000
committerBlaž Hrastnik2022-02-17 05:02:42 +0000
commit6118486eb2dd7ed680ff38a7cc024dbfb26fbb7f (patch)
tree2b1e19fe86bc9beee95c88b0e9fa9f9c1d66eae5 /helix-term/src/ui
parenta1207fd7683c2e038df923704bb5790c6cdaefea (diff)
helix-term: implement buffer completer
In order to implement this completer, the completion function needs to be able to access the compositor's context (to allow it to get the list of buffers currently open in the context's editor).
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r--helix-term/src/ui/mod.rs47
-rw-r--r--helix-term/src/ui/picker.rs8
-rw-r--r--helix-term/src/ui/prompt.rs55
3 files changed, 73 insertions, 37 deletions
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 7f6d9f7c..263342b7 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -30,7 +30,7 @@ pub fn regex_prompt(
cx: &mut crate::commands::Context,
prompt: std::borrow::Cow<'static, str>,
history_register: Option<char>,
- completion_fn: impl FnMut(&str) -> Vec<prompt::Completion> + 'static,
+ completion_fn: impl FnMut(&crate::compositor::Context, &str) -> Vec<prompt::Completion> + 'static,
fun: impl Fn(&mut View, &mut Document, Regex, PromptEvent) + 'static,
) -> Prompt {
let (view, doc) = current!(cx.editor);
@@ -168,18 +168,53 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
}
pub mod completers {
+ use crate::compositor::Context;
use crate::ui::prompt::Completion;
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
use fuzzy_matcher::FuzzyMatcher;
+ use helix_view::document::SCRATCH_BUFFER_NAME;
use helix_view::editor::Config;
use helix_view::theme;
use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::cmp::Reverse;
- pub type Completer = fn(&str) -> Vec<Completion>;
+ pub type Completer = fn(&Context, &str) -> Vec<Completion>;
- pub fn theme(input: &str) -> Vec<Completion> {
+ pub fn none(_cx: &Context, _input: &str) -> Vec<Completion> {
+ Vec::new()
+ }
+
+ pub fn buffer(cx: &Context, input: &str) -> Vec<Completion> {
+ let mut names: Vec<_> = cx
+ .editor
+ .documents
+ .iter()
+ .map(|(_id, doc)| {
+ let name = doc
+ .relative_path()
+ .map(|p| p.display().to_string())
+ .unwrap_or_else(|| String::from(SCRATCH_BUFFER_NAME));
+ ((0..), Cow::from(name))
+ })
+ .collect();
+
+ let matcher = Matcher::default();
+
+ let mut matches: Vec<_> = names
+ .into_iter()
+ .filter_map(|(_range, name)| {
+ matcher.fuzzy_match(&name, input).map(|score| (name, score))
+ })
+ .collect();
+
+ matches.sort_unstable_by_key(|(_file, score)| Reverse(*score));
+ names = matches.into_iter().map(|(name, _)| ((0..), name)).collect();
+
+ names
+ }
+
+ pub fn theme(_cx: &Context, input: &str) -> Vec<Completion> {
let mut names = theme::Loader::read_names(&helix_core::runtime_dir().join("themes"));
names.extend(theme::Loader::read_names(
&helix_core::config_dir().join("themes"),
@@ -207,7 +242,7 @@ pub mod completers {
names
}
- pub fn setting(input: &str) -> Vec<Completion> {
+ pub fn setting(_cx: &Context, input: &str) -> Vec<Completion> {
static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
serde_json::to_value(Config::default())
.unwrap()
@@ -232,7 +267,7 @@ pub mod completers {
.collect()
}
- pub fn filename(input: &str) -> Vec<Completion> {
+ pub fn filename(_cx: &Context, input: &str) -> Vec<Completion> {
filename_impl(input, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
@@ -244,7 +279,7 @@ pub mod completers {
})
}
- pub fn directory(input: &str) -> Vec<Completion> {
+ pub fn directory(_cx: &Context, input: &str) -> Vec<Completion> {
filename_impl(input, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 9cddbc60..dcc64002 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -302,7 +302,7 @@ impl<T> Picker<T> {
let prompt = Prompt::new(
"".into(),
None,
- |_pattern: &str| Vec::new(),
+ |_ctx: &Context, _pattern: &str| Vec::new(),
|_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
//
},
@@ -395,12 +395,12 @@ impl<T> Picker<T> {
.map(|(index, _score)| &self.options[*index])
}
- pub fn save_filter(&mut self) {
+ pub fn save_filter(&mut self, cx: &Context) {
self.filters.clear();
self.filters
.extend(self.matches.iter().map(|(index, _)| *index));
self.filters.sort_unstable(); // used for binary search later
- self.prompt.clear();
+ self.prompt.clear(cx);
}
}
@@ -468,7 +468,7 @@ impl<T: 'static> Component for Picker<T> {
return close_fn;
}
ctrl!(' ') => {
- self.save_filter();
+ self.save_filter(cx);
}
_ => {
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index 4c4fef26..ff6b8c76 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -24,7 +24,7 @@ pub struct Prompt {
selection: Option<usize>,
history_register: Option<char>,
history_pos: Option<usize>,
- completion_fn: Box<dyn FnMut(&str) -> Vec<Completion>>,
+ completion_fn: Box<dyn FnMut(&Context, &str) -> Vec<Completion>>,
callback_fn: Box<dyn FnMut(&mut Context, &str, PromptEvent)>,
pub doc_fn: Box<dyn Fn(&str) -> Option<&'static str>>,
}
@@ -59,14 +59,14 @@ impl Prompt {
pub fn new(
prompt: Cow<'static, str>,
history_register: Option<char>,
- mut completion_fn: impl FnMut(&str) -> Vec<Completion> + 'static,
+ completion_fn: impl FnMut(&Context, &str) -> Vec<Completion> + 'static,
callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
) -> Self {
Self {
prompt,
line: String::new(),
cursor: 0,
- completion: completion_fn(""),
+ completion: Vec::new(),
selection: None,
history_register,
history_pos: None,
@@ -177,13 +177,13 @@ impl Prompt {
}
}
- pub fn insert_char(&mut self, c: char) {
+ pub fn insert_char(&mut self, c: char, cx: &Context) {
self.line.insert(self.cursor, c);
let mut cursor = GraphemeCursor::new(self.cursor, self.line.len(), false);
if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) {
self.cursor = pos;
}
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
self.exit_selection();
}
@@ -205,61 +205,61 @@ impl Prompt {
self.cursor = self.line.len();
}
- pub fn delete_char_backwards(&mut self) {
+ pub fn delete_char_backwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::BackwardChar(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
self.exit_selection();
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
}
- pub fn delete_char_forwards(&mut self) {
+ pub fn delete_char_forwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::ForwardChar(1));
self.line.replace_range(self.cursor..pos, "");
self.exit_selection();
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
}
- pub fn delete_word_backwards(&mut self) {
+ pub fn delete_word_backwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::BackwardWord(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
self.exit_selection();
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
}
- pub fn delete_word_forwards(&mut self) {
+ pub fn delete_word_forwards(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::ForwardWord(1));
self.line.replace_range(self.cursor..pos, "");
self.exit_selection();
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
}
- pub fn kill_to_start_of_line(&mut self) {
+ pub fn kill_to_start_of_line(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::StartOfLine);
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;
self.exit_selection();
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
}
- pub fn kill_to_end_of_line(&mut self) {
+ pub fn kill_to_end_of_line(&mut self, cx: &Context) {
let pos = self.eval_movement(Movement::EndOfLine);
self.line.replace_range(self.cursor..pos, "");
self.exit_selection();
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
}
- pub fn clear(&mut self) {
+ pub fn clear(&mut self, cx: &Context) {
self.line.clear();
self.cursor = 0;
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
self.exit_selection();
}
@@ -442,16 +442,16 @@ impl Component for Prompt {
ctrl!('f') | key!(Right) => self.move_cursor(Movement::ForwardChar(1)),
ctrl!('e') | key!(End) => self.move_end(),
ctrl!('a') | key!(Home) => self.move_start(),
- ctrl!('w') => self.delete_word_backwards(),
- alt!('d') => self.delete_word_forwards(),
- ctrl!('k') => self.kill_to_end_of_line(),
- ctrl!('u') => self.kill_to_start_of_line(),
+ ctrl!('w') => self.delete_word_backwards(cx),
+ alt!('d') => self.delete_word_forwards(cx),
+ ctrl!('k') => self.kill_to_end_of_line(cx),
+ ctrl!('u') => self.kill_to_start_of_line(cx),
ctrl!('h') | key!(Backspace) => {
- self.delete_char_backwards();
+ self.delete_char_backwards(cx);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('d') | key!(Delete) => {
- self.delete_char_forwards();
+ self.delete_char_forwards(cx);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('s') => {
@@ -474,7 +474,7 @@ impl Component for Prompt {
}
key!(Enter) => {
if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
- self.completion = (self.completion_fn)(&self.line);
+ self.completion = (self.completion_fn)(cx, &self.line);
self.exit_selection();
} else {
(self.callback_fn)(cx, &self.line, PromptEvent::Validate);
@@ -515,7 +515,7 @@ impl Component for Prompt {
code: KeyCode::Char(c),
modifiers,
} if !modifiers.contains(KeyModifiers::CONTROL) => {
- self.insert_char(c);
+ self.insert_char(c, cx);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
_ => (),
@@ -525,6 +525,7 @@ impl Component for Prompt {
}
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
+ self.completion = (self.completion_fn)(cx, &self.line);
self.render_prompt(area, surface, cx)
}