Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Some possibly helpful zle widget functions.



I thought I'd share some of the zle widgets I've collected/written
over the years, if you don't know how to use one:
zle -N name-of-widget name_of_function
bindkey "^X^N" name-of-widget

#I like narrow-to-region for grabbing earlier things from history with
ctrl-r searching or just quickly getting a tabcompletion from a
different command, but the default widget makes it hard to see just
what region you're editing, so enclose the edited region with >>| and
|<<.
autoload -U narrow-to-region
function _narrow_to_region_marked()
{
  local right
  local left
  if ((MARK == 0)); then
    zle set-mark-command
  fi
  if ((MARK < CURSOR)); then
    left="$LBUFFER[0,$((MARK-CURSOR-1))]"
    right="$RBUFFER"
  else
    left="$LBUFFER"
    right="$BUFFER[$((MARK+1)),-1]"
  fi
  narrow-to-region -p "$left>>|" -P "|<<$right"
}

#this one just toggles having a space at the start of the line, useful
if you have setopt histignorespace and don't want to go to the trouble
of pressing ^x^x space/delete.
function _space_toggle()
{
  if [[ $BUFFER[1] != " " ]]; then
    BUFFER=" $BUFFER"
    (( CURSOR+=1 ))
  else
    (( CURSOR-=1 ))
    BUFFER=$BUFFER[2,-1]
  fi
}

#reverses the word under the cursor
function _reverse_word()
{
  modify-current-argument '${(j::)${(@Oa)${(s::)ARG}}}'
}

#transpose-chars transposes two characters and moves the cursor to the
right. In effect it "drags" a character to the right with it. Make a
widget that "pushes" the character to the left instead to complement.
function _reverse_transpose_chars () {
  zle .backward-char; zle .transpose-chars; zle .backward-char
}

