]> git.wh0rd.org Git - nano.git/commitdiff
add code to partition a filestruct between a set of arbitrary
authorDavid Lawrence Ramsey <pooka109@gmail.com>
Wed, 3 Nov 2004 22:03:41 +0000 (22:03 +0000)
committerDavid Lawrence Ramsey <pooka109@gmail.com>
Wed, 3 Nov 2004 22:03:41 +0000 (22:03 +0000)
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
TODO
src/files.c
src/global.c
src/nano.c
src/nano.h
src/proto.h
src/search.c
src/utils.c

index 69edcebaa17741360789c13cc79fb64581ee7410..782b92e408b658e1a75f3aee1471b7005f0523cd 100644 (file)
--- 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 8ea816f6f4644d014e52b54ecf6837db769d3e92..d8169416c3d04e391bf8a73aef012c9604353d8d 100644 (file)
--- 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
index b144652cc8ff889dcf9c9b1aa5559eea877d30a8..40e365e7642cd2a185e6ff1210c836a4eb5ac0a8 100644 (file)
@@ -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();
 
index 3cbce14e35ecfb77c856c98c79dbd0badf51f19d..a6ef0b6204c3b4f97af7fe51907514bf367c537c 100644 (file)
@@ -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
index 50d0bf194841e7bae2122c0ddce09fb2b2947158..25784f34b1120b580c798b82fc8965a0f7a047d0 100644 (file)
@@ -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, &current_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
index 163463a2c6e4c39a863de33f93dcdedf53eb510d..c453467fdb2237ba4a8e86aa70b456930e4230e4 100644 (file)
@@ -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
index 108a9c62ade2a8840a209c65a9b065362a5e1af7..396a6dadb7a3f43209fbf72397d302ab12e3de36 100644 (file)
@@ -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
index 71bf7d051ebbdd3300a346dc97b77011daaff4fa..67ecc03d76196a5333fc5dfb43d69e036f3cdc4b 100644 (file)
@@ -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;
 }
 
index 1a52e1f9b6378c8cb887b34f58209e7511273a03..5606c3f7aaa8a759284cc4d371b747eaa189d54d 100644 (file)
@@ -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