aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/terminal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/terminal.c')
-rw-r--r--src/nvim/terminal.c135
1 files changed, 103 insertions, 32 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 08fcefaa88..a52a34cd66 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -37,45 +37,57 @@
// Some code from pangoterm http://www.leonerd.org.uk/code/pangoterm
#include <assert.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <vterm.h>
+#include <vterm_keycodes.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/change.h"
+#include "nvim/channel.h"
#include "nvim/cursor.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/time.h"
-#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/getchar.h"
+#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
-#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
#include "nvim/map.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/optionstr.h"
-#include "nvim/os/input.h"
+#include "nvim/pos.h"
+#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/terminal.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-#include "nvim/window.h"
typedef struct terminal_state {
VimState state;
@@ -111,15 +123,19 @@ struct terminal {
// - receive data from libvterm as a result of key presses.
char textbuf[0x1fff];
- ScrollbackLine **sb_buffer; // Scrollback buffer storage for libvterm
- size_t sb_current; // number of rows pushed to sb_buffer
- size_t sb_size; // sb_buffer size
+ ScrollbackLine **sb_buffer; // Scrollback storage.
+ size_t sb_current; // Lines stored in sb_buffer.
+ size_t sb_size; // Capacity of sb_buffer.
// "virtual index" that points to the first sb_buffer row that we need to
// push to the terminal buffer when refreshing the scrollback. When negative,
// it actually points to entries that are no longer in sb_buffer (because the
// window height has increased) and must be deleted from the terminal buffer
int sb_pending;
+ char *title; // VTermStringFragment buffer
+ size_t title_len; // number of rows pushed to sb_buffer
+ size_t title_size; // sb_buffer size
+
// buf_T instance that acts as a "drawing surface" for libvterm
// we can't store a direct reference to the buffer because the
// refresh_timer_cb may be called after the buffer was freed, and there's
@@ -150,7 +166,7 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
.movecursor = term_movecursor,
.settermprop = term_settermprop,
.bell = term_bell,
- .sb_pushline = term_sb_push,
+ .sb_pushline = term_sb_push, // Called before a line goes offscreen.
.sb_popline = term_sb_pop,
};
@@ -230,7 +246,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
set_option_value("wrap", false, NULL, OPT_LOCAL);
set_option_value("list", false, NULL, OPT_LOCAL);
if (buf->b_ffname != NULL) {
- buf_set_term_title(buf, buf->b_ffname);
+ buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname));
}
RESET_BINDING(curwin);
// Reset cursor in current window.
@@ -371,7 +387,7 @@ void terminal_check_size(Terminal *term)
// Check if there is a window that displays the terminal and find the maximum width and height.
// Skip the autocommand window which isn't actually displayed.
FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp == aucmd_win) {
+ if (is_aucmd_win(wp)) {
continue;
}
if (wp->w_buffer && wp->w_buffer->terminal == term) {
@@ -418,15 +434,15 @@ bool terminal_enter(void)
// placed at end of buffer to "follow" output. #11072
handle_T save_curwin = curwin->handle;
bool save_w_p_cul = curwin->w_p_cul;
- char_u *save_w_p_culopt = NULL;
- char_u save_w_p_culopt_flags = curwin->w_p_culopt_flags;
+ char *save_w_p_culopt = NULL;
+ uint8_t save_w_p_culopt_flags = curwin->w_p_culopt_flags;
int save_w_p_cuc = curwin->w_p_cuc;
long save_w_p_so = curwin->w_p_so;
long save_w_p_siso = curwin->w_p_siso;
if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) {
- if (STRCMP(curwin->w_p_culopt, "number")) {
+ if (strcmp(curwin->w_p_culopt, "number") != 0) {
save_w_p_culopt = curwin->w_p_culopt;
- curwin->w_p_culopt = (char_u *)xstrdup("number");
+ curwin->w_p_culopt = xstrdup("number");
}
curwin->w_p_culopt_flags = CULOPT_NBR;
} else {
@@ -518,7 +534,19 @@ static int terminal_check(VimState *state)
terminal_check_cursor();
if (must_redraw) {
- update_screen(0);
+ update_screen();
+
+ // Make sure an invoked autocmd doesn't delete the buffer (and the
+ // terminal) under our fingers.
+ curbuf->b_locked++;
+
+ // save and restore curwin and curbuf, in case the autocmd changes them
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, curbuf);
+ apply_autocmds(EVENT_TEXTCHANGEDT, NULL, NULL, false, curbuf);
+ aucmd_restbuf(&aco);
+
+ curbuf->b_locked--;
}
if (need_maketitle) { // Update title in terminal-mode. #7248
@@ -636,6 +664,7 @@ void terminal_destroy(Terminal **termpp)
xfree(term->sb_buffer[i]);
}
xfree(term->sb_buffer);
+ xfree(term->title);
vterm_free(term->vt);
xfree(term);
*termpp = NULL; // coverity[dead-store]
@@ -688,8 +717,8 @@ void terminal_paste(long count, char **y_array, size_t y_size)
return;
}
vterm_keyboard_start_paste(curbuf->terminal->vt);
- size_t buff_len = STRLEN(y_array[0]);
- char_u *buff = xmalloc(buff_len);
+ size_t buff_len = strlen(y_array[0]);
+ char *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) { // -V756
// feed the lines to the terminal
for (size_t j = 0; j < y_size; j++) {
@@ -697,23 +726,23 @@ void terminal_paste(long count, char **y_array, size_t y_size)
// terminate the previous line
terminal_send(curbuf->terminal, "\n", 1);
}
- size_t len = STRLEN(y_array[j]);
+ size_t len = strlen(y_array[j]);
if (len > buff_len) {
buff = xrealloc(buff, len);
buff_len = len;
}
- char_u *dst = buff;
- char_u *src = (char_u *)y_array[j];
+ char *dst = buff;
+ char *src = y_array[j];
while (*src != '\0') {
- len = (size_t)utf_ptr2len((char *)src);
- int c = utf_ptr2char((char *)src);
+ len = (size_t)utf_ptr2len(src);
+ int c = utf_ptr2char(src);
if (!is_filter_char(c)) {
memcpy(dst, src, len);
dst += len;
}
src += len;
}
- terminal_send(curbuf->terminal, (char *)buff, (size_t)(dst - buff));
+ terminal_send(curbuf->terminal, buff, (size_t)(dst - buff));
}
}
xfree(buff);
@@ -754,6 +783,22 @@ static int get_rgb(VTermState *state, VTermColor color)
return RGB_(color.rgb.red, color.rgb.green, color.rgb.blue);
}
+static int get_underline_hl_flag(VTermScreenCellAttrs attrs)
+{
+ switch (attrs.underline) {
+ case VTERM_UNDERLINE_OFF:
+ return 0;
+ case VTERM_UNDERLINE_SINGLE:
+ return HL_UNDERLINE;
+ case VTERM_UNDERLINE_DOUBLE:
+ return HL_UNDERDOUBLE;
+ case VTERM_UNDERLINE_CURLY:
+ return HL_UNDERCURL;
+ default:
+ return HL_UNDERLINE;
+ }
+}
+
void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *term_attrs)
{
int height, width;
@@ -790,7 +835,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)
| (cell.attrs.italic ? HL_ITALIC : 0)
| (cell.attrs.reverse ? HL_INVERSE : 0)
- | (cell.attrs.underline ? HL_UNDERLINE : 0)
+ | get_underline_hl_flag(cell.attrs)
| (cell.attrs.strike ? HL_STRIKETHROUGH: 0)
| ((fg_indexed && !fg_set) ? HL_FG_INDEXED : 0)
| ((bg_indexed && !bg_set) ? HL_BG_INDEXED : 0);
@@ -858,13 +903,13 @@ static int term_movecursor(VTermPos new, VTermPos old, int visible, void *data)
return 1;
}
-static void buf_set_term_title(buf_T *buf, char *title)
+static void buf_set_term_title(buf_T *buf, const char *title, size_t len)
FUNC_ATTR_NONNULL_ALL
{
Error err = ERROR_INIT;
dict_set_var(buf->b_vars,
STATIC_CSTR_AS_STRING("term_title"),
- STRING_OBJ(cstr_as_string(title)),
+ STRING_OBJ(((String){ .data = (char *)title, .size = len })),
false,
false,
&err);
@@ -887,7 +932,30 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
case VTERM_PROP_TITLE: {
buf_T *buf = handle_get_buffer(term->buf_handle);
- buf_set_term_title(buf, val->string);
+ VTermStringFragment frag = val->string;
+
+ if (frag.initial && frag.final) {
+ buf_set_term_title(buf, frag.str, frag.len);
+ break;
+ }
+
+ if (frag.initial) {
+ term->title_len = 0;
+ term->title_size = MAX(frag.len, 1024);
+ term->title = xmalloc(sizeof(char *) * term->title_size);
+ } else if (term->title_len + frag.len > term->title_size) {
+ term->title_size *= 2;
+ term->title = xrealloc(term->title, sizeof(char *) * term->title_size);
+ }
+
+ memcpy(term->title + term->title_len, frag.str, frag.len);
+ term->title_len += frag.len;
+
+ if (frag.final) {
+ buf_set_term_title(buf, term->title, term->title_len);
+ xfree(term->title);
+ term->title = NULL;
+ }
break;
}
@@ -908,7 +976,10 @@ static int term_bell(void *data)
return 1;
}
-// Scrollback push handler (from pangoterm).
+/// Scrollback push handler: called just before a line goes offscreen (and libvterm will forget it),
+/// giving us a chance to store it.
+///
+/// Code adapted from pangoterm.
static int term_sb_push(int cols, const VTermScreenCell *cells, void *data)
{
Terminal *term = data;
@@ -1368,14 +1439,14 @@ static bool send_mouse_event(Terminal *term, int c)
int direction = c == K_MOUSEDOWN ? MSCR_DOWN : MSCR_UP;
if (mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL)) {
scroll_redraw(direction, curwin->w_botline - curwin->w_topline);
- } else {
- scroll_redraw(direction, 3L);
+ } else if (p_mousescroll_vert > 0) {
+ scroll_redraw(direction, p_mousescroll_vert);
}
curwin->w_redr_status = true;
curwin = save_curwin;
curbuf = curwin->w_buffer;
- redraw_later(mouse_win, NOT_VALID);
+ redraw_later(mouse_win, UPD_NOT_VALID);
invalidate_terminal(term, -1, -1);
// Only need to exit focus if the scrolled window is the terminal window
return mouse_win == curwin;