zsh-users
 help / color / mirror / code / Atom feed
* Rotate shell words widget
@ 2016-05-11  8:39 Sebastian Gniazdowski
  2016-05-11 21:21 ` Bart Schaefer
  0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Gniazdowski @ 2016-05-11  8:39 UTC (permalink / raw)
  To: Zsh Users

[-- Attachment #1: Type: text/plain, Size: 636 bytes --]

Hello,
I'm sharing my widget that rotates shell words. Thus:

$(( 1 )) $( 2 ) 3

becomes

3 $(( 1 )) $( 2 )

https://asciinema.org/a/7igqc3pmyhunbqcxf09lqs1ju

To use copy the two attached files to *functions directory and add to zshrc:

# Alt-r to rotate shell words right, Alt-R - left
autoload zew-rotate-shell-words
zle -N zew-rotate-shell-words
zle -N zew-rotate-shell-words-backwards zew-rotate-shell-words
bindkey '^[r' zew-rotate-shell-words
bindkey '^[R' zew-rotate-shell-words-backwards

The widget is part of Zsh Editing Workbench plugin:
https://github.com/psprint/zsh-editing-workbench

Best regards,
Sebastian Gniazdowski

[-- Attachment #2: zew-rotate-shell-words --]
[-- Type: application/octet-stream, Size: 1368 bytes --]

# Transpose shell-words, i.e. parts of lines obtained by (Z) flag, i.e.
# as if zsh parsed the line.
#
# Code to activate the functionality with binding to Alt-t:
# autoload zew-rotate-shell-words
# zle -N zew-rotate-shell-words
# zle -N zew-rotate-shell-words-backwards zew-rotate-shell-words
# bindkey '^[r' zew-rotate-shell-words
# bindkey '^[R' zew-rotate-shell-words-backwards

local curcontext=":zle:$WIDGET"
local MATCH MBEGIN MEND i

# Prepare output variables for zew-process-buffer
local ZEW_PB_WORDS ZEW_PB_WORDS_BEGINNINGS ZEW_PB_SPACES
local ZEW_PB_SELECTED_WORD ZEW_PB_LEFT ZEW_PB_RIGHT

autoload zew-process-buffer
zew-process-buffer "$BUFFER"

# No active shell word found (shouldn't happen) (-1)
# or it's the first shell word (1), or word before first
# shell word (0)? Return
[ "$ZEW_PB_SELECTED_WORD" -le 1 ] && return 0

# Rotate
if [[ "$WIDGET" != *-backwards ]]; then 
    ZEW_PB_WORDS=( "${ZEW_PB_WORDS[-1]}" "${(@)ZEW_PB_WORDS[1,-2]}" )
else
    ZEW_PB_WORDS=( "${(@)ZEW_PB_WORDS[2,-1]}" "${ZEW_PB_WORDS[1]}" )
fi

# Build BUFFER
integer size="${#ZEW_PB_WORDS}"
integer newcursor
buf=""

for (( i=1; i<=size; i++ )); do
    buf+="$ZEW_PB_SPACES[i]$ZEW_PB_WORDS[i]"
    [ "$i" = "$ZEW_PB_SELECTED_WORD" ] && newcursor="$#buf"
done

# Append final white spaces
buf+="$ZEW_PB_SPACES[i]"

BUFFER="$buf"
CURSOR="$newcursor"

return 0

# vim:ft=zsh

[-- Attachment #3: zew-process-buffer --]
[-- Type: application/octet-stream, Size: 3350 bytes --]

# Input:
# $1 - optional buffer to process (default is $BUFFER)
# $2 - optional parameter containing cursor (default is $CURSOR)
#
# Output:
# ZEW_PB_WORDS - split of "$1" into shell words; array
# ZEW_PB_WORDS_BEGINNINGS - indexes of first letters of corresponding words in ZEW_PB_WORDS
# ZEW_PB_SPACES - white spaces before corresponding words in ZEW_PB_WORDS
# ZEW_PB_SELECTED_WORD - index in ZEW_PB_WORDS pointing to word activated by cursor position
# ZEW_PB_LEFT - left part of active word
# ZEW_PB_RIGHT - right part of active word
#

emulate -LR zsh
setopt typesetsilent extendedglob noshortloops

local MBEGIN MEND MATCH mbegin mend match

local buf="${1:-$BUFFER}"
local cursor="${2:-$CURSOR}"

ZEW_PB_WORDS=( "${(Z+n+)buf}" )
ZEW_PB_SPACES=( )
ZEW_PB_WORDS_BEGINNINGS=( )
ZEW_PB_SELECTED_WORD="-1"

# (Z+n+) will return 1 element for buf that is empty or only whitespace
if [[ "$buf" = ( |$'\t')# ]]; then
    ZEW_PB_WORDS=( )
    integer nwords=0
else
    integer nwords="${#ZEW_PB_WORDS}"
fi

# Remove ZEW_PB_WORDS one by one, counting characters,
# computing beginning of each word, to find
# place to break the word into 2 halves (for
# complete_in_word option)

local i word
integer char_count=0

# (Z) handles spaces nicely, but we need them for the user
# Also compute words beginnings and the selected word
for (( i=1; i<=nwords; i++ )); do
    # Remove spurious space generated by Z-flag when
    # input is an unbound '$(' (happens with zsh < 5.1)
    # and also real spaces gathered by an unbound '$(',
    # to handle them in a way normal to this loop
    ZEW_PB_WORDS[i]="${ZEW_PB_WORDS[i]%% ##}"
    word="${ZEW_PB_WORDS[i]}"

    # In general, $buf can start with white spaces
    # We will not search for them, but instead for
    # leading character of current shell word,
    # negated. This is an ambition to completely
    # avoid character classes

    # Remove white spaces
    buf="${buf##(#m)[^$word[1]]#}"
    # Count them
    char_count=char_count+"$#MATCH"
    # This is the beginning of current word
    ZEW_PB_WORDS_BEGINNINGS[i]=$(( char_count + 1 ))
    # Remember the spaces
    ZEW_PB_SPACES[i]="$MATCH"

    # Remove the word
    MATCH=""
    buf="${buf#(#m)$word}"

    # If shell word not found, return. This shoudln't happen
    [ -z "$MATCH" ] && return 0

    # Spaces point to previous shell word
    # Visual cursor right after spaces (-ge) -> not enough to select previous word (-gt required)
    [[ "$ZEW_PB_SELECTED_WORD" -eq "-1" && "$char_count" -gt "$cursor" ]] && ZEW_PB_SELECTED_WORD=$(( i-1 ))

    # Actual characters point to current shell word
    # Visual cursor right after letters (-ge) -> enough to select current word
    char_count=char_count+"$#word"
    [[ "$ZEW_PB_SELECTED_WORD" -eq "-1" && "$char_count" -ge "$cursor" ]] && ZEW_PB_SELECTED_WORD="$i"
done 

# What's left in $buf can be only white spaces
char_count=char_count+"$#buf"
ZEW_PB_SPACES[i]="$buf"

# Visual cursor right after spaces (-ge) -> enough to select last word
[[ "$ZEW_PB_SELECTED_WORD" -eq "-1" && "$char_count" -ge "$cursor" ]] && ZEW_PB_SELECTED_WORD=$(( i-1 ))

# Divide active word into two halves
integer diff=$(( cursor - ZEW_PB_WORDS_BEGINNINGS[ZEW_PB_SELECTED_WORD] + 1 ))
word="${ZEW_PB_WORDS[ZEW_PB_SELECTED_WORD]}"
ZEW_PB_LEFT="${word[1,diff]}"
ZEW_PB_RIGHT="${word[diff+1,-1]}"

# vim:ft=zsh

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rotate shell words widget
  2016-05-11  8:39 Rotate shell words widget Sebastian Gniazdowski
@ 2016-05-11 21:21 ` Bart Schaefer
  2016-05-12  7:07   ` Sebastian Gniazdowski
  0 siblings, 1 reply; 7+ messages in thread
From: Bart Schaefer @ 2016-05-11 21:21 UTC (permalink / raw)
  To: Zsh Users

On May 11, 10:39am, Sebastian Gniazdowski wrote:
}
} $(( 1 )) $( 2 ) 3
} 
} becomes
} 
} 3 $(( 1 )) $( 2 )

What common usage did you have in mind for this?

I presume rotate acts on $RBUFFER and rotate-backwards works on $LBUFFER
(approximately).

Might want to have $NUMERIC mean something, e.g. <ESC 2 ESC r> rotates
the 2nd word after the cursor, <ESC - ESC 2 ESC r> rotates the 2nd
word counting back from the end of the line; invert for <ESC 2 ESC R>.

Or not.  Whether that's useful depends on why you want this in the first
place (hence my first question).


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rotate shell words widget
  2016-05-11 21:21 ` Bart Schaefer
