aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRicky Zhou <ricky@rzhou.org>2018-08-25 15:07:52 -0700
committerRicky Zhou <ricky@rzhou.org>2018-09-03 03:25:02 -0700
commit8fd3725cc8d54bced0a8fe1474986d93e9ef0b5b (patch)
tree30370753fb95186f9259db5809bff30f13453634
parent7ff63fcdc0ba1ce2b8500641f3742d5ada68d496 (diff)
downloadrneovim-8fd3725cc8d54bced0a8fe1474986d93e9ef0b5b.tar.gz
rneovim-8fd3725cc8d54bced0a8fe1474986d93e9ef0b5b.tar.bz2
rneovim-8fd3725cc8d54bced0a8fe1474986d93e9ef0b5b.zip
tui: Hint wrapped lines to terminals.
Previously, when neovim would wrap a line across multiple lines, terminal emulators could not detect that the lines represent a single wrapped line as opposed to several separate lines. As a result, many terminals' selection/copying functionality would treat a wrapped line as several newline-delimited lines. Fix this by reenabling a "special trick" from Vim. When a line is wrapped, write the last character of that line followed by the first character of the next line to the terminal. This hints to the terminal that the next line is a continuation of the current line. Extends the raw_line event with a "wrap" parameter which controls when to do wrap hinting.
-rw-r--r--src/nvim/api/ui.c3
-rw-r--r--src/nvim/event/defs.h2
-rw-r--r--src/nvim/screen.c57
-rw-r--r--src/nvim/tui/tui.c18
-rw-r--r--src/nvim/ui.c39
-rw-r--r--src/nvim/ui.h2
-rw-r--r--src/nvim/ui_bridge.c12
7 files changed, 60 insertions, 73 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 76e3927820..509032892b 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -424,7 +424,8 @@ static void remote_ui_put(UI *ui, const char *cell)
static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr,
- const schar_T *chunk, const sattr_T *attrs)
+ Boolean wrap, const schar_T *chunk,
+ const sattr_T *attrs)
{
UIData *data = ui->data;
if (ui->ui_ext[kUINewgrid]) {
diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h
index 55b2d277bb..fdd4f17d5c 100644
--- a/src/nvim/event/defs.h
+++ b/src/nvim/event/defs.h
@@ -4,7 +4,7 @@
#include <assert.h>
#include <stdarg.h>
-#define EVENT_HANDLER_MAX_ARGC 9
+#define EVENT_HANDLER_MAX_ARGC 10
typedef void (*argv_callback)(void **argv);
typedef struct message {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 91840026a5..59e0ec2193 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2012,7 +2012,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
}
screen_line(row + wp->w_winrow, wp->w_wincol, wp->w_width,
- wp->w_width, false, wp, wp->w_hl_attr_normal);
+ wp->w_width, false, wp, wp->w_hl_attr_normal, false);
/*
* Update w_cline_height and w_cline_folded if the cursor line was
@@ -2900,7 +2900,7 @@ win_line (
&& filler_todo <= 0
) {
screen_line(screen_row, wp->w_wincol, col, -wp->w_width, wp->w_p_rl, wp,
- wp->w_hl_attr_normal);
+ wp->w_hl_attr_normal, false);
// Pretend we have finished updating the window. Except when
// 'cursorcolumn' is set.
if (wp->w_p_cuc) {
@@ -3996,7 +3996,7 @@ win_line (
}
}
screen_line(screen_row, wp->w_wincol, col, wp->w_width, wp->w_p_rl, wp,
- wp->w_hl_attr_normal);
+ wp->w_hl_attr_normal, false);
row++;
/*
@@ -4204,8 +4204,22 @@ win_line (
|| (wp->w_p_list && lcs_eol != NUL && p_extra != at_end_str)
|| (n_extra != 0 && (c_extra != NUL || *p_extra != NUL)))
) {
+ bool wrap = wp->w_p_wrap // Wrapping enabled.
+ && filler_todo <= 0 // Not drawing diff filler lines.
+ && lcs_eol_one != -1 // Haven't printed the lcs_eol character.
+ && row != endrow - 1 // Not the last line being displayed.
+ && wp->w_width == Columns // Window spans the width of the screen.
+ && !wp->w_p_rl; // Not right-to-left.
screen_line(screen_row, wp->w_wincol, col - boguscols,
- wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal);
+ wp->w_width, wp->w_p_rl, wp, wp->w_hl_attr_normal, wrap);
+ if (wrap) {
+ // Force a redraw of the first column of the next line.
+ ScreenAttrs[LineOffset[screen_row + 1]] = -1;
+
+ // Remember that the line wraps, used for modeless copy.
+ LineWraps[screen_row] = true;
+ }
+
boguscols = 0;
++row;
++screen_row;
@@ -4232,28 +4246,6 @@ win_line (
break;
}
- if (ui_current_row() == screen_row - 1
- && filler_todo <= 0
- && wp->w_width == Columns) {
- /* Remember that the line wraps, used for modeless copy. */
- LineWraps[screen_row - 1] = TRUE;
-
- // Special trick to make copy/paste of wrapped lines work with
- // xterm/screen: write an extra character beyond the end of
- // the line. This will work with all terminal types
- // (regardless of the xn,am settings).
- // Only do this if the cursor is on the current line
- // (something has been written in it).
- // Don't do this for double-width characters.
- // Don't do this for a window not at the right screen border.
- if (utf_off2cells(LineOffset[screen_row],
- LineOffset[screen_row] + screen_Columns) != 2
- && utf_off2cells(LineOffset[screen_row - 1] + (int)Columns - 2,
- LineOffset[screen_row] + screen_Columns) != 2) {
- ui_add_linewrap(screen_row - 1);
- }
- }
-
col = 0;
off = (unsigned)(current_ScreenLine - ScreenLines);
if (wp->w_p_rl) {
@@ -4319,9 +4311,11 @@ static int char_needs_redraw(int off_from, int off_to, int cols)
* "rlflag" is TRUE in a rightleft window:
* When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
* When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
+ * If "wrap" is true, then hint to the UI that "row" contains a line
+ * which has wrapped into the next row.
*/
-static void screen_line(int row, int coloff, int endcol,
- int clear_width, int rlflag, win_T *wp, int bg_attr)
+static void screen_line(int row, int coloff, int endcol, int clear_width,
+ int rlflag, win_T *wp, int bg_attr, bool wrap)
{
unsigned off_from;
unsigned off_to;
@@ -4482,7 +4476,7 @@ static void screen_line(int row, int coloff, int endcol,
}
if (clear_end > start_dirty) {
ui_line(row, coloff+start_dirty, coloff+end_dirty, coloff+clear_end,
- bg_attr);
+ bg_attr, wrap);
}
}
@@ -5442,7 +5436,8 @@ void screen_puts_line_flush(bool set_cursor)
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);
+ ui_line(put_dirty_row, put_dirty_first, put_dirty_last, put_dirty_last, 0,
+ false);
put_dirty_first = -1;
put_dirty_last = 0;
}
@@ -5801,7 +5796,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1,
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);
+ ui_line(row, dirty_first, last, dirty_last, attr, false);
}
}
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 0781b03965..841294aaad 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1191,7 +1191,8 @@ static void tui_option_set(UI *ui, String name, Object value)
static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
Integer endcol, Integer clearcol, Integer clearattr,
- const schar_T *chunk, const sattr_T *attrs)
+ Boolean wrap, const schar_T *chunk,
+ const sattr_T *attrs)
{
TUIData *data = ui->data;
UGrid *grid = &data->grid;
@@ -1212,6 +1213,21 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1,
cl_attrs);
}
+
+ if (wrap && ui->width == grid->width && linerow + 1 < grid->height) {
+ // Only do line wrapping if the grid width is equal to the terminal
+ // width and the line continuation is within the grid.
+
+ if (endcol != grid->width) {
+ // Print the last cell of the row, if we haven't already done so.
+ cursor_goto(ui, (int)linerow, grid->width - 1);
+ print_cell(ui, &grid->cells[linerow][grid->width - 1]);
+ }
+
+ // Wrap the cursor over to the next line. The next line will be
+ // printed immediately without an intervening newline.
+ final_column_wrap(ui);
+ }
}
static void invalidate(UI *ui, int top, int bot, int left, int right)
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 07aa032a50..e291111f82 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -80,7 +80,7 @@ static char uilog_last_event[1024] = { 0 };
#endif
// UI_CALL invokes a function on all registered UI instances. The functions can
-// have 0-5 arguments (configurable by SELECT_NTH).
+// have 0-10 arguments (configurable by SELECT_NTH).
//
// See http://stackoverflow.com/a/11172679 for how it works.
#ifdef _MSC_VER
@@ -102,9 +102,9 @@ static char uilog_last_event[1024] = { 0 };
} \
} while (0)
#endif
-#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, \
- MORE, MORE, MORE, MORE, MORE, ZERO, ignore)
-#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...) a10
+#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, MORE, \
+ MORE, MORE, MORE, MORE, ZERO, ignore)
+#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
@@ -315,10 +315,11 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
}
}
-void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr)
+void ui_line(int row, int startcol, int endcol, int clearcol, int clearattr,
+ bool wrap)
{
size_t off = LineOffset[row]+(size_t)startcol;
- UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr,
+ UI_CALL(raw_line, 1, row, startcol, endcol, clearcol, clearattr, wrap,
(const schar_T *)ScreenLines+off, (const sattr_T *)ScreenAttrs+off);
if (p_wd) { // 'writedelay': flush & delay each time.
int old_row = row, old_col = col;
@@ -341,32 +342,6 @@ void ui_cursor_goto(int new_row, int new_col)
pending_cursor_update = true;
}
-void ui_add_linewrap(int row)
-{
- // TODO(bfredl): check that this actually still works
- // and move to TUI module in that case.
-#if 0
- // First make sure we are at the end of the screen line,
- // then output the same character again to let the
- // terminal know about the wrap. If the terminal doesn't
- // auto-wrap, we overwrite the character.
- if (ui_current_col() != Columns) {
- screen_char(LineOffset[row] + (unsigned)Columns - 1, row,
- (int)(Columns - 1));
- }
-
- // When there is a multi-byte character, just output a
- // space to keep it simple. */
- if (ScreenLines[LineOffset[row] + (Columns - 1)][1] != 0) {
- ui_putc(' ');
- } else {
- ui_puts(ScreenLines[LineOffset[row] + (Columns - 1)]);
- }
- // force a redraw of the first char on the next line
- ScreenAttrs[LineOffset[row+1]] = (sattr_T)-1;
-#endif
-}
-
void ui_mode_info_set(void)
{
pending_mode_info_update = true;
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 584d8a77c6..df489f569f 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -47,7 +47,7 @@ struct ui_t {
// in to the public grid_line format.
void (*raw_line)(UI *ui, Integer grid, Integer row, Integer startcol,
Integer endcol, Integer clearcol, Integer clearattr,
- const schar_T *chunk, const sattr_T *attrs);
+ Boolean wrap, const schar_T *chunk, const sattr_T *attrs);
void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
void (*stop)(UI *ui);
void (*inspect)(UI *ui, Dictionary *info);
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index a96a24bde7..ebd4651f4d 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -152,24 +152,24 @@ static void ui_bridge_raw_line_event(void **argv)
UI *ui = UI(argv[0]);
ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
- argv[7], argv[8]);
- xfree(argv[7]);
+ PTR2INT(argv[7]), argv[8], argv[9]);
xfree(argv[8]);
+ xfree(argv[9]);
}
static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr,
- const schar_T *chunk, const sattr_T *attrs)
+ Boolean wrap, const schar_T *chunk,
+ const sattr_T *attrs)
{
size_t ncol = (size_t)(endcol-startcol);
schar_T *c = xmemdup(chunk, ncol * sizeof(schar_T));
sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
- UI_BRIDGE_CALL(ui, raw_line, 9, ui, INT2PTR(grid), INT2PTR(row),
+ UI_BRIDGE_CALL(ui, raw_line, 10, ui, INT2PTR(grid), INT2PTR(row),
INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
- INT2PTR(clearattr), c, hl);
+ INT2PTR(clearattr), INT2PTR(wrap), c, hl);
}
-
static void ui_bridge_suspend(UI *b)
{
UIBridgeData *data = (UIBridgeData *)b;