diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2015-09-21 06:07:46 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2015-09-21 06:07:46 -0700 |
commit | 1de86c685ff9a4a36014188c8ef24601d704157c (patch) | |
tree | 177f5a31c640c13f0466b4cb68b868dc5dee1b30 /linenoise | |
parent | bf953d07cf4caa822677b64cae5e8fc444bcdfe2 (diff) | |
download | txr-1de86c685ff9a4a36014188c8ef24601d704157c.tar.gz txr-1de86c685ff9a4a36014188c8ef24601d704157c.tar.bz2 txr-1de86c685ff9a4a36014188c8ef24601d704157c.zip |
linenoise: much more sane, per-history-item undo.
Undo now works intuitively. It does not perform invisible
jumps among history items, restoring instead just the history
for the given line being edited.
* linenoise/linenoise.c (LINENOISE_MAX_UNDO): Raised from 32
to a more generous 200. Since edits are per-history line,
it makes sense to allow a lot more.
(delete_undo): New static function.
(free_undo): Static function removed to free_undo_stack.
Trivial loop around delete_undo.
(record_undo): Do not record the current history index; all
edits are assigned the index INT_MAX. INT_MAX is an indicator
that the edits do not have an assigned history line. The
decision of where to assign them depends on whether history
navigation is used to move to another history line or
Enter is used to submit an edited line.
A stinky part of the history trimming code is rewritten
simply in terms of delete_undo.
(record_triv_undo): Suppress a trivial item only if
the top item belongs to the same history line, or is
nonspecific (INT_MAX).
(undo_pop): Static function removed.
(restore_undo): Rewritten to look for the topmost item
specific to the current history line or an INT_MAX nonspecific
item. Removes undo items for expired lines as it goes.
(undo_subst_hist_idx): New static function.
(renumber_undo_hist): Renamed to undo_renumber_hist_idx.
(edit_history_next): Do not record an undo; history navigation
is no longer considered an edit. Rewrite all the INT_MAX
entries in the undo stack with the current history index,
permanently associating the undo items with the history line
away from which we are navigating.
(edit): Do not record an undo for a line terminating with
Enter. It is not an edit action. When leaving the funtion,
renumber any INT_MAX entries in the undo history to history
index zero. Thus edits to any line which is submitted via
Enter will (correctly) not be associated with that line,
which was not in fact edited, but with the new line that was
submitted.
(lino_cleanup): Follow rename of free_undo.
(lino_hist_add): Follow rename of undo_renumber_hist_idx.
* txr.1: Documented.
Diffstat (limited to 'linenoise')
-rw-r--r-- | linenoise/linenoise.c | 117 |
1 files changed, 60 insertions, 57 deletions
diff --git a/linenoise/linenoise.c b/linenoise/linenoise.c index 70ccc490..d0e3db1d 100644 --- a/linenoise/linenoise.c +++ b/linenoise/linenoise.c @@ -55,6 +55,8 @@ #include <unistd.h> #include <signal.h> #include <time.h> +#include <limits.h> +#include <assert.h> #include "config.h" #if HAVE_POLL #include <poll.h> @@ -65,7 +67,7 @@ #define LINENOISE_MAX_LINE 1024 #define LINENOISE_MAX_DISP (LINENOISE_MAX_LINE * 8) #define LINENOISE_PAREN_DELAY 400000 -#define LINENOISE_MAX_UNDO 32 +#define LINENOISE_MAX_UNDO 200 /* The lino_state structure represents the state during line editing. * We pass this state to functions implementing specific editing @@ -293,20 +295,24 @@ static int generate_beep(lino_t *ls) { return write(ls->ofd, "\x7", 1) > 0; } -static void free_undo(lino_t *l) +static void delete_undo(struct lino_undo **pundo) { - struct lino_undo *top = l->undo_stack; + struct lino_undo *u = *pundo; - if (top != 0) { - l->undo_stack = top->next; - free(top->data); - top->data = 0; - free(top); - free_undo(l); - l->undo_stack = 0; + if (u) { + *pundo = u->next; + free(u->data); + u->data = 0; + free(u); } } +static void free_undo_stack(lino_t *l) +{ + while (l->undo_stack != 0) + delete_undo(&l->undo_stack); +} + static void record_undo(lino_t *l) { struct lino_undo *rec = (struct lino_undo *) chk_malloc(sizeof *rec), *iter; @@ -322,7 +328,7 @@ static void record_undo(lino_t *l) rec->next = l->undo_stack; rec->triv = 0; rec->dpos = l->dpos; - rec->hist_index = l->history_index; + rec->hist_index = INT_MAX; rec->data = data; l->undo_stack = rec; @@ -333,11 +339,8 @@ static void record_undo(lino_t *l) /* empty */; if (iter != 0) { - struct lino_undo *save = l->undo_stack; - l->undo_stack = iter->next; - iter->next = 0; - free_undo(l); - l->undo_stack = save; + while (iter->next) + delete_undo(&iter->next); } } @@ -345,61 +348,60 @@ static void record_triv_undo(lino_t *l) { struct lino_undo *top = l->undo_stack; - if (top != 0 && top->triv && top->dpos < l->dpos) + if (top != 0 && top->triv && + (top->hist_index == INT_MAX || top->hist_index == l->history_index) && + top->dpos < l->dpos) return; record_undo(l); l->undo_stack->triv = 1; } -static void undo_pop(lino_t *l) -{ - struct lino_undo *top = l->undo_stack; - if (top == 0) - return; - l->undo_stack = top->next; - free(top->data); - top->data = 0; - free(top); -} - static void restore_undo(lino_t *l) { - struct lino_undo *top = l->undo_stack; - - if (top == 0) - return; - - if (top->hist_index >= l->history_len - 1) { - undo_pop(l); - restore_undo(l); - return; - } + struct lino_undo **ptop = &l->undo_stack; - strcpy(l->data, top->data); - l->dlen = strlen(top->data); - l->dpos = top->dpos; - l->history_index = top->hist_index; + while (*ptop) { + struct lino_undo *top = *ptop; + int hidx = top->hist_index; - { - int history_pos = l->history_len - 1 - l->history_index; + if (hidx == INT_MAX || hidx == l->history_index) { + strcpy(l->data, top->data); + l->dlen = strlen(top->data); + l->dpos = top->dpos; + l->need_refresh = 1; - if (history_pos >= 0 && history_pos < l->history_len) { - free(l->history[history_pos]); - l->history[history_pos] = chk_strdup_utf8(l->data); + if (hidx == l->history_index) { + int history_pos = l->history_len - 1 - l->history_index; + free(l->history[history_pos]); + l->history[history_pos] = chk_strdup_utf8(l->data); + } + delete_undo(ptop); + break; + } else if (hidx >= l->history_len - 1) { + delete_undo(ptop); + } else { + ptop = &top->next; } } +} - l->need_refresh = 1; - - undo_pop(l); +static void undo_subst_hist_idx(lino_t *l, int from_hist, int to_hist) +{ + struct lino_undo *iter; + for (iter = l->undo_stack; iter != 0; iter = iter->next) { + if (iter->hist_index == from_hist) + iter->hist_index = to_hist; + } } -static void renumber_undo_hist(lino_t *l, int delta) +static void undo_renumber_hist_idx(lino_t *l, int delta) { struct lino_undo *iter; - for (iter = l->undo_stack; iter != 0; iter = iter->next) + for (iter = l->undo_stack; iter != 0; iter = iter->next) { + assert (iter->hist_index != INT_MAX); iter->hist_index += delta; + } } /* ============================== Completion ================================ */ @@ -1206,8 +1208,9 @@ static void edit_move_end(lino_t *l) { #define LINENOISE_HISTORY_NEXT 0 #define LINENOISE_HISTORY_PREV 1 static void edit_history_next(lino_t *l, int dir) { - record_undo(l); clear_sel(l); + undo_subst_hist_idx(l, INT_MAX, l->history_index); + if (l->history_len > 1) { /* Update the current history entry before to * overwrite it with the next one. */ @@ -1573,7 +1576,6 @@ static int edit(lino_t *l, const char *prompt) edit_move_end(l); if (l->need_refresh) refresh_line(l); - record_undo(l); ret = l->len; goto out; case CTL('C'): @@ -1770,7 +1772,8 @@ out: l->history_len--; free(l->history[l->history_len]); l->history[l->history_len] = 0; - renumber_undo_hist(l, -1); + undo_subst_hist_idx(l, INT_MAX, 0); + undo_renumber_hist_idx(l, -1); } return ret; } @@ -1911,7 +1914,7 @@ static void lino_cleanup(lino_t *ls) { disable_raw_mode(ls); free_hist(ls); - free_undo(ls); + free_undo_stack(ls); free(ls->clip); ls->clip = 0; } @@ -2001,7 +2004,7 @@ int lino_hist_add(lino_t *ls, const char *line) { } ls->history[ls->history_len] = linecopy; ls->history_len++; - renumber_undo_hist(ls, 1); + undo_renumber_hist_idx(ls, 1); return 1; } |