@ 2016-05-12  7:07   ` Sebastian Gniazdowski
  2016-05-13  9:23     ` Bart Schaefer
  0 siblings, 1 reply; 7+ messages in thread
From: Sebastian Gniazdowski @ 2016-05-12  7:07 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

On 11 May 2016 at 23:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
> What common usage did you have in mind for this?
>
> I presume rotate acts on $RBUFFER and rotate-backwards works on $LBUFFER
> (approximately).

I wasn't enough specific again? It rotates words in command line not
letters in words. The usage is then I think clear, to go to some
previous word, edit it, and put it in its original place again.
Alternative to Alt-b in bindkey -e.

Too relying on established word set like "shell word". Some users
might not be aware that this will allow them to rotate sophisticated
command lines with words like a\ b\ c.

Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rotate shell words widget
  2016-05-12  7:07   ` Sebastian Gniazdowski
@ 2016-05-13  9:23     ` Bart Schaefer
  2016-05-14 21:30       ` Sebastian Gniazdowski
  0 siblings, 1 reply; 7+ messages in thread
From: Bart Schaefer @ 2016-05-13  9:23 UTC (permalink / raw)
  To: Sebastian Gniazdowski; +Cc: Zsh Users

On Thu, May 12, 2016 at 12:07 AM, Sebastian Gniazdowski
<sgniazdowski@gmail.com> wrote:
> On 11 May 2016 at 23:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
>> What common usage did you have in mind for this?
>>
>> I presume rotate acts on $RBUFFER and rotate-backwards works on $LBUFFER
>> (approximately).
>
> I wasn't enough specific again? It rotates words in command line not
> letters in words.

No, you were quite clear that it acted on words, that wasn't my
question.  I used RBUFFER and LBUFFER as shorthand for "words found
after the cursor" and "words found before the cursor."  Now that
you've said this ...

> The usage is then I think clear, to go to some
> previous word, edit it, and put it in its original place again.

... I see that the difference is whether you rotate the last word to
the front or the first word to the end.  But why would you rotate the
words in order to edit one, rather than just move the cursor to the
word you want to change?

Also it looks like this always rotates all the words in the buffer, so
e.g. the last word moves to command position no matter what word the
cursor is on.  If the reason is to be able to edit a different word,
wouldn't it make more sense to move the word at the end to the cursor
(or the word at the cursor to the end)?

> Alternative to Alt-b in bindkey -e.

I don't follow that at all.  How is rotating words an alternative to
moving backward by one word?


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rotate shell words widget
  2016-05-13  9:23     ` Bart Schaefer
