aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2015-03-08 08:58:31 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2015-03-25 18:57:35 -0300
commitcdedd89d228a016a4e433968702e9e3ce5165e7d (patch)
tree2a98eb4008a6033621c293f84db29cb3127f9740 /src
parent6f471fa4fc24e72e1ac75c1579970414b168381e (diff)
downloadrneovim-cdedd89d228a016a4e433968702e9e3ce5165e7d.tar.gz
rneovim-cdedd89d228a016a4e433968702e9e3ce5165e7d.tar.bz2
rneovim-cdedd89d228a016a4e433968702e9e3ce5165e7d.zip
terminal: New module that implements a terminal emulator
This commit integrates libvterm with Neovim and implements a terminal emulator with nvim buffers as the display mechanism. Terminal buffers can be created using any of the following methods: - Opening a file with name following the "term://[${cwd}//[${pid}:]]${cmd}" URI pattern where: - cwd is the working directory of the process - pid is the process id. This is just for use in session files where a pid would have been assigned to the saved buffer title. - cmd is the command to run - Invoking the `:terminal` ex command - Invoking the `termopen` function which returns a job id for automating the terminal window. Some extra changes were also implemented to adapt with terminal buffers. Here's an overview: - The `main` function now sets a BufReadCmd autocmd to intercept the term:// URI and spawn the terminal buffer instead of reading the file. - terminal buffers behave as if the following local buffer options were set: - `nomodifiable` - `swapfile` - `undolevels=-1` - `bufhidden=hide` - All commands that delete buffers(`:bun`, `:bd` and `:bw`) behave the same for terminal buffers, but only work when bang is passed(eg: `:bwipeout!`) - A new "terminal" mode was added. A consequence is that a new set of mapping commands were implemented with the "t" prefix(tmap, tunmap, tnoremap...) - The `edit` function(which enters insert mode) will actually enter terminal mode if the current buffer is a terminal - The `put` operator was adapted to send data to the terminal instead of modifying the buffer directly. - A window being resized will also trigger a terminal resize if the window displays the terminal.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c50
-rw-r--r--src/nvim/buffer_defs.h7
-rw-r--r--src/nvim/edit.c6
-rw-r--r--src/nvim/eval.c211
-rw-r--r--src/nvim/ex_cmds.c2
-rw-r--r--src/nvim/ex_cmds.lua25
-rw-r--r--src/nvim/ex_docmd.c28
-rw-r--r--src/nvim/fileio.c10
-rw-r--r--src/nvim/fileio.h1
-rw-r--r--src/nvim/fold.c5
-rw-r--r--src/nvim/getchar.c6
-rw-r--r--src/nvim/macros.h2
-rw-r--r--src/nvim/main.c11
-rw-r--r--src/nvim/memline.c8
-rw-r--r--src/nvim/ops.c27
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/os/event.c8
-rw-r--r--src/nvim/quickfix.c15
-rw-r--r--src/nvim/screen.c36
-rw-r--r--src/nvim/syntax.c5
-rw-r--r--src/nvim/terminal.c1129
-rw-r--r--src/nvim/terminal.h33
-rw-r--r--src/nvim/undo.c3
-rw-r--r--src/nvim/vim.h5
-rw-r--r--src/nvim/window.c28
25 files changed, 1577 insertions, 92 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 03adba97cd..dd20d61f75 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -68,6 +68,7 @@
#include "nvim/spell.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/version.h"
@@ -307,20 +308,28 @@ close_buffer (
bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
bool wipe_buf = (action == DOBUF_WIPE);
- /*
- * Force unloading or deleting when 'bufhidden' says so.
- * The caller must take care of NOT deleting/freeing when 'bufhidden' is
- * "hide" (otherwise we could never free or delete a buffer).
- */
- if (buf->b_p_bh[0] == 'd') { /* 'bufhidden' == "delete" */
- del_buf = true;
+ // Force unloading or deleting when 'bufhidden' says so, but not for terminal
+ // buffers.
+ // The caller must take care of NOT deleting/freeing when 'bufhidden' is
+ // "hide" (otherwise we could never free or delete a buffer).
+ if (!buf->terminal) {
+ if (buf->b_p_bh[0] == 'd') { // 'bufhidden' == "delete"
+ del_buf = true;
+ unload_buf = true;
+ } else if (buf->b_p_bh[0] == 'w') { // 'bufhidden' == "wipe"
+ del_buf = true;
+ unload_buf = true;
+ wipe_buf = true;
+ } else if (buf->b_p_bh[0] == 'u') // 'bufhidden' == "unload"
+ unload_buf = true;
+ }
+
+ if (buf->terminal && (unload_buf || del_buf || wipe_buf)) {
+ // terminal buffers can only be wiped
unload_buf = true;
- } else if (buf->b_p_bh[0] == 'w') { /* 'bufhidden' == "wipe" */
del_buf = true;
- unload_buf = true;
wipe_buf = true;
- } else if (buf->b_p_bh[0] == 'u') /* 'bufhidden' == "unload" */
- unload_buf = true;
+ }
if (win_valid(win)) {
/* Set b_last_cursor when closing the last window for the buffer.
@@ -383,6 +392,10 @@ close_buffer (
if (buf->b_nwindows > 0 || !unload_buf)
return;
+ if (buf->terminal) {
+ terminal_close(buf->terminal, NULL);
+ }
+
/* Always remove the buffer when there is no file name. */
if (buf->b_ffname == NULL)
del_buf = TRUE;
@@ -925,8 +938,8 @@ do_buffer (
if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
return FAIL;
- if (!forceit && bufIsChanged(buf)) {
- if ((p_confirm || cmdmod.confirm) && p_write) {
+ if (!forceit && (buf->terminal || bufIsChanged(buf))) {
+ if ((p_confirm || cmdmod.confirm) && p_write && !buf->terminal) {
dialog_changed(buf, FALSE);
if (!buf_valid(buf))
/* Autocommand deleted buffer, oops! It's not changed
@@ -937,9 +950,14 @@ do_buffer (
if (bufIsChanged(buf))
return FAIL;
} else {
- EMSGN(_("E89: No write since last change for buffer %" PRId64
- " (add ! to override)"),
- buf->b_fnum);
+ if (buf->terminal) {
+ EMSG2(_("E89: %s will be killed(add ! to override)"),
+ (char *)buf->b_fname);
+ } else {
+ EMSGN(_("E89: No write since last change for buffer %" PRId64
+ " (add ! to override)"),
+ buf->b_fnum);
+ }
return FAIL;
}
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 3b8b171bd4..35fa3978b6 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -27,7 +27,7 @@
// for String
#include "nvim/api/private/defs.h"
-#define MODIFIABLE(buf) (buf->b_p_ma)
+#define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma)
/*
* Flags for w_valid.
@@ -103,6 +103,9 @@ typedef struct file_buffer buf_T; /* forward declaration */
// for FileID
#include "nvim/os/fs_defs.h"
+// for Terminal
+#include "nvim/terminal.h"
+
/*
* The taggy struct is used to store the information about a :tag command.
*/
@@ -751,6 +754,8 @@ struct file_buffer {
* may use a different synblock_T. */
signlist_T *b_signlist; /* list of signs to draw */
+
+ Terminal *terminal; // Terminal instance associated with the buffer
};
/*
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 516278f34d..8b2ac1943f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -56,6 +56,7 @@
#include "nvim/tag.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
+#include "nvim/terminal.h"
#include "nvim/undo.h"
#include "nvim/window.h"
#include "nvim/os/event.h"
@@ -244,6 +245,11 @@ edit (
long count
)
{
+ if (curbuf->terminal) {
+ terminal_enter(curbuf->terminal, true);
+ return false;
+ }
+
int c = 0;
char_u *ptr;
int lastc;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 08ad33e60e..6d8f47ee9c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1,4 +1,5 @@
/*
+ *
* VIM - Vi IMproved by Bram Moolenaar
*
* Do ":help uganda" in Vim to read copying and usage conditions.
@@ -78,6 +79,7 @@
#include "nvim/tempfile.h"
#include "nvim/ui.h"
#include "nvim/mouse.h"
+#include "nvim/terminal.h"
#include "nvim/undo.h"
#include "nvim/version.h"
#include "nvim/window.h"
@@ -440,6 +442,15 @@ static struct vimvar {
static dictitem_T vimvars_var; /* variable used for v: */
#define vimvarht vimvardict.dv_hashtab
+typedef struct {
+ Job *job;
+ Terminal *term;
+ bool exited;
+ int refcount;
+ char *autocmd_file;
+} TerminalJobData;
+
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.c.generated.h"
#endif
@@ -447,7 +458,6 @@ static dictitem_T vimvars_var; /* variable used for v: */
#define FNE_INCL_BR 1 /* find_name_end(): include [] in name */
#define FNE_CHECK_START 2 /* find_name_end(): check name starts with
valid character */
-
// Memory pool for reusing JobEvent structures
typedef struct {
int id;
@@ -6600,6 +6610,7 @@ static struct fst {
{"tan", 1, 1, f_tan},
{"tanh", 1, 1, f_tanh},
{"tempname", 0, 0, f_tempname},
+ {"termopen", 1, 2, f_termopen},
{"test", 1, 1, f_test},
{"tolower", 1, 1, f_tolower},
{"toupper", 1, 1, f_toupper},
@@ -10750,12 +10761,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
// The last item of argv must be NULL
argv[i] = NULL;
- JobOptions opts = JOB_OPTIONS_INIT;
- opts.argv = argv;
- opts.data = xstrdup((char *)argvars[0].vval.v_string);
- opts.stdout_cb = on_job_stdout;
- opts.stderr_cb = on_job_stderr;
- opts.exit_cb = on_job_exit;
+ JobOptions opts = common_job_options(argv, (char *)argvars[0].vval.v_string);
if (args && argvars[3].v_type == VAR_DICT) {
dict_T *job_opts = argvars[3].vval.v_dict;
@@ -10774,15 +10780,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv)
}
}
- job_start(opts, &rettv->vval.v_number);
-
- if (rettv->vval.v_number <= 0) {
- if (rettv->vval.v_number == 0) {
- EMSG(_(e_jobtblfull));
- } else {
- EMSG(_(e_jobexe));
- }
- }
+ common_job_start(opts, rettv);
}
// "jobstop()" function
@@ -14870,6 +14868,72 @@ static void f_tempname(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = vim_tempname();
}
+// "termopen(cmd[, cwd])" function
+static void f_termopen(typval_T *argvars, typval_T *rettv)
+{
+ if (check_restricted() || check_secure()) {
+ return;
+ }
+
+ if (curbuf->b_changed) {
+ EMSG(_("Can only call this function in an unmodified buffer"));
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_STRING
+ || (argvars[1].v_type != VAR_STRING
+ && argvars[1].v_type != VAR_UNKNOWN)) {
+ // Wrong argument types
+ EMSG(_(e_invarg));
+ return;
+ }
+
+ char **argv = shell_build_argv((char *)argvars[0].vval.v_string, NULL);
+ JobOptions opts = common_job_options(argv, NULL);
+ opts.pty = true;
+ opts.width = curwin->w_width;
+ opts.height = curwin->w_height;
+ opts.term_name = xstrdup("xterm-256color");
+ Job *job = common_job_start(opts, rettv);
+ if (!job) {
+ return;
+ }
+ TerminalJobData *data = opts.data;
+ TerminalOptions topts = TERMINAL_OPTIONS_INIT;
+ topts.data = data;
+ topts.width = curwin->w_width;
+ topts.height = curwin->w_height;
+ topts.write_cb = term_write;
+ topts.resize_cb = term_resize;
+ topts.close_cb = term_close;
+
+ char *cwd = ".";
+ if (argvars[1].v_type == VAR_STRING
+ && os_isdir(argvars[1].vval.v_string)) {
+ cwd = (char *)argvars[1].vval.v_string;
+ }
+ int pid = job_pid(job);
+ char buf[1024];
+ // format the title with the pid to conform with the term:// URI
+ snprintf(buf, sizeof(buf), "term://%s//%d:%s", cwd, pid,
+ (char *)argvars[0].vval.v_string);
+ // at this point the buffer has no terminal instance associated yet, so unset
+ // the 'swapfile' option to ensure no swap file will be created
+ curbuf->b_p_swf = false;
+ (void)setfname(curbuf, (uint8_t *)buf, NULL, true);
+ data->autocmd_file = xstrdup(buf);
+ // Save the job id and pid in b:terminal_job_{id,pid}
+ Error err;
+ dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_id"),
+ INTEGER_OBJ(rettv->vval.v_number), &err);
+ dict_set_value(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
+ INTEGER_OBJ(pid), &err);
+
+ Terminal *term = terminal_open(topts);
+ data->term = term;
+ data->refcount++;
+}
+
/*
* "test(list)" function: Just checking the walls...
*/
@@ -19750,16 +19814,51 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags)
return ret;
}
+static inline JobOptions common_job_options(char **argv, char *autocmd_file)
+{
+ TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData));
+ if (autocmd_file) {
+ data->autocmd_file = xstrdup(autocmd_file);
+ }
+ JobOptions opts = JOB_OPTIONS_INIT;
+ opts.argv = argv;
+ opts.data = data;
+ opts.stdout_cb = on_job_stdout;
+ opts.stderr_cb = on_job_stderr;
+ opts.exit_cb = on_job_exit;
+ return opts;
+}
+
+static inline Job *common_job_start(JobOptions opts, typval_T *rettv)
+{
+ Job *job = job_start(opts, &rettv->vval.v_number);
+ TerminalJobData *data = opts.data;
+ data->refcount++;
+
+ if (rettv->vval.v_number <= 0) {
+ if (rettv->vval.v_number == 0) {
+ EMSG(_(e_jobtblfull));
+ free(data->autocmd_file);
+ } else {
+ EMSG(_(e_jobexe));
+ free(opts.data);
+ }
+ return NULL;
+ }
+ data->job = job;
+ return job;
+}
+
// JobActivity autocommands will execute vimscript code, so it must be executed
// on Nvim main loop
-static inline void push_job_event(Job *job, RStream *rstream, char *type)
+static inline void push_job_event(Job *job, char *type, char *data,
+ size_t count)
{
JobEvent *event_data = kmp_alloc(JobEventPool, job_event_pool);
event_data->received = NULL;
- if (rstream) {
+ if (data) {
event_data->received = list_alloc();
- char *ptr = rstream_read_ptr(rstream);
- size_t count = rstream_pending(rstream);
+ char *ptr = data;
size_t remaining = count;
size_t off = 0;
@@ -19780,10 +19879,10 @@ static inline void push_job_event(Job *job, RStream *rstream, char *type)
off++;
}
list_append_string(event_data->received, (uint8_t *)ptr, off);
- rbuffer_consumed(rstream_buffer(rstream), count);
}
+ TerminalJobData *d = job_data(job);
event_data->id = job_id(job);
- event_data->name = job_data(job);
+ event_data->name = d->autocmd_file;
event_data->type = type;
event_push((Event) {
.handler = on_job_event,
@@ -19793,21 +19892,75 @@ static inline void push_job_event(Job *job, RStream *rstream, char *type)
static void on_job_stdout(RStream *rstream, void *data, bool eof)
{
- if (!eof) {
- push_job_event(data, rstream, "stdout");
- }
+ on_job_output(rstream, data, eof, "stdout");
}
static void on_job_stderr(RStream *rstream, void *data, bool eof)
{
- if (!eof) {
- push_job_event(data, rstream, "stderr");
+ on_job_output(rstream, data, eof, "stderr");
+}
+
+static void on_job_output(RStream *rstream, Job *job, bool eof, char *type)
+{
+ if (eof) {
+ return;
}
+
+ TerminalJobData *data = job_data(job);
+ char *ptr = rstream_read_ptr(rstream);
+ size_t len = rstream_pending(rstream);
+
+ // The order here matters, the terminal must receive the data first because
+ // push_job_event will modify the read buffer(convert NULs into NLs)
+ if (data->term) {
+ terminal_receive(data->term, ptr, len);
+ }
+
+ push_job_event(job, type, ptr, len);
+ rbuffer_consumed(rstream_buffer(rstream), len);
+}
+
+static void on_job_exit(Job *job, void *d)
+{
+ TerminalJobData *data = d;
+ push_job_event(job, "exit", NULL, 0);
+
+ if (data->term && !data->exited) {
+ data->exited = true;
+ terminal_close(data->term,
+ _("\r\n[Program exited, press any key to close]"));
+ }
+ term_job_data_decref(data);
+}
+
+static void term_write(char *buf, size_t size, void *data)
+{
+ Job *job = ((TerminalJobData *)data)->job;
+ WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, free);
+ job_write(job, wbuf);
+}
+
+static void term_resize(uint16_t width, uint16_t height, void *data)
+{
+ job_resize(((TerminalJobData *)data)->job, width, height);
}
-static void on_job_exit(Job *job, void *data)
+static void term_close(void *d)
{
- push_job_event(job, NULL, "exit");
+ TerminalJobData *data = d;
+ if (!data->exited) {
+ data->exited = true;
+ job_stop(data->job);
+ }
+ terminal_destroy(data->term);
+ term_job_data_decref(d);
+}
+
+static void term_job_data_decref(TerminalJobData *data)
+{
+ if (!(--data->refcount)) {
+ free(data);
+ }
}
static void on_job_event(Event event)
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index d85664d1ba..81c7ecab5f 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2724,7 +2724,7 @@ do_ecmd (
/* close the link to the current buffer */
u_sync(FALSE);
close_buffer(oldwin, curbuf,
- (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE);
+ (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, FALSE);
/* Autocommands may open a new window and leave oldwin open
* which leads to crashes since the above call sets
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 0897006555..e1951e88f8 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2236,6 +2236,11 @@ return {
func='ex_tearoff',
},
{
+ command='terminal',
+ flags=bit.bor(BANG, FILES, CMDWIN),
+ func='ex_terminal',
+ },
+ {
command='tfirst',
flags=bit.bor(RANGE, NOTADR, BANG, TRLBAR, ZEROR),
func='ex_tag',
@@ -2256,6 +2261,16 @@ return {
func='ex_tag',
},
{
+ command='tmap',
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN),
+ func='ex_map',
+ },
+ {
+ command='tmapclear',
+ flags=bit.bor(EXTRA, TRLBAR, CMDWIN),
+ func='ex_mapclear',
+ },
+ {
command='tmenu',
flags=bit.bor(RANGE, NOTADR, ZEROR, EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN),
func='ex_menu',
@@ -2266,6 +2281,11 @@ return {
func='ex_tag',
},
{
+ command='tnoremap',
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN),
+ func='ex_map',
+ },
+ {
command='topleft',
flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM),
func='ex_wrongmodifier',
@@ -2291,6 +2311,11 @@ return {
func='ex_tag',
},
{
+ command='tunmap',
+ flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN),
+ func='ex_unmap',
+ },
+ {
command='tunmenu',
flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN),
func='ex_menu',
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index de04a3ea32..776ed844e9 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -62,6 +62,7 @@
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
+#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/version.h"
@@ -71,6 +72,8 @@
#include "nvim/os/time.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/mouse.h"
+#include "nvim/os/rstream.h"
+#include "nvim/os/wstream.h"
static int quitmore = 0;
static int ex_pressedreturn = FALSE;
@@ -1510,7 +1513,9 @@ static char_u * do_one_cmd(char_u **cmdlinep,
errormsg = (char_u *)_(e_sandbox);
goto doend;
}
- if (!MODIFIABLE(curbuf) && (ea.argt & MODIFY)) {
+ if (!MODIFIABLE(curbuf) && (ea.argt & MODIFY)
+ // allow :put in terminals
+ && (!curbuf->terminal || ea.cmdidx != CMD_put)) {
/* Command not allowed in non-'modifiable' buffer */
errormsg = (char_u *)_(e_modifiable);
goto doend;
@@ -2610,7 +2615,7 @@ set_one_cmd_context (
xp->xp_context = EXPAND_FILES;
/* For a shell command more chars need to be escaped. */
- if (usefilter || ea.cmdidx == CMD_bang) {
+ if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) {
#ifndef BACKSLASH_IN_FILENAME
xp->xp_shell = TRUE;
#endif
@@ -5126,8 +5131,10 @@ static void ex_quit(exarg_T *eap)
|| (only_one_window() && check_changed_any(eap->forceit))) {
not_exiting();
} else {
- if (only_one_window()) /* quit last window */
+ if (only_one_window()) {
+ // quit last window
getout(0);
+ }
/* close window; may free buffer */
win_close(curwin, !P_HID(curwin->w_buffer) || eap->forceit);
}
@@ -8060,7 +8067,9 @@ makeopens (
/*
* Wipe out an empty unnamed buffer we started in.
*/
- if (put_line(fd, "if exists('s:wipebuf')") == FAIL)
+ if (put_line(fd, "if exists('s:wipebuf') "
+ "&& getbufvar(s:wipebuf, '&buftype') isnot# 'terminal'")
+ == FAIL)
return FAIL;
if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
return FAIL;
@@ -8269,7 +8278,7 @@ put_view (
* Load the file.
*/
if (wp->w_buffer->b_ffname != NULL
- && !bt_nofile(wp->w_buffer)
+ && (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal)
) {
/*
* Editing a file in this buffer: use ":edit file".
@@ -8857,3 +8866,12 @@ static void ex_folddo(exarg_T *eap)
global_exe(eap->arg);
ml_clearmarked(); /* clear rest of the marks */
}
+
+static void ex_terminal(exarg_T *eap)
+{
+ char cmd[512];
+ snprintf(cmd, sizeof(cmd), ":enew%s | call termopen('%s') | startinsert",
+ eap->forceit==TRUE ? "!" : "",
+ strcmp((char *)eap->arg, "") ? (char *)eap->arg : (char *)p_sh);
+ do_cmdline_cmd((uint8_t *)cmd);
+}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 799a6a2a50..29bcaec84a 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -4756,10 +4756,11 @@ buf_check_timestamp (
char_u *s;
char *reason;
- /* If there is no file name, the buffer is not loaded, 'buftype' is
- * set, we are in the middle of a save or being called recursively: ignore
- * this buffer. */
- if (buf->b_ffname == NULL
+ // If its a terminal, there is no file name, the buffer is not loaded,
+ // 'buftype' is set, we are in the middle of a save or being called
+ // recursively: ignore this buffer.
+ if (buf->terminal
+ || buf->b_ffname == NULL
|| buf->b_ml.ml_mfp == NULL
|| *buf->b_p_bt != NUL
|| buf->b_saving
@@ -5208,6 +5209,7 @@ static struct event_name {
{"TabNew", EVENT_TABNEW},
{"TabNewEntered", EVENT_TABNEWENTERED},
{"TermChanged", EVENT_TERMCHANGED},
+ {"TermOpen", EVENT_TERMOPEN},
{"TermResponse", EVENT_TERMRESPONSE},
{"TextChanged", EVENT_TEXTCHANGED},
{"TextChangedI", EVENT_TEXTCHANGEDI},
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 7a9e2adae1..833a4fade6 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -100,6 +100,7 @@ typedef enum auto_event {
EVENT_TABNEWENTERED, /* after entering a new tab */
EVENT_SHELLCMDPOST, /* after ":!cmd" */
EVENT_SHELLFILTERPOST, /* after ":1,2!cmd", ":w !cmd", ":r !cmd". */
+ EVENT_TERMOPEN, // after opening a terminal buffer
EVENT_TEXTCHANGED, /* text was modified */
EVENT_TEXTCHANGEDI, /* text was modified in Insert mode*/
EVENT_CMDUNDEFINED, ///< command undefined
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 74444d4ab7..267c586543 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -137,7 +137,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to)
int hasAnyFolding(win_T *win)
{
/* very simple now, but can become more complex later */
- return win->w_p_fen
+ return !win->w_buffer->terminal && win->w_p_fen
&& (!foldmethodIsManual(win) || !GA_EMPTY(&win->w_folds));
}
@@ -768,6 +768,9 @@ void clearFolding(win_T *win)
void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
{
fold_T *fp;
+ if (wp->w_buffer->terminal) {
+ return;
+ }
/* Mark all folds from top to bot as maybe-small. */
(void)foldFind(&curwin->w_folds, top, &fp);
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index bb251c102e..d901e99a2d 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2538,6 +2538,7 @@ fix_input_buffer (
* for :xmap mode is VISUAL
* for :smap mode is SELECTMODE
* for :omap mode is OP_PENDING
+ * for :tmap mode is TERM_FOCUS
*
* for :abbr mode is INSERT + CMDLINE
* for :iabbr mode is INSERT
@@ -3056,6 +3057,8 @@ int get_map_mode(char_u **cmdp, int forceit)
mode = SELECTMODE; /* :smap */
else if (modec == 'o')
mode = OP_PENDING; /* :omap */
+ else if (modec == 't')
+ mode = TERM_FOCUS; // :tmap
else {
--p;
if (forceit)
@@ -3923,6 +3926,9 @@ makemap (
case LANGMAP:
c1 = 'l';
break;
+ case TERM_FOCUS:
+ c1 = 't';
+ break;
default:
EMSG(_("E228: makemap: Illegal mode"));
return FAIL;
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 653f46fb44..60f6cd454f 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -154,4 +154,6 @@
/// zero in those cases (-Wdiv-by-zero in GCC).
#define ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0])))))
+#define RGB(r, g, b) ((r << 16) | (g << 8) | b)
+
#endif // NVIM_MACROS_H
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 9f4bc22ae0..47bb2bc515 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -285,6 +285,17 @@ int main(int argc, char **argv)
input_start_stdin(fd);
}
+ // open terminals when opening files that start with term://
+ do_cmdline_cmd((uint8_t *)
+ "autocmd BufReadCmd term://* "
+ ":call termopen( "
+ // Capture the command string
+ "matchstr(expand(\"<amatch>\"), "
+ "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
+ // capture the working directory
+ "get(matchlist(expand(\"<amatch>\"), "
+ "'\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))");
+
/* Execute --cmd arguments. */
exe_pre_commands(&params);
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index d6d7d3db1a..8b2ebfe554 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -278,10 +278,11 @@ int ml_open(buf_T *buf)
/*
* When 'updatecount' is non-zero swap file may be opened later.
*/
- if (p_uc && buf->b_p_swf)
+ if (!buf->terminal && p_uc && buf->b_p_swf) {
buf->b_may_swap = true;
- else
+ } else {
buf->b_may_swap = false;
+ }
/*
* Open the memfile. No swap file is created yet.
@@ -488,7 +489,8 @@ void ml_open_file(buf_T *buf)
char_u *dirp;
mfp = buf->b_ml.ml_mfp;
- if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile) {
+ if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile
+ || buf->terminal) {
return; /* nothing to do */
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 50710cd78a..2714798368 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -45,6 +45,7 @@
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/terminal.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/window.h"
@@ -2643,11 +2644,13 @@ do_put (
return;
}
- /* Autocommands may be executed when saving lines for undo, which may make
- * y_array invalid. Start undo now to avoid that. */
- if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) {
- ELOG(_("Failed to save undo information"));
- return;
+ if (!curbuf->terminal) {
+ // Autocommands may be executed when saving lines for undo, which may make
+ // y_array invalid. Start undo now to avoid that.
+ if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) {
+ ELOG(_("Failed to save undo information"));
+ return;
+ }
}
if (insert_string != NULL) {
@@ -2692,6 +2695,20 @@ do_put (
y_array = y_current->y_array;
}
+ if (curbuf->terminal) {
+ for (int i = 0; i < count; i++) {
+ // feed the lines to the terminal
+ for (int j = 0; j < y_size; j++) {
+ if (j) {
+ // terminate the previous line
+ terminal_send(curbuf->terminal, "\n", 1);
+ }
+ terminal_send(curbuf->terminal, (char *)y_array[j], STRLEN(y_array[j]));
+ }
+ }
+ return;
+ }
+
if (y_type == MLINE) {
if (flags & PUT_LINE_SPLIT) {
/* "p" or "P" in Visual mode: split the lines to put the text in
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 19202030c6..3f12709521 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1746,7 +1746,7 @@ static char *(p_scbopt_values[]) = {"ver", "hor", "jump", NULL};
static char *(p_debug_values[]) = {"msg", "throw", "beep", NULL};
static char *(p_ead_values[]) = {"both", "ver", "hor", NULL};