Index: src/files.c =================================================================== --- src/files.c (revision 5092) +++ src/files.c (working copy) @@ -560,6 +560,7 @@ assert(openfile->fileage != NULL && strlen(buf) == buf_len); fileptr->data = mallocstrcpy(NULL, buf); + fileptr->extra_lines = 0; #ifndef NANO_TINY /* If it's a DOS file ("\r\n"), and file conversion isn't disabled, Index: src/move.c =================================================================== --- src/move.c (revision 5092) +++ src/move.c (working copy) @@ -80,7 +80,7 @@ openfile->current = openfile->current->prev; #ifndef NANO_TINY if (ISSET(SOFTWRAP) && openfile->current) { - skipped += strlenpt(openfile->current->data) / COLS; + skipped += openfile->current->extra_lines; #ifdef DEBUG fprintf(stderr, "do_page_up: i = %d, skipped = %d based on line %ld len %lu\n", i, skipped, (long)openfile->current->lineno, (unsigned long)strlenpt(openfile->current->data)); @@ -587,16 +587,16 @@ #ifndef NANO_TINY if (ISSET(SOFTWRAP)) { /* Compute the amount to scroll. */ - amount = (strlenpt(openfile->current->data) / COLS + openfile->current_y + 2 - + strlenpt(openfile->current->prev->data) / COLS - editwinrows); + amount = (openfile->current->extra_lines + openfile->current_y + 2 + + openfile->current->prev->extra_lines - editwinrows); topline = openfile->edittop; /* Reduce the amount when there are overlong lines at the top. */ for (enough = 1; enough < amount; enough++) { - if (amount <= strlenpt(topline->data) / COLS) { + if (amount <= topline->extra_lines) { amount = enough; break; } - amount -= strlenpt(topline->data) / COLS; + amount -= topline->extra_lines; topline = topline->next; } } Index: src/nano.c =================================================================== --- src/nano.c (revision 5092) +++ src/nano.c (working copy) @@ -67,6 +67,7 @@ newnode->prev = prevnode; newnode->next = NULL; newnode->lineno = (prevnode != NULL) ? prevnode->lineno + 1 : 1; + newnode->extra_lines = 0; #ifndef DISABLE_COLOR newnode->multidata = NULL; @@ -88,6 +89,7 @@ dst->next = src->next; dst->prev = src->prev; dst->lineno = src->lineno; + dst->extra_lines = src->extra_lines; #ifndef DISABLE_COLOR dst->multidata = NULL; #endif @@ -373,6 +375,7 @@ * filestruct. */ openfile->fileage = (filestruct *)nmalloc(sizeof(filestruct)); openfile->fileage->data = mallocstrcpy(NULL, ""); + openfile->fileage->extra_lines = 0; openfile->filebot = openfile->fileage; #ifndef DISABLE_COLOR @@ -1790,7 +1793,7 @@ openfile->current->next && i < mouse_y; openfile->current = openfile->current->next, i++) { openfile->current_y = i; - i += strlenpt(openfile->current->data) / COLS; + i += openfile->current->extra_lines; } #ifdef DEBUG fprintf(stderr, "do_mouse(): moving to current_y = %ld, index i = %lu\n", @@ -1799,8 +1802,19 @@ #endif if (i > mouse_y) { + size_t columns = 0; + const char *line_pos; + openfile->current = openfile->current->prev; - openfile->current_x = actual_x(openfile->current->data, mouse_x + (mouse_y - openfile->current_y) * COLS); + + line_pos = openfile->current->data; + while (mouse_y > openfile->current_y) { + columns += find_break_col(&line_pos, COLS, NULL); + openfile->current_y++; + } + + openfile->current_x = actual_x(openfile->current->data, mouse_x + columns); + #ifdef DEBUG fprintf(stderr, "do_mouse(): i > mouse_y, mouse_x = %d, current_x to = %lu\n", mouse_x, (unsigned long)openfile->current_x); @@ -1989,7 +2003,7 @@ { size_t current_len, i = 0; #ifndef NANO_TINY - size_t orig_lenpt = 0; + size_t orig_extra_lines = 0; #endif char *char_buf = charalloc(mb_cur_max()); @@ -2001,7 +2015,7 @@ #ifndef NANO_TINY if (ISSET(SOFTWRAP)) - orig_lenpt = strlenpt(openfile->current->data); + orig_extra_lines = openfile->current->extra_lines; #endif while (i < output_len) { @@ -2083,14 +2097,6 @@ #endif } -#ifndef NANO_TINY - /* Well, we might also need a full refresh if we've changed the - * line length to be a new multiple of COLS. */ - if (ISSET(SOFTWRAP) && edit_refresh_needed == FALSE) - if (strlenpt(openfile->current->data) / COLS != orig_lenpt / COLS) - edit_refresh_needed = TRUE; -#endif - free(char_buf); openfile->placewewant = xplustabs(); @@ -2101,9 +2107,17 @@ if (edit_refresh_needed == TRUE) { edit_refresh(); edit_refresh_needed = FALSE; - } else + } else { update_line(openfile->current, openfile->current_x); + +#ifndef NANO_TINY + /* We also need a full refresh when the number of softwraps changed. */ + if (ISSET(SOFTWRAP)) + if (openfile->current->extra_lines != orig_extra_lines) + edit_refresh_needed = TRUE; +#endif } +} int main(int argc, char **argv) { Index: src/nano.h =================================================================== --- src/nano.h (revision 5092) +++ src/nano.h (working copy) @@ -299,6 +299,8 @@ /* Next node. */ struct filestruct *prev; /* Previous node. */ + size_t extra_lines; + /* Number of extra screen lines required; zero when not in softwrap mode. */ #ifndef DISABLE_COLOR short *multidata; /* Array of which multi-line regexes apply to this line. */ Index: src/proto.h =================================================================== --- src/proto.h (revision 5092) +++ src/proto.h (working copy) @@ -785,9 +785,11 @@ void bottombars(int menu); void onekey(const char *keystroke, const char *desc, size_t len); void reset_cursor(void); -void edit_draw(filestruct *fileptr, const char *converted, int - line, size_t start); -int update_line(filestruct *fileptr, size_t index); +//void edit_draw(filestruct *fileptr, const char *converted, int +// line, size_t start); +void edit_draw(filestruct *fileptr, int line, const char *buf, size_t start, size_t len); +size_t find_break_col(const char **line, ssize_t goal, size_t *index); +size_t update_line(filestruct *fileptr, size_t index); bool need_horizontal_update(size_t pww_save); bool need_vertical_update(size_t pww_save); void edit_scroll(scroll_dir direction, ssize_t nlines); Index: src/text.c =================================================================== --- src/text.c (revision 5092) +++ src/text.c (working copy) @@ -67,7 +67,7 @@ void do_deletion(undo_type action) { #ifndef NANO_TINY - size_t orig_lenpt = 0; + size_t orig_extra_lines = 0; #endif assert(openfile->current != NULL && openfile->current->data != NULL && openfile->current_x <= strlen(openfile->current->data)); @@ -86,7 +86,7 @@ update_undo(action); if (ISSET(SOFTWRAP)) - orig_lenpt = strlenpt(openfile->current->data); + orig_extra_lines = openfile->current->extra_lines; #endif /* Let's get dangerous. */ @@ -143,16 +143,16 @@ } else return; + if (edit_refresh_needed == FALSE) + update_line(openfile->current, openfile->current_x); + #ifndef NANO_TINY if (ISSET(SOFTWRAP) && edit_refresh_needed == FALSE) - if (strlenpt(openfile->current->data) / COLS != orig_lenpt / COLS) + if (openfile->current->extra_lines != orig_extra_lines) edit_refresh_needed = TRUE; #endif set_modified(); - - if (edit_refresh_needed == FALSE) - update_line(openfile->current, openfile->current_x); } void do_delete(void) @@ -1306,6 +1306,8 @@ /* Current column position in line. */ int char_len = 0; /* Length of current character, in bytes. */ + bool non_blank_seen = FALSE; + /* Whether something else than whitespace was enountered. */ assert(line != NULL); @@ -1317,13 +1319,15 @@ || (newln && *line == '\n') #endif ) { + if (non_blank_seen) blank_loc = cur_loc; #ifndef DISABLE_HELP if (newln && *line == '\n') break; #endif - } + } else + non_blank_seen = TRUE; line += char_len; cur_loc += char_len; Index: src/utils.c =================================================================== --- src/utils.c (revision 5092) +++ src/utils.c (working copy) @@ -464,7 +464,7 @@ * get_page_start(column) < COLS). */ size_t get_page_start(size_t column) { - if (column == 0 || column < COLS - 1) + if (ISSET(SOFTWRAP) || column == 0 || column < COLS - 1) return 0; else if (COLS > 8) return column - 7 - (column - 7) % (COLS - 8); @@ -550,6 +550,7 @@ openfile->filebot->next->prev = openfile->filebot; openfile->filebot->next->next = NULL; openfile->filebot->next->lineno = openfile->filebot->lineno + 1; + openfile->filebot->extra_lines = 0; #ifndef DISABLE_COLOR openfile->filebot->next->multidata = NULL; #endif Index: src/winio.c =================================================================== --- src/winio.c (revision 5092) +++ src/winio.c (working copy) @@ -2436,11 +2436,35 @@ } } +/* Determine the screen row and column numbers for the current cursor + * position. The return value is the column number, the parameter is + * the row number. Should only be called with softwrap enabled. */ +size_t get_softwrap_position(ssize_t *current_y) +{ + size_t index = 0, lines = 0, length = 0; + const char *line_pos; + filestruct *tmp; + openfile->current_y = 0; + + for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next) + openfile->current_y += tmp->extra_lines + 1; + + line_pos = openfile->current->data; + while (lines <= openfile->current->extra_lines && length <= openfile->current_x) { + lines++; + find_break_col(&line_pos, COLS, &index); + length += (index); + } + + length = openfile->current_x - length + index; + openfile->current_y += (lines - 1); + + return strnlenpt(line_pos - index, length); +} /* Reset current_y, based on the position of current, and put the cursor * in the edit window at (current_y, current_x). */ void reset_cursor(void) { - size_t xpt; /* If we haven't opened any files yet, put the cursor in the top * left corner of the edit window and get out. */ if (openfile == NULL) { @@ -2448,22 +2472,15 @@ return; } - xpt = xplustabs(); - #ifndef NANO_TINY if (ISSET(SOFTWRAP)) { - filestruct *tmp; - openfile->current_y = 0; - - for (tmp = openfile->edittop; tmp && tmp != openfile->current; tmp = tmp->next) - openfile->current_y += (strlenpt(tmp->data) / COLS) + 1; - - openfile->current_y += xplustabs() / COLS; + size_t line_col = get_softwrap_position(&openfile->current_y); if (openfile->current_y < editwinrows) - wmove(edit, openfile->current_y, xpt % COLS); + wmove(edit, openfile->current_y, line_col); } else #endif { + size_t xpt = xplustabs(); openfile->current_y = openfile->current->lineno - openfile->edittop->lineno; @@ -2480,14 +2497,13 @@ * character of this page. That is, the first character of converted * corresponds to character number actual_x(fileptr->data, start) of the * line. */ -void edit_draw(filestruct *fileptr, const char *converted, int - line, size_t start) +void edit_draw(filestruct *fileptr, int line, const char *buf, size_t start, size_t len) { #if !defined(NANO_TINY) || !defined(DISABLE_COLOR) - size_t startpos = actual_x(fileptr->data, start); + size_t startpos = actual_x(buf, start); /* The position in fileptr->data of the leftmost character * that displays at least partially on the window. */ - size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1; + size_t endpos = actual_x(buf, start + COLS - 1) + 1; /* The position in fileptr->data of the first character that is * completely off the window to the right. * @@ -2495,6 +2511,8 @@ * string. */ #endif + char *converted = display_string(buf, start, len, !ISSET(SOFTWRAP)); + assert(openfile != NULL && fileptr != NULL && converted != NULL); assert(strlenpt(converted) <= COLS); @@ -2559,7 +2577,7 @@ * unless k is zero. If regexec() returns * REG_NOMATCH, there are no more matches in the * line. */ - if (regexec(tmpcolor->start, &fileptr->data[k], 1, + if (regexec(tmpcolor->start, &buf[k], 1, &startmatch, (k == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) break; @@ -2574,13 +2592,13 @@ else if (startmatch.rm_so < endpos && startmatch.rm_eo > startpos) { x_start = (startmatch.rm_so <= startpos) ? 0 : - strnlenpt(fileptr->data, + strnlenpt(buf, startmatch.rm_so) - start; index = actual_x(converted, x_start); paintlen = actual_x(converted + index, - strnlenpt(fileptr->data, + strnlenpt(buf, startmatch.rm_eo) - start - x_start); assert(0 <= x_start && 0 <= paintlen); @@ -2618,8 +2636,8 @@ mvwaddnstr(edit, line, 0, converted, -1); goto end_of_loop; } else if (md == CBEGINBEFORE) { - regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0); - paintlen = actual_x(converted, strnlenpt(fileptr->data, + regexec(tmpcolor->end, buf, 1, &endmatch, 0); + paintlen = actual_x(converted, strnlenpt(buf, endmatch.rm_eo) - start); mvwaddnstr(edit, line, 0, converted, paintlen); goto end_of_loop; @@ -2692,7 +2710,7 @@ fileptr->multidata[tmpcolor->id] = CWHOLELINE; } else { paintlen = actual_x(converted, - strnlenpt(fileptr->data, + strnlenpt(buf, endmatch.rm_eo) - start); fileptr->multidata[tmpcolor->id] = CBEGINBEFORE; } @@ -2702,7 +2720,7 @@ start_col = 0; while (start_col < endpos) { - if (regexec(tmpcolor->start, fileptr->data + + if (regexec(tmpcolor->start, buf + start_col, 1, &startmatch, (start_col == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH || start_col + startmatch.rm_so >= endpos) @@ -2714,12 +2732,12 @@ startmatch.rm_eo += start_col; x_start = (startmatch.rm_so <= startpos) ? 0 : - strnlenpt(fileptr->data, + strnlenpt(buf, startmatch.rm_so) - start; index = actual_x(converted, x_start); - if (regexec(tmpcolor->end, fileptr->data + + if (regexec(tmpcolor->end, buf + startmatch.rm_eo, 1, &endmatch, (startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == 0) { @@ -2733,7 +2751,7 @@ if (endmatch.rm_eo > startpos && endmatch.rm_eo > startmatch.rm_so) { paintlen = actual_x(converted + index, - strnlenpt(fileptr->data, + strnlenpt(buf, endmatch.rm_eo) - start - x_start); @@ -2814,7 +2832,7 @@ /* x_start is the expanded location of the beginning of the * mark minus the beginning of the page. */ - x_start = strnlenpt(fileptr->data, top_x) - start; + x_start = strnlenpt(buf, top_x) - start; /* If the end of the mark is off the page, paintlen is -1, * meaning that everything on the line gets painted. @@ -2824,7 +2842,7 @@ if (bot_x >= endpos) paintlen = -1; else - paintlen = strnlenpt(fileptr->data, bot_x) - (x_start + + paintlen = strnlenpt(buf, bot_x) - (x_start + start); /* If x_start is before the beginning of the page, shift @@ -2849,30 +2867,57 @@ } } #endif /* !NANO_TINY */ + free(converted); } +/* Determine the column number at which line gets softwrapped. */ +size_t find_break_col(const char **line, ssize_t goal, size_t *location) +{ + size_t column; + size_t index = break_line(*line, goal +#ifndef DISABLE_HELP + , FALSE +#endif + ); + + if (index == -1 || strnlenpt(*line,index) >= goal) { + index = actual_x(*line, goal); + column = goal; + } else { + /* Move forward to the character just after the blank. */ + index += parse_mbchar(*line + index, NULL, NULL); + column = strnlenpt(*line, index); + } + + *line += index; + + if (location) + *location = index; + + return column; +} + /* Just update one line in the edit buffer. This is basically a wrapper * for edit_draw(). The line will be displayed starting with * fileptr->data[index]. Likely arguments are current_x or zero. * Returns: Number of additional lines consumed (needed for SOFTWRAP). */ -int update_line(filestruct *fileptr, size_t index) +size_t update_line(filestruct *fileptr, size_t index) { int line = 0; /* The line in the edit window that we want to update. */ - int extralinesused = 0; - char *converted; - /* fileptr->data converted to have tabs and control characters - * expanded. */ - size_t page_start; + size_t page_start = 0; + size_t end_col = COLS; assert(fileptr != NULL); + fileptr->extra_lines = 0; #ifndef NANO_TINY + const char *line_pos; if (ISSET(SOFTWRAP)) { filestruct *tmp; for (tmp = openfile->edittop; tmp && tmp != fileptr; tmp = tmp->next) - line += (strlenpt(tmp->data) / COLS) + 1; + line += tmp->extra_lines + 1; } else #endif line = fileptr->lineno - openfile->edittop->lineno; @@ -2886,29 +2931,22 @@ /* Next, convert variables that index the line to their equivalent * positions in the expanded line. */ #ifndef NANO_TINY - if (ISSET(SOFTWRAP)) - index = 0; - else + if (ISSET(SOFTWRAP)) { + line_pos = fileptr->data; + end_col = find_break_col(&line_pos, COLS, &index); + } else #endif + { index = strnlenpt(fileptr->data, index); page_start = get_page_start(index); + } - /* Expand the line, replacing tabs with spaces, and control - * characters with their displayed forms. */ #ifdef NANO_TINY - converted = display_string(fileptr->data, page_start, COLS, TRUE); + edit_draw(fileptr, line, fileptr->data, page_start, COLS); #else - converted = display_string(fileptr->data, page_start, COLS, !ISSET(SOFTWRAP)); -#ifdef DEBUG - if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2) - fprintf(stderr, "update_line(): converted(1) line = %s\n", converted); + edit_draw(fileptr, line, fileptr->data, page_start, end_col); #endif -#endif /* !NANO_TINY */ - /* Paint the line. */ - edit_draw(fileptr, converted, line, page_start); - free(converted); - #ifndef NANO_TINY if (!ISSET(SOFTWRAP)) { #endif @@ -2918,30 +2956,22 @@ mvwaddch(edit, line, COLS - 1, '$'); #ifndef NANO_TINY } else { - size_t full_length = strlenpt(fileptr->data); - for (index += COLS; index <= full_length && line < editwinrows; index += COLS) { + size_t length = strlen(fileptr->data) - (index - 1); + while (length > 0) { line++; + end_col = find_break_col(&line_pos, COLS, &index); + length -= index; #ifdef DEBUG fprintf(stderr, "update_line(): softwrap code, moving to %d index %lu\n", line, (unsigned long)index); #endif blank_line(edit, line, 0, COLS); - - /* Expand the line, replacing tabs with spaces, and control - * characters with their displayed forms. */ - converted = display_string(fileptr->data, index, COLS, !ISSET(SOFTWRAP)); -#ifdef DEBUG - if (ISSET(SOFTWRAP) && strlen(converted) >= COLS - 2) - fprintf(stderr, "update_line(): converted(2) line = %s\n", converted); -#endif - - /* Paint the line. */ - edit_draw(fileptr, converted, line, index); - free(converted); - extralinesused++; + edit_draw(fileptr, line, line_pos - index, 0, end_col); + fileptr->extra_lines++; } } #endif /* !NANO_TINY */ - return extralinesused; + + return fileptr->extra_lines; } /* Return TRUE if we need an update after moving horizontally, and FALSE @@ -2985,7 +3015,7 @@ maxrows = 0; for (n = 0; n < editwinrows && foo; n++) { maxrows++; - n += strlenpt(foo->data) / COLS; + n += foo->extra_lines; foo = foo->next; } @@ -3036,7 +3066,7 @@ #ifndef NANO_TINY /* Don't over-scroll on long lines. */ if (ISSET(SOFTWRAP) && direction == UPWARD) { - ssize_t len = strlenpt(openfile->edittop->data) / COLS; + ssize_t len = openfile->edittop->extra_lines; i -= len; if (len > 0) do_redraw = TRUE; @@ -3284,7 +3314,7 @@ foo = foo->prev; #ifndef NANO_TINY if (ISSET(SOFTWRAP) && foo) - goal -= strlenpt(foo->data) / COLS; + goal -= foo->extra_lines; #endif } openfile->edittop = foo; @@ -3373,7 +3403,8 @@ openfile->totsize; statusbar( - _("line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"), + _("y=%d x=%d -- line %ld/%ld (%d%%), col %lu/%lu (%d%%), char %lu/%lu (%d%%)"), + openfile->current_y, openfile->placewewant, (long)openfile->current->lineno, (long)openfile->filebot->lineno, linepct, (unsigned long)cur_xpt, (unsigned long)cur_lenpt, colpct,