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.c137
1 files changed, 77 insertions, 60 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 2c242f4a75..be49048aec 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -55,7 +55,8 @@
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/highlight.h"
-#include "nvim/keymap.h"
+#include "nvim/highlight_group.h"
+#include "nvim/keycodes.h"
#include "nvim/log.h"
#include "nvim/macros.h"
#include "nvim/main.h"
@@ -70,7 +71,6 @@
#include "nvim/os/input.h"
#include "nvim/screen.h"
#include "nvim/state.h"
-#include "nvim/syntax.h"
#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -172,8 +172,20 @@ void terminal_teardown(void)
pmap_init(ptr_t, &invalidated_terminals);
}
+static void term_output_callback(const char *s, size_t len, void *user_data)
+{
+ terminal_send((Terminal *)user_data, (char *)s, len);
+}
+
// public API {{{
+/// Initializes terminal properties, and triggers TermOpen.
+///
+/// The PTY process (TerminalOptions.data) was already started by termopen(),
+/// via ex_terminal() or the term:// BufReadCmd.
+///
+/// @param buf Buffer used for presentation of the terminal.
+/// @param opts PTY process channel, various terminal properties and callbacks.
Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
{
// Create a new terminal instance and configure it
@@ -195,6 +207,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
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);
// 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;
@@ -215,11 +228,13 @@ 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, (char *)buf->b_ffname);
+ buf_set_term_title(buf, buf->b_ffname);
}
RESET_BINDING(curwin);
// 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 inside a TermOpen autocmd
+ rv->sb_buffer = NULL;
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf);
// Local 'scrollback' _after_ autocmds.
@@ -242,7 +257,8 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
snprintf(var, sizeof(var), "terminal_color_%d", i);
char *name = get_config_string(var);
if (name) {
- color_val = name_to_color(name);
+ int dummy;
+ color_val = name_to_color(name, &dummy);
xfree(name);
if (color_val != -1) {
@@ -260,8 +276,12 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
return rv;
}
-void terminal_close(Terminal *term, int status)
+/// Closes the Terminal buffer.
+///
+/// May call terminal_destroy, which sets caller storage to NULL.
+void terminal_close(Terminal **termpp, int status)
{
+ Terminal *term = *termpp;
if (term->destroy) {
return;
}
@@ -270,7 +290,7 @@ void terminal_close(Terminal *term, int status)
if (entered_free_all_mem) {
// If called from close_buffer() inside free_all_mem(), the main loop has
// already been freed, so it is not safe to call the close callback here.
- terminal_destroy(term);
+ terminal_destroy(termpp);
return;
}
#endif
@@ -311,10 +331,14 @@ void terminal_close(Terminal *term, int status)
term->opts.close_cb(term->opts.data);
}
} else if (!only_destroy) {
- // This was called by channel_process_exit_cb() not in process_teardown().
+ // Associated channel has been closed and the editor is not exiting.
// Do not call the close callback now. Wait for the user to press a key.
char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN];
- snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status);
+ if (((Channel *)term->opts.data)->streamtype == kChannelStreamInternal) {
+ snprintf(msg, sizeof msg, "\r\n[Terminal closed]");
+ } else {
+ snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status);
+ }
terminal_receive(term, msg, strlen(msg));
}
@@ -326,6 +350,7 @@ void terminal_close(Terminal *term, int status)
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
tv_dict_add_nr(dict, S_LEN("status"), status);
+ tv_dict_set_keys_readonly(dict);
apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
restore_v_event(dict, &save_v_event);
}
@@ -341,7 +366,6 @@ void terminal_check_size(Terminal *term)
vterm_get_size(term->vt, &curheight, &curwidth);
uint16_t width = 0, height = 0;
-
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer && wp->w_buffer->terminal == term) {
const uint16_t win_width =
@@ -363,6 +387,7 @@ void terminal_check_size(Terminal *term)
invalidate_terminal(term, -1, -1);
}
+/// Implements MODE_TERMINAL state. :help Terminal-mode
void terminal_enter(void)
{
buf_T *buf = curbuf;
@@ -379,13 +404,13 @@ void terminal_enter(void)
int save_state = State;
s->save_rd = RedrawingDisabled;
- State = TERM_FOCUS;
- mapped_ctrl_c |= TERM_FOCUS; // Always map CTRL-C to avoid interrupt.
+ State = MODE_TERMINAL;
+ mapped_ctrl_c |= MODE_TERMINAL; // Always map CTRL-C to avoid interrupt.
RedrawingDisabled = false;
// Disable these options in terminal-mode. They are nonsense because cursor is
// placed at end of buffer to "follow" output. #11072
- win_T *save_curwin = curwin;
+ 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;
@@ -412,7 +437,7 @@ void terminal_enter(void)
curwin->w_redr_status = true; // For mode() in statusline. #8323
ui_busy_start();
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
- trigger_modechanged();
+ may_trigger_modechanged();
s->state.execute = terminal_execute;
s->state.check = terminal_check;
@@ -423,7 +448,7 @@ void terminal_enter(void)
RedrawingDisabled = s->save_rd;
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
- if (save_curwin == curwin) { // save_curwin may be invalid (window closed)!
+ if (save_curwin == curwin->handle) { // Else: window was closed.
curwin->w_p_cul = save_w_p_cul;
if (save_w_p_culopt) {
xfree(curwin->w_p_culopt);
@@ -491,6 +516,7 @@ static int terminal_check(VimState *state)
return 1;
}
+/// Processes one char of terminal-mode input.
static int terminal_execute(VimState *state, int key)
{
TerminalState *s = (TerminalState *)state;
@@ -565,8 +591,11 @@ static int terminal_execute(VimState *state, int key)
return 1;
}
-void terminal_destroy(Terminal *term)
+/// Frees the given Terminal structure and sets the caller storage to NULL (in the spirit of
+/// XFREE_CLEAR).
+void terminal_destroy(Terminal **termpp)
{
+ Terminal *term = *termpp;
buf_T *buf = handle_get_buffer(term->buf_handle);
if (buf) {
term->buf_handle = 0;
@@ -587,6 +616,7 @@ void terminal_destroy(Terminal *term)
xfree(term->sb_buffer);
vterm_free(term->vt);
xfree(term);
+ *termpp = NULL; // coverity[dead-store]
}
}
@@ -636,7 +666,6 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
return;
}
vterm_keyboard_start_paste(curbuf->terminal->vt);
- terminal_flush_output(curbuf->terminal);
size_t buff_len = STRLEN(y_array[0]);
char_u *buff = xmalloc(buff_len);
for (int i = 0; i < count; i++) { // -V756
@@ -654,8 +683,8 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
char_u *dst = buff;
char_u *src = y_array[j];
while (*src != '\0') {
- len = (size_t)utf_ptr2len(src);
- int c = utf_ptr2char(src);
+ len = (size_t)utf_ptr2len((char *)src);
+ int c = utf_ptr2char((char *)src);
if (!is_filter_char(c)) {
memcpy(dst, src, len);
dst += len;
@@ -667,14 +696,6 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
}
xfree(buff);
vterm_keyboard_end_paste(curbuf->terminal->vt);
- terminal_flush_output(curbuf->terminal);
-}
-
-void terminal_flush_output(Terminal *term)
-{
- size_t len = vterm_output_read(term->vt, term->textbuf,
- sizeof(term->textbuf));
- terminal_send(term, term->textbuf, len);
}
void terminal_send_key(Terminal *term, int c)
@@ -693,8 +714,6 @@ void terminal_send_key(Terminal *term, int c)
} else {
vterm_keyboard_unichar(term->vt, (uint32_t)c, mod);
}
-
- terminal_flush_output(term);
}
void terminal_receive(Terminal *term, char *data, size_t len)
@@ -713,7 +732,6 @@ static int get_rgb(VTermState *state, VTermColor color)
return RGB_(color.rgb.red, color.rgb.green, color.rgb.blue);
}
-
void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *term_attrs)
{
int height, width;
@@ -744,8 +762,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
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);
- 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];
+ 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];
int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)
| (cell.attrs.italic ? HL_ITALIC : 0)
@@ -1317,9 +1335,6 @@ static bool send_mouse_event(Terminal *term, int c)
}
mouse_action(term, button, row, col - offset, pressed, 0);
- size_t len = vterm_output_read(term->vt, term->textbuf,
- sizeof(term->textbuf));
- terminal_send(term, term->textbuf, len);
return false;
}
@@ -1344,21 +1359,20 @@ static bool send_mouse_event(Terminal *term, int c)
return mouse_win == curwin;
}
- // ignore left release action if it was not proccessed above
+ // ignore left release action if it was not processed above
// to prevent leaving Terminal mode after entering to it using a mouse
if (c == K_LEFTRELEASE && mouse_win->w_buffer->terminal == term) {
return false;
}
end:
- ins_char_typebuf(c);
+ ins_char_typebuf(vgetc_char, vgetc_mod_mask);
return true;
}
// }}}
// terminal buffer refresh & misc {{{
-
static void fetch_row(Terminal *term, int row, int end_col)
{
int col = 0;
@@ -1368,27 +1382,21 @@ static void fetch_row(Terminal *term, int row, int end_col)
while (col < end_col) {
VTermScreenCell cell;
fetch_cell(term, row, col, &cell);
- int cell_len = 0;
if (cell.chars[0]) {
+ int cell_len = 0;
for (int i = 0; cell.chars[i]; i++) {
- cell_len += utf_char2bytes((int)cell.chars[i],
- (uint8_t *)ptr + cell_len);
+ cell_len += utf_char2bytes((int)cell.chars[i], ptr + cell_len);
}
- } else {
- *ptr = ' ';
- cell_len = 1;
- }
- char c = *ptr;
- ptr += cell_len;
- if (c != ' ') {
- // only increase the line length if the last character is not whitespace
+ ptr += cell_len;
line_len = (size_t)(ptr - term->textbuf);
+ } else {
+ *ptr++ = ' ';
}
col += cell.width;
}
- // trim trailing whitespace
- term->textbuf[line_len] = 0;
+ // end of line
+ term->textbuf[line_len] = NUL;
}
static bool fetch_cell(Terminal *term, int row, int col, VTermScreenCell *cell)
@@ -1451,7 +1459,8 @@ static void refresh_terminal(Terminal *term)
long ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
}
-// Calls refresh_terminal() on all invalidated_terminals.
+
+/// Calls refresh_terminal() on all invalidated_terminals.
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
refresh_pending = false;
@@ -1483,8 +1492,16 @@ static void refresh_size(Terminal *term, buf_T *buf)
term->opts.resize_cb((uint16_t)width, (uint16_t)height, term->opts.data);
}
-/// Adjusts scrollback storage after 'scrollback' option changed.
-static void on_scrollback_option_changed(Terminal *term, buf_T *buf)
+void on_scrollback_option_changed(Terminal *term)
+{
+ // Scrollback buffer may not exist yet, e.g. if 'scrollback' is set in a TermOpen autocmd.
+ if (term->sb_buffer != NULL) {
+ refresh_terminal(term);
+ }
+}
+
+/// Adjusts scrollback storage and the terminal buffer scrollback lines
+static void adjust_scrollback(Terminal *term, buf_T *buf)
{
if (buf->b_p_scbk < 1) { // Local 'scrollback' was set to -1.
buf->b_p_scbk = SB_MAX;
@@ -1503,7 +1520,7 @@ static void on_scrollback_option_changed(Terminal *term, buf_T *buf)
term->sb_current--;
xfree(term->sb_buffer[term->sb_current]);
}
- deleted_lines(1, (long)diff);
+ deleted_lines(1, (linenr_T)diff);
}
// Resize the scrollback storage.
@@ -1526,7 +1543,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
int row_offset = term->sb_pending;
while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) {
fetch_row(term, term->sb_pending - row_offset - 1, width);
- ml_append(0, (uint8_t *)term->textbuf, 0, false);
+ ml_append(0, term->textbuf, 0, false);
appended_lines(0, 1);
term->sb_pending--;
}
@@ -1544,7 +1561,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
}
fetch_row(term, -term->sb_pending - row_offset, width);
int buf_index = (int)buf->b_ml.ml_line_count - height;
- ml_append(buf_index, (uint8_t *)term->textbuf, 0, false);
+ ml_append(buf_index, term->textbuf, 0, false);
appended_lines(buf_index, 1);
term->sb_pending--;
}
@@ -1556,7 +1573,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
deleted_lines(buf->b_ml.ml_line_count, 1);
}
- on_scrollback_option_changed(term, buf);
+ adjust_scrollback(term, buf);
}
// Refresh the screen (visible part of the buffer when the terminal is
@@ -1584,10 +1601,10 @@ static void refresh_screen(Terminal *term, buf_T *buf)
fetch_row(term, r, width);
if (linenr <= buf->b_ml.ml_line_count) {
- ml_replace(linenr, (uint8_t *)term->textbuf, true);
+ ml_replace(linenr, term->textbuf, true);
changed++;
} else {
- ml_append(linenr - 1, (uint8_t *)term->textbuf, 0, false);
+ ml_append(linenr - 1, term->textbuf, 0, false);
added++;
}
}
@@ -1631,7 +1648,7 @@ static int linenr_to_row(Terminal *term, int linenr)
static bool is_focused(Terminal *term)
{
- return State & TERM_FOCUS && curbuf->terminal == term;
+ return State & MODE_TERMINAL && curbuf->terminal == term;
}
static char *get_config_string(char *key)