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.c206
1 files changed, 160 insertions, 46 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 1527738165..b5a3cffe2f 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -43,26 +43,31 @@
#include <vterm.h>
#include <vterm_keycodes.h>
-#include "nvim/api/private/defs.h"
+#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/channel.h"
+#include "nvim/channel_defs.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/defs.h"
+#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
#include "nvim/event/time.h"
#include "nvim/ex_docmd.h"
-#include "nvim/func_attr.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
@@ -74,19 +79,23 @@
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel_defs.h"
-#include "nvim/normal.h"
+#include "nvim/normal_defs.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/pos_defs.h"
#include "nvim/state.h"
+#include "nvim/state_defs.h"
+#include "nvim/strings.h"
#include "nvim/terminal.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/vim_defs.h"
+#include "nvim/window.h"
-typedef struct terminal_state {
+typedef struct {
VimState state;
Terminal *term;
int save_rd; // saved value of RedrawingDisabled
@@ -154,6 +163,9 @@ struct terminal {
bool color_set[16];
+ // When there is a pending TermRequest autocommand, block and store input.
+ StringBuilder *pending_send;
+
size_t refcount; // reference count
};
@@ -169,6 +181,82 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
static Set(ptr_t) invalidated_terminals = SET_INIT;
+static void emit_termrequest(void **argv)
+{
+ Terminal *term = argv[0];
+ char *payload = argv[1];
+ size_t payload_length = (size_t)argv[2];
+ StringBuilder *pending_send = argv[3];
+
+ buf_T *buf = handle_get_buffer(term->buf_handle);
+ String termrequest = { .data = payload, .size = payload_length };
+ Object data = STRING_OBJ(termrequest);
+ set_vim_var_string(VV_TERMREQUEST, payload, (ptrdiff_t)payload_length);
+ apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data);
+ xfree(payload);
+
+ StringBuilder *term_pending_send = term->pending_send;
+ term->pending_send = NULL;
+ if (kv_size(*pending_send)) {
+ terminal_send(term, pending_send->items, pending_send->size);
+ kv_destroy(*pending_send);
+ }
+ if (term_pending_send != pending_send) {
+ term->pending_send = term_pending_send;
+ }
+ xfree(pending_send);
+}
+
+static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length)
+{
+ term->pending_send = xmalloc(sizeof(StringBuilder));
+ kv_init(*term->pending_send);
+ multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length,
+ term->pending_send);
+}
+
+static int on_osc(int command, VTermStringFragment frag, void *user)
+{
+ if (frag.str == NULL) {
+ return 0;
+ }
+ if (!has_event(EVENT_TERMREQUEST)) {
+ return 1;
+ }
+
+ StringBuilder request = KV_INITIAL_VALUE;
+ kv_printf(request, "\x1b]%d;", command);
+ kv_concat_len(request, frag.str, frag.len);
+ schedule_termrequest(user, request.items, request.size);
+ return 1;
+}
+
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ if (command == NULL || frag.str == NULL) {
+ return 0;
+ }
+ if (!has_event(EVENT_TERMREQUEST)) {
+ return 1;
+ }
+
+ StringBuilder request = KV_INITIAL_VALUE;
+ kv_printf(request, "\x1bP%*s", (int)commandlen, command);
+ kv_concat_len(request, frag.str, frag.len);
+ schedule_termrequest(user, request.items, request.size);
+ return 1;
+}
+
+static VTermStateFallbacks vterm_fallbacks = {
+ .control = NULL,
+ .csi = NULL,
+ .osc = on_osc,
+ .dcs = on_dcs,
+ .apc = NULL,
+ .pm = NULL,
+ .sos = NULL,
+};
+
void terminal_init(void)
{
time_watcher_init(&main_loop, &refresh_timer, NULL);
@@ -189,7 +277,7 @@ void terminal_teardown(void)
static void term_output_callback(const char *s, size_t len, void *user_data)
{
- terminal_send((Terminal *)user_data, (char *)s, len);
+ terminal_send((Terminal *)user_data, s, len);
}
// public API {{{
@@ -205,36 +293,37 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
FUNC_ATTR_NONNULL_ALL
{
// Create a new terminal instance and configure it
- Terminal *rv = *termpp = xcalloc(1, sizeof(Terminal));
- rv->opts = opts;
- rv->cursor.visible = true;
+ Terminal *term = *termpp = xcalloc(1, sizeof(Terminal));
+ term->opts = opts;
+ term->cursor.visible = true;
// Associate the terminal instance with the new buffer
- rv->buf_handle = buf->handle;
- buf->terminal = rv;
+ term->buf_handle = buf->handle;
+ buf->terminal = term;
// Create VTerm
- rv->vt = vterm_new(opts.height, opts.width);
- vterm_set_utf8(rv->vt, 1);
+ term->vt = vterm_new(opts.height, opts.width);
+ vterm_set_utf8(term->vt, 1);
// Setup state
- VTermState *state = vterm_obtain_state(rv->vt);
+ VTermState *state = vterm_obtain_state(term->vt);
// Set up screen
- rv->vts = vterm_obtain_screen(rv->vt);
- vterm_screen_enable_altscreen(rv->vts, true);
- vterm_screen_enable_reflow(rv->vts, true);
+ term->vts = vterm_obtain_screen(term->vt);
+ vterm_screen_enable_altscreen(term->vts, true);
+ vterm_screen_enable_reflow(term->vts, true);
// delete empty lines at the end of the buffer
- vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv);
- vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL);
- vterm_screen_reset(rv->vts, 1);
- vterm_output_set_callback(rv->vt, term_output_callback, rv);
+ vterm_screen_set_callbacks(term->vts, &vterm_screen_callbacks, term);
+ vterm_screen_set_unrecognised_fallbacks(term->vts, &vterm_fallbacks, term);
+ vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL);
+ vterm_screen_reset(term->vts, 1);
+ vterm_output_set_callback(term->vt, term_output_callback, term);
// force a initial refresh of the screen to ensure the buffer will always
// have as many lines as screen rows when refresh_scrollback is called
- rv->invalid_start = 0;
- rv->invalid_end = opts.height;
+ term->invalid_start = 0;
+ term->invalid_end = opts.height;
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- refresh_screen(rv, buf);
- set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL);
+ refresh_screen(term, buf);
+ set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL);
// Default settings for terminal buffers
buf->b_p_ma = false; // 'nomodifiable'
@@ -242,8 +331,8 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
buf->b_p_scbk = // 'scrollback' (initialize local from global)
(p_scbk < 0) ? 10000 : MAX(1, p_scbk);
buf->b_p_tw = 0; // 'textwidth'
- set_option_value("wrap", BOOLEAN_OPTVAL(false), OPT_LOCAL);
- set_option_value("list", BOOLEAN_OPTVAL(false), OPT_LOCAL);
+ set_option_value(kOptWrap, BOOLEAN_OPTVAL(false), OPT_LOCAL);
+ set_option_value(kOptList, BOOLEAN_OPTVAL(false), OPT_LOCAL);
if (buf->b_ffname != NULL) {
buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname));
}
@@ -251,7 +340,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
// Reset cursor in current window.
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
// Initialize to check if the scrollback buffer has been allocated in a TermOpen autocmd.
- rv->sb_buffer = NULL;
+ term->sb_buffer = NULL;
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf);
@@ -261,14 +350,14 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
return; // Terminal has already been destroyed.
}
- if (rv->sb_buffer == NULL) {
+ if (term->sb_buffer == NULL) {
// Local 'scrollback' _after_ autocmds.
if (buf->b_p_scbk < 1) {
buf->b_p_scbk = SB_MAX;
}
// Configure the scrollback buffer.
- rv->sb_size = (size_t)buf->b_p_scbk;
- rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
+ term->sb_size = (size_t)buf->b_p_scbk;
+ term->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * term->sb_size);
}
// Configure the color palette. Try to get the color from:
@@ -277,14 +366,12 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
// - g:terminal_color_{NUM}
// - the VTerm instance
for (int i = 0; i < 16; i++) {
- RgbValue color_val = -1;
char var[64];
snprintf(var, sizeof(var), "terminal_color_%d", i);
char *name = get_config_string(var);
if (name) {
int dummy;
- color_val = name_to_color(name, &dummy);
- xfree(name);
+ RgbValue color_val = name_to_color(name, &dummy);
if (color_val != -1) {
VTermColor color;
@@ -293,7 +380,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
(uint8_t)((color_val >> 8) & 0xFF),
(uint8_t)((color_val >> 0) & 0xFF));
vterm_state_set_palette_color(state, i, &color);
- rv->color_set[i] = true;
+ term->color_set[i] = true;
}
}
}
@@ -388,7 +475,8 @@ void terminal_check_size(Terminal *term)
int curwidth, curheight;
vterm_get_size(term->vt, &curheight, &curwidth);
- uint16_t width = 0, height = 0;
+ uint16_t width = 0;
+ uint16_t height = 0;
// 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.
@@ -556,6 +644,8 @@ static int terminal_check(VimState *state)
curbuf->b_locked--;
}
+ may_trigger_win_scrolled_resized();
+
if (need_maketitle) { // Update title in terminal-mode. #7248
maketitle();
}
@@ -681,11 +771,15 @@ void terminal_destroy(Terminal **termpp)
}
}
-void terminal_send(Terminal *term, char *data, size_t size)
+static void terminal_send(Terminal *term, const char *data, size_t size)
{
if (term->closed) {
return;
}
+ if (term->pending_send) {
+ kv_concat_len(*term->pending_send, data, size);
+ return;
+ }
term->opts.write_cb(data, size, term->opts.data);
}
@@ -763,7 +857,7 @@ void terminal_paste(int count, char **y_array, size_t y_size)
vterm_keyboard_end_paste(curbuf->terminal->vt);
}
-void terminal_send_key(Terminal *term, int c)
+static void terminal_send_key(Terminal *term, int c)
{
VTermModifier mod = VTERM_MOD_NONE;
@@ -781,13 +875,27 @@ void terminal_send_key(Terminal *term, int c)
}
}
-void terminal_receive(Terminal *term, char *data, size_t len)
+void terminal_receive(Terminal *term, const char *data, size_t len)
{
if (!data) {
return;
}
- vterm_input_write(term->vt, data, len);
+ if (term->opts.force_crlf) {
+ StringBuilder crlf_data = KV_INITIAL_VALUE;
+
+ for (size_t i = 0; i < len; i++) {
+ if (data[i] == '\n' && (i == 0 || (i > 0 && data[i - 1] != '\r'))) {
+ kv_push(crlf_data, '\r');
+ }
+ kv_push(crlf_data, data[i]);
+ }
+
+ vterm_input_write(term->vt, crlf_data.items, kv_size(crlf_data));
+ kv_destroy(crlf_data);
+ } else {
+ vterm_input_write(term->vt, data, len);
+ }
vterm_screen_flush_damage(term->vts);
}
@@ -840,8 +948,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
bool fg_indexed = VTERM_COLOR_IS_INDEXED(&cell.fg);
bool bg_indexed = VTERM_COLOR_IS_INDEXED(&cell.bg);
- int vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0);
- int vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0);
+ int16_t vt_fg_idx = ((!fg_default && fg_indexed) ? cell.fg.indexed.idx + 1 : 0);
+ int16_t vt_bg_idx = ((!bg_default && bg_indexed) ? cell.bg.indexed.idx + 1 : 0);
bool fg_set = vt_fg_idx && vt_fg_idx <= 16 && term->color_set[vt_fg_idx - 1];
bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx - 1];
@@ -866,6 +974,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
.rgb_bg_color = vt_bg,
.rgb_sp_color = -1,
.hl_blend = -1,
+ .url = -1,
});
}
@@ -926,6 +1035,7 @@ static void buf_set_term_title(buf_T *buf, const char *title, size_t len)
STRING_OBJ(((String){ .data = (char *)title, .size = len })),
false,
false,
+ NULL,
&err);
api_clear_error(&err);
status_redraw_buf(buf);
@@ -1414,15 +1524,19 @@ static void mouse_action(Terminal *term, int button, int row, int col, bool pres
// terminal should lose focus
static bool send_mouse_event(Terminal *term, int c)
{
- int row = mouse_row, col = mouse_col, grid = mouse_grid;
+ int row = mouse_row;
+ int col = mouse_col;
+ int grid = mouse_grid;
win_T *mouse_win = mouse_find_win(&grid, &row, &col);
if (mouse_win == NULL) {
goto end;
}
int offset;
- if (term->forward_mouse && mouse_win->w_buffer->terminal == term
- && col >= (offset = win_col_off(mouse_win))) {
+ if (term->forward_mouse && mouse_win->w_buffer->terminal == term && row >= 0
+ && (grid > 1 || row + mouse_win->w_winbar_height < mouse_win->w_height)
+ && col >= (offset = win_col_off(mouse_win))
+ && (grid > 1 || col < mouse_win->w_width)) {
// event in the terminal window and mouse events was enabled by the
// program. translate and forward the event
int button;
@@ -1802,10 +1916,10 @@ static char *get_config_string(char *key)
{
Error err = ERROR_INIT;
// Only called from terminal_open where curbuf->terminal is the context.
- Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err);
+ Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err);
api_clear_error(&err);
if (obj.type == kObjectTypeNil) {
- obj = dict_get_value(&globvardict, cstr_as_string(key), &err);
+ obj = dict_get_value(&globvardict, cstr_as_string(key), NULL, &err);
api_clear_error(&err);
}
if (obj.type == kObjectTypeString) {