From 40bdb686aa17833840820c73b565dd0ef2d0410a Mon Sep 17 00:00:00 2001 From: David Lawrence Ramsey Date: Wed, 3 Nov 2004 22:03:41 +0000 Subject: [PATCH] add code to partition a filestruct between a set of arbitrary coordinates; given the coordinates of the beginning and end of the mark, this allows proper and easier handling of saving marked selections, replacing text only in marked selections, and spell-checking marked selections using either the internal or alternate spell checker; do all these using a global partition structure git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2050 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- ChangeLog | 12 +++ TODO | 3 +- src/files.c | 59 ++++++-------- src/global.c | 5 ++ src/nano.c | 220 +++++++++++++++++++++++++++++++++++++++++++++------ src/nano.h | 11 +++ src/proto.h | 9 +++ src/search.c | 62 ++++++++++++--- src/utils.c | 51 ++++++++++++ 9 files changed, 358 insertions(+), 74 deletions(-) diff --git a/ChangeLog b/ChangeLog index 69edceba..782b92e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -93,6 +93,18 @@ CVS code - some functions that use them as a parameter to use size_t as well. Also change some related assertions to handle them. (David Benbennick and DLR) + - Add code to partition a filestruct between a set of arbitrary + coordinates. Given the coordinates of the beginning and end + of the mark, this allows proper and easier handling of saving + marked selections, replacing text only in marked selections + (suggested by Joseph Birthisel), and spell-checking marked + selections using either the internal or alternate spell + checker. Do all these using a global partition structure. + New functions partition_filestruct(), + unpartition_filestruct(), remove_magicline(), and + get_totals(); changes to write_marked(), do_int_spell_fix(), + do_alt_speller(), handle_sigwinch(), and do_replace_loop(). + (DLR) - files.c: do_insertfile() - Simplify by reusing variables whereever possible, and add a diff --git a/TODO b/TODO index 8ea816f6..d8169416 100644 --- a/TODO +++ b/TODO @@ -10,8 +10,7 @@ For version 1.4: - Rebindable keys? - Keystroke to implement "Add next sequence as raw" like vi's ^V. [DONE for edit window, needs to be done for statusbar prompt] -- Spell check selected text only. [DONE for internal spell checker, - partially done for external spell checker] +- Spell check selected text only. [DONE] - Make "To Line" (^W^T) and "Read from Command" (^R^X) reenter their parent menu when their keystroke is entered a second time (^W^T^T and (^R^X^X) (requires figuring out when to keep cursor position and when diff --git a/src/files.c b/src/files.c index b144652c..40e365e7 100644 --- a/src/files.c +++ b/src/files.c @@ -1720,49 +1720,36 @@ int write_file(const char *name, int tmp, int append, int nonamechange) int write_marked(const char *name, int tmp, int append) { int retval = -1; - filestruct *fileagebak = fileage; - filestruct *filebotbak = filebot; bool old_modified = ISSET(MODIFIED); /* write_file() unsets the MODIFIED flag. */ - size_t topx; - /* The column of the beginning of the mark. */ - char origchar; - /* We replace the character at the end of the mark with '\0'. - * We save the original character, to restore it. */ - char *origcharloc; - /* The location of the character we nulled. */ - - /* Set fileage as the top of the mark, and filebot as the bottom. */ - if (current->lineno > mark_beginbuf->lineno || - (current->lineno == mark_beginbuf->lineno && - current_x > mark_beginx)) { - fileage = mark_beginbuf; - topx = mark_beginx; - filebot = current; - origcharloc = current->data + current_x; - } else { - fileage = current; - topx = current_x; - filebot = mark_beginbuf; - origcharloc = mark_beginbuf->data + mark_beginx; - } - origchar = *origcharloc; - *origcharloc = '\0'; - fileage->data += topx; + bool added_magicline; + /* Whether we added a magicline after filebot. */ + filestruct *top, *bot; + size_t top_x, bot_x; + + /* Partition the filestruct so that it contains only the marked + * text. */ + mark_order((const filestruct **)&top, &top_x, + (const filestruct **)&bot, &bot_x); + filepart = partition_filestruct(top, top_x, bot, bot_x); /* If the line at filebot is blank, treat it as the magicline and - * hence the end of the file. Otherwise, treat the line after - * filebot as the end of the file. */ - if (filebot->data[0] != '\0' && filebot->next != NULL) - filebot = filebot->next; + * hence the end of the file. Otherwise, add a magicline and treat + * it as the end of the file. */ + added_magicline = (filebot->data[0] != '\0'); + if (added_magicline) + new_magicline(); retval = write_file(name, tmp, append, TRUE); - /* Now restore everything. */ - fileage->data -= topx; - *origcharloc = origchar; - fileage = fileagebak; - filebot = filebotbak; + /* If we added a magicline, remove it now. */ + if (added_magicline) + remove_magicline(); + + /* Unpartition the filestruct so that it contains all the text + * again. */ + unpartition_filestruct(filepart); + if (old_modified) set_modified(); diff --git a/src/global.c b/src/global.c index 3cbce14e..a6ef0b62 100644 --- a/src/global.c +++ b/src/global.c @@ -63,6 +63,11 @@ filestruct *edittop = NULL; /* Pointer to the top of the edit filestruct *filebot = NULL; /* Last node in the file struct */ filestruct *cutbuffer = NULL; /* A place to store cut text */ +#ifndef NANO_SMALL +partition *filepart = NULL; /* A place to store a portion of the + file struct */ +#endif + #ifdef ENABLE_MULTIBUFFER openfilestruct *open_files = NULL; /* The list of open files */ #endif diff --git a/src/nano.c b/src/nano.c index 50d0bf19..25784f34 100644 --- a/src/nano.c +++ b/src/nano.c @@ -616,6 +616,93 @@ void free_filestruct(filestruct *src) } } +#ifndef NANO_SMALL +/* Partition a filestruct so it begins at (top, top_x) and ends at (bot, + * bot_x). */ +partition *partition_filestruct(filestruct *top, size_t top_x, + filestruct *bot, size_t bot_x) +{ + partition *p; + assert(top != NULL && bot != NULL); + + /* Initialize the partition. */ + p = (partition *)nmalloc(sizeof(partition)); + + /* Save the top and bottom of the filestruct. */ + p->fileage = fileage; + p->filebot = filebot; + + /* Set the top and bottom of the partition to top and bot. */ + fileage = top; + filebot = bot; + + /* Save the line above the top of the partition, detach the top of + * the partition from it, and save the text before top_x in + * top_data. */ + p->top_prev = top->prev; + top->prev = NULL; + p->top_data = mallocstrncpy(NULL, top->data, top_x + 1); + p->top_data[top_x] = '\0'; + + /* Save the line below the bottom of the partition, detach the + * bottom of the partition from it, and save the text after bot_x in + * bot_data. */ + p->bot_next = bot->next; + bot->next = NULL; + p->bot_data = mallocstrcpy(NULL, bot->data + bot_x); + + /* Remove all text after bot_x at the bottom of the partition. */ + null_at(&bot->data, bot_x); + + /* Remove all text before top_x at the top of the partition. */ + charmove(top->data, top->data + top_x, strlen(top->data) - + top_x + 1); + align(&top->data); + + /* Return the partition. */ + return p; +} + +/* Unpartition a filestruct so it begins at (fileage, 0) and ends at + * (filebot, strlen(filebot)) again. */ +void unpartition_filestruct(partition *p) +{ + char *tmp; + assert(p != NULL); + + /* Reattach the line above the top of the partition, and restore the + * text before top_x from top_data. Free top_data when we're done + * with it. */ + tmp = mallocstrcpy(NULL, fileage->data); + fileage->prev = p->top_prev; + fileage->prev->next = fileage; + fileage->data = charealloc(fileage->data, strlen(p->top_data) + + strlen(fileage->data) + 1); + strcpy(fileage->data, p->top_data); + free(p->top_data); + strcat(fileage->data, tmp); + free(tmp); + + /* Reattach the line below the bottom of the partition, and restore + * the text after bot_x from bot_data. Free bot_data when we're + * done with it. */ + filebot->next = p->bot_next; + filebot->next->prev = filebot; + filebot->data = charealloc(filebot->data, strlen(filebot->data) + + strlen(p->bot_data) + 1); + strcat(filebot->data, p->bot_data); + free(p->bot_data); + + /* Restore the top and bottom of the filestruct. */ + fileage = p->fileage; + filebot = p->filebot; + + /* Uninitialize the partition. */ + free(p); + p = NULL; +} +#endif + void renumber_all(void) { filestruct *temp; @@ -1442,6 +1529,8 @@ bool do_int_spell_fix(const char *word) #endif #ifndef NANO_SMALL bool old_mark_set = ISSET(MARK_ISSET); + filestruct *top, *bot; + size_t top_x, bot_x; #endif /* Make sure spell-check is case sensitive. */ @@ -1455,10 +1544,6 @@ bool do_int_spell_fix(const char *word) /* Make sure spell-check doesn't use regular expressions. */ UNSET(USE_REGEXP); #endif -#ifndef NANO_SMALL - /* Make sure the marking highlight is off during spell-check. */ - UNSET(MARK_ISSET); -#endif /* Save the current search/replace strings. */ search_init_globals(); @@ -1469,6 +1554,16 @@ bool do_int_spell_fix(const char *word) last_search = mallocstrcpy(NULL, word); last_replace = mallocstrcpy(NULL, word); +#ifndef NANO_SMALL + if (old_mark_set) { + mark_order((const filestruct **)&top, &top_x, + (const filestruct **)&bot, &bot_x); + filepart = partition_filestruct(top, top_x, bot, bot_x); + edittop = fileage; + UNSET(MARK_ISSET); + } +#endif + /* Start from the top of the file. */ edittop = fileage; current = fileage; @@ -1493,9 +1588,17 @@ bool do_int_spell_fix(const char *word) do_replace_highlight(FALSE, word); if (!canceled && strcmp(word, answer) != 0) { + bool added_magicline = (filebot->data[0] != '\0'); + /* Whether we added a magicline after + * filebot. */ + current_x--; do_replace_loop(word, current, ¤t_x, TRUE, &canceled); + + /* If we added a magicline, remove it now. */ + if (added_magicline) + remove_magicline(); } break; @@ -1508,6 +1611,15 @@ bool do_int_spell_fix(const char *word) free(last_replace); last_replace = save_replace; +#ifndef NANO_SMALL + /* If the mark was on, unpartition the filestruct so that it + * contains all the text again, and turn the mark back on. */ + if (old_mark_set) { + unpartition_filestruct(filepart); + SET(MARK_ISSET); + } +#endif + /* Restore where we were. */ edittop = edittop_save; current = current_save; @@ -1528,11 +1640,6 @@ bool do_int_spell_fix(const char *word) if (regexp_set) SET(USE_REGEXP); #endif -#ifndef NANO_SMALL - /* Restore marking highlight. */ - if (old_mark_set) - SET(MARK_ISSET); -#endif return !canceled; } @@ -1737,13 +1844,23 @@ const char *do_alt_speller(char *tempfile_name) FILE *f; #ifndef NANO_SMALL bool old_mark_set = ISSET(MARK_ISSET); + bool added_magicline = FALSE; + /* Whether we added a magicline after filebot. */ int mbb_lineno_cur = 0; /* We're going to close the current file, and open the output of * the alternate spell command. The line that mark_beginbuf * points to will be freed, so we save the line number and * restore afterwards. */ + int old_totlines = totlines; + /* Our saved value of totlines, used when we spell-check a + * marked selection. */ + long old_totsize = totsize; + /* Our saved value of totsize, used when we spell-check a marked + * selection. */ if (old_mark_set) { + /* If the mark is on, save the number of the line it starts on, + * and then turn the mark off. */ mbb_lineno_cur = mark_beginbuf->lineno; UNSET(MARK_ISSET); } @@ -1798,24 +1915,77 @@ const char *do_alt_speller(char *tempfile_name) #ifndef NANO_SMALL if (old_mark_set) { + filestruct *top, *bot; + size_t top_x, bot_x; + int part_totlines; + long part_totsize; + + /* If the mark was on, partition the filestruct so that it + * contains only the marked text, and keep track of whether the + * temp file (which should contain the spell-checked marked + * text) will have a magicline added when it's reloaded. */ + mark_order((const filestruct **)&top, &top_x, + (const filestruct **)&bot, &bot_x); + filepart = partition_filestruct(top, top_x, bot, bot_x); + added_magicline = (filebot->data[0] != '\0'); + + /* Get the number of lines and the number of characters in the + * marked text, and subtract them from the saved values of + * totlines and totsize. */ + get_totals(top, bot, &part_totlines, &part_totsize); + old_totlines -= part_totlines; + old_totsize -= part_totsize; + } +#endif + + /* Reinitialize the filestruct. */ + free_filestruct(fileage); + global_init(TRUE); + + /* Reload the temp file. Do what load_buffer() would do, except for + * making a new buffer for the temp file if multibuffer support is + * available. */ + open_file(tempfile_name, FALSE, &f); + read_file(f, tempfile_name); + current = fileage; + +#ifndef NANO_SMALL + if (old_mark_set) { + filestruct *old_top = fileage; + + /* If we added a magicline, remove it now. */ + if (added_magicline) + remove_magicline(); + + /* If the mark was on, unpartition the filestruct so that it + * contains all the text again. Note that we've replaced the + * marked text originally in the partition with the + * spell-checked marked text in the temp file. */ + unpartition_filestruct(filepart); + + /* Renumber starting with the beginning line of the old + * partition. Also add the number of lines and characters in + * the spell-checked marked text to the saved values of totlines + * and totsize, and then make those saved values the actual + * values. */ + renumber(old_top); + old_totlines += totlines; + old_totsize += totsize; + totlines = old_totlines; + totsize = old_totsize; + + /* Assign mark_beginbuf to the line where the mark began + * before. */ do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0); mark_beginbuf = current; - /* In case the line got shorter, assign mark_beginx. */ + + /* Assign mark_beginx to the location in mark_beginbuf where the + * mark began before, adjusted for any shortening of the + * line. */ mark_beginx = current_x; - SET(MARK_ISSET); - } else { -#endif - /* Only reload the temp file if it isn't a marked selection. */ - free_filestruct(fileage); - global_init(TRUE); - /* Do what load_buffer() would do, except for making a new - * buffer for the temp file if multibuffer support is - * available. */ - open_file(tempfile_name, FALSE, &f); - read_file(f, tempfile_name); - current = fileage; -#ifndef NANO_SMALL + /* Turn the mark back on. */ + SET(MARK_ISSET); } #endif @@ -2845,6 +3015,10 @@ void handle_sigwinch(int s) memset(hblank, ' ', COLS); hblank[COLS] = '\0'; + /* If we've partitioned the filestruct, unpartition it now. */ + if (filepart != NULL) + unpartition_filestruct(filepart); + #ifdef USE_SLANG /* Slang curses emulation brain damage, part 1: If we just do what * curses does here, it'll only work properly if the resize made the diff --git a/src/nano.h b/src/nano.h index 163463a2..c453467f 100644 --- a/src/nano.h +++ b/src/nano.h @@ -183,6 +183,17 @@ typedef struct openfilestruct { } openfilestruct; #endif +#ifndef NANO_SMALL +typedef struct partition { + filestruct *fileage; + filestruct *top_prev; + char *top_data; + filestruct *filebot; + filestruct *bot_next; + char *bot_data; +} partition; +#endif + typedef struct shortcut { /* Key values that aren't used should be set to NANO_NO_KEY. */ int ctrlval; /* Special sentinel key or control key we want diff --git a/src/proto.h b/src/proto.h index 108a9c62..396a6dad 100644 --- a/src/proto.h +++ b/src/proto.h @@ -87,6 +87,7 @@ extern struct stat fileinfo; extern filestruct *current, *fileage, *edittop, *filebot; extern filestruct *cutbuffer; #ifndef NANO_SMALL +extern partition *filepart; extern filestruct *mark_beginbuf; #endif @@ -301,6 +302,11 @@ void unlink_node(const filestruct *fileptr); void delete_node(filestruct *fileptr); filestruct *copy_filestruct(const filestruct *src); void free_filestruct(filestruct *src); +#ifndef NANO_SMALL +partition *partition_filestruct(filestruct *top, size_t top_x, + filestruct *bot, size_t bot_x); +void unpartition_filestruct(partition *p); +#endif void renumber_all(void); void renumber(filestruct *fileptr); void print1opt(const char *shortflag, const char *longflag, const char @@ -496,6 +502,9 @@ char *mallocstrcpy(char *dest, const char *src); char *mallocstrassn(char *dest, char *src); void new_magicline(void); #ifndef NANO_SMALL +void remove_magicline(void); +void get_totals(const filestruct *begin, const filestruct *end, int + *lines, long *size); void mark_order(const filestruct **top, size_t *top_x, const filestruct **bot, size_t *bot_x); #endif diff --git a/src/search.c b/src/search.c index 71bf7d05..67ecc03d 100644 --- a/src/search.c +++ b/src/search.c @@ -669,8 +669,22 @@ ssize_t do_replace_loop(const char *needle, const filestruct #endif #ifndef NANO_SMALL bool old_mark_set = ISSET(MARK_ISSET); + filestruct *edittop_save = edittop, *top, *bot; + size_t top_x, bot_x; + bool right_side_up = FALSE; + /* TRUE if (mark_beginbuf, mark_beginx) is the top of the mark, + * FALSE if (current, current_x) is. */ if (old_mark_set) { + /* If the mark is on, partition the filestruct so that it + * contains only the marked text, set right_side_up properly, + * set edittop to the top of the marked text, turn the mark off, + * and refresh the screen. */ + mark_order((const filestruct **)&top, &top_x, + (const filestruct **)&bot, &bot_x); + right_side_up = (bot == current && bot_x == current_x); + filepart = partition_filestruct(top, top_x, bot, bot_x); + edittop = fileage; UNSET(MARK_ISSET); edit_refresh(); } @@ -764,18 +778,33 @@ ssize_t do_replace_loop(const char *needle, const filestruct length_change = strlen(copy) - strlen(current->data); #ifndef NANO_SMALL + /* Keep mark_beginx in sync with the text changes. */ if (current == mark_beginbuf && mark_beginx > current_x) { - if (mark_beginx < current_x + match_len) - mark_beginx = current_x; - else - mark_beginx += length_change; + /* If the mark was on and (mark_beginbuf, mark_begin_x) + * was the top of it, don't change mark_beginx. */ + if (!old_mark_set || !right_side_up) { + if (mark_beginx < current_x + match_len) + mark_beginx = current_x; + else + mark_beginx += length_change; + } } #endif - if (current == real_current && current_x <= *real_current_x) { - if (*real_current_x < current_x + match_len) - *real_current_x = current_x + match_len; - *real_current_x += length_change; + /* Keep real_current_x in sync with the text changes. */ + if (current == real_current && current_x <= + *real_current_x) { +#ifndef NANO_SMALL + /* If the mark was on and (current, current_x) was the + * top of it, don't change real_current_x. */ + if (!old_mark_set || right_side_up) { +#endif + if (*real_current_x < current_x + match_len) + *real_current_x = current_x + match_len; + *real_current_x += length_change; +#ifndef NANO_SMALL + } +#endif } /* Set the cursor at the last character of the replacement @@ -806,15 +835,22 @@ ssize_t do_replace_loop(const char *needle, const filestruct } } - /* If text has been added to the magicline, make a new magicline. */ - if (filebot->data[0] != '\0') - new_magicline(); - #ifndef NANO_SMALL - if (old_mark_set) + /* If the mark was on, unpartition the filestruct so that it + * contains all the text again, set edittop back to what it was + * before, turn the mark back on, and refresh the screen. */ + if (old_mark_set) { + unpartition_filestruct(filepart); + edittop = edittop_save; SET(MARK_ISSET); + edit_refresh(); + } #endif + /* If text has been added to the magicline, make a new magicline. */ + if (filebot->data[0] != '\0') + new_magicline(); + return numreplaced; } diff --git a/src/utils.c b/src/utils.c index 1a52e1f9..5606c3f7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -441,6 +441,57 @@ void new_magicline(void) } #ifndef NANO_SMALL +/* Remove the magicline from filebot, if there is one. */ +void remove_magicline(void) +{ + if (filebot->data[0] == '\0') { + filebot = filebot->prev; + free_filestruct(filebot->next); + filebot->next = NULL; + totlines--; + totsize--; + } +} + +/* Calculate the number of lines and the number of characters between + * begin and end, and return them in lines and size, respectively. */ +void get_totals(const filestruct *begin, const filestruct *end, int + *lines, long *size) +{ + const filestruct *f; + + if (lines != NULL) + *lines = 0; + if (size != NULL) + *size = 0; + + /* Go through the lines from begin to end->prev, if we can. */ + for (f = begin; f != NULL && f != end; f = f->next) { + /* Count this line. */ + (*lines)++; + + /* Count the number of characters on this line. */ + *size += strlen(f->data); + + /* Count the newline if we have one. */ + if (f->next != NULL) + (*size)++; + } + + /* Go through the line at end, if we can. */ + if (f != NULL) { + /* Count this line. */ + (*lines)++; + + /* Count the number of characters on this line. */ + *size += strlen(f->data); + + /* Count the newline if we have one. */ + if (f->next != NULL) + (*size)++; + } +} + /* Set top_x and bot_x to the top and bottom x-coordinates of the mark, * respectively, based on the locations of top and bot. */ void mark_order(const filestruct **top, size_t *top_x, const filestruct -- 2.39.5