overhaul the history code to work more consistently, and clean up

various parts of it; note that history tab completion has been removed


git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2533 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
master
David Lawrence Ramsey 2005-05-23 16:30:06 +00:00
parent 7ae4cc6964
commit 934f9687bb
7 changed files with 206 additions and 282 deletions

View File

@ -20,6 +20,15 @@ CVS code -
do_gotolinecolumn()), do_gotoline_void() (renamed
do_gotolinecolumn_void()), nano.1, and nano.texi. (DLR,
suggested by PFTank)
- Overhaul the history code to work more consistently, and clean
up various parts of it. Note that history tab completion has
been removed. New function history_has_changed(); changes to
load_history(), writehist(), thanks_for_all_the_fish(),
history_init(), find_node() (renamed find_history()),
update_history(), get_history_older(), get_history_newer(),
do_search(), do_replace(), nanogetstr(), and statusq();
removal of remove_node(), insert_node(), and
get_history_completion(). (DLR)
- cut.c:
cut_line()
- Set placewewant properly after cutting a line, to avoid a

View File

@ -2866,7 +2866,12 @@ void load_history(void)
;
}
} else {
historyheadtype *history = &search_history;
/* Load a history (first the search history, then the
* replace history) from oldest to newest. Assume the last
* history entry is a blank line. */
filestruct **history = &search_history;
filestruct **historyage = &searchage;
filestruct **historybot = &searchbot;
char *line = NULL;
size_t buflen = 0;
ssize_t read;
@ -2878,31 +2883,38 @@ void load_history(void)
}
if (read > 0) {
unsunder(line, read);
update_history(history, line);
} else
update_history(history, historyage, historybot,
line);
} else {
history = &replace_history;
historyage = &replaceage;
historybot = &replacebot;
}
}
fclose(hist);
free(line);
UNSET(HISTORY_CHANGED);
}
free(nanohist);
}
}
bool writehist(FILE *hist, historyheadtype *histhead)
bool writehist(FILE *hist, filestruct *h)
{
historytype *p;
filestruct *p;
/* Write oldest history first. */
for (p = histhead->tail; p->prev != NULL; p = p->prev) {
/* Write history from oldest to newest. Assume the last history
* entry is a blank line. */
for (p = h; p != NULL; p = p->next) {
size_t p_len = strlen(p->data);
sunder(p->data);
if (fwrite(p->data, sizeof(char), p_len, hist) < p_len ||
putc('\n', hist) == EOF)
return FALSE;
}
return TRUE;
}
@ -2912,8 +2924,8 @@ void save_history(void)
char *nanohist;
/* Don't save unchanged or empty histories. */
if (!ISSET(HISTORY_CHANGED) || (search_history.count == 0 &&
replace_history.count == 0))
if (!history_has_changed() || (searchbot->lineno == 1 &&
replacebot->lineno == 1))
return;
nanohist = histfilename();
@ -2929,13 +2941,14 @@ void save_history(void)
* history file. */
chmod(nanohist, S_IRUSR | S_IWUSR);
if (!writehist(hist, &search_history) ||
putc('\n', hist) == EOF ||
!writehist(hist, &replace_history))
if (!writehist(hist, searchage) || !writehist(hist,
replaceage))
rcfile_error(N_("Error writing %s: %s"), nanohist,
strerror(errno));
fclose(hist);
}
free(nanohist);
}
}

View File

@ -168,8 +168,12 @@ toggle *toggles = NULL;
#endif
#ifndef NANO_SMALL
historyheadtype search_history;
historyheadtype replace_history;
filestruct *search_history = NULL;
filestruct *searchage = NULL;
filestruct *searchbot = NULL;
filestruct *replace_history = NULL;
filestruct *replaceage = NULL;
filestruct *replacebot = NULL;
#endif
/* Regular expressions */
@ -1260,8 +1264,8 @@ void thanks_for_all_the_fish(void)
#endif /* ENABLE_COLOR */
#ifndef NANO_SMALL
/* Free the history lists. */
free_history(&search_history);
free_history(&replace_history);
free_filestruct(searchage);
free_filestruct(replaceage);
#endif
#ifdef ENABLE_NANORC
free(homedir);