#this function tries to mimic vim's ctrl-a and ctrl-x bindings for
increasing or decreasing a number.
function _increase_number() {
  integer pos NUMBER i first last prelength diff
  pos=$CURSOR
  # find numbers starting from the left of the cursor to the end of the line
  while [[ $BUFFER[$pos] != [[:digit:]] ]]; do
    (( pos++ ))
    (( $pos > $#BUFFER )) && return
  done

  # use the numeric argument and default to 1
  # negate if called as decrease-number
  NUMBER=${NUMERIC:-1}
  if [[ $WIDGET = decrease-number ]]; then
    (( NUMBER = 0 - $NUMBER ))
  fi

  # find the start of the number
  i=$pos
  while [[ $BUFFER[$i-1] = [[:digit:]] ]]; do
    (( i-- ))
  done
  first=$i

  # include one leading - if found
  if [[ $BUFFER[$first-1] = - ]]; then
    (( first-- ))
  fi

  # find the end of the number
  i=$pos
  while [[ $BUFFER[$i+1] = [[:digit:]] ]]; do
    (( i++ ))
  done
  last=$i

  # change the number and move cursor after it
  prelength=$#BUFFER
  (( BUFFER[$first,$last] += $NUMBER ))
  (( diff = $#BUFFER - $prelength ))
  (( CURSOR = last + diff ))
}

#useful when you pasted a url with lots of ^?& stuff in it
#takes a alt-number argument to select type of quoting.
function _quote_word()
{
  q=qqqq
  modify-current-argument '${('$q[1,${NUMERIC:-1}]')ARG}'
}

#useful when you want to put a url back in a browser perhaps, or just
get the clean name of a tabcompleted file to paste somewhere else.
function _unquote_word()
{
  modify-current-argument '${(Q)ARG}'
}

#change the quoting of a word from one type to another. If the word
contains spaces you can't use _unquote_word followed by _quote_word
since it won't be just one word by then.
function _quote_unquote_word()
{
  q=qqqq
  modify-current-argument '${('$q[1,${NUMERIC:-1}]')${(Q)ARG}}'
}

#this one is a bit crazy, i wrote it mostly for practice. obviously
only useful if you use git, but i've actually found myself using it a
couple of times.
autoload -U modify-current-argument
autoload -U split-shell-arguments
function _gitref()
{
  zle -R "Select one of s, S, t, a, c, v, q, Q, h (help)."
  read -k
  case $REPLY in
    (s)
      modify-current-argument '$(git-rev-parse --short='${NUMERIC:-4}'
${(Q)ARG} 2> /dev/null)'
    ;;
    (S)
      modify-current-argument '$(git-rev-parse ${(Q)ARG} 2> /dev/null)'
    ;;
    (t)
      modify-current-argument '$(git-describe --tags ${(Q)ARG} 2> /dev/null)'
    ;;
    (a)
      modify-current-argument '$(git-describe --all ${(Q)ARG} 2> /dev/null)'
    ;;
    (c)
      modify-current-argument '${(q)$(git-describe --contains
${(Q)ARG} 2> /dev/null)}'
    ;;
    (v)
      modify-current-argument '$(git-svn find-rev r$ARG 2> /dev/null
|| git-svn find-rev $ARG 2> /dev/null)'
    ;;
    (q)
      _quote_word
    ;;
    (Q)
      _unquote_word
    ;;
    (h)
      [[ $1 = help ]] && return
      local -a reply
      local word
      integer REPLY REPLY2
      split-shell-arguments
      #have to duplicate some of modify-current-argument to get the word
      #_under_ the cursor, not after.
      setopt localoptions noksharrays multibyte
      if (( REPLY > 1 )); then
        if (( REPLY & 1 )); then
          (( REPLY-- ))
        fi
      fi
      word=${(Q)reply[$REPLY]}
      zle -M \
"
s: convert word to sha1, takes numeric argument (default: 4)
($(git-rev-parse --short=${NUMERIC:-4} $word 2> /dev/null))
S: convert word to full length sha1 ($(git-rev-parse $word 2> /dev/null))
t: convert word to described tag ($(git-describe --tags $word 2> /dev/null))
a: convert word to described ref ($(git-describe --all $word 2> /dev/null))
c: convert word to contained tag (${(q)$(git-describe --contains $word
2> /dev/null)})
v: convert word with git-svn find-rev
q: quote word
Q: unquote word
h: this help message"
      _gitref help
    ;;
  esac
  zle -R -c
}

#finally a function that isn't a zle widget, takes a filename and lets
you edit it, then renames the old name to the new name. I think it
originally came from this list, but I made some modifications.
#I just realized _unquote_word from above is useful here, since if you
tabcomplete in the minibuffer here, the word will be quoted, so you
would have to remove the quoting manually otherwise.
function name() {
  [[ $#@ -eq 1 ]] || { echo Give exactly one argument ; return 1 }
  test -e "$1" || { echo No such file or directory: "$1" ; return 1 }
  newname=$1
  if vared -c -p 'rename to: ' newname &&
    [[ -n $newname && $newname != $1 ]]
  then
    command mv -i $1 $newname
  else
    echo Some error occured; return 1
  fi
}

#If you're lazy, here's my section to bind all these to keys:
zle -N _narrow_to_region_marked
bindkey "^X^I"    _narrow_to_region_marked
zle -N _space_toggle
bindkey "^[^@" _space_toggle
zle -N _gitref
bindkey "^X^G" _gitref
zle -N _quote_word
bindkey "^Xq" _quote_word
zle -N _unquote_word
bindkey "^XQ" _unquote_word
zle -N _quote_unquote_word
bindkey "^X^[q" _quote_unquote_word
zle -N _reverse_word
bindkey "^X^R" _reverse_word
zle -N _reverse_transpose_chars
bindkey "^[^T" _reverse_transpose_chars
zle -N increase-number _increase_number
bindkey "^X^N" increase-number
zle -N decrease-number _increase_number
bindkey "^X^D" decrease-number

-- 
Mikael Magnusson



Messages sorted by: Reverse Date, Date, Thread, Author