diff options
| author | Thiago de Arruda <tpadilha84@gmail.com> | 2015-03-08 08:58:31 -0300 |
|---|---|---|
| committer | Thiago de Arruda <tpadilha84@gmail.com> | 2015-03-25 18:57:35 -0300 |
| commit | cdedd89d228a016a4e433968702e9e3ce5165e7d (patch) | |
| tree | 2a98eb4008a6033621c293f84db29cb3127f9740 /src | |
| parent | 6f471fa4fc24e72e1ac75c1579970414b168381e (diff) | |
| download | rneovim-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.c | 50 | ||||
| -rw-r--r-- | src/nvim/buffer_defs.h | 7 | ||||
| -rw-r--r-- | src/nvim/edit.c | 6 | ||||
| -rw-r--r-- | src/nvim/eval.c | 211 | ||||
| -rw-r--r-- | src/nvim/ex_cmds.c | 2 | ||||
| -rw-r--r-- | src/nvim/ex_cmds.lua | 25 | ||||
| -rw-r--r-- | src/nvim/ex_docmd.c | 28 | ||||
| -rw-r--r-- | src/nvim/fileio.c | 10 | ||||
| -rw-r--r-- | src/nvim/fileio.h | 1 | ||||
| -rw-r--r-- | src/nvim/fold.c | 5 | ||||
| -rw-r--r-- | src/nvim/getchar.c | 6 | ||||
| -rw-r--r-- | src/nvim/macros.h | 2 | ||||
| -rw-r--r-- | src/nvim/main.c | 11 | ||||
| -rw-r--r-- | src/nvim/memline.c | 8 | ||||
| -rw-r--r-- | src/nvim/ops.c | 27 | ||||
| -rw-r--r-- | src/nvim/option.c | 8 | ||||
| -rw-r--r-- | src/nvim/os/event.c | 8 | ||||
| -rw-r--r-- | src/nvim/quickfix.c | 15 | ||||
| -rw-r--r-- | src/nvim/screen.c | 36 | ||||
| -rw-r--r-- | src/nvim/syntax.c | 5 | ||||
| -rw-r--r-- | src/nvim/terminal.c | 1129 | ||||
| -rw-r--r-- | src/nvim/terminal.h | 33 | ||||
| -rw-r--r-- | src/nvim/undo.c | 3 | ||||
| -rw-r--r-- | src/nvim/vim.h | 5 | ||||
| -rw-r--r-- | src/nvim/window.c | 28 |
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(¶ms); 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}; |