Skip to content

Commit

Permalink
fix: Recalculate completion when going through prompt history (helix-…
Browse files Browse the repository at this point in the history
…editor#3193)

* fix: Recalculate completion when going through prompt history

* Update completion when the prompt line is changed

It should not be possible to update the line without also updating the
completion since the completion holds an index into the line.

* Fix Prompt::with_line recalculate completion

with_line was the last function where recalculate completion had to be
done manually. This function now also recalculates the completion so
that it's impossible to forget.

* Exit selection when recalculating completion

Keeping the selection index when the completion has been recalculated
doesn't make sense. This clears the selection automatically, removing
most needs to manually clear it.

* Remove &mut on save_filter

Co-authored-by: Blaž Hrastnik <[email protected]>
  • Loading branch information
2 people authored and Shekhinah Memmel committed Dec 11, 2022
1 parent 35ca58c commit 17d911a
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 57 deletions.
8 changes: 4 additions & 4 deletions helix-term/src/commands/dap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ pub fn dap_edit_condition(cx: &mut Context) {
None => return,
};
let callback = Box::pin(async move {
let call: Callback = Box::new(move |_editor, compositor| {
let call: Callback = Box::new(move |editor, compositor| {
let mut prompt = Prompt::new(
"condition:".into(),
None,
Expand All @@ -607,7 +607,7 @@ pub fn dap_edit_condition(cx: &mut Context) {
},
);
if let Some(condition) = breakpoint.condition {
prompt.insert_str(&condition)
prompt.insert_str(&condition, editor)
}
compositor.push(Box::new(prompt));
});
Expand All @@ -624,7 +624,7 @@ pub fn dap_edit_log(cx: &mut Context) {
None => return,
};
let callback = Box::pin(async move {
let call: Callback = Box::new(move |_editor, compositor| {
let call: Callback = Box::new(move |editor, compositor| {
let mut prompt = Prompt::new(
"log-message:".into(),
None,
Expand All @@ -648,7 +648,7 @@ pub fn dap_edit_log(cx: &mut Context) {
},
);
if let Some(log_message) = breakpoint.log_message {
prompt.insert_str(&log_message);
prompt.insert_str(&log_message, editor);
}
compositor.push(Box::new(prompt));
});
Expand Down
19 changes: 6 additions & 13 deletions helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ pub fn prompt(
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
) {
show_prompt(
cx,
Prompt::new(prompt, history_register, completion_fn, callback_fn),
);
let mut prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn);
// Calculate the initial completion
prompt.recalculate_completion(cx.editor);
cx.push_layer(Box::new(prompt));
}

pub fn prompt_with_input(
Expand All @@ -53,15 +53,8 @@ pub fn prompt_with_input(
completion_fn: impl FnMut(&Editor, &str) -> Vec<prompt::Completion> + 'static,
callback_fn: impl FnMut(&mut crate::compositor::Context, &str, PromptEvent) + 'static,
) {
show_prompt(
cx,
Prompt::new(prompt, history_register, completion_fn, callback_fn).with_line(input),
);
}

fn show_prompt(cx: &mut crate::commands::Context, mut prompt: Prompt) {
// Calculate initial completion
prompt.recalculate_completion(cx.editor);
let prompt = Prompt::new(prompt, history_register, completion_fn, callback_fn)
.with_line(input, cx.editor);
cx.push_layer(Box::new(prompt));
}

Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/ui/picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ impl<T: Item> Picker<T> {
self.filters
.extend(self.matches.iter().map(|(index, _)| *index));
self.filters.sort_unstable(); // used for binary search later
self.prompt.clear(cx);
self.prompt.clear(cx.editor);
}

pub fn toggle_preview(&mut self) {
Expand Down
79 changes: 40 additions & 39 deletions helix-term/src/ui/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ impl Prompt {
}
}

pub fn with_line(mut self, line: String) -> Self {
pub fn with_line(mut self, line: String, editor: &Editor) -> Self {
let cursor = line.len();
self.line = line;
self.cursor = cursor;
self.recalculate_completion(editor);
self
}

Expand All @@ -95,6 +96,7 @@ impl Prompt {
}

pub fn recalculate_completion(&mut self, editor: &Editor) {
self.exit_selection();
self.completion = (self.completion_fn)(editor, &self.line);
}

Expand Down Expand Up @@ -213,12 +215,12 @@ impl Prompt {
self.cursor = pos;
}
self.recalculate_completion(cx.editor);
self.exit_selection();
}

pub fn insert_str(&mut self, s: &str) {
pub fn insert_str(&mut self, s: &str, editor: &Editor) {
self.line.insert_str(self.cursor, s);
self.cursor += s.len();
self.recalculate_completion(editor);
}

pub fn move_cursor(&mut self, movement: Movement) {
Expand All @@ -234,65 +236,65 @@ impl Prompt {
self.cursor = self.line.len();
}

pub fn delete_char_backwards(&mut self, cx: &Context) {
pub fn delete_char_backwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::BackwardChar(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;

self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}

pub fn delete_char_forwards(&mut self, cx: &Context) {
pub fn delete_char_forwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::ForwardChar(1));
self.line.replace_range(self.cursor..pos, "");

self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}

pub fn delete_word_backwards(&mut self, cx: &Context) {
pub fn delete_word_backwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::BackwardWord(1));
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;

self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}

pub fn delete_word_forwards(&mut self, cx: &Context) {
pub fn delete_word_forwards(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::ForwardWord(1));
self.line.replace_range(self.cursor..pos, "");

self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}

pub fn kill_to_start_of_line(&mut self, cx: &Context) {
pub fn kill_to_start_of_line(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::StartOfLine);
self.line.replace_range(pos..self.cursor, "");
self.cursor = pos;

self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}

pub fn kill_to_end_of_line(&mut self, cx: &Context) {
pub fn kill_to_end_of_line(&mut self, editor: &Editor) {
let pos = self.eval_movement(Movement::EndOfLine);
self.line.replace_range(self.cursor..pos, "");

self.exit_selection();
self.recalculate_completion(cx.editor);
self.recalculate_completion(editor);
}

pub fn clear(&mut self, cx: &Context) {
pub fn clear(&mut self, editor: &Editor) {
self.line.clear();
self.cursor = 0;
self.recalculate_completion(cx.editor);
self.exit_selection();
self.recalculate_completion(editor);
}

pub fn change_history(&mut self, register: &[String], direction: CompletionDirection) {
pub fn change_history(
&mut self,
cx: &mut Context,
register: char,
direction: CompletionDirection,
) {
let register = cx.editor.registers.get_mut(register).read();

if register.is_empty() {
return;
}
Expand All @@ -312,6 +314,7 @@ impl Prompt {
self.history_pos = Some(index);

self.move_end();
self.recalculate_completion(cx.editor);
}

pub fn change_completion_selection(&mut self, direction: CompletionDirection) {
Expand Down Expand Up @@ -494,16 +497,18 @@ 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') | alt!(Backspace) | ctrl!(Backspace) => self.delete_word_backwards(cx),
alt!('d') | alt!(Delete) | ctrl!(Delete) => self.delete_word_forwards(cx),
ctrl!('k') => self.kill_to_end_of_line(cx),
ctrl!('u') => self.kill_to_start_of_line(cx),
ctrl!('w') | alt!(Backspace) | ctrl!(Backspace) => {
self.delete_word_backwards(cx.editor)
}
alt!('d') | alt!(Delete) | ctrl!(Delete) => self.delete_word_forwards(cx.editor),
ctrl!('k') => self.kill_to_end_of_line(cx.editor),
ctrl!('u') => self.kill_to_start_of_line(cx.editor),
ctrl!('h') | key!(Backspace) => {
self.delete_char_backwards(cx);
self.delete_char_backwards(cx.editor);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('d') | key!(Delete) => {
self.delete_char_forwards(cx);
self.delete_char_forwards(cx.editor);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
ctrl!('s') => {
Expand All @@ -520,14 +525,13 @@ impl Component for Prompt {
);
let line = text.slice(range.from()..range.to()).to_string();
if !line.is_empty() {
self.insert_str(line.as_str());
self.insert_str(line.as_str(), cx.editor);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
key!(Enter) => {
if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
self.recalculate_completion(cx.editor);
self.exit_selection();
} else {
// handle executing with last command in history if nothing entered
let input: Cow<str> = if self.line.is_empty() {
Expand All @@ -553,15 +557,13 @@ impl Component for Prompt {
}
ctrl!('p') | key!(Up) => {
if let Some(register) = self.history_register {
let register = cx.editor.registers.get_mut(register);
self.change_history(register.read(), CompletionDirection::Backward);
self.change_history(cx, register, CompletionDirection::Backward);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
ctrl!('n') | key!(Down) => {
if let Some(register) = self.history_register {
let register = cx.editor.registers.get_mut(register);
self.change_history(register.read(), CompletionDirection::Forward);
self.change_history(cx, register, CompletionDirection::Forward);
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
}
}
Expand All @@ -570,7 +572,6 @@ impl Component for Prompt {
// if single completion candidate is a directory list content in completion
if self.completion.len() == 1 && self.line.ends_with(std::path::MAIN_SEPARATOR) {
self.recalculate_completion(cx.editor);
self.exit_selection();
}
(self.callback_fn)(cx, &self.line, PromptEvent::Update)
}
Expand Down Expand Up @@ -602,8 +603,8 @@ impl Component for Prompt {
.read(c)
.and_then(|r| r.first())
.map_or("", |r| r.as_str()),
context.editor,
);
prompt.recalculate_completion(context.editor);
}));
(self.callback_fn)(cx, &self.line, PromptEvent::Update);
return EventResult::Consumed(None);
Expand Down
1 change: 1 addition & 0 deletions helix-term/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ mod test {
mod auto_pairs;
mod commands;
mod movement;
mod prompt;
mod write;
}
18 changes: 18 additions & 0 deletions helix-term/tests/test/prompt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use super::*;

use helix_term::application::Application;

#[tokio::test]
async fn test_history_completion() -> anyhow::Result<()> {
test_key_sequence(
&mut Application::new(Args::default(), Config::default())?,
Some(":asdf<ret>:theme d<C-n><tab>"),
Some(&|app| {
assert!(!app.editor.is_err());
}),
false,
)
.await?;

Ok(())
}

0 comments on commit 17d911a

Please sign in to comment.