Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--helix-term/src/commands.rs98
-rw-r--r--helix-term/src/commands/typed.rs35
2 files changed, 66 insertions, 67 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 18f24a63..8edf5944 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -22,7 +22,8 @@ pub use typed::*;
use helix_core::{
char_idx_at_visual_offset,
chars::char_is_word,
- command_line, comment,
+ command_line::{self, Args},
+ comment,
doc_formatter::TextFormat,
encoding, find_workspace,
graphemes::{self, next_grapheme_boundary},
@@ -46,6 +47,7 @@ use helix_core::{
use helix_view::{
document::{FormatterError, Mode, SCRATCH_BUFFER_NAME},
editor::Action,
+ expansion,
info::Info,
input::KeyEvent,
keyboard::KeyCode,
@@ -6256,64 +6258,52 @@ enum ShellBehavior {
}
fn shell_pipe(cx: &mut Context) {
- shell_prompt(cx, "pipe:".into(), ShellBehavior::Replace);
+ shell_prompt_for_behavior(cx, "pipe:".into(), ShellBehavior::Replace);
}
fn shell_pipe_to(cx: &mut Context) {
- shell_prompt(cx, "pipe-to:".into(), ShellBehavior::Ignore);
+ shell_prompt_for_behavior(cx, "pipe-to:".into(), ShellBehavior::Ignore);
}
fn shell_insert_output(cx: &mut Context) {
- shell_prompt(cx, "insert-output:".into(), ShellBehavior::Insert);
+ shell_prompt_for_behavior(cx, "insert-output:".into(), ShellBehavior::Insert);
}
fn shell_append_output(cx: &mut Context) {
- shell_prompt(cx, "append-output:".into(), ShellBehavior::Append);
+ shell_prompt_for_behavior(cx, "append-output:".into(), ShellBehavior::Append);
}
fn shell_keep_pipe(cx: &mut Context) {
- ui::prompt(
- cx,
- "keep-pipe:".into(),
- Some('|'),
- ui::completers::none,
- move |cx, input: &str, event: PromptEvent| {
- let shell = &cx.editor.config().shell;
- if event != PromptEvent::Validate {
- return;
- }
- if input.is_empty() {
- return;
- }
- let (view, doc) = current!(cx.editor);
- let selection = doc.selection(view.id);
+ shell_prompt(cx, "keep-pipe:".into(), |cx, args| {
+ let shell = &cx.editor.config().shell;
+ let (view, doc) = current!(cx.editor);
+ let selection = doc.selection(view.id);
- let mut ranges = SmallVec::with_capacity(selection.len());
- let old_index = selection.primary_index();
- let mut index: Option<usize> = None;
- let text = doc.text().slice(..);
+ let mut ranges = SmallVec::with_capacity(selection.len());
+ let old_index = selection.primary_index();
+ let mut index: Option<usize> = None;
+ let text = doc.text().slice(..);
- for (i, range) in selection.ranges().iter().enumerate() {
- let fragment = range.slice(text);
- if let Err(err) = shell_impl(shell, input, Some(fragment.into())) {
- log::debug!("Shell command failed: {}", err);
- } else {
- ranges.push(*range);
- if i >= old_index && index.is_none() {
- index = Some(ranges.len() - 1);
- }
+ for (i, range) in selection.ranges().iter().enumerate() {
+ let fragment = range.slice(text);
+ if let Err(err) = shell_impl(shell, args.join(" ").as_str(), Some(fragment.into())) {
+ log::debug!("Shell command failed: {}", err);
+ } else {
+ ranges.push(*range);
+ if i >= old_index && index.is_none() {
+ index = Some(ranges.len() - 1);
}
}
+ }
- if ranges.is_empty() {
- cx.editor.set_error("No selections remaining");
- return;
- }
+ if ranges.is_empty() {
+ cx.editor.set_error("No selections remaining");
+ return;
+ }
- let index = index.unwrap_or_else(|| ranges.len() - 1);
- doc.set_selection(view.id, Selection::new(ranges, index));
- },
- );
+ let index = index.unwrap_or_else(|| ranges.len() - 1);
+ doc.set_selection(view.id, Selection::new(ranges, index));
+ });
}
fn shell_impl(shell: &[String], cmd: &str, input: Option<Rope>) -> anyhow::Result<Tendril> {
@@ -6468,25 +6458,35 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
view.ensure_cursor_in_view(doc, config.scrolloff);
}
-fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
+fn shell_prompt<F>(cx: &mut Context, prompt: Cow<'static, str>, mut callback_fn: F)
+where
+ F: FnMut(&mut compositor::Context, Args) + 'static,
+{
ui::prompt(
cx,
prompt,
Some('|'),
- ui::completers::shell,
- move |cx, input: &str, event: PromptEvent| {
- if event != PromptEvent::Validate {
+ |editor, input| complete_command_args(editor, SHELL_SIGNATURE, &SHELL_COMPLETER, input, 0),
+ move |cx, input, event| {
+ if event != PromptEvent::Validate || input.is_empty() {
return;
}
- if input.is_empty() {
- return;
+ match Args::parse(input, SHELL_SIGNATURE, true, |token| {
+ expansion::expand(cx.editor, token).map_err(|err| err.into())
+ }) {
+ Ok(args) => callback_fn(cx, args),
+ Err(err) => cx.editor.set_error(err.to_string()),
}
-
- shell(cx, input, &behavior);
},
);
}
+fn shell_prompt_for_behavior(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) {
+ shell_prompt(cx, prompt, move |cx, args| {
+ shell(cx, args.join(" ").as_str(), &behavior)
+ })
+}
+
fn suspend(_cx: &mut Context) {
#[cfg(not(windows))]
{
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 263d337a..d26b4705 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -29,15 +29,6 @@ pub struct TypableCommand {
pub signature: Signature,
}
-impl TypableCommand {
- fn completer_for_argument_number(&self, n: usize) -> &Completer {
- match self.completer.positional_args.get(n) {
- Some(completer) => completer,
- _ => &self.completer.var_args,
- }
- }
-}
-
#[derive(Clone)]
pub struct CommandCompleter {
// Arguments with specific completion methods based on their position.
@@ -68,6 +59,13 @@ impl CommandCompleter {
var_args: completer,
}
}
+
+ fn for_argument_number(&self, n: usize) -> &Completer {
+ match self.positional_args.get(n) {
+ Some(completer) => completer,
+ _ => &self.var_args,
+ }
+ }
}
fn quit(cx: &mut compositor::Context, _args: Args, event: PromptEvent) -> anyhow::Result<()> {
@@ -2658,13 +2656,13 @@ const BUFFER_CLOSE_OTHERS_SIGNATURE: Signature = Signature {
// but Signature does not yet allow for var args.
/// This command handles all of its input as-is with no quoting or flags.
-const SHELL_SIGNATURE: Signature = Signature {
+pub const SHELL_SIGNATURE: Signature = Signature {
positionals: (1, Some(2)),
raw_after: Some(1),
..Signature::DEFAULT
};
-const SHELL_COMPLETER: CommandCompleter = CommandCompleter::positional(&[
+pub const SHELL_COMPLETER: CommandCompleter = CommandCompleter::positional(&[
// Command name
completers::program,
// Shell argument(s)
@@ -3831,14 +3829,15 @@ fn complete_command_line(editor: &Editor, input: &str) -> Vec<ui::prompt::Comple
.get(command)
.map_or_else(Vec::new, |cmd| {
let args_offset = command.len() + 1;
- complete_command_args(editor, cmd, rest, args_offset)
+ complete_command_args(editor, cmd.signature, &cmd.completer, rest, args_offset)
})
}
}
-fn complete_command_args(
+pub fn complete_command_args(
editor: &Editor,
- command: &TypableCommand,
+ signature: Signature,
+ completer: &CommandCompleter,
input: &str,
offset: usize,
) -> Vec<ui::prompt::Completion> {
@@ -3850,7 +3849,7 @@ fn complete_command_args(
let cursor = input.len();
let prefix = &input[..cursor];
let mut tokenizer = Tokenizer::new(prefix, false);
- let mut args = Args::new(command.signature, false);
+ let mut args = Args::new(signature, false);
let mut final_token = None;
let mut is_last_token = true;
@@ -3894,7 +3893,7 @@ fn complete_command_args(
.len()
.checked_sub(1)
.expect("completion state to be positional");
- let completer = command.completer_for_argument_number(n);
+ let completer = completer.for_argument_number(n);
completer(editor, &token.content)
.into_iter()
@@ -3903,7 +3902,7 @@ fn complete_command_args(
}
CompletionState::Flag(_) => fuzzy_match(
token.content.trim_start_matches('-'),
- command.signature.flags.iter().map(|flag| flag.name),
+ signature.flags.iter().map(|flag| flag.name),
false,
)
.into_iter()
@@ -3928,7 +3927,7 @@ fn complete_command_args(
.len()
.checked_sub(1)
.expect("completion state to be positional");
- command.completer_for_argument_number(n)
+ completer.for_argument_number(n)
});
complete_expand(editor, &token, arg_completer, offset + token.content_start)
}