@ 2016-05-14 21:30       ` Sebastian Gniazdowski
  2016-05-14 22:41         ` René Wilhelm
  2016-05-15 10:49         ` Bart Schaefer
  0 siblings, 2 replies; 7+ messages in thread
From: Sebastian Gniazdowski @ 2016-05-14 21:30 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

On 13 May 2016 at 11:23, Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Thu, May 12, 2016 at 12:07 AM, Sebastian Gniazdowski
> <sgniazdowski@gmail.com> wrote:
>> On 11 May 2016 at 23:21, Bart Schaefer <schaefer@brasslantern.com> wrote:
>> The usage is then I think clear, to go to some
>> previous word, edit it, and put it in its original place again.
>
> ... I see that the difference is whether you rotate the last word to
> the front or the first word to the end.  But why would you rotate the
> words in order to edit one, rather than just move the cursor to the
> word you want to change?

I thought about it as an extension of transpose-words, which I use
instead of Alt-b pressed once. Now I see transpose-words can be used
to e.g. revert "mv" command by doing opposite rename (not that I
didn't use it that way). Well, I always preferred transposing than
Alt-b-jumping and cursor keys. Careful positioning with Alt-b and
cursor keys, and then Ctrl-E to continue at the end, I don't like it.

> Also it looks like this always rotates all the words in the buffer, so
> e.g. the last word moves to command position no matter what word the
> cursor is on.  If the reason is to be able to edit a different word,
> wouldn't it make more sense to move the word at the end to the cursor
> (or the word at the cursor to the end)?

I'm not fully following this, maybe an example? Seems sophisticated, though.

Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rotate shell words widget
  2016-05-14 21:30       ` Sebastian Gniazdowski
