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

Re: About insert-last-word and "command args | less"



On Oct 15, 11:14pm, Vincent Lefevre wrote:
} Subject: Re: About insert-last-word and "command args | less"
}
} On 2006-10-15 11:49:43 -0700, Bart Schaefer wrote:
} > Maybe what you really want here is not smart-insert-last-word, but a
} > variant of _history_complete_word that considers only the rightmost
} > "interesting" word of each command in the history?
} 
} Perhaps (not sure if smart-insert-last-word was supposed to do that).

Well, no, it really wasn't supposed to do that.  It was supposed to find 
an "interesting" word if there was one, and otherwise behave just like
insert-last-word.  I'm not sure the behavior of continuing to search up
through additional history lines is always wanted.
 
} Also, I've seen another bug in smart-insert-last-word: When I recall
} a previous history line, e.g. with up-line-or-history or with
} history-incremental-search-backward, then kill the last word and use
} insert-last-word, instead of getting the last word of the lastest
} history line, I get the last word of the history line that was
} entered just before the recalled history line.

Hrm.

This reveals two problems.  (1) The definition of the "current" history
line, where [builtin] insert-last-word begins, does not mean the line
currently displayed in the editor buffer.  (2) Parsing $BUFFER to find
words for insertion isn't safe, because BUFFER reflects edits made to
any history lines during the current zle session, but insert-last-word
looks at the original unedited lines.

I'm a bit annoyed by the remifications of this, namely that to emulate
insert-last-word it's necessary to load the zsh/parameter module and
pull lines out of the $history hash.

Here's a new copy of smart-insert-last-word (the diff is only 9 lines
shorter than the entire function) which attempts to address all of this.
It introduces a new zstyle "auto-previous" to control the behavior that
Vincent wants, and employs an "always" block and the side-effects of
assignment to HISTNO to handle invocations on history lines other than
the most recent.  It requires my auto-suffix-retain patch; comment out
that line if you want to try it with something less than the latest CVS
HEAD checkout.

If this looks OK, I'll commit it along with a documentation update.

--- 8< --- snip --- 8< ---
# smart-insert-last-word
# Inspired by Christoph Lange <langec@xxxxxx> from zsh-users/3265;
# rewritten to correct multiple-call behavior after zsh-users/3270;
# modified to work with copy-earlier-word after zsh-users/5832.
# Edited further per zsh-users/10881 and zsh-users/10884.
#
# This function as a ZLE widget can replace insert-last-word, like so:
#
#   zle -N insert-last-word smart-insert-last-word
#
# With a numeric prefix, behaves like insert-last-word, except that words
# in comments are ignored when interactive_comments is set.
#
# Otherwise, the rightmost "interesting" word from any previous command is
# found and inserted.  The default definition of "interesting" is that the
# word contains at least one alphabetic character, slash, or backslash.
# This definition can be overridden by use of a style like so:
#
#   zstyle :insert-last-word match '*[[:alpha:]/\\]*'
#
# For example, you might want to include words that contain spaces:
#
#   zstyle :insert-last-word match '*[[:alpha:][:space:]/\\]*'
#
# Or include numbers as long as the word is at least two characters long:
#
#   zstyle :insert-last-word match '*([[:digit:]]?|[[:alpha:]/\\])*'
#
# That causes redirections like "2>" to be included.
#
# Note also that the style is looked up based on the widget name, so you
# can bind this function to different widgets to use different patterns:
#
#   zle -N insert-last-assignment smart-insert-last-word
#   zstyle :insert-last-assignment match '[[:alpha:]][][[:alnum:]]#=*'
#   bindkey '\e=' insert-last-assignment
#
# The "auto-previous" style, if set to a true value, causes the search to
# proceed upward through the history until an interesting word is found.
# If auto-previous is unset or false and there is no interesting word, the
# last word is returned.

emulate -L zsh
setopt extendedglob nohistignoredups

# Begin by preserving completion suffix if any
zle auto-suffix-retain

# Not strictly necessary:
# (($+_ilw_hist)) || integer -g _ilw_hist _ilw_count _ilw_cursor _ilw_lcursor

integer cursor=$CURSOR lcursor=$CURSOR
local lastcmd pattern numeric=$NUMERIC

# Save state for repeated calls
if (( HISTNO == _ilw_hist && cursor == _ilw_cursor )); then
    NUMERIC=$[_ilw_count+1]
    lcursor=$_ilw_lcursor
else
    NUMERIC=1
    _ilw_lcursor=$lcursor
fi
# Handle the up to three arguments of .insert-last-word
if (( $+1 )); then
    if (( $+3 )); then
	((NUMERIC = -($1)))
    else
	((NUMERIC = _ilw_count - $1))
    fi
    (( NUMERIC )) || LBUFFER[lcursor+1,cursor+1]=''
    numeric=$((-(${2:--numeric})))
fi
_ilw_hist=$HISTNO
_ilw_count=$NUMERIC

if [[ -z "$numeric" ]]
then
    zstyle -s :$WIDGET match pattern ||	pattern='*[[:alpha:]/\\]*'
fi

# Note that we must use .up-history for navigation here because of
# possible "holes" in the $history hash (the result of dup expiry).
# We need $history because $BUFFER retains edits in progress as the
# user moves around the history, but we search the unedited lines.

{
  zmodload -i zsh/parameter
  zle .end-of-history              # Start from final command
  zle .up-history || return 1      # Retrieve previous command
  local buffer=$history[$HISTNO]   # Get unedited history line
  lastcmd=( ${${(z)buffer}:#\;} )  # Split into shell words
  if [[ -n "$pattern" ]]
  then
      # This is the "smart" part -- search right-to-left and
      # latest-to-earliest through the history for a word.
      integer n=0 found=$lastcmd[(I)$pattern]
      if zstyle -t :$WIDGET auto-previous
      then
          while (( found == 0 && ++n ))
          do
              zle .up-history || return 1
              buffer=$history[$HISTNO]
              lastcmd=( ${${(z)buffer}:#\;} )
              found=$lastcmd[(I)$pattern]
          done
      fi
      (( found-- > 0 &&            # Account for 1-based index
        (numeric = $#lastcmd - found) ))
  fi
} always {
  HISTNO=$_ilw_hist                # Return to current command
  CURSOR=$cursor                   # Restore cursor position
  NUMERIC=${numeric:-1}            # In case of fall-through
}

(( NUMERIC > $#lastcmd )) && return 1

LBUFFER[lcursor+1,cursor+1]=$lastcmd[-NUMERIC]
_ilw_cursor=$CURSOR



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