aboutsummaryrefslogtreecommitdiff
path: root/helix-term
diff options
context:
space:
mode:
authorMichael Davis2022-11-17 01:00:48 +0000
committerGitHub2022-11-17 01:00:48 +0000
commitc6b83368b3b626cb63120b5ac684cc8b1b693172 (patch)
treef14721ce897e1e525e63c384b2fa46fbef141ae4 /helix-term
parentb474ee1843d5cb7cb5291bee4166490a223e5aac (diff)
Capture word parts while calculating shellwords (#4632)
This fixes an edge case for completing shellwords. With a file "a b.txt" in the current directory, the sequence `:open a\<tab>` will result in the prompt containing `:open aa\ b.txt`. This is because the length of the input which is trimmed when replacing with completion is calculated on the part of the input which is parsed by shellwords and then escaped (in a separate operation), which is lossy. In this case it loses the trailing backslash. The fix provided here refactors shellwords to track both the _words_ (shellwords with quotes and escapes resolved) and the _parts_ (chunks of the input which turned into each word, with separating whitespace removed). When calculating how much of the input to delete when replacing with the completion item, we now use the length of the last part. This also allows us to eliminate the duplicate work done in the `ends_with_whitespace` check.
Diffstat (limited to 'helix-term')
-rw-r--r--helix-term/src/commands/typed.rs25
1 files changed, 15 insertions, 10 deletions
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index c6810f05..4bbb2082 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -2253,7 +2253,10 @@ pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableComma
.collect()
});
+#[allow(clippy::unnecessary_unwrap)]
pub(super) fn command_mode(cx: &mut Context) {
+ use shellwords::Shellwords;
+
let mut prompt = Prompt::new(
":".into(),
Some(':'),
@@ -2261,10 +2264,10 @@ pub(super) fn command_mode(cx: &mut Context) {
static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
- let parts = shellwords::shellwords(input);
- let ends_with_whitespace = shellwords::ends_with_whitespace(input);
+ let shellwords = Shellwords::from(input);
+ let words = shellwords.words();
- if parts.is_empty() || (parts.len() == 1 && !ends_with_whitespace) {
+ if words.is_empty() || (words.len() == 1 && !shellwords.ends_with_whitespace()) {
// If the command has not been finished yet, complete commands.
let mut matches: Vec<_> = typed::TYPABLE_COMMAND_LIST
.iter()
@@ -2283,19 +2286,20 @@ pub(super) fn command_mode(cx: &mut Context) {
} else {
// Otherwise, use the command's completer and the last shellword
// as completion input.
- let part = if parts.len() == 1 {
- &Cow::Borrowed("")
+ let (part, part_len) = if words.len() == 1 || shellwords.ends_with_whitespace() {
+ (&Cow::Borrowed(""), 0)
} else {
- parts.last().unwrap()
+ (
+ words.last().unwrap(),
+ shellwords.parts().last().unwrap().len(),
+ )
};
if let Some(typed::TypableCommand {
completer: Some(completer),
..
- }) = typed::TYPABLE_COMMAND_MAP.get(&parts[0] as &str)
+ }) = typed::TYPABLE_COMMAND_MAP.get(&words[0] as &str)
{
- let part_len = shellwords::escape(part.clone()).len();
-
completer(editor, part)
.into_iter()
.map(|(range, file)| {
@@ -2328,7 +2332,8 @@ pub(super) fn command_mode(cx: &mut Context) {
// Handle typable commands
if let Some(cmd) = typed::TYPABLE_COMMAND_MAP.get(parts[0]) {
- let args = shellwords::shellwords(input);
+ let shellwords = Shellwords::from(input);
+ let args = shellwords.words();
if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
cx.editor.set_error(format!("{}", e));