From: David Lawrence Ramsey Date: Tue, 23 Nov 2004 04:08:28 +0000 (+0000) Subject: overhaul the cutting and uncutting routines and the justify-related X-Git-Tag: v1.3.6~276 X-Git-Url: https://git.wh0rd.org/?a=commitdiff_plain;h=93c8405bfa0a0f8e68eb34c56aaa38c0bfd35b68;p=nano.git overhaul the cutting and uncutting routines and the justify-related routines to back up and restore unjustified text to use the partitioning code, as it greatly simplifies how they work; also back up and restore unjustified text in its own buffer instead of the cutbuffer, and add a few minor bug fixes to the overhauled routines git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2124 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- diff --git a/ChangeLog b/ChangeLog index e143a45f..55296e37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,40 @@ CVS code - +- General: + - Overhaul the cutting and uncutting routines to use the + partitioning code, as it greatly simplifies how they work. + New functions move_to_filestruct(), copy_from_filestruct(), + cut_line(), cut_marked(), and cut_to_eol(); changes to + add_to_cutbuffer(), do_cut_text(), do_uncut_text(), etc.; + removal of functions get_cutbottom(), add_to_cutbuffer(), and + cut_marked_segment(). (DLR) + - Overhaul the justify-related routines to back up and restore + unjustified text to use the partitioning code, as it greatly + simplifies how they work, and to store such text in its own + buffer rather than the cutbuffer. Changes to backup_lines(), + do_justify(), etc. (DLR) +- cut.c: + do_uncut_text() + - No longer duplicate Pico's adding an extra magicline to the + file if uncutting leaves the cursor on the current one, as + it's actually a bug. (DLR) +- global.c: + thanks_for_all_the_fish() + - Free the justify buffer if it isn't empty. (DLR) - nano.c: + handle_sigwinch() + - If the justify buffer isn't empty, blow it away and don't + display "UnJustify" in the shortcut list anymore. (DLR) do_wrap() - Make wrap_loc and word_back ssize_t's, to match fill. (DLR) + do_justify() + - For consistency, preserve placewewant if we didn't unjustify + instead of setting it to 0. (DLR) +- winio.c: + get_edit_input() + - Remove parameter allow_funcs, as it was only needed as a + workaround for when justified text was stored in the cutbuffer + and the cut shortcut was hit at the "Can now UnJustify!" + prompt. (DLR) GNU nano 1.3.5 - 2004.11.22 - General: diff --git a/src/cut.c b/src/cut.c index 3a7a8ab2..a90defa6 100644 --- a/src/cut.c +++ b/src/cut.c @@ -32,12 +32,6 @@ static bool keep_cutbuffer = FALSE; /* Should we keep the contents of the cutbuffer? */ -static cut_type marked_cut = CUT_LINE; - /* What type of cut is in the cutbuffer? */ -#ifndef NANO_SMALL -static bool concatenate_cut = FALSE; - /* Should we add this cut string to the end of the last one? */ -#endif static filestruct *cutbottom = NULL; /* Pointer to end of cutbuffer. */ @@ -46,444 +40,133 @@ void cutbuffer_reset(void) keep_cutbuffer = FALSE; } -filestruct *get_cutbottom(void) +/* If we're not on the magicline, move all the text of the current line, + * plus the newline at the end, to the cutbuffer. */ +void cut_line(void) { - return cutbottom; -} - -void add_to_cutbuffer(filestruct *inptr, bool allow_concat) -{ -#ifdef DEBUG - fprintf(stderr, "add_to_cutbuffer(): inptr->data = %s\n", inptr->data); -#endif - - if (cutbuffer == NULL) - cutbuffer = inptr; -#ifndef NANO_SMALL - else if (allow_concat && concatenate_cut) { - /* If allow_concat is TRUE and we're concatenating, tack the - * text in inptr onto the text in cutbottom. */ - cutbottom->data = charealloc(cutbottom->data, - strlen(cutbottom->data) + strlen(inptr->data) + 1); - strcat(cutbottom->data, inptr->data); - return; - } -#endif - else { - cutbottom->next = inptr; - inptr->prev = cutbottom; - } - cutbottom = inptr; - cutbottom->next = NULL; + if (current->next != NULL) + move_to_filestruct(&cutbuffer, &cutbottom, current, 0, + current->next, 0); } #ifndef NANO_SMALL -/* Cut a marked segment instead of a whole line. - * - * The first cut character is top->data[top_x]. Unless top == bot, the - * last cut line has length bot_x. That is, if bot_x > 0 then we cut to - * bot->data[bot_x - 1]. - * - * We maintain totsize, totlines, filebot, the magicline, and line - * numbers. Also, we set current and current_x so the cursor will be on - * the first character after what was cut. We do not do any screen - * updates. - * - * Note cutbuffer might not be NULL if cut to end is used. */ -void cut_marked_segment(void) +/* Move all currently marked text to the cutbuffer, and set the current + * place we want to where the text used to start. */ +void cut_marked(void) { - filestruct *top; - filestruct *bot; - filestruct *tmp; - size_t top_x; - size_t bot_x; - size_t newsize; + filestruct *top, *bot; + size_t top_x, bot_x; - /* If the mark doesn't cover any text, get out. */ - if (current == mark_beginbuf && current_x == mark_beginx) - return; - assert(current != NULL && mark_beginbuf != NULL); - - /* Set up the top and bottom lines and coordinates of the marked - * text. */ mark_order((const filestruct **)&top, &top_x, - (const filestruct **)&bot, &bot_x, NULL); - - /* Make the first cut line manually. Move the cut part of the top - * line into tmp, and set newsize to that partial line's length. */ - tmp = copy_node(top); - newsize = (top == bot ? bot_x - top_x : strlen(top->data + top_x)); - charmove(tmp->data, tmp->data + top_x, newsize); - null_at(&tmp->data, newsize); - - /* Add the contents of tmp to the cutbuffer. Note that cutbuffer - * might be non-NULL if we have cut to end enabled. */ - if (cutbuffer == NULL) { - cutbuffer = tmp; - cutbottom = tmp; - } else { - if (concatenate_cut) { - /* If we're concatenating, tack the text in the first line - * of tmp onto the text in the bottom of the cutbuffer, and - * move tmp one line down to where its next line begins. */ - cutbottom->data = charealloc(cutbottom->data, - strlen(cutbottom->data) + strlen(tmp->data) + 1); - strcat(cutbottom->data, tmp->data); - tmp = tmp->next; - } - - /* Put tmp on the line after the bottom of the cutbuffer. */ - cutbottom->next = tmp; - - if (!concatenate_cut) { - /* Tf we're not concatenating, attach tmp to the bottom of - * the cutbuffer, and then move the bottom of the cutbuffer - * one line down to where tmp is. */ - tmp->prev = cutbottom; - cutbottom = tmp; - } - } + (const filestruct **)&bot, &bot_x, NULL); - /* And make the top remainder line manually too. Update current_x - * and totlines to account for all the cut text, and update totsize - * to account for the length of the cut part of the first line. */ - current_x = top_x; - totsize -= newsize; - totlines -= bot->lineno - top->lineno; - - /* Now set newsize to be the length of the top remainder line plus - * the bottom remainder line, plus one for the null terminator. */ - newsize = top_x + strlen(bot->data + bot_x) + 1; - - if (top == bot) { - /* In this case, we're only cutting one line or part of one - * line, so the remainder line is shorter. This means that we - * must move text from the end forward first. */ - charmove(top->data + top_x, bot->data + bot_x, newsize - top_x); - top->data = charealloc(top->data, newsize); - - cutbottom->next = NULL; -#ifdef DEBUG - dump_buffer(cutbuffer); + move_to_filestruct(&cutbuffer, &cutbottom, top, top_x, bot, bot_x); + placewewant = xplustabs(); +} #endif - return; - } - - /* Update totsize to account for the cut part of the last line. */ - totsize -= bot_x + 1; - - /* Here, the top remainder line might get longer (if the bottom - * remainder line is added to the end of it), so we realloc() it - * first. */ - top->data = charealloc(top->data, newsize); - charmove(top->data + top_x, bot->data + bot_x, newsize - top_x); - - assert(cutbottom != NULL && cutbottom->next != NULL); - /* We're cutting multiple lines, so in particular the next line is - * cut too. */ - cutbottom->next->prev = cutbottom; - - /* Update totsize to account for all the complete lines that have - * been cut. After this, totsize is fully up to date. */ - for (tmp = top->next; tmp != bot; tmp = tmp->next) - totsize -= strlen(tmp->data) + 1; - - /* Make the last cut line manually. */ - null_at(&bot->data, bot_x); - - /* Move the rest of the cut text (other than the cut part of the top - * line) from the buffer to the end of the cutbuffer, and fix the - * edit buffer to account for the cut text. */ - top->next = bot->next; - cutbottom = bot; - cutbottom->next = NULL; - if (top->next != NULL) - top->next->prev = top; - renumber(top); - current = top; - /* If the bottom line of the cut was the magicline, set filebot - * properly, and add a new magicline if the top remainder line - * (which is now the new bottom line) is non-blank. */ - if (bot == filebot) { - filebot = top; - assert(bot_x == 0); - if (top_x > 0) - new_magicline(); +/* If we're not at the end of the current line, move all the text from + * the current cursor position to the end of the current line, + * not counting the newline at the end, to the cutbuffer. If we are, + * and we're not on the magicline, move the newline at the end to the + * cutbuffer, and set the current place we want to where the newline + * used to be. */ +void cut_to_eol(void) +{ + size_t data_len = strlen(current->data); + + assert(current_x <= data_len); + + if (current_x < data_len) + /* If we're not at the end of the line, move all the text from + * the current position up to it, not counting the newline at + * the end, to the cutbuffer. */ + move_to_filestruct(&cutbuffer, &cutbottom, current, current_x, + current, data_len); + else if (current->next != NULL) { + /* If we're at the end of the line, and the next line isn't the + * magicline, move all the text from the current position up to + * the beginning of the next line, i.e, the newline at the + * end, to the cutbuffer. */ + move_to_filestruct(&cutbuffer, &cutbottom, current, current_x, + current->next, 0); + placewewant = xplustabs(); } -#ifdef DEBUG - dump_buffer(cutbuffer); -#endif } -#endif +/* Move text from the current filestruct into the cutbuffer. */ void do_cut_text(void) { - filestruct *fileptr; - assert(current != NULL && current->data != NULL); check_statusblank(); + /* If keep_cutbuffer is FALSE, blow away the text in the + * cutbuffer. */ if (!keep_cutbuffer) { free_filestruct(cutbuffer); cutbuffer = NULL; - marked_cut = CUT_LINE; -#ifndef NANO_SMALL - concatenate_cut = FALSE; -#endif #ifdef DEBUG fprintf(stderr, "Blew away cutbuffer =)\n"); #endif } - /* You can't cut the magicline except with the mark. But trying - * does clear the cutbuffer if keep_cutbuffer is FALSE. */ - if (current == filebot -#ifndef NANO_SMALL - && !ISSET(MARK_ISSET) -#endif - ) - return; - + /* Set keep_cutbuffer to TRUE, so that the text we're going to move + * into the cutbuffer will be added to the text already in the + * cutbuffer instead of replacing it. */ keep_cutbuffer = TRUE; #ifndef NANO_SMALL - if (ISSET(CUT_TO_END) && !ISSET(MARK_ISSET)) { - assert(current_x <= strlen(current->data)); - - if (current->data[current_x] == '\0') { - /* If the line is empty and we didn't just cut a non-blank - * line, create a dummy blank line and add it to the - * cutbuffer. */ - if (marked_cut != CUT_MARKED && current->next != filebot) { - filestruct *junk = make_new_node(current); - - junk->data = mallocstrcpy(NULL, ""); - add_to_cutbuffer(junk, TRUE); -#ifdef DEBUG - dump_buffer(cutbuffer); -#endif - } - - do_delete(); - marked_cut = CUT_TO_EOL; - return; - } else { - SET(MARK_ISSET); - - mark_beginx = strlen(current->data); - mark_beginbuf = current; - } - } - if (ISSET(MARK_ISSET)) { - cut_marked_segment(); - - placewewant = xplustabs(); + /* If the mark is on, move the marked text to the cutbuffer and + * turn the mark off. */ + cut_marked(); UNSET(MARK_ISSET); - - marked_cut = CUT_MARKED; - concatenate_cut = TRUE; - - edit_refresh(); - set_modified(); - return; - } -#endif /* !NANO_SMALL */ - - totlines--; - totsize -= strlen(current->data) + 1; - fileptr = current; - current = current->next; - current->prev = fileptr->prev; - add_to_cutbuffer(fileptr, TRUE); -#ifdef DEBUG - dump_buffer(cutbuffer); + } else #endif - - if (fileptr == fileage) - fileage = current; + if (ISSET(CUT_TO_END)) + /* Otherwise, if the CUT_TO_END flag is set, move all text up to + * the end of the line into the cutbuffer. */ + cut_to_eol(); else - current->prev->next = current; - - if (fileptr == edittop) - edittop = current; + /* Otherwise, move the entire line into the cutbuffer. */ + cut_line(); - renumber(current); - current_x = 0; edit_refresh(); set_modified(); - marked_cut = CUT_LINE; -#ifndef NANO_SMALL - concatenate_cut = FALSE; + +#ifdef DEBUG + dump_buffer(cutbuffer); #endif } +/* Copy text from the cutbuffer into the current filestruct. */ void do_uncut_text(void) { - filestruct *tmp = current; - filestruct *newbuf = NULL; - filestruct *newend = NULL; + assert(current != NULL && current->data != NULL); #ifndef DISABLE_WRAPPING wrap_reset(); #endif - check_statusblank(); - if (cutbuffer == NULL || current == NULL) - return; /* AIEEEEEEEEEEEE */ - - /* If we're uncutting a previously non-marked block, uncut to end if - * we're not at the beginning of the line. If we are at the - * beginning of the line, set placewewant to 0. Pico does both of - * these. */ - if (marked_cut == CUT_LINE) { - if (current_x > 0) - marked_cut = CUT_TO_EOL; - else - placewewant = 0; - } - - /* If we're going to uncut on the magicline, always make a new - * magicline in advance, as Pico does. */ - if (current->next == NULL) - new_magicline(); - - if (marked_cut == CUT_LINE || cutbuffer->next != NULL) { - newbuf = copy_filestruct(cutbuffer); - for (newend = newbuf; newend->next != NULL && newend != NULL; - newend = newend->next) - totlines++; - } - - /* Hook newbuf in at current. */ - if (marked_cut != CUT_LINE) { - filestruct *hold = current; - - /* If there's only one line in the cutbuffer... */ - if (cutbuffer->next == NULL) { - size_t buf_len = strlen(cutbuffer->data); - size_t cur_len = strlen(current->data); - - current->data = charealloc(current->data, cur_len + - buf_len + 1); - charmove(current->data + current_x + buf_len, - current->data + current_x, cur_len - current_x + 1); - strncpy(current->data + current_x, cutbuffer->data, - buf_len); - /* Use strncpy() to not copy the null terminator. */ - current_x += buf_len; - totsize += buf_len; - - placewewant = xplustabs(); - } else { /* Yuck -- no kidding! */ - char *tmpstr, *tmpstr2; - - tmp = current->next; - - /* New beginning. */ - tmpstr = charalloc(current_x + strlen(newbuf->data) + 1); - strncpy(tmpstr, current->data, current_x); - strcpy(&tmpstr[current_x], newbuf->data); - totsize += strlen(newbuf->data) + strlen(newend->data) + 1; - - /* New end. */ - tmpstr2 = charalloc(strlen(newend->data) + - strlen(¤t->data[current_x]) + 1); - strcpy(tmpstr2, newend->data); - strcat(tmpstr2, ¤t->data[current_x]); - - free(current->data); - current->data = tmpstr; - current->next = newbuf->next; - newbuf->next->prev = current; - delete_node(newbuf); - - current_x = strlen(newend->data); - placewewant = xplustabs(); - free(newend->data); - newend->data = tmpstr2; - - newend->next = tmp; - - /* If tmp isn't NULL, we're in the middle: update the - * prev pointer. If it IS NULL, we're at the end; update - * the filebot pointer. */ - if (tmp != NULL) - tmp->prev = newend; - else { - filebot = newend; - new_magicline(); - } - - /* Recalculate current_y and totsize. */ - for (tmp = current->next; tmp != newend; tmp = tmp->next) { - current_y++; - totsize += strlen(tmp->data) + 1; - } - - current = newend; - } - - /* If we're doing a cut to end, we don't want anything else on - * the line, so we have to screw up all the work we just did and - * separate the line. */ - if (marked_cut == CUT_TO_EOL) { - tmp = make_new_node(current); - tmp->data = mallocstrcpy(NULL, current->data + current_x); - splice_node(current, tmp, current->next); - null_at(¤t->data, current_x); - current = current->next; - current_x = 0; - placewewant = 0; - - /* Extra line added; update stuff. */ - totlines++; - totsize++; - } - - /* Renumber from BEFORE where we pasted ;) */ - renumber(hold); + check_statusblank(); -#ifdef DEBUG - dump_buffer(fileage); - dump_buffer(cutbuffer); -#endif - set_modified(); - edit_refresh(); + /* If the cutbuffer is empty, get out. */ + if (cutbuffer == NULL) return; - } - - if (current != fileage) { - tmp = current->prev; - tmp->next = newbuf; - newbuf->prev = tmp; - } else - fileage = newbuf; - totlines++; /* Unmarked uncuts don't split lines. */ + /* Add a copy of the text in the cutbuffer to the current filestruct + * at the current cursor position. */ + copy_from_filestruct(cutbuffer, cutbottom); - /* This is so uncutting at the top of the buffer will work => */ - if (current_y == 0) - edittop = newbuf; + /* Set the current place we want to where the text from the + * cutbuffer ends. */ + placewewant = xplustabs(); - /* Connect the end of the buffer to the filestruct. */ - newend->next = current; - current->prev = newend; - - /* Recalculate current_y and totsize. */ - for (tmp = newbuf; tmp != current; tmp = tmp->next) { - current_y++; - totsize += strlen(tmp->data) + 1; - } - - renumber(newbuf); edit_refresh(); + set_modified(); #ifdef DEBUG dump_buffer_reverse(); #endif - - set_modified(); } diff --git a/src/global.c b/src/global.c index 580d2e35..761d2e09 100644 --- a/src/global.c +++ b/src/global.c @@ -63,6 +63,9 @@ filestruct *edittop = NULL; /* Pointer to the top of the edit file struct */ filestruct *filebot = NULL; /* Last node in the file struct */ filestruct *cutbuffer = NULL; /* A place to store cut text */ +#ifndef DISABLE_JUSTIFY +filestruct *jusbuffer = NULL; /* A place to store unjustified text */ +#endif partition *filepart = NULL; /* A place to store a portion of the file struct */ @@ -1160,7 +1163,10 @@ void thanks_for_all_the_fish(void) free(answer); if (cutbuffer != NULL) free_filestruct(cutbuffer); - +#ifndef DISABLE_JUSTIFY + if (jusbuffer != NULL) + free_filestruct(jusbuffer); +#endif free_shortcutage(&main_list); free_shortcutage(&whereis_list); free_shortcutage(&replace_list); diff --git a/src/nano.c b/src/nano.c index 88c9ccb7..6268773a 100644 --- a/src/nano.c +++ b/src/nano.c @@ -78,6 +78,11 @@ static int pid; /* The PID of the newly forked process * it. */ #endif +#ifndef DISABLE_JUSTIFY +static filestruct *jusbottom = NULL; + /* Pointer to end of justify buffer. */ +#endif + /* What we do when we're all set to exit. */ void finish(void) { @@ -714,6 +719,169 @@ void unpartition_filestruct(partition **p) *p = NULL; } +/* Move all the text between (top, top_x) and (bot, bot_x) in the + * current filestruct to a filestruct beginning with file_top and ending + * with file_bot. If no text is between (top, top_x) and (bot, bot_x), + * don't do anything. */ +void move_to_filestruct(filestruct **file_top, filestruct **file_bot, + filestruct *top, size_t top_x, filestruct *bot, size_t bot_x) +{ + filestruct *top_save; + long part_totsize; + bool at_edittop; +#ifndef NANO_SMALL + bool mark_inside = FALSE; +#endif + + assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL); + + /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */ + if (top == bot && top_x == bot_x) + return; + + /* Partition the filestruct so that it contains only the text from + * (top, top_x) to (bot, bot_x), keep track of whether the top of + * the partition is the top of the edit window, and keep track of + * whether the mark begins inside the partition. */ + filepart = partition_filestruct(top, top_x, bot, bot_x); + at_edittop = (fileage == edittop); +#ifndef NANO_SMALL + if (ISSET(MARK_ISSET)) + mark_inside = (mark_beginbuf->lineno >= fileage->lineno && + mark_beginbuf->lineno <= filebot->lineno && + (mark_beginbuf != fileage || mark_beginx >= top_x) && + (mark_beginbuf != filebot || mark_beginx <= bot_x)); +#endif + + /* Get the number of characters in the text, and subtract it from + * totsize. */ + get_totals(top, bot, NULL, &part_totsize); + totsize -= part_totsize; + + if (*file_top == NULL) { + /* If file_top is empty, just move all the text directly into + * it. This is equivalent to tacking the text in top onto the + * (lack of) text at the end of file_top. */ + *file_top = fileage; + *file_bot = filebot; + } else { + /* Otherwise, tack the text in top onto the text at the end of + * file_bot. */ + (*file_bot)->data = charealloc((*file_bot)->data, + strlen((*file_bot)->data) + strlen(fileage->data) + 1); + strcat((*file_bot)->data, fileage->data); + + /* Attach the line after top to the line after file_bot. Then, + * if there's more than one line after top, move file_bot down + * to bot. */ + (*file_bot)->next = fileage->next; + if ((*file_bot)->next != NULL) { + (*file_bot)->next->prev = *file_bot; + *file_bot = filebot; + } + } + + /* Since the text has now been saved, remove it from the filestruct. + * If the top of the partition was the top of the edit window, set + * edittop to where the text used to start. If the mark began + * inside the partition, set the beginning of the mark to where the + * text used to start. */ + fileage = (filestruct *)nmalloc(sizeof(filestruct)); + fileage->data = mallocstrcpy(NULL, ""); + filebot = fileage; + if (at_edittop) + edittop = fileage; +#ifndef NANO_SMALL + if (mark_inside) { + mark_beginbuf = fileage; + mark_beginx = top_x; + } +#endif + + /* Restore the current line and cursor position. */ + current = fileage; + current_x = top_x; + + top_save = fileage; + + /* Unpartition the filestruct so that it contains all the text + * again, minus the saved text. */ + unpartition_filestruct(&filepart); + + /* Renumber starting with the beginning line of the old + * partition. */ + renumber(top_save); + + if (filebot->data[0] != '\0') + new_magicline(); + + /* Set totlines to the new number of lines in the file. */ + totlines = filebot->lineno; +} + +/* Copy all the text from the filestruct beginning with file_top and + * ending with file_bot to the current filestruct at the current cursor + * position. */ +void copy_from_filestruct(filestruct *file_top, filestruct *file_bot) +{ + filestruct *top_save; + int part_totlines; + long part_totsize; + bool at_edittop; + + assert(file_top != NULL && file_bot != NULL); + + /* Partition the filestruct so that it contains no text, and keep + * track of whether the top of the partition is the top of the edit + * window. */ + filepart = partition_filestruct(current, current_x, current, + current_x); + at_edittop = (fileage == edittop); + + /* Put the top and bottom of the filestruct at copies of file_top + * and file_bot. */ + fileage = copy_filestruct(file_top); + filebot = fileage; + while (filebot->next != NULL) + filebot = filebot->next; + + /* Restore the current line and cursor position. */ + current = filebot; + current_x = strlen(filebot->data); + if (fileage == filebot) + current_x += strlen(filepart->top_data); + + /* Get the number of lines and the number of characters in the saved + * text, and add the latter to totsize. */ + get_totals(fileage, filebot, &part_totlines, &part_totsize); + totsize += part_totsize; + + /* If the top of the partition was the top of the edit window, set + * edittop to where the saved text now starts, and update the + * current y-coordinate to account for the number of lines it + * has, less one since the first line will be tacked onto the + * current line. */ + if (at_edittop) + edittop = fileage; + current_y += part_totlines - 1; + + top_save = fileage; + + /* Unpartition the filestruct so that it contains all the text + * again, minus the saved text. */ + unpartition_filestruct(&filepart); + + /* Renumber starting with the beginning line of the old + * partition. */ + renumber(top_save); + + if (filebot->data[0] != '\0') + new_magicline(); + + /* Set totlines to the new number of lines in the file. */ + totlines = filebot->lineno; +} + void renumber_all(void) { filestruct *temp; @@ -2342,39 +2510,69 @@ void do_para_end(void) edit_redraw(old_current, old_pww); } -/* Put the next par_len lines, starting with first_line, in the - * cutbuffer, not allowing them to be concatenated. We assume there - * are enough lines after first_line. We leave copies of the lines in - * place, too. We return the new copy of first_line. */ +/* Put the next par_len lines, starting with first_line, into the + * justify buffer, leaving copies of those lines in place. Assume there + * are enough lines after first_line. Return the new copy of + * first_line. */ filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t quote_len) { - /* We put the original lines, not copies, into the cutbuffer, just - * out of a misguided sense of consistency, so if you uncut, you get - * the actual same paragraph back, not a copy. */ - filestruct *alice = first_line; + filestruct *top = first_line; + /* The top of the paragraph we're backing up. */ + filestruct *bot = first_line; + /* The bottom of the paragraph we're backing up. */ + size_t i; + /* Generic loop variable. */ + size_t current_x_save = current_x; + int fl_lineno_save = first_line->lineno; + int edittop_lineno_save = edittop->lineno; + int current_lineno_save = current->lineno; +#ifndef NANO_SMALL + bool old_mark_set = ISSET(MARK_ISSET); + int mbb_lineno_save = 0; - set_modified(); - cutbuffer = NULL; - for (; par_len > 0; par_len--) { - filestruct *bob = copy_node(alice); + if (old_mark_set) + mbb_lineno_save = mark_beginbuf->lineno; +#endif + + /* Move bot down par_len lines to the newline after the last line of + * the paragraph. */ + for (i = par_len; i > 0; i--) + bot = bot->next; + + /* Move the paragraph from the main filestruct to the justify + * buffer. */ + move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot, 0); + + /* Copy the paragraph from the justify buffer to the main + * filestruct. */ + copy_from_filestruct(jusbuffer, jusbottom); - if (alice == first_line) - first_line = bob; - if (alice == current) - current = bob; - if (alice == edittop) - edittop = bob; + /* Move upward from the last line of the paragraph to the first + * line, putting first_line, edittop, current, and mark_beginbuf at + * the same lines in the copied paragraph that they had in the + * original paragraph. */ + top = current->prev; + for (i = par_len; i > 0; i--) { + if (top->lineno == fl_lineno_save) + first_line = top; + if (top->lineno == edittop_lineno_save) + edittop = top; + if (top->lineno == current_lineno_save) + current = top; #ifndef NANO_SMALL - if (alice == mark_beginbuf) - mark_beginbuf = bob; + if (old_mark_set && top->lineno == mbb_lineno_save) + mark_beginbuf = top; #endif - - assert(alice != NULL && bob != NULL); - add_to_cutbuffer(alice, FALSE); - splice_node(bob->prev, bob, bob->next); - alice = bob->next; + top = top->prev; } + + /* Put current_x at the same place in the copied paragraph that it + * had in the original paragraph. */ + current_x = current_x_save; + + set_modified(); + return first_line; } @@ -2561,13 +2759,10 @@ void do_justify(bool full_justify) { filestruct *first_par_line = NULL; /* Will be the first line of the resulting justified paragraph. - * For restoring after uncut. */ + * For restoring after unjustify. */ filestruct *last_par_line; - /* Will be the last line of the result, also for uncut. */ - filestruct *cutbuffer_save = cutbuffer; - /* When the paragraph gets modified, all lines from the changed - * one down are stored in the cutbuffer. We back up the - * original to restore it later. */ + /* Will be the line containing the newline after the last line + * of the result. Also for restoring after unjustify. */ bool allow_respacing; /* Whether we should change the spacing at the end of a line * after justifying it. This should be TRUE whenever we move @@ -2611,10 +2806,7 @@ void do_justify(bool full_justify) * get out. */ if (do_para_search("e_len, &par_len)) { if (full_justify) { - /* This should be safe in the event of filebot->prev's - * being NULL, since only last_par_line->next is used if - * we eventually unjustify. */ - last_par_line = filebot->prev; + last_par_line = filebot; break; } else { edit_refresh(); @@ -2641,7 +2833,7 @@ void do_justify(bool full_justify) quote_len); /* If we haven't already done it, copy the original - * paragraph to the cutbuffer for unjustification. */ + * paragraph to the justify buffer. */ if (first_par_line == NULL) first_par_line = backup_lines(current, full_justify ? filebot->lineno - current->lineno : par_len, @@ -2829,7 +3021,7 @@ void do_justify(bool full_justify) * fileage, and renumber() since edit_refresh() needs the line * numbers to be right (but only do the last two if we actually * justified something). */ - last_par_line = current->prev; + last_par_line = current; if (first_par_line != NULL) { if (first_par_line->prev == NULL) fileage = first_par_line; @@ -2846,53 +3038,65 @@ void do_justify(bool full_justify) /* Now get a keystroke and see if it's unjustify; if not, unget the * keystroke and return. */ - kbinput = get_edit_input(&meta_key, &func_key, FALSE); + kbinput = get_edit_input(&meta_key, &func_key); if (!meta_key && !func_key && kbinput == NANO_UNJUSTIFY_KEY) { /* Restore the justify we just did (ungrateful user!). */ - filestruct *cutbottom = get_cutbottom(); - current = current_save; current_x = current_x_save; current_y = current_y_save; edittop = edittop_save; - /* Splice the cutbuffer back into the file, but only if we + /* Splice the justify buffer back into the file, but only if we * actually justified something. */ if (first_par_line != NULL) { - cutbottom->next = last_par_line->next; - cutbottom->next->prev = cutbottom; - /* The line numbers after the end of the paragraph have been - * changed, so we change them back. */ - renumber(cutbottom->next); - if (first_par_line->prev != NULL) { - cutbuffer->prev = first_par_line->prev; - cutbuffer->prev->next = cutbuffer; - } else - fileage = cutbuffer; + filestruct *bot_save; - last_par_line->next = NULL; - free_filestruct(first_par_line); - } + /* Partition the filestruct so that it contains only the + * text of the justified paragraph. */ + filepart = partition_filestruct(first_par_line, 0, + last_par_line, 0); - /* Restore global variables from before the justify. */ - totsize = totsize_save; - totlines = filebot->lineno; + /* Remove the text of the justified paragraph, and + * put the text in the justify buffer in its place. */ + free_filestruct(fileage); + fileage = jusbuffer; + filebot = jusbottom; + + bot_save = filebot; + + /* Unpartition the filestruct so that it contains all the + * text again. Note that the justified paragraph has been + * replaced with the unjustified paragraph. */ + unpartition_filestruct(&filepart); + + /* Renumber starting with the ending line of the old + * partition. */ + if (bot_save->next != NULL) + renumber(bot_save->next); + + /* Restore global variables from before the justify. */ + totsize = totsize_save; + totlines = filebot->lineno; #ifndef NANO_SMALL - mark_beginbuf = mark_beginbuf_save; - mark_beginx = mark_beginx_save; + mark_beginbuf = mark_beginbuf_save; + mark_beginx = mark_beginx_save; #endif - flags = flags_save; - if (!ISSET(MODIFIED)) - titlebar(NULL); - edit_refresh(); + flags = flags_save; + + /* Clear the justify buffer. */ + jusbuffer = NULL; + + if (!ISSET(MODIFIED)) + titlebar(NULL); + edit_refresh(); + } } else { - placewewant = 0; - unget_kbinput(kbinput, meta_key, func_key); + /* Blow away the justify buffer.*/ + free_filestruct(jusbuffer); + jusbuffer = NULL; } - cutbuffer = cutbuffer_save; - /* Note that now cutbottom is invalid, but that's okay. */ blank_statusbar(); /* Display the shortcut list with UnCut. */ @@ -3060,6 +3264,16 @@ void handle_sigwinch(int s) if (filepart != NULL) unpartition_filestruct(&filepart); +#ifndef DISABLE_JUSTIFY + /* If the justify buffer isn't empty, blow it away and display the + * shortcut list with UnCut. */ + if (jusbuffer != NULL) { + free_filestruct(jusbuffer); + jusbuffer = NULL; + shortcut_init(FALSE); + } +#endif + #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 @@ -3741,7 +3955,7 @@ int main(int argc, char **argv) currshortcut = main_list; #endif - kbinput = get_edit_input(&meta_key, &func_key, TRUE); + kbinput = get_edit_input(&meta_key, &func_key); /* Last gasp, stuff that's not in the main lists. */ if (kbinput != ERR && !is_cntrl_char(kbinput)) { diff --git a/src/nano.h b/src/nano.h index d2d55e18..7e79053b 100644 --- a/src/nano.h +++ b/src/nano.h @@ -145,10 +145,6 @@ #endif /* Enumeration types. */ -typedef enum { - CUT_LINE, CUT_MARKED, CUT_TO_EOL -} cut_type; - typedef enum { NIX_FILE, DOS_FILE, MAC_FILE } file_format; diff --git a/src/proto.h b/src/proto.h index 094af605..4065da18 100644 --- a/src/proto.h +++ b/src/proto.h @@ -86,6 +86,9 @@ extern char *alt_speller; extern struct stat fileinfo; extern filestruct *current, *fileage, *edittop, *filebot; extern filestruct *cutbuffer; +#ifndef DISABLE_JUSTIFY +extern filestruct *jusbuffer; +#endif extern partition *filepart; #ifndef NANO_SMALL extern filestruct *mark_beginbuf; @@ -158,11 +161,11 @@ void update_color(void); /* Public functions in cut.c. */ void cutbuffer_reset(void); -filestruct *get_cutbottom(void); -void add_to_cutbuffer(filestruct *inptr, bool allow_concat); +void cut_line(void); #ifndef NANO_SMALL -void cut_marked_segment(void); +void cut_marked(void); #endif +void cut_to_eol(void); void do_cut_text(void); void do_uncut_text(void); @@ -308,6 +311,9 @@ void free_filestruct(filestruct *src); partition *partition_filestruct(filestruct *top, size_t top_x, filestruct *bot, size_t bot_x); void unpartition_filestruct(partition **p); +void move_to_filestruct(filestruct **file_top, filestruct **file_bot, + filestruct *top, size_t top_x, filestruct *bot, size_t bot_x); +void copy_from_filestruct(filestruct *file_top, filestruct *file_bot); void renumber_all(void); void renumber(filestruct *fileptr); void print1opt(const char *shortflag, const char *longflag, const char @@ -504,11 +510,11 @@ 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, bool *right_side_up); #endif +void get_totals(const filestruct *begin, const filestruct *end, int + *lines, long *size); #ifndef DISABLE_TABCOMP int check_wildcard_match(const char *text, const char *pattern); #endif @@ -549,7 +555,7 @@ const shortcut *get_shortcut(const shortcut *s_list, int kbinput, bool #ifndef NANO_SMALL const toggle *get_toggle(int kbinput, bool meta_key); #endif -int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs); +int get_edit_input(bool *meta_key, bool *func_key); #ifndef DISABLE_MOUSE bool get_edit_mouse(void); #endif diff --git a/src/utils.c b/src/utils.c index 463bce99..7ee72f1b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -456,6 +456,35 @@ void remove_magicline(void) } } +/* 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. If + * right_side_up isn't NULL, set it to TRUE If the mark begins with + * (mark_beginbuf, mark_beginx) and ends with (current, current_x), or + * FALSE otherwise. */ +void mark_order(const filestruct **top, size_t *top_x, const filestruct + **bot, size_t *bot_x, bool *right_side_up) +{ + assert(top != NULL && top_x != NULL && bot != NULL && bot_x != NULL); + + if ((current->lineno == mark_beginbuf->lineno && current_x > + mark_beginx) || current->lineno > mark_beginbuf->lineno) { + *top = mark_beginbuf; + *top_x = mark_beginx; + *bot = current; + *bot_x = current_x; + if (right_side_up != NULL) + *right_side_up = TRUE; + } else { + *bot = mark_beginbuf; + *bot_x = mark_beginx; + *top = current; + *top_x = current_x; + if (right_side_up != NULL) + *right_side_up = FALSE; + } +} +#endif + /* 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 @@ -501,35 +530,6 @@ void get_totals(const filestruct *begin, const filestruct *end, int } } -/* 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. If - * right_side_up isn't NULL, set it to TRUE If the mark begins with - * (mark_beginbuf, mark_beginx) and ends with (current, current_x), or - * FALSE otherwise. */ -void mark_order(const filestruct **top, size_t *top_x, const filestruct - **bot, size_t *bot_x, bool *right_side_up) -{ - assert(top != NULL && top_x != NULL && bot != NULL && bot_x != NULL); - - if ((current->lineno == mark_beginbuf->lineno && current_x > - mark_beginx) || current->lineno > mark_beginbuf->lineno) { - *top = mark_beginbuf; - *top_x = mark_beginx; - *bot = current; - *bot_x = current_x; - if (right_side_up != NULL) - *right_side_up = TRUE; - } else { - *bot = mark_beginbuf; - *bot_x = mark_beginx; - *top = current; - *top_x = current_x; - if (right_side_up != NULL) - *right_side_up = FALSE; - } -} -#endif - #ifndef DISABLE_TABCOMP /* * Routine to see if a text string is matched by a wildcard pattern. diff --git a/src/winio.c b/src/winio.c index 7c8be144..47b636f6 100644 --- a/src/winio.c +++ b/src/winio.c @@ -1491,7 +1491,7 @@ const toggle *get_toggle(int kbinput, bool meta_key) } #endif /* !NANO_SMALL */ -int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs) +int get_edit_input(bool *meta_key, bool *func_key) { bool keyhandled = FALSE; int kbinput, retval; @@ -1536,12 +1536,10 @@ int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs) if (s->func != do_cut_text) cutbuffer_reset(); if (s->func != NULL) { - if (allow_funcs) { - if (ISSET(VIEW_MODE) && !s->viewok) - print_view_warning(); - else - s->func(); - } + if (ISSET(VIEW_MODE) && !s->viewok) + print_view_warning(); + else + s->func(); keyhandled = TRUE; } } @@ -1555,8 +1553,7 @@ int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs) * corresponding flag. */ if (t != NULL) { cutbuffer_reset(); - if (allow_funcs) - do_toggle(t); + do_toggle(t); keyhandled = TRUE; } } @@ -1565,7 +1562,7 @@ int get_edit_input(bool *meta_key, bool *func_key, bool allow_funcs) /* If we got a shortcut with a corresponding function or a toggle, * reset meta_key and retval. If we didn't, keep the value of * meta_key and return the key we got in retval. */ - if (allow_funcs && keyhandled) { + if (keyhandled) { *meta_key = FALSE; retval = ERR; } else {