From: David Lawrence Ramsey Date: Fri, 27 Sep 2002 14:21:59 +0000 (+0000) Subject: DB's rewrite of the screen update and color routines X-Git-Tag: v1.1.11~4 X-Git-Url: https://git.wh0rd.org/?a=commitdiff_plain;h=1f28b8f400beacc457e63a702357f5b0c172333e;p=nano.git DB's rewrite of the screen update and color routines git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1285 35c25a1d-7b9e-4130-9fde-d3aeb78583b8 --- diff --git a/ChangeLog b/ChangeLog index 193dba82..aedb9a04 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,9 @@ CVS code - properly. New function init_operating_dir() to handle setting it both on the command line and in the nanorc file. (David Benbennick) + - Major rewrite of color and screen update routines to fix + minor bugs and increase efficiency. New function + set_colorpairs() for the former. (David Benbennick) - configure.ac: - Added pt_BR to ALL_LINGUAS (Jordi). - Changed --enable-color warning to be slightly less severe. @@ -122,7 +125,9 @@ CVS code - format ({} instead of \{\}) (found by DLR). - Add a better string matching sequence that includes escaped quotes (thanks to Carl E. Lindberg, who doesn't even know he - helped ;-) + helped ;-). Some unneeded \'s in that sequence removed, and + a new sequence to handle multi-line quotes added, by David + Benbennick. - rcfile.c: parse_colors() - Stop infinite loop when syntax doesn't begin with " char. diff --git a/color.c b/color.c index beae8890..e26b5a58 100644 --- a/color.c +++ b/color.c @@ -40,72 +40,91 @@ #define _(string) (string) #endif -void do_colorinit(void) +/* For each syntax list entry, we go through the list of colors and + * assign color pairs. */ +void set_colorpairs(void) { - int i; - colortype *tmpcolor = NULL, *beforenow = NULL; - int defok = 0; + const syntaxtype *this_syntax = syntaxes; + + for(; this_syntax != NULL; this_syntax = this_syntax->next) { + colortype *this_color = this_syntax->color; + int color_pair = 1; + + for(; this_color != NULL; this_color = this_color->next) { + const colortype *beforenow = this_syntax->color; + + for(; beforenow != NULL && beforenow != this_color && + (beforenow->fg != this_color->fg || + beforenow->bg != this_color->bg || + beforenow->bright != this_color->bright); + beforenow = beforenow->next) + ; + + if (beforenow != NULL && beforenow != this_color) + this_color->pairnum = beforenow->pairnum; + else { + this_color->pairnum = color_pair; + color_pair++; + } + } + } +} +void do_colorinit(void) +{ if (has_colors()) { + const colortype *tmpcolor = NULL; +#ifdef HAVE_USE_DEFAULT_COLORS + int defok; +#endif + start_color(); /* Add in colors, if available */ #ifdef HAVE_USE_DEFAULT_COLORS - if (use_default_colors() != ERR) - defok = 1; + defok = use_default_colors() != ERR; #endif - i = 1; for (tmpcolor = colorstrings; tmpcolor != NULL; tmpcolor = tmpcolor->next) { + short background = tmpcolor->bg; - for (beforenow = colorstrings; beforenow != NULL - && beforenow != tmpcolor && - (beforenow->fg != tmpcolor->fg || beforenow->bg != tmpcolor->bg - || beforenow->bright != tmpcolor->bright); - beforenow = beforenow->next) - ; + if (background == -1) +#ifdef HAVE_USE_DEFAULT_COLORS + if (!defok) +#endif + background = COLOR_BLACK; - if (beforenow != NULL && beforenow != tmpcolor) { - tmpcolor->pairnum = beforenow->pairnum; - continue; - } - - if (defok && tmpcolor->bg == -1) - init_pair(i, tmpcolor->fg, -1); - else if (tmpcolor->bg == -1) - init_pair(i, tmpcolor->fg, COLOR_BLACK); - else /* They picked a fg and bg color */ - init_pair(i, tmpcolor->fg, tmpcolor->bg); + init_pair(tmpcolor->pairnum, tmpcolor->fg, background); #ifdef DEBUG fprintf(stderr, _("Running init_pair with fg = %d and bg = %d\n"), tmpcolor->fg, tmpcolor->bg); #endif - - tmpcolor->pairnum = i; - i++; } } - - return; } /* Update the color information based on the current filename */ void update_color(void) { - syntaxtype *tmpsyntax; + const syntaxtype *tmpsyntax; colorstrings = NULL; for (tmpsyntax = syntaxes; tmpsyntax != NULL; tmpsyntax = tmpsyntax->next) { - exttype *e; + const exttype *e; + for (e = tmpsyntax->extensions; e != NULL; e = e->next) { - regcomp(&syntaxfile_regexp, e->val, REG_EXTENDED); + regex_t syntaxfile_regexp; + + regcomp(&syntaxfile_regexp, e->val, REG_EXTENDED | REG_NOSUB); /* Set colorstrings if we matched the extension regex */ - if (!regexec(&syntaxfile_regexp, filename, 1, synfilematches, 0)) + if (!regexec(&syntaxfile_regexp, filename, 0, NULL, 0)) colorstrings = tmpsyntax->color; regfree(&syntaxfile_regexp); + if (colorstrings != NULL) + break; } } diff --git a/global.c b/global.c index 74a171f7..0e978606 100644 --- a/global.c +++ b/global.c @@ -125,7 +125,7 @@ shortcut *browser_list = NULL; #endif #ifdef ENABLE_COLOR - colortype *colorstrings = NULL; + const colortype *colorstrings = NULL; syntaxtype *syntaxes = NULL; char *syntaxstr = NULL; #endif @@ -145,13 +145,6 @@ regex_t search_regexp; /* Global to store compiled search regexp */ regmatch_t regmatches[10]; /* Match positions for parenthetical subexpressions, max of 10 */ #endif -#ifdef ENABLE_COLOR -regex_t color_regexp; /* Global to store compiled search regexp */ -regmatch_t colormatches[1]; /* Match positions for parenthetical */ - -regex_t syntaxfile_regexp; /* Global to store compiled search regexp */ -regmatch_t synfilematches[1]; /* Match positions for parenthetical */ -#endif /* ENABLE_COLOR */ int length_of_list(const shortcut *s) { diff --git a/nanorc.sample b/nanorc.sample index 15305bc7..908cd458 100644 --- a/nanorc.sample +++ b/nanorc.sample @@ -102,7 +102,8 @@ # because syntax highlighting rules will be applied in the order they are # read in -# color brightyellow "<[^= ]*>" "\"(\\.|[^\\"])*\"" +# color brightyellow "<[^= ]*>" ""(\\.|[^\"])*"" +# color brightyellow start=""(\\.|[^\"])*\\( | )*$" end="^(\\.|[^\"])*"" # color brightblue "//.*" # color brightblue start="/\*" end="\*/" diff --git a/proto.h b/proto.h index 50fb5549..0818fb35 100644 --- a/proto.h +++ b/proto.h @@ -71,11 +71,9 @@ extern openfilestruct *open_files; #endif #ifdef ENABLE_COLOR -extern colortype *colorstrings; +extern const colortype *colorstrings; extern syntaxtype *syntaxes; extern char *syntaxstr; -extern regex_t color_regexp; -extern regmatch_t colormatches[1]; #endif extern shortcut *shortcut_list; @@ -110,6 +108,7 @@ extern toggle *toggles; /* Public functions in color.c */ #ifdef ENABLE_COLOR +void set_colorpairs(void); void do_colorinit(void); void update_color(void); #endif /* ENABLE_COLOR */ @@ -401,15 +400,18 @@ void set_modified(void); void titlebar(const char *path); void bottombars(const shortcut *s); void onekey(const char *keystroke, const char *desc, int len); -int get_page_start_virtual(int page); -int get_page_from_virtual(int virtual); -int get_page_end_virtual(int page); +#ifndef NDEBUG +int check_linenumbers(const filestruct *fileptr); +#endif int get_page_start(int column); void reset_cursor(void); void add_marked_sameline(int begin, int end, filestruct *fileptr, int y, int virt_cur_x, int this_page); -void edit_add(filestruct *fileptr, int yval, int start, int virt_cur_x, - int virt_mark_beginx, int this_page); +void edit_add(const filestruct *fileptr, int yval, int start +#ifndef NANO_SMALL + , int virt_mark_beginx, int virt_cur_x +#endif + ); void update_line(filestruct *fileptr, int index); void update_cursor(void); void center_cursor(void); diff --git a/rcfile.c b/rcfile.c index 32c06fb4..0f55f116 100644 --- a/rcfile.c +++ b/rcfile.c @@ -631,6 +631,9 @@ void do_rcfile(void) } free(nanorc); +#ifdef ENABLE_COLOR + set_colorpairs(); +#endif } #endif /* ENABLE_NANORC */ diff --git a/winio.c b/winio.c index e672ced1..fb79153b 100644 --- a/winio.c +++ b/winio.c @@ -59,32 +59,6 @@ int do_last_line(void) return 1; } - -/* Like xplustabs, but for a specific index of a specific filestruct */ -int xpt(const filestruct *fileptr, int index) -{ - int i, tabs = 0; - - if (fileptr == NULL || fileptr->data == NULL) - return 0; - - for (i = 0; i < index && fileptr->data[i] != 0; i++) { - tabs++; - - if (fileptr->data[i] == NANO_CONTROL_I) { - if (tabs % tabsize == 0); - else - tabs += tabsize - (tabs % tabsize); - } else if (is_cntrl_char((int)fileptr->data[i])) - tabs++; - else if (fileptr->data[i] & 0x80) - /* Make 8 bit chars only 1 column! */ - ; - } - - return tabs; -} - /* Return the placewewant associated with current_x. That is, xplustabs * is the zero-based column position of the cursor. Value is no smaller * than current_x. */ @@ -575,41 +549,23 @@ void onekey(const char *keystroke, const char *desc, int len) } } -/* And so start the display update routines. Given a column, this - * returns the "page" it is on. "page", in the case of the display - * columns, means which set of 80 characters is viewable (e.g. page 1 - * shows from 1 to COLS). */ -int get_page_from_virtual(int virtual) -{ - int page = 2; - - if (virtual <= COLS - 2) - return 1; - virtual -= (COLS - 2); - - while (virtual > COLS - 2 - 7) { - virtual -= (COLS - 2 - 7); - page++; - } - - return page; -} +/* And so start the display update routines. */ -/* The inverse of the above function */ -int get_page_start_virtual(int page) +#ifndef NDEBUG +int check_linenumbers(const filestruct *fileptr) { - int virtual; - virtual = --page * (COLS - 7); - if (page) - virtual -= 2 * page - 1; - return virtual; -} + int check_line = 0; + const filestruct *filetmp; -int get_page_end_virtual(int page) -{ - return get_page_start_virtual(page) + COLS - 1; + for (filetmp = edittop; filetmp != fileptr; filetmp = filetmp->next) + check_line++; + return check_line; } +#endif + /* nano scrolls horizontally within a line in chunks. This function + * returns the column number of the first character displayed in the + * window when the cursor is at the given column. */ int get_page_start(int column) { assert(COLS > 9); @@ -639,474 +595,380 @@ void reset_cursor(void) wmove(edit, current_y, x - get_page_start(x)); } -#ifndef NANO_SMALL -/* This takes care of the case where there is a mark that covers only - * the current line. It expects a line with no tab characters (i.e. - * the type that edit_add() deals with. */ -void add_marked_sameline(int begin, int end, filestruct *fileptr, int y, - int virt_cur_x, int this_page) -{ - /* - * The general idea is to break the line up into 3 sections: before - * the mark, the mark, and after the mark. We then paint each in - * turn (for those that are currently visible, of course) - * - * 3 start points: 0 -> begin, begin->end, end->strlen(data) - * in data : pre sel post - */ - int this_page_start = get_page_start_virtual(this_page), - this_page_end = get_page_end_virtual(this_page); - - /* likewise, 3 data lengths */ - int pre_data_len = begin, sel_data_len = end - begin, post_data_len = 0; /* Determined from the other two */ - - /* now fix the start locations & lengths according to the cursor's - * position (i.e.: our page) */ - if (pre_data_len < this_page_start) - pre_data_len = 0; - else - pre_data_len -= this_page_start; - - if (begin < this_page_start) - begin = this_page_start; - - if (end < this_page_start) - end = this_page_start; - - if (begin > this_page_end) - begin = this_page_end; - - if (end > this_page_end) - end = this_page_end; - - /* Now calculate the lengths */ - sel_data_len = end - begin; - post_data_len = this_page_end - end; - - wattron(edit, A_REVERSE); - mvwaddnstr(edit, y, begin - this_page_start, - &fileptr->data[begin], sel_data_len); - - wattroff(edit, A_REVERSE); - -} -#endif - /* edit_add() takes care of the job of actually painting a line into * the edit window. Called only from update_line(). Expects a * converted-to-not-have-tabs line. */ -void edit_add(filestruct *fileptr, int yval, int start, int virt_cur_x, - int virt_mark_beginx, int this_page) +void edit_add(const filestruct *fileptr, int yval, int start +#ifndef NANO_SMALL + , int virt_mark_beginx, int virt_cur_x +#endif + ) { - -#ifdef ENABLE_COLOR - const colortype *tmpcolor = NULL; - int k, paintlen; - filestruct *e, *s; - regoff_t ematch, smatch; +#ifdef DEBUG + fprintf(stderr, "Painting line %d, current is %d\n", fileptr->lineno, + current->lineno); #endif /* Just paint the string in any case (we'll add color or reverse on just the text that needs it */ - mvwaddnstr(edit, yval, 0, &fileptr->data[start], - get_page_end_virtual(this_page) - start + 1); + mvwaddnstr(edit, yval, 0, &fileptr->data[start], COLS); #ifdef ENABLE_COLOR - if (colorstrings != NULL) - for (tmpcolor = colorstrings; tmpcolor != NULL; - tmpcolor = tmpcolor->next) { - + if (colorstrings != NULL) { + const colortype *tmpcolor = colorstrings; + + for (; tmpcolor != NULL; tmpcolor = tmpcolor->next) { + int x_start; + /* Starting column for mvwaddnstr. Zero-based. */ + int paintlen; + /* number of chars to paint on this line. There are COLS + * characters on a whole line. */ + regex_t start_regexp; /* Compiled search regexp */ + regmatch_t startmatch; /* match position for start_regexp*/ + regmatch_t endmatch; /* match position for end_regexp*/ + + regcomp(&start_regexp, tmpcolor->start, REG_EXTENDED); + + if (tmpcolor->bright) + wattron(edit, A_BOLD); + wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); + /* Two notes about regexec. Return value 0 means there is a + * match. Also, rm_eo is the first non-matching character + * after the match. */ + + /* First case, tmpcolor is a single-line expression. */ if (tmpcolor->end == NULL) { - - /* First, highlight all single-line regexes */ - k = start; - regcomp(&color_regexp, tmpcolor->start, REG_EXTENDED); - while (!regexec(&color_regexp, &fileptr->data[k], 1, - colormatches, 0)) { - - if (colormatches[0].rm_eo - colormatches[0].rm_so < 1) { - statusbar(_("Refusing 0 length regex match")); + size_t k = 0; + + /* We increment k by rm_eo, to move past the end of the + last match. Even though two matches may overlap, we + want to ignore them, so that we can highlight C-strings + correctly. */ + while (k < start + COLS) { + /* Note the fifth parameter to regexec. It says not to + * match the beginning-of-line character unless + * k == 0. If regexec returns non-zero, there are + * no more matches in the line. */ + if (regexec(&start_regexp, &fileptr->data[k], 1, + &startmatch, k == 0 ? 0 : REG_NOTBOL)) break; - } -#ifdef DEBUG - fprintf(stderr, _("Match! (%d chars) \"%s\"\n"), - colormatches[0].rm_eo - colormatches[0].rm_so, - &fileptr->data[k + colormatches[0].rm_so]); -#endif - if (colormatches[0].rm_so < COLS - 1) { - if (tmpcolor->bright) - wattron(edit, A_BOLD); - wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); - - if (colormatches[0].rm_eo + k <= COLS) { - paintlen = - colormatches[0].rm_eo - colormatches[0].rm_so; -#ifdef DEBUG - fprintf(stderr, _("paintlen (%d) = eo (%d) - so (%d)\n"), - paintlen, colormatches[0].rm_eo, colormatches[0].rm_so); -#endif - - } - else { - paintlen = COLS - k - colormatches[0].rm_so - 1; -#ifdef DEBUG - fprintf(stderr, _("paintlen (%d) = COLS (%d) - k (%d), - rm.so (%d) - 1\n"), - paintlen, COLS, k, colormatches[0].rm_so); -#endif - } - - mvwaddnstr(edit, yval, colormatches[0].rm_so + k, - &fileptr->data[k + colormatches[0].rm_so], - paintlen); - - } - - if (tmpcolor->bright) - wattroff(edit, A_BOLD); - wattroff(edit, COLOR_PAIR(tmpcolor->pairnum)); - - k += colormatches[0].rm_eo; - + /* Translate the match to the beginning of the line. */ + startmatch.rm_so += k; + startmatch.rm_eo += k; + if (startmatch.rm_so == startmatch.rm_eo) + statusbar(_("Refusing 0 length regex match")); + else if (startmatch.rm_so < start + COLS && + startmatch.rm_eo > start) { + x_start = startmatch.rm_so - start; + if (x_start < 0) + x_start = 0; + paintlen = startmatch.rm_eo - start - x_start; + if (paintlen > COLS - x_start) + paintlen = COLS - x_start; + + assert(0 <= x_start && 0 < paintlen && + x_start + paintlen <= COLS); + mvwaddnstr(edit, yval, x_start, + fileptr->data + start + x_start, paintlen); + } + k = startmatch.rm_eo; } - regfree(&color_regexp); - - } - /* Now, if there's an 'end' somewhere below, and a 'start' - somewhere above, things get really fun. We have to look - down for an end, make sure there's not a start before - the end after us, and then look up for a start, - and see if there's an end after the start, before us :) */ - else { - - s = fileptr; - while (s != NULL) { - regcomp(&color_regexp, tmpcolor->start, REG_EXTENDED); - if (!regexec - (&color_regexp, s->data, 1, colormatches, 0)) { - regfree(&color_regexp); + } else { + /* This is a multi-line regexp. There are two steps. + * First, we have to see if the beginning of the line is + * colored by a start on an earlier line, and an end on + * this line or later. + * + * We find the first line before fileptr matching the + * start. If every match on that line is followed by an + * end, then go to step two. Otherwise, find the next line + * after start_line matching the end. If that line is not + * before fileptr, then paint the beginning of this line. */ + + regex_t end_regexp; /* Compiled search regexp */ + const filestruct *start_line = fileptr->prev; + /* the first line before fileptr matching start*/ + regoff_t start_col; + /* where it starts in that line */ + const filestruct *end_line; + int searched_later_lines = 0; + /* Used in step 2. Have we looked for an end on + * lines after fileptr? */ + + regcomp(&end_regexp, tmpcolor->end, REG_EXTENDED); + + while (start_line != NULL && + regexec(&start_regexp, start_line->data, 1, + &startmatch, 0)) { + /* If there is an end on this line, there is no need + * to look for starts on earlier lines. */ + if (!regexec(&end_regexp, start_line->data, 1, + &endmatch, 0)) + goto step_two; + start_line = start_line->prev; + } + /* No start found, so skip to the next step. */ + if (start_line == NULL) + goto step_two; + /* Now start_line is the first line before fileptr + * containing a start match. Is there a start on this + * line not followed by an end on this line? */ + + start_col = 0; + while (1) { + start_col += startmatch.rm_so; + startmatch.rm_eo -= startmatch.rm_so; + if (regexec(&end_regexp, + start_line->data + start_col + startmatch.rm_eo, + 1, &endmatch, + start_col + startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) + /* No end found after this start */ break; - } - s = s->prev; - regfree(&color_regexp); + start_col++; + if (regexec(&start_regexp, + start_line->data + start_col, 1, &startmatch, + REG_NOTBOL)) + /* No later start on this line. */ + goto step_two; } - - if (s != NULL) { - /* We found a start, mark it */ - smatch = colormatches[0].rm_so; - - e = s; - while (e != NULL && e != fileptr) { - regcomp(&color_regexp, tmpcolor->end, REG_EXTENDED); - if (!regexec - (&color_regexp, e->data, 1, colormatches, 0)) { - regfree(&color_regexp); - break; - } - e = e->next; - regfree(&color_regexp); + /* Indeed, there is a start not followed on this line by an + * end. */ + + /* We have already checked that there is no end before + * fileptr and after the start. Is there an end after + * the start at all? We don't paint unterminated starts. */ + end_line = fileptr; + while (end_line != NULL && + regexec(&end_regexp, end_line->data, 1, + &endmatch, 0)) + end_line = end_line->next; + + /* No end found, or it is too early. */ + if (end_line == NULL || + end_line->lineno < fileptr->lineno || + (end_line == fileptr && endmatch.rm_eo <= start)) + goto step_two; + + /* Now paint the start of fileptr. */ + paintlen = end_line != fileptr + ? COLS : endmatch.rm_eo - start; + if (paintlen > COLS) + paintlen = COLS; + + assert(0 < paintlen && paintlen <= COLS); + mvwaddnstr(edit, yval, 0, fileptr->data + start, paintlen); + + /* We have already painted the whole line. */ + if (paintlen == COLS) + goto skip_step_two; + + + step_two: /* Second step, we look for starts on this line. */ + start_col = 0; + while (start_col < start + COLS) { + if (regexec(&start_regexp, fileptr->data + start_col, 1, + &startmatch, start_col == 0 ? 0 : REG_NOTBOL) + || start_col + startmatch.rm_so >= start + COLS) + /* No more starts on this line. */ + break; + /* Translate the match to be relative to the + * beginning of the line. */ + startmatch.rm_so += start_col; + startmatch.rm_eo += start_col; + + x_start = startmatch.rm_so - start; + if (x_start < 0) { + x_start = 0; + startmatch.rm_so = start; } - - if (e != fileptr) - continue; /* There's an end before us */ - else { /* Keep looking for an end */ - while (e != NULL) { - regcomp(&color_regexp, tmpcolor->end, REG_EXTENDED); - if (!regexec - (&color_regexp, e->data, 1, colormatches, - 0)) { - regfree(&color_regexp); - break; - } - e = e->next; - regfree(&color_regexp); + if (!regexec(&end_regexp, fileptr->data + startmatch.rm_eo, + 1, &endmatch, + startmatch.rm_eo == 0 ? 0 : REG_NOTBOL)) { + /* Translate the end match to be relative to the + beginning of the line. */ + endmatch.rm_so += startmatch.rm_eo; + endmatch.rm_eo += startmatch.rm_eo; + /* There is an end on this line. But does it + appear on this page, and is the match more than + zero characters long? */ + if (endmatch.rm_eo > start && + endmatch.rm_eo > startmatch.rm_so) { + paintlen = endmatch.rm_eo - start - x_start; + if (x_start + paintlen > COLS) + paintlen = COLS - x_start; + + assert(0 <= x_start && 0 < paintlen && + x_start + paintlen <= COLS); + mvwaddnstr(edit, yval, x_start, + fileptr->data + start + x_start, paintlen); } - - if (e == NULL) - continue; /* There's no start before the end :) */ - else { /* Okay, we found an end, mark it! */ - ematch = colormatches[0].rm_eo; - - while (e != NULL) { - regcomp(&color_regexp, tmpcolor->end, REG_EXTENDED); - if (!regexec - (&color_regexp, e->data, 1, - colormatches, 0)) { - regfree(&color_regexp); - break; - } e = e->next; - regfree(&color_regexp); - } - - if (e == NULL) - continue; /* No end, oh well :) */ - - /* Didn't find another end, we must be in the - middle of a highlighted bit */ - - if (tmpcolor->bright) - wattron(edit, A_BOLD); - - wattron(edit, COLOR_PAIR(tmpcolor->pairnum)); - - if (s == fileptr && e == fileptr && ematch < COLS) { - mvwaddnstr(edit, yval, start + smatch, - &fileptr->data[start + smatch], - ematch - smatch); -#ifdef DEBUG - fprintf(stderr, _("start = %d, smatch = %d, ematch = %d\n"), start, - smatch, ematch); -#endif - - } else if (s == fileptr) - mvwaddnstr(edit, yval, start + smatch, - &fileptr->data[start + smatch], - COLS - smatch); - else if (e == fileptr) - mvwaddnstr(edit, yval, start, - &fileptr->data[start], - COLS - start); - else - mvwaddnstr(edit, yval, start, - &fileptr->data[start], - COLS); - - if (tmpcolor->bright) - wattroff(edit, A_BOLD); - - wattroff(edit, COLOR_PAIR(tmpcolor->pairnum)); - + } else if (!searched_later_lines) { + searched_later_lines = 1; + /* There is no end on this line. But we haven't + * yet looked for one on later lines. */ + end_line = fileptr->next; + while (end_line != NULL && + regexec(&end_regexp, end_line->data, 1, + &endmatch, 0)) + end_line = end_line->next; + if (end_line != NULL) { + assert(0 <= x_start && x_start < COLS); + mvwaddnstr(edit, yval, x_start, + fileptr->data + start + x_start, + COLS - x_start); + /* We painted to the end of the line, so + * don't bother checking any more starts. */ + break; } - } + start_col = startmatch.rm_so + 1; + } /* while start_col < start + COLS */ - /* Else go to the next string, yahoo! =) */ - } - - } - - } + skip_step_two: + regfree(&end_regexp); + } /* if (tmp_color->end != NULL) */ + regfree(&start_regexp); + wattroff(edit, A_BOLD); + wattroff(edit, COLOR_PAIR(tmpcolor->pairnum)); + } /* for tmpcolor in colorstrings */ + } #endif /* ENABLE_COLOR */ -#ifndef NANO_SMALL - /* There are quite a few cases that could take place; we'll deal - * with them each in turn */ - if (ISSET(MARK_ISSET) && - !((fileptr->lineno > mark_beginbuf->lineno - && fileptr->lineno > current->lineno) - || (fileptr->lineno < mark_beginbuf->lineno - && fileptr->lineno < current->lineno))) { - /* If we get here we are on a line that is at least - * partially selected. The lineno checks above determined - * that */ - if (fileptr != mark_beginbuf && fileptr != current) { - /* We are on a completely marked line, paint it all - * inverse */ +#ifndef NANO_SMALL + if (ISSET(MARK_ISSET) + && (fileptr->lineno <= mark_beginbuf->lineno + || fileptr->lineno <= current->lineno) + && (fileptr->lineno >= mark_beginbuf->lineno + || fileptr->lineno >= current->lineno)) { + /* fileptr is at least partially selected. */ + + int x_start; + /* Starting column for mvwaddnstr. Zero-based. */ + int paintlen; + /* number of chars to paint on this line. There are COLS + * characters on a whole line. */ + + if (mark_beginbuf == fileptr && current == fileptr) { + x_start = virt_mark_beginx < virt_cur_x ? virt_mark_beginx + : virt_cur_x; + paintlen = abs(virt_mark_beginx - virt_cur_x); + } else { + if (mark_beginbuf->lineno < fileptr->lineno || + current->lineno < fileptr->lineno) + x_start = 0; + else + x_start = mark_beginbuf == fileptr ? virt_mark_beginx + : virt_cur_x; + if (mark_beginbuf->lineno > fileptr->lineno || + current->lineno > fileptr->lineno) + paintlen = start + COLS; + else + paintlen = mark_beginbuf == fileptr ? virt_mark_beginx + : virt_cur_x; + } + x_start -= start; + if (x_start < 0) { + paintlen += x_start; + x_start = 0; + } + if (x_start + paintlen > COLS) + paintlen = COLS - x_start; + if (paintlen > 0) { wattron(edit, A_REVERSE); - - mvwaddnstr(edit, yval, 0, fileptr->data, COLS); - + assert(x_start >= 0 && paintlen > 0 && x_start + paintlen <= COLS); + mvwaddnstr(edit, yval, x_start, + fileptr->data + start + x_start, paintlen); wattroff(edit, A_REVERSE); - - } else if (fileptr == mark_beginbuf && fileptr == current) { - /* Special case, we're still on the same line we started - * marking -- so we call our helper function */ - if (virt_cur_x < virt_mark_beginx) { - /* To the right of us is marked */ - add_marked_sameline(virt_cur_x, virt_mark_beginx, - fileptr, yval, virt_cur_x, this_page); - } else { - /* To the left of us is marked */ - add_marked_sameline(virt_mark_beginx, virt_cur_x, - fileptr, yval, virt_cur_x, this_page); - } - } else if (fileptr == mark_beginbuf) { - /* - * We're updating the line that was first marked, - * but we're not currently on it. So we want to - * figure out which half to invert based on our - * relative line numbers. - * - * I.e. if we're above the "beginbuf" line, we want to - * mark the left side. Otherwise, we're below, so we - * mark the right. - */ - int target; - - if (mark_beginbuf->lineno > current->lineno) { - - wattron(edit, A_REVERSE); - - target = - (virt_mark_beginx < - COLS - 1) ? virt_mark_beginx : COLS - 1; - - mvwaddnstr(edit, yval, 0, fileptr->data, target); - - wattroff(edit, A_REVERSE); - - } - - if (mark_beginbuf->lineno < current->lineno) { - - wattron(edit, A_REVERSE); - target = (COLS - 1) - virt_mark_beginx; - - if (target < 0) - target = 0; - - mvwaddnstr(edit, yval, virt_mark_beginx, - &fileptr->data[virt_mark_beginx], target); - - wattroff(edit, A_REVERSE); - } - - } else if (fileptr == current) { - /* We're on the cursor's line, but it's not the first - * one we marked. Similar to the previous logic. */ - int this_page_start = get_page_start_virtual(this_page), - this_page_end = get_page_end_virtual(this_page); - - if (mark_beginbuf->lineno < current->lineno) { - - wattron(edit, A_REVERSE); - - if (virt_cur_x > COLS - 2) { - mvwaddnstr(edit, yval, 0, - &fileptr->data[this_page_start], - virt_cur_x - this_page_start); - } else - mvwaddnstr(edit, yval, 0, fileptr->data, virt_cur_x); - - wattroff(edit, A_REVERSE); - - } - - if (mark_beginbuf->lineno > current->lineno) { - - wattron(edit, A_REVERSE); - if (virt_cur_x > COLS - 2) - mvwaddnstr(edit, yval, virt_cur_x - this_page_start, - &fileptr->data[virt_cur_x], - this_page_end - virt_cur_x); - else - mvwaddnstr(edit, yval, virt_cur_x, - &fileptr->data[virt_cur_x], - COLS - virt_cur_x); - - wattroff(edit, A_REVERSE); - - } } } -#endif - +#endif /* !NANO_SMALL */ } -/* - * Just update one line in the edit buffer. Basically a wrapper for - * edit_add(). index gives us a place in the string to update starting - * from. Likely args are current_x or 0. - */ +/* Just update one line in the edit buffer. Basically a wrapper for + * edit_add(). If fileptr != current, then index is considered 0. + * The line will be displayed starting with fileptr->data[index]. + * Likely args are current_x or 0. */ void update_line(filestruct *fileptr, int index) { - filestruct *filetmp; - int line = 0, col = 0; - int virt_cur_x = current_x, virt_mark_beginx = mark_beginx; - char *realdata, *tmp; - int i, pos, len, page; + int line; + /* line in the edit window for CURSES calls */ +#ifndef NANO_SMALL + int virt_cur_x; + int virt_mark_beginx; +#endif + char *original; + /* The original string fileptr->data. */ + char *converted; + /* fileptr->data converted to have tabs and control characters + * expanded. */ + size_t pos; + size_t page_start; if (!fileptr) return; - /* First, blank out the line (at a minimum) */ - for (filetmp = edittop; filetmp != fileptr && filetmp != editbot; - filetmp = filetmp->next) - line++; + line = fileptr->lineno - edittop->lineno; - mvwaddstr(edit, line, 0, hblank); + /* We assume the line numbers are valid. Is that really true? */ + assert(line < 0 || line == check_linenumbers(fileptr)); + + if (line < 0 || line >= editwinrows) + return; - /* Next, convert all the tabs to spaces, so everything else is easy */ - index = xpt(fileptr, index); + /* First, blank out the line (at a minimum) */ + mvwaddstr(edit, line, 0, hblank); - realdata = fileptr->data; - len = strlen(realdata); - fileptr->data = charalloc(xpt(fileptr, len) + 1); + original = fileptr->data; + converted = charalloc(strlenpt(original) + 1); + + /* Next, convert all the tabs to spaces, so everything else is easy. + * Note the internal speller sends us index == -1. */ + index = fileptr == current && index > 0 ? strnlenpt(original, index) : 0; +#ifndef NANO_SMALL + virt_cur_x = fileptr == current ? strnlenpt(original, current_x) : current_x; + virt_mark_beginx = fileptr == mark_beginbuf ? strnlenpt(original, mark_beginx) : mark_beginx; +#endif pos = 0; - for (i = 0; i < len; i++) { - if (realdata[i] == '\t') { + for (; *original != '\0'; original++) { + if (*original == '\t') do { - fileptr->data[pos++] = ' '; - if (i < current_x) - virt_cur_x++; - if (i < mark_beginx) - virt_mark_beginx++; + converted[pos++] = ' '; } while (pos % tabsize); - /* must decrement once to account for tab-is-one-character */ - if (i < current_x) - virt_cur_x--; - if (i < mark_beginx) - virt_mark_beginx--; - } else if (realdata[i] == 127) { - /* Treat delete characters (ASCII 127's) as ^?'s */ - fileptr->data[pos++] = '^'; - fileptr->data[pos++] = '?'; - if (i < current_x) - virt_cur_x++; - if (i < mark_beginx) - virt_mark_beginx++; - } else if (realdata[i] == 10) { - /* Treat newlines (ASCII 10's) embedded in a line as encoded - nulls (ASCII 0's); the line in question should be run - through unsunder() before reaching here */ - fileptr->data[pos++] = '^'; - fileptr->data[pos++] = '@'; - if (i < current_x) - virt_cur_x++; - if (i < mark_beginx) - virt_mark_beginx++; - } else if (is_cntrl_char(realdata[i])) { - /* Treat control characters as ^symbol's */ - fileptr->data[pos++] = '^'; - fileptr->data[pos++] = realdata[i] + 64; - if (i < current_x) - virt_cur_x++; - if (i < mark_beginx) - virt_mark_beginx++; - } else { - fileptr->data[pos++] = realdata[i]; - } + else if (is_cntrl_char(*original)) { + converted[pos++] = '^'; + if (*original == 127) + converted[pos++] = '?'; + else if (*original == '\n') + /* Treat newlines (ASCII 10's) embedded in a line as encoded + * nulls (ASCII 0's); the line in question should be run + * through unsunder() before reaching here */ + converted[pos++] = '@'; + else + converted[pos++] = *original + 64; + } else + converted[pos++] = *original; } - - fileptr->data[pos] = '\0'; + converted[pos] = '\0'; /* Now, paint the line */ - if (current == fileptr && index > COLS - 2) { - /* This handles when the current line is beyond COLS */ - /* It requires figuring out what page we're on */ - page = get_page_from_virtual(index); - col = get_page_start_virtual(page); + original = fileptr->data; + fileptr->data = converted; + page_start = get_page_start(index); + edit_add(fileptr, line, page_start +#ifndef NANO_SMALL + , virt_mark_beginx, virt_cur_x +#endif + ); + free(converted); + fileptr->data = original; - edit_add(filetmp, line, col, virt_cur_x, virt_mark_beginx, page); + if (page_start > 0) mvwaddch(edit, line, 0, '$'); - - if (strlenpt(fileptr->data) > get_page_end_virtual(page) + 1) - mvwaddch(edit, line, COLS - 1, '$'); - } else { - /* It's not the current line means that it's at x=0 and page=1 */ - /* If it is the current line, then we're in the same boat */ - edit_add(filetmp, line, 0, virt_cur_x, virt_mark_beginx, 1); - - if (strlenpt(&filetmp->data[col]) > COLS) - mvwaddch(edit, line, COLS - 1, '$'); - } - - /* Clean up our mess */ - tmp = fileptr->data; - fileptr->data = realdata; - free(tmp); + if (pos > page_start + COLS) + mvwaddch(edit, line, COLS - 1, '$'); } /* This function updates current, based on where current_y is; @@ -1151,7 +1013,7 @@ void edit_refresh(void) if (edittop == NULL) edittop = current; - /* Don't make the cursor jump around the scrrn whilst updating */ + /* Don't make the cursor jump around the screen whilst updating */ leaveok(edit, TRUE); editbot = edittop;