const VTermScreenCallbacks *callbacks;
void *cbdata;
+ int callbacks_has_pushline4;
VTermDamageSize damage_merge;
/* start_row == -1 => no damage */
return 1;
}
-static void sb_pushline_from_row(VTermScreen *screen, int row)
+static void sb_pushline_from_row(VTermScreen *screen, int row, int continuation)
{
VTermPos pos;
pos.row = row;
for(pos.col = 0; pos.col < screen->cols; pos.col++)
vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
- (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
+ if(screen->callbacks_has_pushline4 && screen->callbacks->sb_pushline4)
+ (screen->callbacks->sb_pushline4)(screen->cols, screen->sb_buffer, continuation, screen->cbdata);
+ else
+ (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
}
-static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+static int premove(VTermRect rect, void *user)
{
VTermScreen *screen = user;
- if(screen->callbacks && screen->callbacks->sb_pushline &&
- dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner
- dest.end_col == screen->cols && // full width
+ if(((screen->callbacks && screen->callbacks->sb_pushline) ||
+ (screen->callbacks_has_pushline4 && screen->callbacks && screen->callbacks->sb_pushline4)) &&
+ rect.start_row == 0 && rect.start_col == 0 && // starts top-left corner
+ rect.end_col == screen->cols && // full width
screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
- for(int row = 0; row < src.start_row; row++)
- sb_pushline_from_row(screen, row);
+ for(int row = 0; row < rect.end_row; row++) {
+ const VTermLineInfo *lineinfo = vterm_state_get_lineinfo(screen->state, row);
+ sb_pushline_from_row(screen, row, lineinfo->continuation);
+ }
}
+ return 1;
+}
+
+static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
int cols = src.end_col - src.start_col;
int downward = src.start_row - dest.start_row;
if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
/* Push spare lines to scrollback buffer */
- if(screen->callbacks && screen->callbacks->sb_pushline)
- for(int row = 0; row <= old_row; row++)
- sb_pushline_from_row(screen, row);
+ if((screen->callbacks && screen->callbacks->sb_pushline) ||
+ (screen->callbacks_has_pushline4 && screen->callbacks && screen->callbacks->sb_pushline4))
+ for(int row = 0; row <= old_row; row++) {
+ const VTermLineInfo *lineinfo = old_lineinfo + row;
+ sb_pushline_from_row(screen, row, lineinfo->continuation);
+ }
if(active)
statefields->pos.row -= (old_row + 1);
}
&resize, // resize
&setlineinfo, // setlineinfo
&sb_clear, //sb_clear
+ &premove, // premove
};
/*
screen->callbacks = NULL;
screen->cbdata = NULL;
+ screen->callbacks_has_pushline4 = 0;
screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
}
vterm_state_set_callbacks(screen->state, &state_cbs, screen);
+ vterm_state_callbacks_has_premove(screen->state);
return screen;
}
return screen->cbdata;
}
+void vterm_screen_callbacks_has_pushline4(VTermScreen *screen)
+{
+ screen->callbacks_has_pushline4 = 1;
+}
+
void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user)
{
vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
typedef struct sb_line_S {
int sb_cols; // can differ per line
+ int sb_bytes; // length in bytes of text
cellattr_T *sb_cells; // allocated
cellattr_T sb_fill_attr; // for short line
char_u *sb_text; // for tl_scrollback_postponed
+ char_u continuation;
} sb_line_T;
#ifdef MSWIN
garray_T tl_scrollback;
int tl_scrollback_scrolled;
garray_T tl_scrollback_postponed;
+ int tl_scrollback_snapshot;
+ int tl_buffer_scrolled;
char_u *tl_highlight_name; // replaces "Terminal"; allocated
}
}
+/*
+ * Find the location of a scrollbackline in the buffer
+ */
+ static void
+scrollbackline_pos_in_buf(term_T *term, int row, linenr_T *lnum, int *start_col, size_t *start_pos)
+{
+ sb_line_T *lines = (sb_line_T *)term->tl_scrollback.ga_data;
+ linenr_T calc_lnum = term->tl_buffer_scrolled;
+ size_t calc_pos = 0;
+ int calc_col = 0;
+ int i;
+
+ if (row < 0 || row >= term->tl_scrollback.ga_len)
+ return;
+
+ if (row > term->tl_scrollback_scrolled)
+ {
+ // Lookback how far along in the top line we are
+ for (i = term->tl_scrollback_scrolled + 1; i > 0 && lines[i].continuation; --i)
+ {
+ calc_pos += lines[i - 1].sb_bytes;
+ calc_col += lines[i - 1].sb_cols;
+ }
+ i = term->tl_scrollback_scrolled + 1;
+ calc_lnum = term->tl_buffer_scrolled + 1;
+ // Do not count this line's bytes/cols twice
+ if (lines[i].continuation)
+ ++i;
+ }
+ else
+ {
+ i = 1;
+ calc_lnum = 1;
+ }
+
+ for (; i <= row; ++i)
+ {
+ if (!lines[i].continuation)
+ {
+ ++calc_lnum;
+ calc_pos = 0;
+ calc_col = 0;
+ }
+ else
+ {
+ calc_pos += lines[i - 1].sb_bytes;
+ calc_col += lines[i - 1].sb_cols;
+ }
+ }
+
+ *lnum = calc_lnum;
+ if (start_col)
+ *start_col = calc_col;
+ if (start_pos)
+ *start_pos = calc_pos;
+}
+
+/*
+ * Find the location of a buffer line in the scrollback
+ */
+ static void
+bufline_pos_in_scrollback(term_T *term, linenr_T lnum, int col, int *row, int *wrapped_col)
+{
+ buf_T *buf = term->tl_buffer;
+ sb_line_T *lines = (sb_line_T *)term->tl_scrollback.ga_data;
+ linenr_T calc_row = term->tl_scrollback_scrolled;
+ int calc_col = col;
+ linenr_T l;
+
+ if (lnum > buf->b_ml.ml_line_count)
+ return;
+
+ if (lnum > term->tl_buffer_scrolled)
+ {
+ calc_row = term->tl_scrollback_scrolled;
+ l = term->tl_buffer_scrolled + 1;
+
+ while (calc_row < term->tl_scrollback.ga_len && lines[calc_row].continuation)
+ ++calc_row;
+ }
+ else
+ {
+ calc_row = 0;
+ l = 1;
+ }
+
+ while (calc_row < term->tl_scrollback.ga_len && l < lnum)
+ {
+ ++calc_row;
+ if (!lines[calc_row].continuation)
+ ++l;
+ }
+
+ while (calc_row + 1 < term->tl_scrollback.ga_len && lines[calc_row + 1].continuation
+ && calc_col >= lines[calc_row].sb_cols)
+ {
+ calc_col -= lines[calc_row].sb_cols;
+ ++calc_row;
+ }
+
+ if (row)
+ *row = calc_row;
+ if (wrapped_col)
+ *wrapped_col = calc_col;
+}
+
/*
* Invoked when "msg" output from a job was received. Write it to the terminal
* of "buffer".
* Add the last line of the scrollback buffer to the buffer in the window.
*/
static void
-add_scrollback_line_to_buffer(term_T *term, char_u *text, int len)
+add_scrollback_line_to_buffer(term_T *term, char_u *text, int len, int append)
{
buf_T *buf = term->tl_buffer;
int empty = (buf->b_ml.ml_flags & ML_EMPTY);
linenr_T lnum = buf->b_ml.ml_line_count;
#ifdef MSWIN
+ char_u *tmp = text;
if (!enc_utf8 && enc_codepage > 0)
{
WCHAR *ret = NULL;
WideCharToMultiByte_alloc(enc_codepage, 0,
ret, length, (char **)&text, &len, 0, 0);
vim_free(ret);
- ml_append_buf(term->tl_buffer, lnum, text, len, FALSE);
- vim_free(text);
}
}
+#endif
+
+ if (append)
+ {
+ char_u *prev_text = ml_get_buf(buf, lnum, FALSE);
+ size_t prev_len = STRLEN(prev_text);
+
+ char_u *both = alloc(len + prev_len + 2);
+ if (both == NULL)
+ return;
+ vim_strncpy(both, prev_text, prev_len + 1);
+ vim_strncpy(both + prev_len, text, len + 1);
+
+ curbuf = buf;
+ ml_replace(lnum, both, FALSE);
+ curbuf = curwin->w_buffer;
+ }
else
+ ml_append_buf(buf, lnum, text, len + 1, FALSE);
+
+#ifdef MSWIN
+ if (tmp != text)
+ vim_free(text);
#endif
- ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE);
if (empty)
{
// Delete the empty line that was in the empty buffer.
}
}
line->sb_cols = 0;
+ line->sb_bytes = 0;
line->sb_cells = NULL;
line->sb_fill_attr = *fill_attr;
++term->tl_scrollback.ga_len;
{
sb_line_T *line;
garray_T *gap;
+ char_u *bufline;
+ size_t bufline_length;
curbuf = term->tl_buffer;
gap = &term->tl_scrollback;
- while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled
- && gap->ga_len > 0)
+ bufline = ml_get_buf(curbuf, curbuf->b_ml.ml_line_count, FALSE);
+ bufline_length = STRLEN(bufline);
+ while (term->tl_scrollback_snapshot && gap->ga_len > 0)
{
- ml_delete(curbuf->b_ml.ml_line_count);
line = (sb_line_T *)gap->ga_data + gap->ga_len - 1;
+ if (line->sb_bytes < 0 || (size_t)line->sb_bytes > bufline_length)
+ break;
+ bufline_length -= line->sb_bytes;
+ if (!bufline_length)
+ {
+ ml_delete(curbuf->b_ml.ml_line_count);
+ bufline = ml_get_buf(curbuf, curbuf->b_ml.ml_line_count, FALSE);
+ bufline_length = STRLEN(bufline);
+ }
vim_free(line->sb_cells);
--gap->ga_len;
+ --term->tl_scrollback_snapshot;
+ }
+ if (bufline_length < STRLEN(bufline))
+ {
+ char_u *shortened = vim_strnsave(bufline, bufline_length);
+ ml_replace(curbuf->b_ml.ml_line_count, shortened, FALSE);
}
curbuf = curwin->w_buffer;
if (curbuf == term->tl_buffer)
update_snapshot(term_T *term)
{
VTermScreen *screen;
+ VTermState *state;
int len;
int lines_skipped = 0;
VTermPos pos;
cleanup_scrollback(term);
screen = vterm_obtain_screen(term->tl_vterm);
+ state = vterm_obtain_state(term->tl_vterm);
fill_attr = new_fill_attr = term->tl_default_color;
for (pos.row = 0; pos.row < term->tl_rows; ++pos.row)
{
// Line was skipped, add an empty line.
--lines_skipped;
if (add_empty_scrollback(term, &fill_attr, 0) == OK)
- add_scrollback_line_to_buffer(term, (char_u *)"", 0);
+ {
+ add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0);
+ ++term->tl_scrollback_snapshot;
+ }
}
if (len == 0)
{
garray_T ga;
int width;
+ const VTermLineInfo *lineinfo;
sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data
+ term->tl_scrollback.ga_len;
+ lineinfo = vterm_state_get_lineinfo(state, pos.row);
+
ga_init2(&ga, 1, 100);
for (pos.col = 0; pos.col < len; pos.col += width)
{
}
}
line->sb_cols = len;
+ line->sb_bytes = ga.ga_len;
line->sb_cells = p;
line->sb_fill_attr = new_fill_attr;
+ line->continuation = (char_u)lineinfo->continuation;
fill_attr = new_fill_attr;
++term->tl_scrollback.ga_len;
+ ++term->tl_scrollback_snapshot;
if (ga_grow(&ga, 1) == FAIL)
- add_scrollback_line_to_buffer(term, (char_u *)"", 0);
+ add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0);
else
{
*((char_u *)ga.ga_data + ga.ga_len) = NUL;
- add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len);
+ add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len,
+ lineinfo->continuation);
}
ga_clear(&ga);
}
++pos.row)
{
if (add_empty_scrollback(term, &fill_attr, 0) == OK)
- add_scrollback_line_to_buffer(term, (char_u *)"", 0);
+ {
+ add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0);
+ ++term->tl_scrollback_snapshot;
+ }
}
term->tl_dirty_snapshot = FALSE;
// Update the snapshot only if something changes or the buffer does not
// have all the lines.
if (term->tl_dirty_snapshot || term->tl_buffer->b_ml.ml_line_count
- <= term->tl_scrollback_scrolled)
+ <= term->tl_buffer_scrolled)
update_snapshot(term);
if (redraw)
static void
term_enter_normal_mode(void)
{
- term_T *term = curbuf->b_term;
+ term_T *term = curbuf->b_term;
+ linenr_T lnum;
+ int col;
set_terminal_mode(term, TRUE);
// Move the window cursor to the position of the cursor in the
// terminal.
- curwin->w_cursor.lnum = term->tl_scrollback_scrolled
- + term->tl_cursor_pos.row + 1;
+ lnum = term->tl_buffer_scrolled + 1 + term->tl_cursor_pos.row;
+ col = term->tl_cursor_pos.col;
+ scrollbackline_pos_in_buf(term, term->tl_cursor_pos.row + term->tl_scrollback_scrolled, &lnum, &col, NULL);
+
+ curwin->w_cursor.lnum = lnum;
check_cursor();
- if (coladvance(term->tl_cursor_pos.col) == FAIL)
+ if (coladvance(col) == FAIL)
coladvance(MAXCOL);
curwin->w_set_curswant = TRUE;
-
- // Display the same lines as in the terminal.
- curwin->w_topline = term->tl_scrollback_scrolled + 1;
+ curwin->w_topline = term->tl_buffer_scrolled + 1;
}
/*
int todo = MAX(term->tl_buffer->b_p_twsl / 10,
gap->ga_len - term->tl_buffer->b_p_twsl);
int i;
+ sb_line_T *sb_lines = (sb_line_T *)gap->ga_data;
curbuf = term->tl_buffer;
for (i = 0; i < todo; ++i)
{
- vim_free(((sb_line_T *)gap->ga_data + i)->sb_cells);
- if (update_buffer)
+ if (update_buffer && (!sb_lines[i].continuation || !i))
+ {
ml_delete(1);
+ --term->tl_buffer_scrolled;
+ }
+ vim_free(sb_lines[i].sb_cells);
}
+ // Continue until end of wrapped line
+ for (; todo < gap->ga_len && sb_lines[todo].continuation; ++todo)
+ vim_free(sb_lines[todo].sb_cells);
curbuf = curwin->w_buffer;
gap->ga_len -= todo;
mch_memmove(gap->ga_data,
- (sb_line_T *)gap->ga_data + todo,
- sizeof(sb_line_T) * gap->ga_len);
+ (sb_line_T *)gap->ga_data + todo,
+ sizeof(sb_line_T) * gap->ga_len);
if (update_buffer)
{
win_T *curwin_save = curwin;
* Handle a line that is pushed off the top of the screen.
*/
static int
-handle_pushline(int cols, const VTermScreenCell *cells, void *user)
+handle_pushline(int cols, const VTermScreenCell *cells, int continuation, void *user)
{
term_T *term = (term_T *)user;
garray_T *gap;
*(text + text_len) = NUL;
}
if (update_buffer)
- add_scrollback_line_to_buffer(term, text, text_len);
+ add_scrollback_line_to_buffer(term, text, text_len, continuation);
line = (sb_line_T *)gap->ga_data + gap->ga_len;
line->sb_cols = len;
+ line->sb_bytes = text_len;
line->sb_cells = p;
line->sb_fill_attr = fill_attr;
+ line->continuation = (char_u)continuation;
if (update_buffer)
{
line->sb_text = NULL;
++term->tl_scrollback_scrolled;
+ if (!continuation)
+ ++term->tl_buffer_scrolled;
ga_clear(&ga); // free the text
}
else
text = pp_line->sb_text;
if (text == NULL)
text = (char_u *)"";
- add_scrollback_line_to_buffer(term, text, (int)STRLEN(text));
+ add_scrollback_line_to_buffer(term, text, (int)STRLEN(text), pp_line->continuation);
vim_free(pp_line->sb_text);
line = (sb_line_T *)term->tl_scrollback.ga_data
+ term->tl_scrollback.ga_len;
line->sb_cols = pp_line->sb_cols;
+ line->sb_bytes = pp_line->sb_bytes;
line->sb_cells = pp_line->sb_cells;
line->sb_fill_attr = pp_line->sb_fill_attr;
line->sb_text = NULL;
++term->tl_scrollback_scrolled;
++term->tl_scrollback.ga_len;
+ if (!pp_line->continuation)
+ ++term->tl_buffer_scrolled;
}
ga_clear(&term->tl_scrollback_postponed);
handle_settermprop, // settermprop
handle_bell, // bell
handle_resize, // resize
- handle_pushline, // sb_pushline
+ NULL, // sb_pushline
NULL, // sb_popline
- NULL // sb_clear
+ NULL, // sb_clear
+ handle_pushline // sb_pushline4
};
/*
term_T *term = buf->b_term;
sb_line_T *line;
cellattr_T *cellattr;
+ int sb_line = -1;
+ int sb_col = col;
- if (lnum > term->tl_scrollback.ga_len)
+ if (term->tl_scrollback.ga_len)
+ bufline_pos_in_scrollback(term, lnum, col, &sb_line, &sb_col);
+
+ if (sb_line < 0)
cellattr = &term->tl_default_color;
else
{
- line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1;
- if (col < 0 || col >= line->sb_cols)
+ line = (sb_line_T *)term->tl_scrollback.ga_data + sb_line;
+ if (sb_col < 0 || sb_col >= line->sb_cols)
cellattr = &line->sb_fill_attr;
else
- cellattr = line->sb_cells + col;
+ cellattr = line->sb_cells + sb_col;
}
return cell2attr(term, wp, &cellattr->attrs, &cellattr->fg, &cellattr->bg);
}
vterm_screen_set_callbacks(screen, &screen_callbacks, term);
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL);
+ vterm_screen_callbacks_has_pushline4(screen);
// TODO: depends on 'encoding'.
vterm_set_utf8(vterm, 1);
if (max_cells < ga_cell.ga_len)
max_cells = ga_cell.ga_len;
line->sb_cols = ga_cell.ga_len;
+ line->sb_bytes = ga_text.ga_len;
line->sb_cells = ga_cell.ga_data;
line->sb_fill_attr = term->tl_default_color;
+ line->continuation = 0;
++term->tl_scrollback.ga_len;
ga_init(&ga_cell);
if (term->tl_vterm == NULL)
{
- linenr_T lnum = row + term->tl_scrollback_scrolled + 1;
+ linenr_T lnum = 0;
+ size_t offset = 0;
+ int sb_row = term->tl_scrollback_scrolled + row;
+ sb_line_T *line;
+
+ if (sb_row < 0 || sb_row >= term->tl_scrollback.ga_len)
+ return;
+ line = (sb_line_T *)term->tl_scrollback.ga_data + sb_row;
+
+ scrollbackline_pos_in_buf(term, sb_row, &lnum, NULL, &offset);
// vterm is finished, get the text from the buffer
if (lnum > 0 && lnum <= buf->b_ml.ml_line_count)
- rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE));
+ {
+ char_u *p = ml_get_buf(buf, lnum, FALSE);
+ if (STRLEN(p) >= offset + line->sb_bytes)
+ rettv->vval.v_string = vim_strnsave(p + offset, line->sb_bytes);
+ }
}
else
{
buf = term_get_buf(argvars, "term_getscrolled()");
if (buf == NULL)
return;
- rettv->vval.v_number = buf->b_term->tl_scrollback_scrolled;
+ rettv->vval.v_number = buf->b_term->tl_buffer_scrolled;
}
/*
}
else
{
- linenr_T lnum = pos.row + term->tl_scrollback_scrolled;
+ int sb_row = term->tl_scrollback_scrolled + pos.row;
+ linenr_T lnum = 0;
+ size_t offset = 0;
- if (lnum < 0 || lnum >= term->tl_scrollback.ga_len)
+ scrollbackline_pos_in_buf(term, sb_row, &lnum, NULL, &offset);
+
+ if (sb_row >= term->tl_scrollback.ga_len || lnum <= 0 || lnum > buf->b_ml.ml_line_count)
return;
- p = ml_get_buf(buf, lnum + 1, FALSE);
- line = (sb_line_T *)term->tl_scrollback.ga_data + lnum;
+
+ line = (sb_line_T *)term->tl_scrollback.ga_data + sb_row;
+ p = ml_get_buf(buf, lnum, FALSE);
+
+ if (STRLEN(p) < offset + line->sb_bytes)
+ return;
+
+ p += offset;
}
for (pos.col = 0; pos.col < term->tl_cols; )