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
- 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
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();
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
}
}
+#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;
#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. */
/* 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();
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;
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;
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;
if (regexp_set)
SET(USE_REGEXP);
#endif
-#ifndef NANO_SMALL
- /* Restore marking highlight. */
- if (old_mark_set)
- SET(MARK_ISSET);
-#endif
return !canceled;
}
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);
}
#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
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
} 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
extern filestruct *current, *fileage, *edittop, *filebot;
extern filestruct *cutbuffer;
#ifndef NANO_SMALL
+extern partition *filepart;
extern filestruct *mark_beginbuf;
#endif
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
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
#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();
}
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
}
}
- /* 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;
}
}
#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