aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/screen.c')
-rw-r--r--src/nvim/screen.c377
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
*