From 849f4068de9831fdaa635c2372dada9131fb5a39 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 5 Mar 2002 16:33:19 +0000 Subject: 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. --- ChangeLog | 10 +++ Doc/Zsh/contrib.yo | 14 ++++ Doc/Zsh/zle.yo | 31 +++++++++ Functions/Zle/copy-earlier-word | 19 ++++++ Src/Zle/zle_hist.c | 138 +++++++++++++++++++++++++++++++++------- 5 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 Functions/Zle/copy-earlier-word diff --git a/ChangeLog b/ChangeLog index d4cda0a26..3c75a655c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2002-03-05 Peter Stephenson + + * 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 * 16759: Src/builtin.c: from Eric Norum : diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 654fa699c..21997b7d8 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -524,6 +524,20 @@ example(zle -N insert-last-assignment smart-insert-last-word zstyle :insert-last-assignment match '[[:alpha:]][][[:alnum:]]#=*' 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() subsect(Styles) diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index c184f0a87..aee624eb7 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -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 history event prior to the one just used; numeric arguments can be used in 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) item(tt(vi-repeat-search) (unbound) (n) (unbound))( diff --git a/Functions/Zle/copy-earlier-word b/Functions/Zle/copy-earlier-word new file mode 100644 index 000000000..bbc8af35c --- /dev/null +++ b/Functions/Zle/copy-earlier-word @@ -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 diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 2b0f582ba..5bea98b26 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -410,46 +410,138 @@ endofhistory(char **args) int insertlastword(char **args) { - int n; + int n, nwords, histstep = -1, wordpos = 0, deleteword = 0; 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 int lasthist, lastpos; - int evhist = addhistnum(curhist, -1, HIST_FOREIGN), save; + static int lasthist, lastpos, lastlen; + int evhist, save; - if (lastinsert) { - int lastlen = ztrlen(lastinsert); - int pos = cs; + /* + * If we have at least one argument, the first is the history + * 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 && - lastlen == pos - lastpos && - memcmp(lastinsert, (char *)&line[lastpos], lastlen) == 0) { - evhist = addhistnum(lasthist, -1, HIST_FOREIGN); + if (lastinsert && lastlen && + lastpos <= cs && + lastlen == cs - lastpos && + 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; 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) - return 1; - if (zmult > 0) { - n = he->nwords - (zmult - 1); + if (wordpos) { + n = (wordpos > 0) ? wordpos : nwords + wordpos + 1; + } else if (zmult > 0) { + n = nwords - (zmult - 1); } else { 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; - 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; *t = '\0'; /* ignore trailing whitespace */ - lasthist = evhist; lastpos = cs; - lastinsert = ztrdup(s); + lastlen = t - s; + lastinsert = zalloc(t - s); + memcpy(lastinsert, s, lastlen); n = zmult; zmult = 1; doinsert(s); -- cgit 1.4.1