mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-04 10:41:11 +02:00
16767: Src/Zle/zle_hist.c, Doc/Zsh/zle.yo, Doc/Zsh/contrib.yo,
Functions/Zle/copy-earlier-word: Enhance insert-last-word to pick different lines from the history (including the current line) and different words from that line. Add copy-earlier-word as suggested by Dominik Vogt to copy words from either the current line, or (following an insert-last-word) a previous history line.
This commit is contained in:
parent
15630b234a
commit
849f4068de
5 changed files with 189 additions and 23 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
||||||
|
2002-03-05 Peter Stephenson <pws@csr.com>
|
||||||
|
|
||||||
|
* 16767: Src/Zle/zle_hist.c, Doc/Zsh/zle.yo, Doc/Zsh/contrib.yo,
|
||||||
|
Functions/Zle/copy-earlier-word: Enhance insert-last-word to
|
||||||
|
pick different lines from the history (including the current
|
||||||
|
line) and different words from that line. Add copy-earlier-word
|
||||||
|
as suggested by Dominik Vogt to copy words from either the
|
||||||
|
current line, or (following an insert-last-word) a previous
|
||||||
|
history line.
|
||||||
|
|
||||||
2002-03-04 Peter Stephenson <pws@csr.com>
|
2002-03-04 Peter Stephenson <pws@csr.com>
|
||||||
|
|
||||||
* 16759: Src/builtin.c: from Eric Norum <eric.norum@usask.ca>:
|
* 16759: Src/builtin.c: from Eric Norum <eric.norum@usask.ca>:
|
||||||
|
|
|
@ -524,6 +524,20 @@ example(zle -N insert-last-assignment smart-insert-last-word
|
||||||
zstyle :insert-last-assignment match '[[:alpha:]][][[:alnum:]]#=*'
|
zstyle :insert-last-assignment match '[[:alpha:]][][[:alnum:]]#=*'
|
||||||
bindkey '\e=' insert-last-assignment)
|
bindkey '\e=' insert-last-assignment)
|
||||||
)
|
)
|
||||||
|
findex(copy-earlier-word)
|
||||||
|
item(tt(copy-earlier-word))(
|
||||||
|
This widget works like a combination of tt(insert-last-word) and
|
||||||
|
tt(copy-prev-shell-word). Repeated invocations of the widget retrieve
|
||||||
|
earlier words on the relevant history line. With a numeric argument
|
||||||
|
var(N), insert the var(N)th word from the history line; var(N) may be
|
||||||
|
negative to count from the end of the line.
|
||||||
|
|
||||||
|
If tt(insert-last-word) has been used to retrieve the last word on a
|
||||||
|
previous history line, repeated invocations will replace that word with
|
||||||
|
earlier words from the same line.
|
||||||
|
|
||||||
|
Otherwise, the widget applies to words on the line currently being edited.
|
||||||
|
)
|
||||||
enditem()
|
enditem()
|
||||||
|
|
||||||
subsect(Styles)
|
subsect(Styles)
|
||||||
|
|
|
@ -941,6 +941,37 @@ left (zero inserts the previous command word). Repeating this command
|
||||||
replaces the word just inserted with the last word from the
|
replaces the word just inserted with the last word from the
|
||||||
history event prior to the one just used; numeric arguments can be used in
|
history event prior to the one just used; numeric arguments can be used in
|
||||||
the same way to pick a word from that event.
|
the same way to pick a word from that event.
|
||||||
|
|
||||||
|
When called from a shell function invoked from a user-defined widget, the
|
||||||
|
command can take one to three arguments. The first argument specifies a
|
||||||
|
history offset which applies to successive calls to this widget: if is -1,
|
||||||
|
the default behaviour is used, while if it is 1, successive calls will move
|
||||||
|
forwards through the history. The value 0 can be used to indicate that the
|
||||||
|
history line examined by the previous execution of the command will be
|
||||||
|
reexamined. Note that negative numbers should be preceeded with a
|
||||||
|
`tt(-)tt(-)' argument to avoid confusing them with options.
|
||||||
|
|
||||||
|
If two arguments are given, the second specifies the word on the command
|
||||||
|
line in normal array index notation (as a more natural alternative to the
|
||||||
|
prefix argument). Hence 1 is the first word, and -1 (the default) is the
|
||||||
|
last word.
|
||||||
|
|
||||||
|
If a third argument is given, its value is ignored, but it is used to
|
||||||
|
signify that the history offset is relative to the current history line,
|
||||||
|
rather than the one remembered after the previous invocations of
|
||||||
|
tt(insert-last-word).
|
||||||
|
|
||||||
|
For example, the default behaviour of the command corresponds to
|
||||||
|
|
||||||
|
example(zle insert-last-word -- -1 -1)
|
||||||
|
|
||||||
|
while the command
|
||||||
|
|
||||||
|
example(zle insert-last-word -- -1 1 -)
|
||||||
|
|
||||||
|
always copies the first word of the line in the history immediately before
|
||||||
|
the line being edited. This has the side effect that later invocations of
|
||||||
|
the widget will be relative to that line.
|
||||||
)
|
)
|
||||||
tindex(vi-repeat-search)
|
tindex(vi-repeat-search)
|
||||||
item(tt(vi-repeat-search) (unbound) (n) (unbound))(
|
item(tt(vi-repeat-search) (unbound) (n) (unbound))(
|
||||||
|
|
19
Functions/Zle/copy-earlier-word
Normal file
19
Functions/Zle/copy-earlier-word
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Copy the word before the one you last copied --- call repeatedly
|
||||||
|
# to cycle through the list of words on the history line.
|
||||||
|
#
|
||||||
|
# Words in combination with insert-last-word to use the line reached,
|
||||||
|
# and start from the word before last. Otherwise, it will operate on
|
||||||
|
# the current line.
|
||||||
|
|
||||||
|
if (( ${NUMERIC:-0} )); then
|
||||||
|
# 1 means last word, 2 second last, etc.
|
||||||
|
(( __copyword = ${NUMERIC:-0} ))
|
||||||
|
elif [[ -n $__copyword && $WIDGET = $LASTWIDGET ]]; then
|
||||||
|
(( __copyword-- ))
|
||||||
|
elif [[ $LASTWIDGET = *insert-last-word ]]; then
|
||||||
|
__copyword=-2
|
||||||
|
else
|
||||||
|
__copyword=-1
|
||||||
|
fi
|
||||||
|
|
||||||
|
zle .insert-last-word 0 $__copyword
|
|
@ -410,46 +410,138 @@ endofhistory(char **args)
|
||||||
int
|
int
|
||||||
insertlastword(char **args)
|
insertlastword(char **args)
|
||||||
{
|
{
|
||||||
int n;
|
int n, nwords, histstep = -1, wordpos = 0, deleteword = 0;
|
||||||
char *s, *t;
|
char *s, *t;
|
||||||
Histent he;
|
Histent he = NULL;
|
||||||
|
LinkList l = NULL;
|
||||||
|
LinkNode node;
|
||||||
|
|
||||||
/* multiple calls will now search back through the history, pem */
|
|
||||||
static char *lastinsert;
|
static char *lastinsert;
|
||||||
static int lasthist, lastpos;
|
static int lasthist, lastpos, lastlen;
|
||||||
int evhist = addhistnum(curhist, -1, HIST_FOREIGN), save;
|
int evhist, save;
|
||||||
|
|
||||||
if (lastinsert) {
|
/*
|
||||||
int lastlen = ztrlen(lastinsert);
|
* If we have at least one argument, the first is the history
|
||||||
int pos = cs;
|
* step. The default is -1 (go back). Repeated calls take
|
||||||
|
* a step in this direction. A value of 0 is allowed and doesn't
|
||||||
|
* move the line.
|
||||||
|
*
|
||||||
|
* If we have two arguments, the second is the position of
|
||||||
|
* the word to extract, 1..N. The default is to use the
|
||||||
|
* numeric argument, or the last word if that is not set.
|
||||||
|
*
|
||||||
|
* If we have three arguments, we reset the history pointer to
|
||||||
|
* the current history event before applying the history step.
|
||||||
|
*/
|
||||||
|
if (*args)
|
||||||
|
{
|
||||||
|
histstep = (int)zstrtol(*args, NULL, 10);
|
||||||
|
if (*++args)
|
||||||
|
{
|
||||||
|
wordpos = (int)zstrtol(*args, NULL, 10);
|
||||||
|
if (*++args)
|
||||||
|
lasthist = curhist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (lastpos <= pos &&
|
if (lastinsert && lastlen &&
|
||||||
lastlen == pos - lastpos &&
|
lastpos <= cs &&
|
||||||
memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) {
|
lastlen == cs - lastpos &&
|
||||||
evhist = addhistnum(lasthist, -1, HIST_FOREIGN);
|
memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0)
|
||||||
|
deleteword = 1;
|
||||||
|
else
|
||||||
|
lasthist = curhist;
|
||||||
|
evhist = histstep ? addhistnum(lasthist, histstep, HIST_FOREIGN) :
|
||||||
|
lasthist;
|
||||||
|
|
||||||
|
if (evhist == curhist) {
|
||||||
|
/*
|
||||||
|
* The line we are currently editing. If we are going to
|
||||||
|
* replace an existing word, delete the old one now to avoid
|
||||||
|
* confusion.
|
||||||
|
*/
|
||||||
|
if (deleteword) {
|
||||||
|
int pos = cs;
|
||||||
cs = lastpos;
|
cs = lastpos;
|
||||||
foredel(pos - cs);
|
foredel(pos - cs);
|
||||||
|
/*
|
||||||
|
* Mark that this has been deleted.
|
||||||
|
* For consistency with history lines, we really ought to
|
||||||
|
* insert it back if the current command later fails. But
|
||||||
|
* - we can't be bothered
|
||||||
|
* - the problem that this can screw up going to other
|
||||||
|
* lines in the history because we don't update
|
||||||
|
* the history line isn't really relevant
|
||||||
|
* - you can see what you're copying, dammit, so you
|
||||||
|
* shouldn't make errors.
|
||||||
|
* Of course, I could have implemented it in the time
|
||||||
|
* it took to say why I haven't.
|
||||||
|
*/
|
||||||
|
deleteword = 0;
|
||||||
}
|
}
|
||||||
zsfree(lastinsert);
|
/*
|
||||||
lastinsert = NULL;
|
* Can only happen fail if the line is empty, I hope.
|
||||||
|
* In that case, we don't need to worry about restoring
|
||||||
|
* a deleted word, because that can only have come
|
||||||
|
* from a non-empty line. I think.
|
||||||
|
*/
|
||||||
|
if (!(l = bufferwords(NULL, NULL, NULL)))
|
||||||
|
return 1;
|
||||||
|
nwords = countlinknodes(l);
|
||||||
|
} else {
|
||||||
|
/* Some stored line. */
|
||||||
|
if (!(he = quietgethist(evhist)) || !he->nwords)
|
||||||
|
return 1;
|
||||||
|
nwords = he->nwords;
|
||||||
}
|
}
|
||||||
if (!(he = quietgethist(evhist)) || !he->nwords)
|
if (wordpos) {
|
||||||
return 1;
|
n = (wordpos > 0) ? wordpos : nwords + wordpos + 1;
|
||||||
if (zmult > 0) {
|
} else if (zmult > 0) {
|
||||||
n = he->nwords - (zmult - 1);
|
n = nwords - (zmult - 1);
|
||||||
} else {
|
} else {
|
||||||
n = 1 - zmult;
|
n = 1 - zmult;
|
||||||
}
|
}
|
||||||
if (n < 1 || n > he->nwords)
|
if (n < 1 || n > nwords) {
|
||||||
|
/*
|
||||||
|
* We can't put in the requested word, but we did find the
|
||||||
|
* history entry, so we remember the position in the history
|
||||||
|
* list. This avoids getting stuck on a history line with
|
||||||
|
* fewer words than expected. The cursor location cs
|
||||||
|
* has not changed, and lastinsert is still valid.
|
||||||
|
*/
|
||||||
|
lasthist = evhist;
|
||||||
return 1;
|
return 1;
|
||||||
s = he->text + he->words[2*n-2];
|
}
|
||||||
t = he->text + he->words[2*n-1];
|
/*
|
||||||
|
* Only remove the old word from the command line if we have
|
||||||
|
* successfully found a new one to insert.
|
||||||
|
*/
|
||||||
|
if (deleteword > 0) {
|
||||||
|
int pos = cs;
|
||||||
|
cs = lastpos;
|
||||||
|
foredel(pos - cs);
|
||||||
|
}
|
||||||
|
if (lastinsert) {
|
||||||
|
zfree(lastinsert, lastlen);
|
||||||
|
lastinsert = NULL;
|
||||||
|
}
|
||||||
|
if (l) {
|
||||||
|
for (node = firstnode(l); --n; incnode(node))
|
||||||
|
;
|
||||||
|
s = (char *)getdata(node);
|
||||||
|
t = s + strlen(s);
|
||||||
|
} else {
|
||||||
|
s = he->text + he->words[2*n-2];
|
||||||
|
t = he->text + he->words[2*n-1];
|
||||||
|
}
|
||||||
|
|
||||||
save = *t;
|
save = *t;
|
||||||
*t = '\0'; /* ignore trailing whitespace */
|
*t = '\0'; /* ignore trailing whitespace */
|
||||||
|
|
||||||
lasthist = evhist;
|
lasthist = evhist;
|
||||||
lastpos = cs;
|
lastpos = cs;
|
||||||
lastinsert = ztrdup(s);
|
lastlen = t - s;
|
||||||
|
lastinsert = zalloc(t - s);
|
||||||
|
memcpy(lastinsert, s, lastlen);
|
||||||
n = zmult;
|
n = zmult;
|
||||||
zmult = 1;
|
zmult = 1;
|
||||||
doinsert(s);
|
doinsert(s);
|
||||||
|
|
Loading…
Reference in a new issue