Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffer complete #1134

Merged
merged 5 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 62 additions & 24 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1435,7 +1435,7 @@ fn select_regex(cx: &mut Context) {
cx,
"select:".into(),
Some(reg),
|_input: &str| Vec::new(),
ui::completers::none,
move |view, doc, regex, event| {
if event != PromptEvent::Update {
return;
Expand All @@ -1458,7 +1458,7 @@ fn split_selection(cx: &mut Context) {
cx,
"split:".into(),
Some(reg),
|_input: &str| Vec::new(),
ui::completers::none,
move |view, doc, regex, event| {
if event != PromptEvent::Update {
return;
Expand Down Expand Up @@ -1600,7 +1600,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
cx,
"search:".into(),
Some(reg),
move |input: &str| {
move |_editor: &Editor, input: &str| {
completions
.iter()
.filter(|comp| comp.starts_with(input))
Expand Down Expand Up @@ -1701,7 +1701,7 @@ fn global_search(cx: &mut Context) {
cx,
"global-search:".into(),
None,
move |input: &str| {
move |_editor: &Editor, input: &str| {
completions
.iter()
.filter(|comp| comp.starts_with(input))
Expand Down Expand Up @@ -2079,26 +2079,62 @@ pub mod cmd {
Ok(())
}

fn buffer_close_impl(
editor: &mut Editor,
args: &[Cow<str>],
force: bool,
) -> anyhow::Result<()> {
if args.is_empty() {
let doc_id = view!(editor).doc;
editor.close_document(doc_id, force)?;
return Ok(());
}

let mut nonexistent_buffers = vec![];
for arg in args {
let doc_id = editor.documents().find_map(|doc| {
let arg_path = Some(Path::new(arg.as_ref()));
if doc.path().map(|p| p.as_path()) == arg_path
|| doc.relative_path().as_deref() == arg_path
{
Some(doc.id())
} else {
None
}
});

match doc_id {
Some(doc_id) => editor.close_document(doc_id, force)?,
None => nonexistent_buffers.push(arg),
}
}

let nonexistent_buffers: Vec<_> = nonexistent_buffers
.into_iter()
.map(|str| format!("'{}'", str))
.collect();
editor.set_error(format!(
"couldn't close buffer(s) {}: does not exist",
cole-h marked this conversation as resolved.
Show resolved Hide resolved
nonexistent_buffers.join(", ")
));

Ok(())
}

fn buffer_close(
cx: &mut compositor::Context,
_args: &[Cow<str>],
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let view = view!(cx.editor);
let doc_id = view.doc;
cx.editor.close_document(doc_id, false)?;
Ok(())
buffer_close_impl(cx.editor, args, false)
}

fn force_buffer_close(
cx: &mut compositor::Context,
_args: &[Cow<str>],
args: &[Cow<str>],
_event: PromptEvent,
) -> anyhow::Result<()> {
let view = view!(cx.editor);
let doc_id = view.doc;
cx.editor.close_document(doc_id, true)?;
Ok(())
buffer_close_impl(cx.editor, args, true)
}

fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
Expand Down Expand Up @@ -2927,14 +2963,14 @@ pub mod cmd {
aliases: &["bc", "bclose"],
doc: "Close the current buffer.",
fun: buffer_close,
completer: None, // FIXME: buffer completer
completer: Some(completers::buffer),
},
TypableCommand {
name: "buffer-close!",
aliases: &["bc!", "bclose!"],
doc: "Close the current buffer forcefully (ignoring unsaved changes).",
fun: force_buffer_close,
completer: None, // FIXME: buffer completer
completer: Some(completers::buffer),
},
TypableCommand {
name: "write",
Expand Down Expand Up @@ -3262,7 +3298,7 @@ fn command_mode(cx: &mut Context) {
let mut prompt = Prompt::new(
":".into(),
Some(':'),
|input: &str| {
|editor: &Editor, input: &str| {
static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);

Expand Down Expand Up @@ -3294,7 +3330,7 @@ fn command_mode(cx: &mut Context) {
..
}) = cmd::TYPABLE_COMMAND_MAP.get(parts[0])
{
completer(part)
completer(editor, part)
.into_iter()
.map(|(range, file)| {
// offset ranges to input
Expand Down Expand Up @@ -3357,6 +3393,8 @@ fn command_mode(cx: &mut Context) {
None
});

// Calculate initial completion
prompt.recalculate_completion(cx.editor);
cx.push_layer(Box::new(prompt));
}

Expand Down Expand Up @@ -3385,7 +3423,7 @@ fn buffer_picker(cx: &mut Context) {
.map(helix_core::path::get_relative_path);
let path = match path.as_deref().and_then(Path::to_str) {
Some(path) => path,
None => return Cow::Borrowed(SCRATCH_BUFFER_NAME),
None => SCRATCH_BUFFER_NAME,
};

let mut flags = Vec::new();
Expand All @@ -3401,7 +3439,7 @@ fn buffer_picker(cx: &mut Context) {
} else {
format!(" ({})", flags.join(""))
};
Cow::Owned(format!("{}{}", path, flag))
Cow::Owned(format!("{} {}{}", self.id, path, flag))
}
}

Expand Down Expand Up @@ -5358,7 +5396,7 @@ fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) {
cx,
if !remove { "keep:" } else { "remove:" }.into(),
Some(reg),
|_input: &str| Vec::new(),
ui::completers::none,
move |view, doc, regex, event| {
if event != PromptEvent::Update {
return;
Expand Down Expand Up @@ -6122,7 +6160,7 @@ fn shell_keep_pipe(cx: &mut Context) {
let prompt = Prompt::new(
"keep-pipe:".into(),
Some('|'),
|_input: &str| Vec::new(),
ui::completers::none,
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
let shell = &cx.editor.config.shell;
if event != PromptEvent::Validate {
Expand Down Expand Up @@ -6218,7 +6256,7 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
let prompt = Prompt::new(
prompt,
Some('|'),
|_input: &str| Vec::new(),
ui::completers::none,
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
let shell = &cx.editor.config.shell;
if event != PromptEvent::Validate {
Expand Down Expand Up @@ -6314,7 +6352,7 @@ fn rename_symbol(cx: &mut Context) {
let prompt = Prompt::new(
"rename-to:".into(),
None,
|_input: &str| Vec::new(),
ui::completers::none,
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
if event != PromptEvent::Validate {
return;
Expand Down
7 changes: 4 additions & 3 deletions helix-term/src/commands/dap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,9 @@ fn debug_parameter_prompt(
let completer = match field_type {
"filename" => ui::completers::filename,
"directory" => ui::completers::directory,
_ => |_input: &str| Vec::new(),
_ => ui::completers::none,
};

Prompt::new(
format!("{}: ", name).into(),
None,
Expand Down Expand Up @@ -696,7 +697,7 @@ pub fn dap_edit_condition(cx: &mut Context) {
let mut prompt = Prompt::new(
"condition:".into(),
None,
|_input: &str| Vec::new(),
ui::completers::none,
move |cx, input: &str, event: PromptEvent| {
if event != PromptEvent::Validate {
return;
Expand Down Expand Up @@ -740,7 +741,7 @@ pub fn dap_edit_log(cx: &mut Context) {
let mut prompt = Prompt::new(
"log-message:".into(),
None,
|_input: &str| Vec::new(),
ui::completers::none,
move |cx, input: &str, event: PromptEvent| {
if event != PromptEvent::Validate {
return;
Expand Down
56 changes: 46 additions & 10 deletions helix-term/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ pub use text::Text;

use helix_core::regex::Regex;
use helix_core::regex::RegexBuilder;
use helix_view::{Document, View};
use helix_view::{Document, Editor, View};

use std::path::PathBuf;

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(&Editor, &str) -> Vec<prompt::Completion> + 'static,
fun: impl Fn(&mut View, &mut Document, Regex, PromptEvent) + 'static,
) -> Prompt {
let (view, doc) = current!(cx.editor);
let view_id = view.id;
let snapshot = doc.selection(view_id).clone();
let offset_snapshot = view.offset;

Prompt::new(
let mut prompt = Prompt::new(
prompt,
history_register,
completion_fn,
Expand Down Expand Up @@ -91,7 +91,10 @@ pub fn regex_prompt(
}
}
},
)
);
// Calculate initial completion
prompt.recalculate_completion(cx.editor);
prompt
}

pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePicker<PathBuf> {
Expand Down Expand Up @@ -171,15 +174,48 @@ pub mod completers {
use crate::ui::prompt::Completion;
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
use fuzzy_matcher::FuzzyMatcher;
use helix_view::editor::Config;
use helix_view::document::SCRATCH_BUFFER_NAME;
use helix_view::theme;
use helix_view::{editor::Config, Editor};
use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::cmp::Reverse;

pub type Completer = fn(&str) -> Vec<Completion>;
pub type Completer = fn(&Editor, &str) -> Vec<Completion>;

pub fn none(_editor: &Editor, _input: &str) -> Vec<Completion> {
Vec::new()
}

pub fn buffer(editor: &Editor, input: &str) -> Vec<Completion> {
let mut names: Vec<_> = 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(input: &str) -> Vec<Completion> {
pub fn theme(_editor: &Editor, 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"),
Expand Down Expand Up @@ -207,7 +243,7 @@ pub mod completers {
names
}

pub fn setting(input: &str) -> Vec<Completion> {
pub fn setting(_editor: &Editor, input: &str) -> Vec<Completion> {
static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
serde_json::to_value(Config::default())
.unwrap()
Expand All @@ -232,7 +268,7 @@ pub mod completers {
.collect()
}

pub fn filename(input: &str) -> Vec<Completion> {
pub fn filename(_editor: &Editor, input: &str) -> Vec<Completion> {
filename_impl(input, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());

Expand All @@ -244,7 +280,7 @@ pub mod completers {
})
}

pub fn directory(input: &str) -> Vec<Completion> {
pub fn directory(_editor: &Editor, input: &str) -> Vec<Completion> {
filename_impl(input, |entry| {
let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());

Expand Down
10 changes: 5 additions & 5 deletions helix-term/src/ui/picker.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
compositor::{Component, Compositor, Context, EventResult},
ctrl, key, shift,
ui::EditorView,
ui::{self, EditorView},
};
use crossterm::event::Event;
use tui::{
Expand Down Expand Up @@ -302,7 +302,7 @@ impl<T> Picker<T> {
let prompt = Prompt::new(
"".into(),
None,
|_pattern: &str| Vec::new(),
ui::completers::none,
|_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
//
},
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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) {
Expand Down
Loading