on master help sign in
helix-term: implement buffer completer
6118486Cole Helbling
helix-term/src/commands.rs+50−22
@@ -1435,7 +1435,7 @@ fn select_regex(cx: &mut Context) {
1435 cx,
1435 cx,
1436 "select:".into(),
1436 "select:".into(),
1437 Some(reg),
1437 Some(reg),
1438− |_input: &str| Vec::new(),
1438+ |_ctx: &compositor::Context, _input: &str| Vec::new(),
1439 move |view, doc, regex, event| {
1439 move |view, doc, regex, event| {
1440 if event != PromptEvent::Update {
1440 if event != PromptEvent::Update {
1441 return;
1441 return;
@@ -1458,7 +1458,7 @@ fn split_selection(cx: &mut Context) {
1458 cx,
1458 cx,
1459 "split:".into(),
1459 "split:".into(),
1460 Some(reg),
1460 Some(reg),
1461− |_input: &str| Vec::new(),
1461+ |_ctx: &compositor::Context, _input: &str| Vec::new(),
1462 move |view, doc, regex, event| {
1462 move |view, doc, regex, event| {
1463 if event != PromptEvent::Update {
1463 if event != PromptEvent::Update {
1464 return;
1464 return;
@@ -1600,7 +1600,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
1600 cx,
1600 cx,
1601 "search:".into(),
1601 "search:".into(),
1602 Some(reg),
1602 Some(reg),
1603− move |input: &str| {
1603+ move |_ctx: &compositor::Context, input: &str| {
1604 completions
1604 completions
1605 .iter()
1605 .iter()
1606 .filter(|comp| comp.starts_with(input))
1606 .filter(|comp| comp.starts_with(input))
@@ -1701,7 +1701,7 @@ fn global_search(cx: &mut Context) {
1701 cx,
1701 cx,
1702 "global-search:".into(),
1702 "global-search:".into(),
1703 None,
1703 None,
1704− move |input: &str| {
1704+ move |_ctx: &compositor::Context, input: &str| {
1705 completions
1705 completions
1706 .iter()
1706 .iter()
1707 .filter(|comp| comp.starts_with(input))
1707 .filter(|comp| comp.starts_with(input))
@@ -2079,26 +2079,54 @@ pub mod cmd {
2079 Ok(())
2079 Ok(())
2080 }
2080 }
2081
2081
2082+ fn buffer_close_impl(
2083+ editor: &mut Editor,
2084+ args: &[Cow<str>],
2085+ force: bool,
2086+ ) -> anyhow::Result<()> {
2087+ if args.is_empty() {
2088+ let doc_id = view!(editor).doc;
2089+ editor.close_document(doc_id, force)?;
2090+ return Ok(());
2091+ }
2092+
2093+ for arg in args {
2094+ let doc_id = editor.documents().find_map(|doc| {
2095+ let arg_path = Some(Path::new(arg.as_ref()));
2096+ if doc.path().map(|p| p.as_path()) == arg_path
2097+ || doc.relative_path().as_deref() == arg_path
2098+ {
2099+ Some(doc.id())
2100+ } else {
2101+ None
2102+ }
2103+ });
2104+
2105+ match doc_id {
2106+ Some(doc_id) => editor.close_document(doc_id, force)?,
2107+ None => {
2108+ editor.set_error(format!("couldn't close buffer '{}': does not exist", arg));
2109+ }
2110+ }
2111+ }
2112+
2113+ Ok(())
2114+ }
2115+
2082 fn buffer_close(
2116 fn buffer_close(
2083 cx: &mut compositor::Context,
2117 cx: &mut compositor::Context,
2084− _args: &[Cow<str>],
2118+ args: &[Cow<str>],
2085 _event: PromptEvent,
2119 _event: PromptEvent,
2086 ) -> anyhow::Result<()> {
2120 ) -> anyhow::Result<()> {
2087− let view = view!(cx.editor);
2121+ buffer_close_impl(cx.editor, args, false)
2088− let doc_id = view.doc;
2089− cx.editor.close_document(doc_id, false)?;
2090− Ok(())
2091 }
2122 }
2092
2123
2093 fn force_buffer_close(
2124 fn force_buffer_close(
2094 cx: &mut compositor::Context,
2125 cx: &mut compositor::Context,
2095− _args: &[Cow<str>],
2126+ args: &[Cow<str>],
2096 _event: PromptEvent,
2127 _event: PromptEvent,
2097 ) -> anyhow::Result<()> {
2128 ) -> anyhow::Result<()> {
2098− let view = view!(cx.editor);
2129+ buffer_close_impl(cx.editor, args, true)
2099− let doc_id = view.doc;
2100− cx.editor.close_document(doc_id, true)?;
2101− Ok(())
2102 }
2130 }
2103
2131
2104 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
2132 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
@@ -2927,14 +2955,14 @@ pub mod cmd {
2927 aliases: &["bc", "bclose"],
2955 aliases: &["bc", "bclose"],
2928 doc: "Close the current buffer.",
2956 doc: "Close the current buffer.",
2929 fun: buffer_close,
2957 fun: buffer_close,
2930− completer: None, // FIXME: buffer completer
2958+ completer: Some(completers::buffer),
2931 },
2959 },
2932 TypableCommand {
2960 TypableCommand {
2933 name: "buffer-close!",
2961 name: "buffer-close!",
2934 aliases: &["bc!", "bclose!"],
2962 aliases: &["bc!", "bclose!"],
2935 doc: "Close the current buffer forcefully (ignoring unsaved changes).",
2963 doc: "Close the current buffer forcefully (ignoring unsaved changes).",
2936 fun: force_buffer_close,
2964 fun: force_buffer_close,
2937− completer: None, // FIXME: buffer completer
2965+ completer: Some(completers::buffer),
2938 },
2966 },
2939 TypableCommand {
2967 TypableCommand {
2940 name: "write",
2968 name: "write",
@@ -3262,7 +3290,7 @@ fn command_mode(cx: &mut Context) {
3262 let mut prompt = Prompt::new(
3290 let mut prompt = Prompt::new(
3263 ":".into(),
3291 ":".into(),
3264 Some(':'),
3292 Some(':'),
3265− |input: &str| {
3293+ |ctx: &compositor::Context, input: &str| {
3266 static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
3294 static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
3267 Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
3295 Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
3268
3296
@@ -3294,7 +3322,7 @@ fn command_mode(cx: &mut Context) {
3294 ..
3322 ..
3295 }) = cmd::TYPABLE_COMMAND_MAP.get(parts[0])
3323 }) = cmd::TYPABLE_COMMAND_MAP.get(parts[0])
3296 {
3324 {
3297− completer(part)
3325+ completer(ctx, part)
3298 .into_iter()
3326 .into_iter()
3299 .map(|(range, file)| {
3327 .map(|(range, file)| {
3300 // offset ranges to input
3328 // offset ranges to input
@@ -5358,7 +5386,7 @@ fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) {
5358 cx,
5386 cx,
5359 if !remove { "keep:" } else { "remove:" }.into(),
5387 if !remove { "keep:" } else { "remove:" }.into(),
5360 Some(reg),
5388 Some(reg),
5361− |_input: &str| Vec::new(),
5389+ |_ctx: &compositor::Context, _input: &str| Vec::new(),
5362 move |view, doc, regex, event| {
5390 move |view, doc, regex, event| {
5363 if event != PromptEvent::Update {
5391 if event != PromptEvent::Update {
5364 return;
5392 return;
@@ -6122,7 +6150,7 @@ fn shell_keep_pipe(cx: &mut Context) {
6122 let prompt = Prompt::new(
6150 let prompt = Prompt::new(
6123 "keep-pipe:".into(),
6151 "keep-pipe:".into(),
6124 Some('|'),
6152 Some('|'),
6125− |_input: &str| Vec::new(),
6153+ |_ctx: &compositor::Context, _input: &str| Vec::new(),
6126 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
6154 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
6127 let shell = &cx.editor.config.shell;
6155 let shell = &cx.editor.config.shell;
6128 if event != PromptEvent::Validate {
6156 if event != PromptEvent::Validate {
@@ -6218,7 +6246,7 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
6218 let prompt = Prompt::new(
6246 let prompt = Prompt::new(
6219 prompt,
6247 prompt,
6220 Some('|'),
6248 Some('|'),
6221− |_input: &str| Vec::new(),
6249+ |_ctx: &compositor::Context, _input: &str| Vec::new(),
6222 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
6250 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
6223 let shell = &cx.editor.config.shell;
6251 let shell = &cx.editor.config.shell;
6224 if event != PromptEvent::Validate {
6252 if event != PromptEvent::Validate {
@@ -6314,7 +6342,7 @@ fn rename_symbol(cx: &mut Context) {
6314 let prompt = Prompt::new(
6342 let prompt = Prompt::new(
6315 "rename-to:".into(),
6343 "rename-to:".into(),
6316 None,
6344 None,
6317− |_input: &str| Vec::new(),
6345+ |_ctx: &compositor::Context, _input: &str| Vec::new(),
6318 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
6346 move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
6319 if event != PromptEvent::Validate {
6347 if event != PromptEvent::Validate {
6320 return;
6348 return;
helix-term/src/commands/dap.rs+4−3
@@ -361,8 +361,9 @@ fn debug_parameter_prompt(
361 let completer = match field_type {
361 let completer = match field_type {
362 "filename" => ui::completers::filename,
362 "filename" => ui::completers::filename,
363 "directory" => ui::completers::directory,
363 "directory" => ui::completers::directory,
364− _ => |_input: &str| Vec::new(),
364+ _ => ui::completers::none,
365 };
365 };
366+
366 Prompt::new(
367 Prompt::new(
367 format!("{}: ", name).into(),
368 format!("{}: ", name).into(),
368 None,
369 None,
@@ -696,7 +697,7 @@ pub fn dap_edit_condition(cx: &mut Context) {
696 let mut prompt = Prompt::new(
697 let mut prompt = Prompt::new(
697 "condition:".into(),
698 "condition:".into(),
698 None,
699 None,
699− |_input: &str| Vec::new(),
700+ ui::completers::none,
700 move |cx, input: &str, event: PromptEvent| {
701 move |cx, input: &str, event: PromptEvent| {
701 if event != PromptEvent::Validate {
702 if event != PromptEvent::Validate {
702 return;
703 return;
@@ -740,7 +741,7 @@ pub fn dap_edit_log(cx: &mut Context) {
740 let mut prompt = Prompt::new(
741 let mut prompt = Prompt::new(
741 "log-message:".into(),
742 "log-message:".into(),
742 None,
743 None,
743− |_input: &str| Vec::new(),
744+ ui::completers::none,
744 move |cx, input: &str, event: PromptEvent| {
745 move |cx, input: &str, event: PromptEvent| {
745 if event != PromptEvent::Validate {
746 if event != PromptEvent::Validate {
746 return;
747 return;
helix-term/src/ui/mod.rs+41−6
@@ -30,7 +30,7 @@ pub fn regex_prompt(
30 cx: &mut crate::commands::Context,
30 cx: &mut crate::commands::Context,
31 prompt: std::borrow::Cow<'static, str>,
31 prompt: std::borrow::Cow<'static, str>,
32 history_register: Option<char>,
32 history_register: Option<char>,
33− completion_fn: impl FnMut(&str) -> Vec<prompt::Completion> + 'static,
33+ completion_fn: impl FnMut(&crate::compositor::Context, &str) -> Vec<prompt::Completion> + 'static,
34 fun: impl Fn(&mut View, &mut Document, Regex, PromptEvent) + 'static,
34 fun: impl Fn(&mut View, &mut Document, Regex, PromptEvent) + 'static,
35) -> Prompt {
35) -> Prompt {
36 let (view, doc) = current!(cx.editor);
36 let (view, doc) = current!(cx.editor);
@@ -168,18 +168,53 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi
168}
168}
169
169
170pub mod completers {
170pub mod completers {
171+ use crate::compositor::Context;
171 use crate::ui::prompt::Completion;
172 use crate::ui::prompt::Completion;
172 use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
173 use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
173 use fuzzy_matcher::FuzzyMatcher;
174 use fuzzy_matcher::FuzzyMatcher;
175+ use helix_view::document::SCRATCH_BUFFER_NAME;
174 use helix_view::editor::Config;
176 use helix_view::editor::Config;
175 use helix_view::theme;
177 use helix_view::theme;
176 use once_cell::sync::Lazy;
178 use once_cell::sync::Lazy;
177 use std::borrow::Cow;
179 use std::borrow::Cow;
178 use std::cmp::Reverse;
180 use std::cmp::Reverse;
179
181
180− pub type Completer = fn(&str) -> Vec<Completion>;
182+ pub type Completer = fn(&Context, &str) -> Vec<Completion>;
181
183
184+ pub fn none(_cx: &Context, _input: &str) -> Vec<Completion> {
185+ Vec::new()
186+ }
187+
188+ pub fn buffer(cx: &Context, input: &str) -> Vec<Completion> {
189+ let mut names: Vec<_> = cx
190+ .editor
191+ .documents
192+ .iter()
193+ .map(|(_id, doc)| {
194+ let name = doc
195+ .relative_path()
196+ .map(|p| p.display().to_string())
197+ .unwrap_or_else(|| String::from(SCRATCH_BUFFER_NAME));
198+ ((0..), Cow::from(name))
199+ })
200+ .collect();
201+
202+ let matcher = Matcher::default();
203+
204+ let mut matches: Vec<_> = names
205+ .into_iter()
206+ .filter_map(|(_range, name)| {
207+ matcher.fuzzy_match(&name, input).map(|score| (name, score))
208+ })
209+ .collect();
210+
211+ matches.sort_unstable_by_key(|(_file, score)| Reverse(*score));
212+ names = matches.into_iter().map(|(name, _)| ((0..), name)).collect();
213+
214+ names
215+ }
216+
182− pub fn theme(input: &str) -> Vec<Completion> {
217+ pub fn theme(_cx: &Context, input: &str) -> Vec<Completion> {
183 let mut names = theme::Loader::read_names(&helix_core::runtime_dir().join("themes"));
218 let mut names = theme::Loader::read_names(&helix_core::runtime_dir().join("themes"));
184 names.extend(theme::Loader::read_names(
219 names.extend(theme::Loader::read_names(
185 &helix_core::config_dir().join("themes"),
220 &helix_core::config_dir().join("themes"),
@@ -207,7 +242,7 @@ pub mod completers {
207 names
242 names
208 }
243 }
209
244
210− pub fn setting(input: &str) -> Vec<Completion> {
245+ pub fn setting(_cx: &Context, input: &str) -> Vec<Completion> {
211 static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
246 static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
212 serde_json::to_value(Config::default())
247 serde_json::to_value(Config::default())
213 .unwrap()
248 .unwrap()
@@ -232,7 +267,7 @@ pub mod completers {
232 .collect()
267 .collect()
233 }
268 }
234
269
235− pub fn filename(input: &str) -> Vec<Completion> {
270+ pub fn filename(_cx: &Context, input: &str) -> Vec<Completion> {
236 filename_impl(input, |entry| {
271 filename_impl(input, |entry| {
237 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
272 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
238
273
@@ -244,7 +279,7 @@ pub mod completers {
244 })
279 })
245 }
280 }
246
281
247− pub fn directory(input: &str) -> Vec<Completion> {
282+ pub fn directory(_cx: &Context, input: &str) -> Vec<Completion> {
248 filename_impl(input, |entry| {
283 filename_impl(input, |entry| {
249 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
284 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
250
285
helix-term/src/ui/picker.rs+4−4
@@ -302,7 +302,7 @@ impl<T> Picker<T> {
302 let prompt = Prompt::new(
302 let prompt = Prompt::new(
303 "".into(),
303 "".into(),
304 None,
304 None,
305− |_pattern: &str| Vec::new(),
305+ |_ctx: &Context, _pattern: &str| Vec::new(),
306 |_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
306 |_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
307 //
307 //
308 },
308 },
@@ -395,12 +395,12 @@ impl<T> Picker<T> {
395 .map(|(index, _score)| &self.options[*index])
395 .map(|(index, _score)| &self.options[*index])
396 }
396 }
397
397
398− pub fn save_filter(&mut self) {
398+ pub fn save_filter(&mut self, cx: &Context) {
399 self.filters.clear();
399 self.filters.clear();
400 self.filters
400 self.filters
401 .extend(self.matches.iter().map(|(index, _)| *index));
401 .extend(self.matches.iter().map(|(index, _)| *index));
402 self.filters.sort_unstable(); // used for binary search later
402 self.filters.sort_unstable(); // used for binary search later
403− self.prompt.clear();
403+ self.prompt.clear(cx);
404 }
404 }
405}
405}
406
406
@@ -468,7 +468,7 @@ impl<T: 'static> Component for Picker<T> {
468 return close_fn;
468 return close_fn;
469 }
469 }
470 ctrl!(' ') => {
470 ctrl!(' ') => {
471− self.save_filter();
471+ self.save_filter(cx);
472 }
472 }
473 _ => {
473 _ => {
474 if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
474 if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
helix-term/src/ui/prompt.rs+28−27
@@ -24,7 +24,7 @@ pub struct Prompt {
24 selection: Option<usize>,
24 selection: Option<usize>,
25 history_register: Option<char>,
25 history_register: Option<char>,
26 history_pos: Option<usize>,
26 history_pos: Option<usize>,
27− completion_fn: Box<dyn FnMut(&str) -> Vec<Completion>>,
27+ completion_fn: Box<dyn FnMut(&Context, &str) -> Vec<Completion>>,
28 callback_fn: Box<dyn FnMut(&mut Context, &str, PromptEvent)>,
28 callback_fn: Box<dyn FnMut(&mut Context, &str, PromptEvent)>,
29 pub doc_fn: Box<dyn Fn(&str) -> Option<&'static str>>,
29 pub doc_fn: Box<dyn Fn(&str) -> Option<&'static str>>,
30}
30}
@@ -59,14 +59,14 @@ impl Prompt {
59 pub fn new(
59 pub fn new(
60 prompt: Cow<'static, str>,
60 prompt: Cow<'static, str>,
61 history_register: Option<char>,
61 history_register: Option<char>,
62− mut completion_fn: impl FnMut(&str) -> Vec<Completion> + 'static,
62+ completion_fn: impl FnMut(&Context, &str) -> Vec<Completion> + 'static,
63 callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
63 callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
64 ) -> Self {
64 ) -> Self {
65 Self {
65 Self {
66 prompt,
66 prompt,
67 line: String::new(),
67 line: String::new(),
68 cursor: 0,
68 cursor: 0,
69− completion: completion_fn(""),
69+ completion: Vec::new(),
70 selection: None,
70 selection: None,
71 history_register,
71 history_register,
72 history_pos: None,
72 history_pos: None,
@@ -177,13 +177,13 @@ impl Prompt {
177 }
177 }
178 }
178 }
179
179
180− pub fn insert_char(&mut self, c: char) {
180+ pub fn insert_char(&mut self, c: char, cx: &Context) {
181 self.line.insert(self.cursor, c);
181 self.line.insert(self.cursor, c);
182 let mut cursor = GraphemeCursor::new(self.cursor, self.line.len(), false);
182 let mut cursor = GraphemeCursor::new(self.cursor, self.line.len(), false);
183 if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) {
183 if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) {
184 self.cursor = pos;
184 self.cursor = pos;
185 }
185 }
186− self.completion = (self.completion_fn)(&self.line);
186+ self.completion = (self.completion_fn)(cx, &self.line);
187 self.exit_selection();
187 self.exit_selection();
188 }
188 }
189
189
@@ -205,61 +205,61 @@ impl Prompt {
205 self.cursor = self.line.len();
205 self.cursor = self.line.len();
206 }
206 }
207
207
208− pub fn delete_char_backwards(&mut self) {
208+ pub fn delete_char_backwards(&mut self, cx: &Context) {
209 let pos = self.eval_movement(Movement::BackwardChar(1));
209 let pos = self.eval_movement(Movement::BackwardChar(1));
210 self.line.replace_range(pos..self.cursor, "");
210 self.line.replace_range(pos..self.cursor, "");
211 self.cursor = pos;
211 self.cursor = pos;
212
212
213 self.exit_selection();
213 self.exit_selection();
214− self.completion = (self.completion_fn)(&self.line);
214+ self.completion = (self.completion_fn)(cx, &self.line);
215 }
215 }
216
216
217− pub fn delete_char_forwards(&mut self) {
217+ pub fn delete_char_forwards(&mut self, cx: &Context) {
218 let pos = self.eval_movement(Movement::ForwardChar(1));
218 let pos = self.eval_movement(Movement::ForwardChar(1));
219 self.line.replace_range(self.cursor..pos, "");
219 self.line.replace_range(self.cursor..pos, "");
220
220
221 self.exit_selection();
221 self.exit_selection();
222− self.completion = (self.completion_fn)(&self.line);
222+ self.completion = (self.completion_fn)(cx, &self.line);
223 }
223 }
224
224
225− pub fn delete_word_backwards(&mut self) {
225+ pub fn delete_word_backwards(&mut self, cx: &Context) {
226 let pos = self.eval_movement(Movement::BackwardWord(1));
226 let pos = self.eval_movement(Movement::BackwardWord(1));
227 self.line.replace_range(pos..self.cursor, "");
227 self.line.replace_range(pos..self.cursor, "");
228 self.cursor = pos;
228 self.cursor = pos;
229
229
230 self.exit_selection();
230 self.exit_selection();
231− self.completion = (self.completion_fn)(&self.line);
231+ self.completion = (self.completion_fn)(cx, &self.line);
232 }
232 }
233
233
234− pub fn delete_word_forwards(&mut self) {
234+ pub fn delete_word_forwards(&mut self, cx: &Context) {
235 let pos = self.eval_movement(Movement::ForwardWord(1));
235 let pos = self.eval_movement(Movement::ForwardWord(1));
236 self.line.replace_range(self.cursor..pos, "");
236 self.line.replace_range(self.cursor..pos, "");
237
237
238 self.exit_selection();
238 self.exit_selection();
239− self.completion = (self.completion_fn)(&self.line);
239+ self.completion = (self.completion_fn)(cx, &self.line);
240 }
240 }
241
241
242− pub fn kill_to_start_of_line(&mut self) {
242+ pub fn kill_to_start_of_line(&mut self, cx: &Context) {
243 let pos = self.eval_movement(Movement::StartOfLine);
243 let pos = self.eval_movement(Movement::StartOfLine);
244 self.line.replace_range(pos..self.cursor, "");
244 self.line.replace_range(pos..self.cursor, "");
245 self.cursor = pos;
245 self.cursor = pos;
246
246
247 self.exit_selection();
247 self.exit_selection();
248− self.completion = (self.completion_fn)(&self.line);
248+ self.completion = (self.completion_fn)(cx, &self.line);
249 }
249 }
250
250
251− pub fn kill_to_end_of_line(&mut self) {
251+ pub fn kill_to_end_of_line(&mut self, cx: &Context) {
252 let pos = self.eval_movement(Movement::EndOfLine);
252 let pos = self.eval_movement(Movement::EndOfLine);
253 self.line.replace_range(self.cursor..pos, "");
253 self.line.replace_range(self.cursor..pos, "");
254
254
255 self.exit_selection();
255 self.exit_selection();
256− self.completion = (self.completion_fn)(&self.line);
256+ self.completion = (self.completion_fn)(cx, &self.line);
257 }
257 }
258
258
259− pub fn clear(&mut self) {
259+ pub fn clear(&mut self, cx: &Context) {
260 self.line.clear();
260 self.line.clear();
261 self.cursor = 0;
261 self.cursor = 0;
262− self.completion = (self.completion_fn)(&self.line);
262+ self.completion = (self.completion_fn)(cx, &self.line);
263 self.exit_selection();
263 self.exit_selection();
264 }
264 }
265
265
@@ -442,16 +442,16 @@ impl Component for Prompt {
442 ctrl!('f') | key!(Right) => self.move_cursor(Movement::ForwardChar(1)),
442 ctrl!('f') | key!(Right) => self.move_cursor(Movement::ForwardChar(1)),
443 ctrl!('e') | key!(End) => self.move_end(),
443 ctrl!('e') | key!(End) => self.move_end(),
444 ctrl!('a') | key!(Home) => self.move_start(),
444 ctrl!('a') | key!(Home) => self.move_start(),
445− ctrl!('w') => self.delete_word_backwards(),
445+ ctrl!('w') => self.delete_word_backwards(cx),
446− alt!('d') => self.delete_word_forwards(),
446+ alt!('d') => self.delete_word_forwards(cx),
447− ctrl!('k') => self.kill_to_end_of_line(),
447+ ctrl!('k') => self.kill_to_end_of_line(cx),
448− ctrl!('u') => self.kill_to_start_of_line(),
448+ ctrl!('u') => self.kill_to_start_of_line(cx),
449 ctrl!('h') | key!(Backspace) => {
449 ctrl!('h') | key!(Backspace) => {
450− self.delete_char_backwards();
450+ self.delete_char_backwards(cx);
451 (self.callback_fn)(cx, &self.line, PromptEvent::Update);
451 (self.callback_fn)(cx, &self.line, PromptEvent::Update);
452 }
452 }
453 ctrl!('d') | key!(Delete) => {
453 ctrl!('d') | key!(Delete) => {
454− self.delete_char_forwards();
454+ self.delete_char_forwards(cx);
455 (self.callback_fn)(cx, &self.line, PromptEvent::Update);
455 (self.callback_fn)(cx, &self.line, PromptEvent::Update);
456 }
456 }
457 ctrl!('s') => {
457 ctrl!('s') => {
@@ -474,7 +474,7 @@ impl Component for Prompt {
474 }
474 }
475 key!(Enter) => {
475 key!(Enter) => {
476 if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
476 if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
477− self.completion = (self.completion_fn)(&self.line);
477+ self.completion = (self.completion_fn)(cx, &self.line);
478 self.exit_selection();
478 self.exit_selection();
479 } else {
479 } else {
480 (self.callback_fn)(cx, &self.line, PromptEvent::Validate);
480 (self.callback_fn)(cx, &self.line, PromptEvent::Validate);
@@ -515,7 +515,7 @@ impl Component for Prompt {
515 code: KeyCode::Char(c),
515 code: KeyCode::Char(c),
516 modifiers,
516 modifiers,
517 } if !modifiers.contains(KeyModifiers::CONTROL) => {
517 } if !modifiers.contains(KeyModifiers::CONTROL) => {
518− self.insert_char(c);
518+ self.insert_char(c, cx);
519 (self.callback_fn)(cx, &self.line, PromptEvent::Update);
519 (self.callback_fn)(cx, &self.line, PromptEvent::Update);
520 }
520 }
521 _ => (),
521 _ => (),
@@ -525,6 +525,7 @@ impl Component for Prompt {
525 }
525 }
526
526
527 fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
527 fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
528+ self.completion = (self.completion_fn)(cx, &self.line);
528 self.render_prompt(area, surface, cx)
529 self.render_prompt(area, surface, cx)
529 }
530 }
530
531