View File

@ -265,25 +265,6 @@ typedef struct syntaxtype {
} syntaxtype;
#endif
#ifndef NANO_SMALL
typedef struct historytype {
struct historytype *next;
struct historytype *prev;
char *data;
} historytype;
typedef struct historyheadtype {
struct historytype *next; /* Keep *next and *prev members
* together. */
struct historytype *prev; /* And in the same order as in
* historytype. */
struct historytype *tail;
struct historytype *current;
int count;
int len;
} historyheadtype;
#endif
/* Bitwise flags so that we can save space (or, more correctly, not
* waste it). */
#define MODIFIED (1<<0)
@ -310,13 +291,12 @@ typedef struct historyheadtype {
#define NO_RCFILE (1<<21)
#define NO_COLOR_SYNTAX (1<<22)
#define PRESERVE (1<<23)
#define HISTORY_CHANGED (1<<24)
#define HISTORYLOG (1<<25)
#define RESTRICTED (1<<26)
#define SMART_HOME (1<<27)
#define WHITESPACE_DISPLAY (1<<28)
#define MORE_SPACE (1<<29)
#define NO_UTF8 (1<<30)
#define HISTORYLOG (1<<24)
#define RESTRICTED (1<<25)
#define SMART_HOME (1<<26)
#define WHITESPACE_DISPLAY (1<<27)
#define MORE_SPACE (1<<28)
#define NO_UTF8 (1<<29)
/* Control key sequences. Changing these would be very, very bad. */
#define NANO_CONTROL_SPACE 0
@ -518,7 +498,8 @@ typedef struct historyheadtype {
/* Default width of a tab. */
#define WIDTH_OF_TAB 8
/* Maximum number of search/replace history strings saved. */
/* Maximum number of search/replace history strings saved, not counting
* the blank lines at their ends. */
#define MAX_SEARCH_HISTORY 100
/* Maximum number of bytes we read from a file at one time. */

View File

@ -144,8 +144,12 @@ extern toggle *toggles;
#endif
#ifndef NANO_SMALL
extern historyheadtype search_history;
extern historyheadtype replace_history;
extern filestruct *search_history;
extern filestruct *searchage;
extern filestruct *searchbot;
extern filestruct *replace_history;
extern filestruct *replaceage;
extern filestruct *replacebot;
#endif
extern bool curses_ended;
@ -316,7 +320,7 @@ char *do_browse_from(const char *inpath);
#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
char *histfilename(void);
void load_history(void);
bool writehist(FILE *hist, historyheadtype *histhead);
bool writehist(FILE *hist, filestruct *histhead);
void save_history(void);
#endif
@ -524,17 +528,13 @@ void do_gotopos(int line, size_t pos_x, int pos_y, size_t pos_pww);
#endif
void do_find_bracket(void);
#ifndef NANO_SMALL
bool history_has_changed(void);
void history_init(void);
historytype *find_node(historytype *h, const char *s);
void remove_node(historytype *r);
void insert_node(historytype *h, const char *s);
void update_history(historyheadtype *h, const char *s);
char *get_history_older(historyheadtype *h);
char *get_history_newer(historyheadtype *h);
char *get_history_completion(historyheadtype *h, char *s);
#ifdef DEBUG
void free_history(historyheadtype *h);
#endif
filestruct *find_history(filestruct *h, const char *s);
void update_history(filestruct **h, filestruct **hage, filestruct
**hbot, const char *s);
char *get_history_older(filestruct **h);
char *get_history_newer(filestruct **h);
#endif
/* Public functions in utils.c. */
@ -652,7 +652,7 @@ char *display_string(const char *buf, size_t start_col, size_t len, bool
void nanoget_repaint(const char *buf, const char *inputbuf, size_t x);
int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
#ifndef NANO_SMALL
historyheadtype *history_list,
filestruct *history_list,
#endif
const shortcut *s
#ifndef DISABLE_TABCOMP
@ -661,7 +661,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
);
int statusq(bool allow_tabs, const shortcut *s, const char *curranswer,
#ifndef NANO_SMALL
historyheadtype *history_list,
filestruct *history_list,
#endif
const char *msg, ...);
void statusq_abort(void);

View File

@ -35,6 +35,10 @@
static bool search_last_line = FALSE;
/* Have we gone past the last line while searching? */
#ifndef NANO_SMALL
static bool history_changed = FALSE;
/* Have any of the history lists changed? */
#endif
#ifdef HAVE_REGEX_H
static bool regexp_compiled = FALSE;
/* Have we compiled any regular expressions? */
@ -145,10 +149,6 @@ int search_init(bool replacing, bool use_answer)
search_init_globals();
#ifndef NANO_SMALL
search_history.current = (historytype *)&search_history.next;
#endif
if (last_search[0] != '\0') {
char *disp = display_string(last_search, 0, COLS / 3, FALSE);
@ -165,7 +165,7 @@ int search_init(bool replacing, bool use_answer)
i = statusq(FALSE, replacing ? replace_list : whereis_list,
backupstring,
#ifndef NANO_SMALL
&search_history,
search_history,
#endif
"%s%s%s%s%s%s", _("Search"),
@ -212,9 +212,6 @@ int search_init(bool replacing, bool use_answer)
if (i == -1 || (i < 0 && last_search[0] == '\0') ||
(!replacing && i == 0 && answer[0] == '\0')) {
statusbar(_("Cancelled"));
#ifndef NANO_SMALL
search_history.current = search_history.next;
#endif
return -1;
} else {
switch (i) {
@ -252,9 +249,6 @@ int search_init(bool replacing, bool use_answer)
backupstring = mallocstrcpy(backupstring, answer);
return -2; /* Call the opposite search function. */
case NANO_TOGOTOLINE_KEY:
#ifndef NANO_SMALL
search_history.current = search_history.next;
#endif
do_gotolinecolumn(current->lineno, placewewant, TRUE,
TRUE, FALSE);
/* Put answer up on the statusbar and
@ -481,7 +475,7 @@ void do_search(void)
/* If answer is not "", add this search string to the search history
* list. */
if (answer[0] != '\0')
update_history(&search_history, answer);
update_history(&search_history, &searchage, &searchbot, answer);
#endif
findnextstr_wrap_reset();
@ -910,19 +904,14 @@ void do_replace(void)
* copy answer into last_search. */
if (answer[0] != '\0') {
#ifndef NANO_SMALL
update_history(&search_history, answer);
update_history(&search_history, &searchage, &searchbot, answer);
#endif
last_search = mallocstrcpy(last_search, answer);
}
#ifndef NANO_SMALL
replace_history.current = (historytype *)&replace_history.next;
last_replace = mallocstrcpy(last_replace, "");
#endif
i = statusq(FALSE, replace_list_2, last_replace,
#ifndef NANO_SMALL
&replace_history,
replace_history,
#endif
_("Replace with"));
@ -930,7 +919,8 @@ void do_replace(void)
/* Add this replace string to the replace history list. i == 0
* means that the string is not "". */
if (i == 0)
update_history(&replace_history, answer);
update_history(&replace_history, &replaceage, &replacebot,
answer);
#endif
if (i != 0 && i != -2) {
@ -1126,129 +1116,123 @@ void do_find_bracket(void)
#endif
#ifndef NANO_SMALL
/*
* search and replace history list support functions
*/
/* initialize search and replace history lists */
void history_init(void)
/* Indicate whether any of the history lists have changed. */
bool history_has_changed(void)
{
search_history.next = (historytype *)&search_history.prev;
search_history.prev = NULL;
search_history.tail = (historytype *)&search_history.next;
search_history.current = search_history.next;
search_history.count = 0;
search_history.len = 0;
replace_history.next = (historytype *)&replace_history.prev;
replace_history.prev = NULL;
replace_history.tail = (historytype *)&replace_history.next;
replace_history.current = replace_history.next;
replace_history.count = 0;
replace_history.len = 0;
return history_changed;
}
/* find first node containing string *s in history list *h */
historytype *find_node(historytype *h, const char *s)
/* Initialize the search and replace history lists. */
void history_init(void)
{
search_history = make_new_node(NULL);
search_history->data = mallocstrcpy(NULL, "");
searchage = search_history;
searchbot = search_history;
replace_history = make_new_node(NULL);
replace_history->data = mallocstrcpy(NULL, "");
replaceage = replace_history;
replacebot = replace_history;
}
/* Return the first node containing the string s in the history list,
* starting at h, or NULL if there isn't one. */
filestruct *find_history(filestruct *h, const char *s)
{
assert(h != NULL);
for (; h->next != NULL; h = h->next) {
if (strcmp(s, h->data) == 0)
return h;
}
return NULL;
}
/* remove node *r */
void remove_node(historytype *r)
/* Update a history list. h should be the current position in the list,
* hage should be the top of the list, and hbot should be the bottom of
* the list. */
void update_history(filestruct **h, filestruct **hage, filestruct
**hbot, const char *s)
{
r->prev->next = r->next;
r->next->prev = r->prev;
free(r->data);
free(r);
}
filestruct *p;
/* add a node after node *h */
void insert_node(historytype *h, const char *s)
{
historytype *a;
assert(h != NULL && hage != NULL && hbot != NULL && s != NULL);
a = (historytype *)nmalloc(sizeof(historytype));
a->next = h->next;
a->prev = h;
h->next->prev = a;
h->next = a;
a->data = mallocstrcpy(NULL, s);
}
/* If this string is already in the history, delete it. */
p = find_history(*hage, s);
/* update history list */
void update_history(historyheadtype *h, const char *s)
{
historytype *p;
if (p != NULL) {
filestruct *foo, *bar;
if ((p = find_node(h->next, s)) != NULL) {
if (p == h->next) /* catch delete and re-insert of
same string in 1st node */
goto up_hs;
remove_node(p); /* delete identical older string */
h->count--;
/* If the string is at the current position, don't do
* anything. */
if (p == *h)
return;
/* If the string is at the beginning, move the beginning down to
* the next string. */
if (p == *hage)
*hage = (*hage)->next;
/* Delete the string. */
foo = p;
bar = p->next;
unlink_node(foo);
delete_node(foo);
renumber(bar);
}
if (h->count == MAX_SEARCH_HISTORY) { /* list 'full', delete oldest */
remove_node(h->tail);
h->count--;
/* If the history is full, delete the beginning entry to make room
* for the new entry at the end. */
if ((*hbot)->lineno == MAX_SEARCH_HISTORY + 1) {
filestruct *foo = *hage;
*hage = (*hage)->next;
unlink_node(foo);
delete_node(foo);
renumber(*hage);
}
insert_node((historytype *)h, s);
h->count++;
SET(HISTORY_CHANGED);
up_hs:
h->current = h->next;
/* Add the new entry to the end. */
(*hbot)->data = mallocstrcpy(NULL, s);
splice_node(*hbot, make_new_node(*hbot), (*hbot)->next);
*hbot = (*hbot)->next;
(*hbot)->data = mallocstrcpy(NULL, "");
/* Indicate that the history's been changed. */
history_changed = TRUE;
/* Set the current position in the list to the bottom. */
*h = *hbot;
}
/* return a pointer to either the next older history or NULL if no more */
char *get_history_older(historyheadtype *h)
/* Return the string in the history list just before h, or NULL if there
* isn't one. */
char *get_history_older(filestruct **h)
{
if (h->current->next != NULL) { /* any older entries? */
h->current = h->current->next; /* yes */
return h->current->data; /* return it */
}
return NULL; /* end of list */
assert(h != NULL);
if ((*h)->prev == NULL)
return NULL;
*h = (*h)->prev;
return (*h)->data;
}
char *get_history_newer(historyheadtype *h)
/* Return the string in the history list just after h, or NULL if there
* isn't one. */
char *get_history_newer(filestruct **h)
{
if (h->current->prev != NULL) {
h->current = h->current->prev;
if (h->current->prev != NULL)
return h->current->data;
}
return NULL;
assert(h != NULL);
if ((*h)->next == NULL)
return NULL;
*h = (*h)->next;
return (*h)->data;
}
/* get a completion */
char *get_history_completion(historyheadtype *h, char *s)
{
historytype *p;
for (p = h->current->next; p->next != NULL; p = p->next) {
if (strncmp(s, p->data, h->len) == 0 && strlen(p->data) != h->len) {
h->current = p;
return p->data;
}
}
h->current = (historytype *)h;
null_at(&s, h->len);
return s;
}
#ifdef DEBUG
/* free a history list */
void free_history(historyheadtype *h)
{
historytype *p;
for (p = h->next; p->next != NULL; p = p->next)
remove_node(p);
}
#endif
/* end of history support functions */
#endif /* !NANO_SMALL */

View File

@ -2430,7 +2430,7 @@ void nanoget_repaint(const char *buf, const char *inputbuf, size_t x)
* statusq(). */
int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
#ifndef NANO_SMALL
historyheadtype *history_list,
filestruct *history_list,
#endif
const shortcut *s
#ifndef DISABLE_TABCOMP
@ -2443,23 +2443,15 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
size_t answer_len = strlen(curranswer);
#ifndef DISABLE_TABCOMP
bool tabbed = FALSE;
/* Used by input_tab(). */
/* Whether we've pressed Tab more than once consecutively. */
#endif
#ifndef NANO_SMALL
/* For history. */
char *history = NULL;
char *currentbuf = NULL;
char *complete = NULL;
int last_kbinput = 0;
/* This variable is used in the search history code. use_cb == 0
* means that we're using the existing history and ignoring
* currentbuf. use_cb == 1 means that the entry in answer should be
* moved to currentbuf or restored from currentbuf to answer.
* use_cb == 2 means that the entry in currentbuf should be moved to
* answer or restored from answer to currentbuf. */
int use_cb = 0;
/* The current history string. */
char *magichistory = NULL;
/* The temporary string typed at the bottom of the history, if
* any. */
#endif
/* Only put statusbar_x at the end of the string if it's
@ -2500,28 +2492,6 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
switch (kbinput) {
case NANO_TAB_KEY:
#ifndef NANO_SMALL
/* Tab history completion. */
if (history_list != NULL) {
if (complete == NULL ||
last_kbinput != NANO_TAB_KEY) {
history_list->current =
(historytype *)history_list;
history_list->len = strlen(answer);
}
if (history_list->len > 0) {
complete = get_history_completion(history_list,
answer);
answer = mallocstrcpy(answer, complete);
answer_len = strlen(answer);
statusbar_x = answer_len;
}
}
#ifndef DISABLE_TABCOMP
else
#endif
#endif
#ifndef DISABLE_TABCOMP
if (allow_tabs) {
answer = input_tab(answer, &statusbar_x, &tabbed,
@ -2533,43 +2503,23 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
case NANO_PREVLINE_KEY:
#ifndef NANO_SMALL
if (history_list != NULL) {
/* If currentbuf is NULL, or if use_cb is 1 and
* currentbuf is different from answer, it means
* that we're scrolling up at the top of the search
* history, and we need to save the current answer
* in currentbuf. Do this and reset use_cb to 0. */
if (currentbuf == NULL || (use_cb == 1 &&
strcmp(currentbuf, answer) != 0)) {
currentbuf = mallocstrcpy(currentbuf, answer);
use_cb = 0;
}
/* If we're scrolling up at the bottom of the
* history list, answer isn't blank, and
* magichistory isn't set, save answer in
* magichistory. */
if (history_list->next == NULL &&
answer[0] != '\0' && magichistory == NULL)
magichistory = mallocstrcpy(NULL, answer);
/* If currentbuf isn't NULL, use_cb is 2, and
* currentbuf is different from answer, it means
* that we're scrolling up at the bottom of the
* search history, and we need to restore the
* current answer from currentbuf. Do this, blow
* away currentbuf since we don't need it anymore,
* and reset use_cb to 0. */
if (currentbuf != NULL && use_cb == 2 &&
strcmp(currentbuf, answer) != 0) {
answer = mallocstrcpy(answer, currentbuf);
answer_len = strlen(answer);
free(currentbuf);
currentbuf = NULL;
use_cb = 0;
/* Otherwise, get the older search from the history
* list and save it in answer. If there is no older
* search, blank out answer. */
} else if ((history =
get_history_older(history_list)) != NULL) {
/* Get the older search from the history list and
* save it in answer. If there is no older search,
* don't do anything. */
if ((history =
get_history_older(&history_list)) != NULL) {
answer = mallocstrcpy(answer, history);
answer_len = strlen(history);
} else {
answer = mallocstrcpy(answer, "");
answer_len = 0;
answer_len = strlen(answer);
statusbar_x = answer_len;
}
statusbar_x = answer_len;
/* This key has a shortcut list entry when it's used
* to move to an older search, which means that
@ -2584,39 +2534,25 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
#ifndef NANO_SMALL
if (history_list != NULL) {
/* Get the newer search from the history list and
* save it in answer. */
* save it in answer. If there is no newer search,
* don't do anything. */
if ((history =
get_history_newer(history_list)) != NULL) {
get_history_newer(&history_list)) != NULL) {
answer = mallocstrcpy(answer, history);
answer_len = strlen(history);
/* If currentbuf isn't NULL and use_cb isn't 2, it
* means that we're scrolling down at the bottom of
* the search history and we need to restore the
* current answer from currentbuf. Do this, blow
* away currentbuf since we don't need it anymore,
* and set use_cb to 1. */
} else if (currentbuf != NULL && use_cb != 2) {
answer = mallocstrcpy(answer, currentbuf);
answer_len = strlen(answer);
free(currentbuf);
currentbuf = NULL;
use_cb = 1;
/* Otherwise, if currentbuf is NULL and use_cb isn't
* 2, it means that we're scrolling down at the
* bottom of the search history and we need to save
* the current answer (if it's not blank) in
* currentbuf. Do this, blank out answer, and set
* use_cb to 2. */
} else if (use_cb != 2) {
if (answer[0] != '\0') {
currentbuf = mallocstrcpy(currentbuf,
answer);
answer = mallocstrcpy(answer, "");
}
answer_len = 0;
use_cb = 2;
statusbar_x = answer_len;
}
/* If, after scrolling down, we're at the bottom of
* the history list, answer is blank, and
* magichistory is set, save magichistory in
* answer. */
if (history_list->next == NULL &&
answer[0] == '\0' && magichistory != NULL) {
answer = mallocstrcpy(answer, magichistory);
answer_len = strlen(answer);
statusbar_x = answer_len;
}
statusbar_x = answer_len;
}
#endif
break;
@ -2628,9 +2564,6 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
if (finished)
break;
#ifndef NANO_SMALL
last_kbinput = kbinput;
#endif
nanoget_repaint(buf, answer, statusbar_x);
wrefresh(bottomwin);
}
@ -2653,7 +2586,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
* completion. */
int statusq(bool allow_tabs, const shortcut *s, const char *curranswer,
#ifndef NANO_SMALL
historyheadtype *history_list,
filestruct *history_list,
#endif
const char *msg, ...)
{