7

I have a common situation in the shell when a command is attempted

command "/some/complex/and terrible/path" -vf --various --flags --and -- things --config-file="/some other/annoyingly/large/path/to/somewhere"

and it turns out that I'm mostly there but need to move some stuff around. Suppose I have to move the last argument all the way to the front to attempt this next.

command --config-file="/some other/annoyingly/large/path/to/somewhere" "/some/complex/and terrible/path" -vf --various --flags --and -- things

Trying to take a keystroke golf look at this, it's a dire situation. This is actually a nightmare shell command entry scenario where you kind of have to re-type (or copy&paste) almost half the command. The best hope of sanity might be to use an external editor [like this].

Even still, Vim isn't smart enough to do arg parsing (so there is no quick way to slurp out the last arg there using e.g. daW, though it should be possible to complete the job within 8 keystrokes or so given the power of vim).

I think that this can be a decent workaround, because I do bring a lot of software to bear on editing text in vim since I use it to do all of my coding.

But what I want is if Zsh (or even Bash!) line editor could be programmed somehow to let me shove an arg around. Kind of like this: https://github.com/AndrewRadev/sideways.vim But of course, this plugin won't work on space delimited shell arguments either. And it requires vim.

I'd be happy with either a directional hop (so that I can put my cursor somewhere on the --config-file arg and then mash HopLeft to hop the arg to the left) or a yank & paste (so i can put my cursor somewhere on the --config-file arg, yank it out, hit home to go to the front and paste it after it).

This way we can rapidly recompose shell arguments by navigating the args as a unit rather than navigating individual characters as a unit. It would speed up general command editing a lot. I care about this a lot because I do an unusually large amount of coding directly in the shell.

If we are to take the idea further this would be implemented as an editor mode, let's call it Argument Edit Mode, where the cursor moves across args instead of characters and a more developed set of actions can be performed on args as a unit.

Let's come back down to earth now. The question is this: can I programmatically cause zsh to delete the shell arg that i'm currently on? And then paste it back in? Can I bind a key to let me hop based on actual args instead of just hopping words?

There doesn't seem to be ready-to-use binds for use with bindkey, but it feels like there might be a way to do it if i can programmatically control the position of the cursor. I do use a plugin that can live syntax highlight my command after all. https://github.com/zsh-users/zsh-syntax-highlighting

6
  • Not quite what you're asking for, but I tend to take a word-designated command history approach to these situations. While a 'word' in ZLE is (based on my usage w/ vim keybindings, at least) simplistic and jumps between spaces, word designations in history expansion understand quotes. Your example of 'run the last command, but as command --last-arg --all-other-args' can be typed as !!:0 !!:$ !!:1-, and tab-completion will fill it in so you can verify what you're about to execute (and/or tweak with ZLE).
    – brhfl
    Jan 16, 2018 at 21:14
  • Yeah! That's a feature that definitely is arg-aware that i hadn't thought about leveraging, but the issues therein are related to: the mental overload of !!:1- (mostly remembering what 1- means and how it works) and the fact that once I tab-complete something like !!:-3 I can't go back and adjust it to be !!:-2 without back-deleting it and typing it out again. Stuff like that. It is a workable thing to do, yes. But It's just not quite there from a user experience perspective.
    – Steven Lu
    Jan 16, 2018 at 21:37
  • Here is another thing that is common that is extremely impractical to use, I want to grab the third-from-last arg from the last command I ran. !!:-3 doesnt provide me that, it actually provides me the command and first 3 args. I am having a hard time finding docs that go over this stuff and I do not know if it is possible to fetch an nth-from-last arg. At the end of the day, the point is that my brain works by pointing a cursor at an arg to refer to it, not by scanning the entire command to count which arg i'm looking at to type it in.
    – Steven Lu
    Jan 16, 2018 at 21:43
  • Understood, that's why I didn't consider offering it as an actual answer, though I think it's worth keeping in mind as a different approach to the problem. I agree that !!:-3 reads as though it should be third-from-last; instead that dash means 'do a range' and with no beginning index, 0 is implied. The ZLE command undo will, in fact, revert tab-completion. I think this is bound to ctrl-_ in emacs bindings; I'm unsure what the vi command binding u points to since it sure doesn't work.
    – brhfl
    Jan 16, 2018 at 22:00
  • Note to self: Research the ZLE undo functionality a little bit, this will be very very useful. At the moment I use ctrl+_ to horizontally split a pane for tmux so it will need to be rebound
    – Steven Lu
    Nov 4, 2021 at 18:38

2 Answers 2

5

You may be looking for the select-word-style framework and in particular its shell word style. See

info zsh select-word-style

For instance, add to your ~/.zshrc:

autoload -U select-word-style
zle -N select-word-style
bindkey '\ez' select-word-style

Then (assuming you use the emacs mode)

  • press Alt+Z to invoke select-word-style
  • you'll get a menu where you can select the word style. Press S for the shell word style.
  • then Ctrl+W will delete the previous word as a full argument (a shell word).
  • Ctrl+A or Home to go to the beginning of the line
  • Alt+F to move one word to the right
  • Ctrl+Y to yank the previously killed word.
  • Alt+ZN if you want to go back to the normal word style.
12
  • I’m pretty sure that this isn’t what I’m looking for per se, but this is definitely a step in the right direction, and a feature I did not know about. It does seem like maybe if this could be scripted then I could implement what I want on top of it...
    – Steven Lu
    Jan 19, 2018 at 15:38
  • This does not seem to work. The Alt+z bind appears to produce a menu that lets me choose stuff, but the ctrl+w works the same exact way whichever choice is made. zsh 5.3.1
    – Steven Lu
    Jan 19, 2018 at 23:26
  • I can not find any documentation on select-word-style.
    – Steven Lu
    Jan 19, 2018 at 23:32
  • I think the reason this doesn't work is that prezto (which I use my own fork of) sets $WORDCHARS to mimic bash's behavior, thus making the normal mode work just like bash mode for this.
    – Steven Lu
    Jan 20, 2018 at 0:05
  • If you know the documentation of this stuff, please do post it. Does "shell" mode set a different $WORDCHARS?
    – Steven Lu
    Jan 20, 2018 at 0:08
3

The question is this: can I programmatically cause zsh to delete the shell arg that i'm currently on? And then paste it back in? Can I bind a key to let me hop based on actual args instead of just hopping words?

Yes, you can. Just use my Zsh Edit plugin. It includes keybindings for skipping and cutting entire shell words.

5
  • Thank you for posting! Very good timing as I'm about to revisit this topic while I make a second effort to properly set up my zsh prompt; I expect this next effort will be able to last me for the next 2 decades. This looks really neat and promising. Thanks for making it so I don't have to!
    – Steven Lu
    Nov 3, 2021 at 0:09
  • 1
    Here's my entire zsh config, if you're interested: github.com/marlonrichert/.config/tree/main/zsh Nov 4, 2021 at 8:49
  • It's not clear to me what those two "main keymap" columns represent. Or which of those bindings deletes the word I'm on (which I interpret as meaning something like vim's diw / daw / diW / daW). Apr 28, 2022 at 7:21
  • The main keymap is what this always means in zsh: whichever keymap you’ve aliased to main. That’s emacs by default, but it can be whichever keymap you want. That’s where these keybindings will be added when you source the plugin. Apr 29, 2022 at 18:15
  • It does not have any bindings for deleting the word you are on. That operation is rarely used outside of vi or vim. Heck, I don’t even know what “vim's diw / daw / diW / daW” does. :) Apr 29, 2022 at 18:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .