diff options
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r-- | src/nvim/screen.c | 377 |
1 files changed, 192 insertions, 185 deletions
diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 41a430bb8b..65a3c17286 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -300,7 +300,8 @@ void update_screen(int type) type = CLEAR; } else if (type != CLEAR) { check_for_delay(false); - if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows, NULL) == FAIL) { + if (screen_ins_lines(0, msg_scrolled, (int)Rows, 0, (int)Columns) + == FAIL) { type = CLEAR; } FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { @@ -4314,13 +4315,14 @@ static void screen_line(int row, int coloff, int endcol, /* 2: occupies two display cells */ # define CHAR_CELLS char_cells + int start_dirty = -1, end_dirty = 0; + /* Check for illegal row and col, just in case. */ if (row >= Rows) row = Rows - 1; if (endcol > Columns) endcol = Columns; - off_from = (unsigned)(current_ScreenLine - ScreenLines); off_to = LineOffset[row] + coloff; max_off_from = off_from + screen_Columns; @@ -4368,6 +4370,10 @@ static void screen_line(int row, int coloff, int endcol, if (redraw_this) { + if (start_dirty == -1) { + start_dirty = col; + } + end_dirty = col + char_cells; // When writing a single-width character over a double-width // character and at the end of the redrawn text, need to clear out // the right halve of the old character. @@ -4388,12 +4394,11 @@ static void screen_line(int row, int coloff, int endcol, } ScreenAttrs[off_to] = ScreenAttrs[off_from]; - /* For simplicity set the attributes of second half of a - * double-wide character equal to the first half. */ - if (char_cells == 2) + // For simplicity set the attributes of second half of a + // double-wide character equal to the first half. + if (char_cells == 2) { ScreenAttrs[off_to + 1] = ScreenAttrs[off_from]; - - screen_char(off_to, row, col + coloff); + } } off_to += CHAR_CELLS; @@ -4405,23 +4410,29 @@ static void screen_line(int row, int coloff, int endcol, /* Clear the second half of a double-wide character of which the left * half was overwritten with a single-wide character. */ schar_from_ascii(ScreenLines[off_to], ' '); - screen_char(off_to, row, col + coloff); + end_dirty++; } + int clear_end = -1; if (clear_width > 0 && !rlflag) { // blank out the rest of the line - while (col < clear_width && ScreenLines[off_to][0] == ' ' - && ScreenLines[off_to][1] == NUL - && ScreenAttrs[off_to] == bg_attr - ) { - ++off_to; - ++col; - } - if (col < clear_width) { - screen_fill(row, row + 1, col + coloff, clear_width + coloff, ' ', ' ', - bg_attr); - off_to += clear_width - col; - col = clear_width; + // TODO(bfredl): we could cache winline widths + while (col < clear_width) { + if (ScreenLines[off_to][0] != ' ' || ScreenLines[off_to][1] != NUL + || ScreenAttrs[off_to] != bg_attr) { + ScreenLines[off_to][0] = ' '; + ScreenLines[off_to][1] = NUL; + ScreenAttrs[off_to] = bg_attr; + if (start_dirty == -1) { + start_dirty = col; + end_dirty = col; + } else if (clear_end == -1) { + end_dirty = endcol; + } + clear_end = col+1; + } + col++; + off_to++; } } @@ -4436,11 +4447,25 @@ static void screen_line(int row, int coloff, int endcol, || ScreenAttrs[off_to] != hl) { schar_copy(ScreenLines[off_to], sc); ScreenAttrs[off_to] = hl; - screen_char(off_to, row, col + coloff); + if (start_dirty == -1) { + start_dirty = col; + } + end_dirty = col+1; } } else LineWraps[row] = FALSE; } + + if (clear_end < end_dirty) { + clear_end = end_dirty; + } + if (start_dirty == -1) { + start_dirty = end_dirty; + } + if (clear_end > start_dirty) { + ui_line(row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end, + bg_attr); + } } /* @@ -4722,11 +4747,11 @@ win_redr_status_matches ( /* Put the wildmenu just above the command line. If there is * no room, scroll the screen one line up. */ if (cmdline_row == Rows - 1) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - ++msg_scrolled; + screen_del_lines(0, 1, (int)Rows, 0, (int)Columns); + msg_scrolled++; } else { - ++cmdline_row; - ++row; + cmdline_row++; + row++; } wild_menu_showing = WM_SCROLLED; } else { @@ -5090,6 +5115,8 @@ win_redr_custom ( /* * Draw each snippet with the specified highlighting. */ + screen_puts_line_start(row); + curattr = attr; p = buf; for (n = 0; hltab[n].start != NULL; n++) { @@ -5110,6 +5137,8 @@ win_redr_custom ( // Make sure to use an empty string instead of p, if p is beyond buf + len. screen_puts(p >= buf + len ? (char_u *)"" : p, row, col, curattr); + screen_puts_line_flush(false); + if (wp == NULL) { // Fill the tab_page_click_defs array for clicking in the tab pages line. col = 0; @@ -5207,7 +5236,6 @@ void screen_getbytes(int row, int col, char_u *bytes, int *attrp) } } - /* * Put string '*text' on the screen at position 'row' and 'col', with * attributes 'attr', and update ScreenLines[] and ScreenAttrs[]. @@ -5219,6 +5247,20 @@ void screen_puts(char_u *text, int row, int col, int attr) screen_puts_len(text, -1, row, col, attr); } +static int put_dirty_row = -1; +static int put_dirty_first = -1; +static int put_dirty_last = 0; + +/// Start a group of screen_puts_len calls that builds a single screen line. +/// +/// Must be matched with a screen_puts_line_flush call before moving to +/// another line. +void screen_puts_line_start(int row) +{ + assert(put_dirty_row == -1); + put_dirty_row = row; +} + /* * Like screen_puts(), but output "text[len]". When "len" is -1 output up to * a NUL. @@ -5242,6 +5284,16 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) int force_redraw_next = FALSE; int need_redraw; + bool do_flush = false; + if (put_dirty_row == -1) { + screen_puts_line_start(row); + do_flush = true; + } else { + if (row != put_dirty_row) { + abort(); + } + } + if (ScreenLines == NULL || row >= screen_Rows) /* safety check */ return; off = LineOffset[row] + col; @@ -5252,9 +5304,12 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) schar_from_ascii(ScreenLines[off - 1], ' '); ScreenAttrs[off - 1] = 0; // redraw the previous cell, make it empty - screen_char(off - 1, row, col - 1); - /* force the cell at "col" to be redrawn */ - force_redraw_next = TRUE; + if (put_dirty_first == -1) { + put_dirty_first = col-1; + } + put_dirty_last = col+1; + // force the cell at "col" to be redrawn + force_redraw_next = true; } max_off = LineOffset[row] + screen_Columns; @@ -5333,8 +5388,12 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) ScreenLines[off + 1][0] = 0; ScreenAttrs[off + 1] = attr; } - screen_char(off, row, col); + if (put_dirty_first == -1) { + put_dirty_first = col; + } + put_dirty_last = col+mbyte_cells; } + off += mbyte_cells; col += mbyte_cells; ptr += mbyte_blen; @@ -5345,11 +5404,29 @@ void screen_puts_len(char_u *text, int textlen, int row, int col, int attr) } } - /* If we detected the next character needs to be redrawn, but the text - * doesn't extend up to there, update the character here. */ - if (force_redraw_next && col < screen_Columns) { - screen_char(off, row, col); + if (do_flush) { + screen_puts_line_flush(true); + } +} + +/// End a group of screen_puts_len calls and send the screen buffer to the UI +/// layer. +/// +/// @param set_cursor Move the visible cursor to the end of the changed region. +/// This is a workaround for not yet refactored code paths +/// and shouldn't be used in new code. +void screen_puts_line_flush(bool set_cursor) +{ + assert(put_dirty_row != -1); + if (put_dirty_first != -1) { + if (set_cursor) { + ui_cursor_goto(put_dirty_row, put_dirty_last); + } + ui_line(put_dirty_row, put_dirty_first, put_dirty_last, put_dirty_last, 0); + put_dirty_first = -1; + put_dirty_last = 0; } + put_dirty_row = -1; } /* @@ -5641,32 +5718,6 @@ next_search_hl_pos( return 0; } -/* - * Put character ScreenLines["off"] on the screen at position "row" and "col", - * using the attributes from ScreenAttrs["off"]. - */ -static void screen_char(unsigned off, int row, int col) -{ - // Check for illegal values, just in case (could happen just after resizing). - if (row >= screen_Rows || col >= screen_Columns) { - return; - } - - // Outputting the last character on the screen may scrollup the screen. - // Don't to it! Mark the character invalid (update it when scrolled up) - // FIXME: The premise here is not actually true (cf. deferred wrap). - if (row == screen_Rows - 1 && col == screen_Columns - 1 - // account for first command-line character in rightleft mode - && !cmdmsg_rl) { - ScreenAttrs[off] = (sattr_T)-1; - return; - } - - ui_cursor_goto(row, col); - ui_set_highlight(ScreenAttrs[off]); - - ui_puts(ScreenLines[off]); -} /* * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col' @@ -5675,12 +5726,6 @@ static void screen_char(unsigned off, int row, int col) */ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, int c2, int attr) { - int row; - int col; - int off; - int end_off; - int did_delete; - int c; schar_T sc; if (end_row > screen_Rows) /* safety check */ @@ -5692,8 +5737,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, || start_col >= end_col) /* nothing to do */ return; - /* it's a "normal" terminal when not in a GUI or cterm */ - for (row = start_row; row < end_row; ++row) { + for (int row = start_row; row < end_row; row++) { if (has_mbyte) { // When drawing over the right halve of a double-wide char clear // out the left halve. When drawing over the left halve of a @@ -5706,71 +5750,52 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, screen_puts_len((char_u *)" ", 1, row, end_col, 0); } } - /* - * Try to use delete-line termcap code, when no attributes or in a - * "normal" terminal, where a bold/italic space is just a - * space. - */ - did_delete = FALSE; - if (c2 == ' ' - && end_col == Columns - && attr == 0) { - /* - * check if we really need to clear something - */ - col = start_col; - if (c1 != ' ') /* don't clear first char */ - ++col; - - off = LineOffset[row] + col; - end_off = LineOffset[row] + end_col; - - // skip blanks (used often, keep it fast!) - while (off < end_off && ScreenLines[off][0] == ' ' - && ScreenLines[off][1] == 0 && ScreenAttrs[off] == 0) { - off++; - } - if (off < end_off) { // something to be cleared - col = off - LineOffset[row]; - ui_clear_highlight(); - ui_cursor_goto(row, col); // clear rest of this screen line - ui_call_eol_clear(); - col = end_col - col; - while (col--) { // clear chars in ScreenLines - schar_from_ascii(ScreenLines[off], ' '); - ScreenAttrs[off] = 0; - ++off; - } - } - did_delete = TRUE; /* the chars are cleared now */ - } - off = LineOffset[row] + start_col; - c = c1; - schar_from_char(sc, c); + int dirty_first = INT_MAX; + int dirty_last = 0; + int col = start_col; + schar_from_char(sc, c1); + int lineoff = LineOffset[row]; for (col = start_col; col < end_col; col++) { + int off = lineoff + col; if (schar_cmp(ScreenLines[off], sc) || ScreenAttrs[off] != attr) { schar_copy(ScreenLines[off], sc); ScreenAttrs[off] = attr; - if (!did_delete || c != ' ') - screen_char(off, row, col); + if (dirty_first == INT_MAX) { + dirty_first = col; + } + dirty_last = col+1; } - ++off; if (col == start_col) { - if (did_delete) - break; - c = c2; - schar_from_char(sc, c); + schar_from_char(sc, c2); } } - if (end_col == Columns) - LineWraps[row] = FALSE; - if (row == Rows - 1) { /* overwritten the command line */ - redraw_cmdline = TRUE; - if (c1 == ' ' && c2 == ' ') - clear_cmdline = FALSE; /* command line has been cleared */ - if (start_col == 0) - mode_displayed = FALSE; /* mode cleared or overwritten */ + if (dirty_last > dirty_first) { + // TODO(bfredl): support a cleared suffix even with a batched line? + if (put_dirty_row == row) { + if (put_dirty_first == -1) { + put_dirty_first = dirty_first; + } + put_dirty_last = MAX(put_dirty_last, dirty_last); + } else { + int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' '); + ui_line(row, dirty_first, last, dirty_last, attr); + } + } + + if (end_col == Columns) { + LineWraps[row] = false; + } + + // TODO(bfredl): The relevant caller should do this + if (row == Rows - 1) { // overwritten the command line + redraw_cmdline = true; + if (c1 == ' ' && c2 == ' ') { + clear_cmdline = false; // command line has been cleared + } + if (start_col == 0) { + mode_displayed = false; // mode cleared or overwritten + } } } } @@ -6027,15 +6052,13 @@ static void screenclear2(void) return; } - ui_clear_highlight(); // don't want highlighting here - /* blank out ScreenLines */ for (i = 0; i < Rows; ++i) { lineclear(LineOffset[i], (int)Columns); LineWraps[i] = FALSE; } - ui_call_clear(); // clear the display + ui_call_grid_clear(1); // clear the display clear_cmdline = false; mode_displayed = false; screen_cleared = true; // can use contents of ScreenLines now @@ -6064,18 +6087,16 @@ static void lineclear(unsigned off, int width) (void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T)); } -/* - * Copy part of a Screenline for vertically split window "wp". - */ -static void linecopy(int to, int from, win_T *wp) +/// Copy part of a Screenline for vertically split window. +static void linecopy(int to, int from, int col, int width) { - const unsigned off_to = LineOffset[to] + wp->w_wincol; - const unsigned off_from = LineOffset[from] + wp->w_wincol; + unsigned off_to = LineOffset[to] + col; + unsigned off_from = LineOffset[from] + col; memmove(ScreenLines + off_to, ScreenLines + off_from, - wp->w_width * sizeof(schar_T)); + width * sizeof(schar_T)); memmove(ScreenAttrs + off_to, ScreenAttrs + off_from, - wp->w_width * sizeof(ScreenAttrs[0])); + width * sizeof(sattr_T)); } /* @@ -6153,15 +6174,16 @@ static int win_do_lines(win_T *wp, int row, int line_count, // otherwise it will stay there forever. clear_cmdline = TRUE; int retval; - ui_set_scroll_region(wp, row); + if (del) { - retval = screen_del_lines(wp->w_winrow + row, 0, line_count, - wp->w_height - row, wp); + retval = screen_del_lines(wp->w_winrow + row, line_count, + wp->w_winrow + wp->w_height, + wp->w_wincol, wp->w_width); } else { - retval = screen_ins_lines(wp->w_winrow + row, 0, line_count, - wp->w_height - row, wp); + retval = screen_ins_lines(wp->w_winrow + row, line_count, + wp->w_winrow + wp->w_height, + wp->w_wincol, wp->w_width); } - ui_reset_scroll_region(); return retval; } @@ -6189,19 +6211,13 @@ static void win_rest_invalid(win_T *wp) */ -// insert lines on the screen and update ScreenLines[] -// 'end' is the line after the scrolled part. Normally it is Rows. -// When scrolling region used 'off' is the offset from the top for the region. -// 'row' and 'end' are relative to the start of the region. -// -// return FAIL for failure, OK for success. -int screen_ins_lines ( - int off, - int row, - int line_count, - int end, - win_T *wp /* NULL or window to use width from */ -) +/// insert lines on the screen and update ScreenLines[] +/// 'end' is the line after the scrolled part. Normally it is Rows. +/// When scrolling region used 'off' is the offset from the top for the region. +/// 'row' and 'end' are relative to the start of the region. +/// +/// @return FAIL for failure, OK for success. +int screen_ins_lines(int row, int line_count, int end, int col, int width) { int i; int j; @@ -6213,18 +6229,16 @@ int screen_ins_lines ( // Shift LineOffset[] line_count down to reflect the inserted lines. // Clear the inserted lines in ScreenLines[]. - row += off; - end += off; - for (i = 0; i < line_count; ++i) { - if (wp != NULL && wp->w_width != Columns) { + for (i = 0; i < line_count; i++) { + if (width != Columns) { // need to copy part of a line j = end - 1 - i; while ((j -= line_count) >= row) { - linecopy(j + line_count, j, wp); + linecopy(j + line_count, j, col, width); } j += line_count; - lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); - LineWraps[j] = FALSE; + lineclear(LineOffset[j] + col, width); + LineWraps[j] = false; } else { j = end - 1 - i; temp = LineOffset[j]; @@ -6233,29 +6247,23 @@ int screen_ins_lines ( LineWraps[j + line_count] = LineWraps[j]; } LineOffset[j + line_count] = temp; - LineWraps[j + line_count] = FALSE; + LineWraps[j + line_count] = false; lineclear(temp, (int)Columns); } } - ui_call_scroll(-line_count); + ui_call_grid_scroll(1, row, end, col, col+width, -line_count, 0); return OK; } -// delete lines on the screen and update ScreenLines[] -// 'end' is the line after the scrolled part. Normally it is Rows. -// When scrolling region used 'off' is the offset from the top for the region. -// 'row' and 'end' are relative to the start of the region. -// -// Return OK for success, FAIL if the lines are not deleted. -int screen_del_lines ( - int off, - int row, - int line_count, - int end, - win_T *wp /* NULL or window to use width from */ -) +/// delete lines on the screen and update ScreenLines[] +/// 'end' is the line after the scrolled part. Normally it is Rows. +/// When scrolling region used 'off' is the offset from the top for the region. +/// 'row' and 'end' are relative to the start of the region. +/// +/// Return OK for success, FAIL if the lines are not deleted. +int screen_del_lines(int row, int line_count, int end, int col, int width) { int j; int i; @@ -6267,18 +6275,16 @@ int screen_del_lines ( // Now shift LineOffset[] line_count up to reflect the deleted lines. // Clear the inserted lines in ScreenLines[]. - row += off; - end += off; - for (i = 0; i < line_count; ++i) { - if (wp != NULL && wp->w_width != Columns) { + for (i = 0; i < line_count; i++) { + if (width != Columns) { // need to copy part of a line j = row + i; while ((j += line_count) <= end - 1) { - linecopy(j - line_count, j, wp); + linecopy(j - line_count, j, col, width); } j -= line_count; - lineclear(LineOffset[j] + wp->w_wincol, wp->w_width); - LineWraps[j] = FALSE; + lineclear(LineOffset[j] + col, width); + LineWraps[j] = false; } else { // whole width, moving the line pointers is faster j = row + i; @@ -6288,16 +6294,17 @@ int screen_del_lines ( LineWraps[j - line_count] = LineWraps[j]; } LineOffset[j - line_count] = temp; - LineWraps[j - line_count] = FALSE; + LineWraps[j - line_count] = false; lineclear(temp, (int)Columns); } } - ui_call_scroll(line_count); + ui_call_grid_scroll(1, row, end, col, col+width, line_count, 0); return OK; } + /* * show the current mode and ruler * |