@ 2016-05-14 22:41         ` René Wilhelm
  2016-05-15 10:49         ` Bart Schaefer
  1 sibling, 0 replies; 7+ messages in thread
From: René Wilhelm @ 2016-05-14 22:41 UTC (permalink / raw)
  To: Sebastian Gniazdowski; +Cc: Bart Schaefer, Zsh Users

[-- Attachment #1: Type: text/plain, Size: 786 bytes --]

On 14 May 2016 at 23:30, Sebastian Gniazdowski <sgniazdowski@gmail.com>
wrote:

> I thought about it as an extension of transpose-words, which I use
> instead of Alt-b pressed once. Now I see transpose-words can be used
> to e.g. revert "mv" command by doing opposite rename (not that I
> didn't use it that way). Well, I always preferred transposing than
> Alt-b-jumping and cursor keys. Careful positioning with Alt-b and
> cursor keys, and then Ctrl-E to continue at the end, I don't like it.
>


​You can prefix most things with a number, like <ESC 2​ ESC b> to move the
cursor two words backwards. In conjunction with a little intuitive counting
(https://en.wikipedia.org/wiki/Subitizing), this should allow for pretty
good cursor positioning. Hope that helps.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Rotate shell words widget
  2016-05-14 21:30       ` Sebastian Gniazdowski
  2016-05-14 22:41         ` René Wilhelm
@ 2016-05-15 10:49         ` Bart Schaefer
  1 sibling, 0 replies; 7+ messages in thread
From: Bart Schaefer @ 2016-05-15 10:49 UTC (permalink / raw)
  To: Sebastian Gniazdowski; +Cc: Zsh Users

On Sat, May 14, 2016 at 2:30 PM, Sebastian Gniazdowski
<sgniazdowski@gmail.com> wrote:
> On 13 May 2016 at 11:23, Bart Schaefer <schaefer@brasslantern.com> wrote:
>> Also it looks like this always rotates all the words in the buffer, so
>> e.g. the last word moves to command position no matter what word the
>> cursor is on.  If the reason is to be able to edit a different word,
>> wouldn't it make more sense to move the word at the end to the cursor
>> (or the word at the cursor to the end)?
>
> I'm not fully following this, maybe an example? Seems sophisticated, though.

Suppose I have:

% echo one two three four

and the cursor is on the "w".  With your widget as-is, rotating "forward" gives

% four echo one two three

with the cursor on the space between "one" and "two".  I don't
understand why someone would ever want to do that.  It doesn't make
sense for "four" to become the command word, and the final cursor
placement isn't helpful.  (Also the choice of calling this "forward"
rotation is a bit arbitrary, because the most obvious change is that
"four" moved *backward*, it's the rest of the line that went forward;
but you can define it either way.)  If instead rotate forward produced

% echo one four two three

with the cursor either still on the "w" or somewhere in the word
"four", that might have some utility.  (This is what I meant by "acts
on $RBUFFER").  Similarly I'd expect rotate backward to do one of

% echo one three four two

OR ("acts on $LBUFFER")

% two echo one three four

though again I don't know why you'd shift a different word into
command position.  Maybe "rotate-arguments" would be a better widget,
something that would produce

% echo four one two three

i.e. without moving the "echo".

And in that case ESC 2 ESC r would produce

% echo three four one two

Cursor placement still needs some thought.


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-05-15 10:49 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-11  8:39 Rotate shell words widget Sebastian Gniazdowski
2016-05-11 21:21 ` Bart Schaefer
2016-05-12  7:07   ` Sebastian Gniazdowski
2016-05-13  9:23     ` Bart Schaefer
2016-05-14 21:30       ` Sebastian Gniazdowski
2016-05-14 22:41         ` René Wilhelm
2016-05-15 10:49         ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).