mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
api: allow open non-current buffer as terminal (+ xmas bonus)
vim.api.nvim_chan_send(vim.api.nvim_open_term(0), io.open("/path/to/smile.cat", "r"):read("*a"))
This commit is contained in:
parent
097ec71bd8
commit
ed08936987
@ -39,6 +39,7 @@
|
|||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
#include "nvim/eval/userfunc.h"
|
#include "nvim/eval/userfunc.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
|
#include "nvim/move.h"
|
||||||
#include "nvim/ops.h"
|
#include "nvim/ops.h"
|
||||||
#include "nvim/option.h"
|
#include "nvim/option.h"
|
||||||
#include "nvim/state.h"
|
#include "nvim/state.h"
|
||||||
@ -1246,6 +1247,99 @@ fail:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Open a terminal instance in a buffer
|
||||||
|
///
|
||||||
|
/// By default (and currently the only option) the terminal will not be
|
||||||
|
/// connected to an external process. Instead, input send on the channel
|
||||||
|
/// will be echoed directly by the terminal. This is useful to disply
|
||||||
|
/// ANSI terminal sequences returned as part of a rpc message, or similar.
|
||||||
|
///
|
||||||
|
/// Note: to directly initiate the terminal using the right size, display the
|
||||||
|
/// buffer in a configured window before calling this. For instance, for a
|
||||||
|
/// floating display, first create an empty buffer using |nvim_create_buf()|,
|
||||||
|
/// then display it using |nvim_open_win()|, and then call this function.
|
||||||
|
/// Then |nvim_chan_send()| cal be called immediately to process sequences
|
||||||
|
/// in a virtual terminal having the intended size.
|
||||||
|
///
|
||||||
|
/// @param buffer the buffer to use (expected to be empty)
|
||||||
|
/// @param opts Optional parameters. Reserved for future use.
|
||||||
|
/// @param[out] err Error details, if any
|
||||||
|
Integer nvim_open_term(Buffer buffer, Dictionary opts, Error *err)
|
||||||
|
FUNC_API_SINCE(7)
|
||||||
|
{
|
||||||
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
|
if (!buf) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.size > 0) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalOptions topts;
|
||||||
|
Channel *chan = channel_alloc(kChannelStreamInternal);
|
||||||
|
topts.data = chan;
|
||||||
|
// NB: overriden in terminal_check_size if a window is already
|
||||||
|
// displaying the buffer
|
||||||
|
topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0);
|
||||||
|
topts.height = (uint16_t)curwin->w_height_inner;
|
||||||
|
topts.write_cb = term_write;
|
||||||
|
topts.resize_cb = term_resize;
|
||||||
|
topts.close_cb = term_close;
|
||||||
|
Terminal *term = terminal_open(buf, topts);
|
||||||
|
terminal_check_size(term);
|
||||||
|
chan->term = term;
|
||||||
|
channel_incref(chan);
|
||||||
|
return (Integer)chan->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void term_write(char *buf, size_t size, void *data)
|
||||||
|
{
|
||||||
|
// TODO(bfredl): lua callback
|
||||||
|
}
|
||||||
|
|
||||||
|
static void term_resize(uint16_t width, uint16_t height, void *data)
|
||||||
|
{
|
||||||
|
// TODO(bfredl): lua callback
|
||||||
|
}
|
||||||
|
|
||||||
|
static void term_close(void *data)
|
||||||
|
{
|
||||||
|
Channel *chan = data;
|
||||||
|
terminal_destroy(chan->term);
|
||||||
|
chan->term = NULL;
|
||||||
|
channel_decref(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Send data to channel `id`. For a job, it writes it to the
|
||||||
|
/// stdin of the process. For the stdio channel |channel-stdio|,
|
||||||
|
/// it writes to Nvim's stdout. For an internal terminal instance
|
||||||
|
/// (|nvim_open_term()|) it writes directly to terimal output.
|
||||||
|
/// See |channel-bytes| for more information.
|
||||||
|
///
|
||||||
|
/// This function writes raw data, not RPC messages. If the channel
|
||||||
|
/// was created with `rpc=true` then the channel expects RPC
|
||||||
|
/// messages, use |vim.rpcnotify()| and |vim.rpcrequest()| instead.
|
||||||
|
///
|
||||||
|
/// @param chan id of the channel
|
||||||
|
/// @param data data to write. 8-bit clean: can contain NUL bytes.
|
||||||
|
/// @param[out] err Error details, if any
|
||||||
|
void nvim_chan_send(Integer chan, String data, Error *err)
|
||||||
|
FUNC_API_SINCE(7) FUNC_API_REMOTE_ONLY FUNC_API_LUA_ONLY
|
||||||
|
{
|
||||||
|
const char *error = NULL;
|
||||||
|
if (!data.size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_send((uint64_t)chan, data.data, data.size, &error);
|
||||||
|
if (error) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "%s", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Open a new window.
|
/// Open a new window.
|
||||||
///
|
///
|
||||||
/// Currently this is used to open floating and external windows.
|
/// Currently this is used to open floating and external windows.
|
||||||
|
@ -161,7 +161,7 @@ void channel_init(void)
|
|||||||
///
|
///
|
||||||
/// Channel is allocated with refcount 1, which should be decreased
|
/// Channel is allocated with refcount 1, which should be decreased
|
||||||
/// when the underlying stream closes.
|
/// when the underlying stream closes.
|
||||||
static Channel *channel_alloc(ChannelStreamType type)
|
Channel *channel_alloc(ChannelStreamType type)
|
||||||
{
|
{
|
||||||
Channel *chan = xcalloc(1, sizeof(*chan));
|
Channel *chan = xcalloc(1, sizeof(*chan));
|
||||||
if (type == kChannelStreamStdio) {
|
if (type == kChannelStreamStdio) {
|
||||||
@ -503,7 +503,7 @@ size_t channel_send(uint64_t id, char *data, size_t len, const char **error)
|
|||||||
{
|
{
|
||||||
Channel *chan = find_channel(id);
|
Channel *chan = find_channel(id);
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
EMSG(_(e_invchan));
|
*error = _(e_invchan);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,6 +518,11 @@ size_t channel_send(uint64_t id, char *data, size_t len, const char **error)
|
|||||||
return len * written;
|
return len * written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chan->streamtype == kChannelStreamInternal && chan->term) {
|
||||||
|
terminal_receive(chan->term, data, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Stream *in = channel_instream(chan);
|
Stream *in = channel_instream(chan);
|
||||||
if (in->closed) {
|
if (in->closed) {
|
||||||
@ -724,8 +729,8 @@ static void channel_callback_call(Channel *chan, CallbackReader *reader)
|
|||||||
/// Open terminal for channel
|
/// Open terminal for channel
|
||||||
///
|
///
|
||||||
/// Channel `chan` is assumed to be an open pty channel,
|
/// Channel `chan` is assumed to be an open pty channel,
|
||||||
/// and curbuf is assumed to be a new, unmodified buffer.
|
/// and `buf` is assumed to be a new, unmodified buffer.
|
||||||
void channel_terminal_open(Channel *chan)
|
void channel_terminal_open(buf_T *buf, Channel *chan)
|
||||||
{
|
{
|
||||||
TerminalOptions topts;
|
TerminalOptions topts;
|
||||||
topts.data = chan;
|
topts.data = chan;
|
||||||
@ -734,8 +739,8 @@ void channel_terminal_open(Channel *chan)
|
|||||||
topts.write_cb = term_write;
|
topts.write_cb = term_write;
|
||||||
topts.resize_cb = term_resize;
|
topts.resize_cb = term_resize;
|
||||||
topts.close_cb = term_close;
|
topts.close_cb = term_close;
|
||||||
curbuf->b_p_channel = (long)chan->id; // 'channel' option
|
buf->b_p_channel = (long)chan->id; // 'channel' option
|
||||||
Terminal *term = terminal_open(topts);
|
Terminal *term = terminal_open(buf, topts);
|
||||||
chan->term = term;
|
chan->term = term;
|
||||||
channel_incref(chan);
|
channel_incref(chan);
|
||||||
}
|
}
|
||||||
|
@ -10731,7 +10731,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
INTEGER_OBJ(pid), false, false, &err);
|
INTEGER_OBJ(pid), false, false, &err);
|
||||||
api_clear_error(&err);
|
api_clear_error(&err);
|
||||||
|
|
||||||
channel_terminal_open(chan);
|
channel_terminal_open(curbuf, chan);
|
||||||
channel_create_event(chan, NULL);
|
channel_create_event(chan, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,8 +104,11 @@ for _,f in ipairs(shallowcopy(functions)) do
|
|||||||
elseif startswith(f.name, "nvim_tabpage_") then
|
elseif startswith(f.name, "nvim_tabpage_") then
|
||||||
ismethod = true
|
ismethod = true
|
||||||
end
|
end
|
||||||
|
f.remote = f.remote_only or not f.lua_only
|
||||||
|
f.lua = f.lua_only or not f.remote_only
|
||||||
|
f.eval = (not f.lua_only) and (not f.remote_only)
|
||||||
else
|
else
|
||||||
f.remote_only = true
|
f.remote = true
|
||||||
f.since = 0
|
f.since = 0
|
||||||
f.deprecated_since = 1
|
f.deprecated_since = 1
|
||||||
end
|
end
|
||||||
@ -127,7 +130,8 @@ for _,f in ipairs(shallowcopy(functions)) do
|
|||||||
newf.return_type = "Object"
|
newf.return_type = "Object"
|
||||||
end
|
end
|
||||||
newf.impl_name = f.name
|
newf.impl_name = f.name
|
||||||
newf.remote_only = true
|
newf.lua = false
|
||||||
|
newf.eval = false
|
||||||
newf.since = 0
|
newf.since = 0
|
||||||
newf.deprecated_since = 1
|
newf.deprecated_since = 1
|
||||||
functions[#functions+1] = newf
|
functions[#functions+1] = newf
|
||||||
@ -192,7 +196,7 @@ end
|
|||||||
-- the real API.
|
-- the real API.
|
||||||
for i = 1, #functions do
|
for i = 1, #functions do
|
||||||
local fn = functions[i]
|
local fn = functions[i]
|
||||||
if fn.impl_name == nil and not fn.lua_only then
|
if fn.impl_name == nil and fn.remote then
|
||||||
local args = {}
|
local args = {}
|
||||||
|
|
||||||
output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
|
output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
|
||||||
@ -323,7 +327,7 @@ void msgpack_rpc_init_method_table(void)
|
|||||||
|
|
||||||
for i = 1, #functions do
|
for i = 1, #functions do
|
||||||
local fn = functions[i]
|
local fn = functions[i]
|
||||||
if not fn.lua_only then
|
if fn.remote then
|
||||||
output:write(' msgpack_rpc_add_method_handler('..
|
output:write(' msgpack_rpc_add_method_handler('..
|
||||||
'(String) {.data = "'..fn.name..'", '..
|
'(String) {.data = "'..fn.name..'", '..
|
||||||
'.size = sizeof("'..fn.name..'") - 1}, '..
|
'.size = sizeof("'..fn.name..'") - 1}, '..
|
||||||
@ -492,7 +496,7 @@ local function process_function(fn)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _, fn in ipairs(functions) do
|
for _, fn in ipairs(functions) do
|
||||||
if not fn.remote_only or fn.name:sub(1, 4) == '_vim' then
|
if fn.lua or fn.name:sub(1, 4) == '_vim' then
|
||||||
process_function(fn)
|
process_function(fn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,7 +25,7 @@ local gperfpipe = io.open(funcsfname .. '.gperf', 'wb')
|
|||||||
local funcs = require('eval').funcs
|
local funcs = require('eval').funcs
|
||||||
local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all"))
|
local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all"))
|
||||||
for _,fun in ipairs(metadata) do
|
for _,fun in ipairs(metadata) do
|
||||||
if not (fun.remote_only or fun.lua_only) then
|
if fun.eval then
|
||||||
funcs[fun.name] = {
|
funcs[fun.name] = {
|
||||||
args=#fun.parameters,
|
args=#fun.parameters,
|
||||||
func='api_wrapper',
|
func='api_wrapper',
|
||||||
|
@ -169,19 +169,20 @@ void terminal_teardown(void)
|
|||||||
multiqueue_free(refresh_timer.events);
|
multiqueue_free(refresh_timer.events);
|
||||||
time_watcher_close(&refresh_timer, NULL);
|
time_watcher_close(&refresh_timer, NULL);
|
||||||
pmap_free(ptr_t)(invalidated_terminals);
|
pmap_free(ptr_t)(invalidated_terminals);
|
||||||
|
invalidated_terminals = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public API {{{
|
// public API {{{
|
||||||
|
|
||||||
Terminal *terminal_open(TerminalOptions opts)
|
Terminal *terminal_open(buf_T *buf, TerminalOptions opts)
|
||||||
{
|
{
|
||||||
// Create a new terminal instance and configure it
|
// Create a new terminal instance and configure it
|
||||||
Terminal *rv = xcalloc(1, sizeof(Terminal));
|
Terminal *rv = xcalloc(1, sizeof(Terminal));
|
||||||
rv->opts = opts;
|
rv->opts = opts;
|
||||||
rv->cursor.visible = true;
|
rv->cursor.visible = true;
|
||||||
// Associate the terminal instance with the new buffer
|
// Associate the terminal instance with the new buffer
|
||||||
rv->buf_handle = curbuf->handle;
|
rv->buf_handle = buf->handle;
|
||||||
curbuf->terminal = rv;
|
buf->terminal = rv;
|
||||||
// Create VTerm
|
// Create VTerm
|
||||||
rv->vt = vterm_new(opts.height, opts.width);
|
rv->vt = vterm_new(opts.height, opts.width);
|
||||||
vterm_set_utf8(rv->vt, 1);
|
vterm_set_utf8(rv->vt, 1);
|
||||||
@ -198,28 +199,36 @@ Terminal *terminal_open(TerminalOptions opts)
|
|||||||
// have as many lines as screen rows when refresh_scrollback is called
|
// have as many lines as screen rows when refresh_scrollback is called
|
||||||
rv->invalid_start = 0;
|
rv->invalid_start = 0;
|
||||||
rv->invalid_end = opts.height;
|
rv->invalid_end = opts.height;
|
||||||
refresh_screen(rv, curbuf);
|
|
||||||
|
aco_save_T aco;
|
||||||
|
aucmd_prepbuf(&aco, buf);
|
||||||
|
|
||||||
|
refresh_screen(rv, buf);
|
||||||
set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666
|
set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666
|
||||||
|
|
||||||
// Default settings for terminal buffers
|
// Default settings for terminal buffers
|
||||||
curbuf->b_p_ma = false; // 'nomodifiable'
|
buf->b_p_ma = false; // 'nomodifiable'
|
||||||
curbuf->b_p_ul = -1; // 'undolevels'
|
buf->b_p_ul = -1; // 'undolevels'
|
||||||
curbuf->b_p_scbk = // 'scrollback' (initialize local from global)
|
buf->b_p_scbk = // 'scrollback' (initialize local from global)
|
||||||
(p_scbk < 0) ? 10000 : MAX(1, p_scbk);
|
(p_scbk < 0) ? 10000 : MAX(1, p_scbk);
|
||||||
curbuf->b_p_tw = 0; // 'textwidth'
|
buf->b_p_tw = 0; // 'textwidth'
|
||||||
set_option_value("wrap", false, NULL, OPT_LOCAL);
|
set_option_value("wrap", false, NULL, OPT_LOCAL);
|
||||||
set_option_value("list", false, NULL, OPT_LOCAL);
|
set_option_value("list", false, NULL, OPT_LOCAL);
|
||||||
buf_set_term_title(curbuf, (char *)curbuf->b_ffname);
|
if (buf->b_ffname != NULL) {
|
||||||
|
buf_set_term_title(buf, (char *)buf->b_ffname);
|
||||||
|
}
|
||||||
RESET_BINDING(curwin);
|
RESET_BINDING(curwin);
|
||||||
// Reset cursor in current window.
|
// Reset cursor in current window.
|
||||||
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
|
curwin->w_cursor = (pos_T){ .lnum = 1, .col = 0, .coladd = 0 };
|
||||||
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
|
// Apply TermOpen autocmds _before_ configuring the scrollback buffer.
|
||||||
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, curbuf);
|
apply_autocmds(EVENT_TERMOPEN, NULL, NULL, false, buf);
|
||||||
// Local 'scrollback' _after_ autocmds.
|
// Local 'scrollback' _after_ autocmds.
|
||||||
curbuf->b_p_scbk = (curbuf->b_p_scbk < 1) ? SB_MAX : curbuf->b_p_scbk;
|
buf->b_p_scbk = (buf->b_p_scbk < 1) ? SB_MAX : buf->b_p_scbk;
|
||||||
|
|
||||||
|
aucmd_restbuf(&aco);
|
||||||
|
|
||||||
// Configure the scrollback buffer.
|
// Configure the scrollback buffer.
|
||||||
rv->sb_size = (size_t)curbuf->b_p_scbk;
|
rv->sb_size = (size_t)buf->b_p_scbk;
|
||||||
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
|
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
|
||||||
|
|
||||||
// Configure the color palette. Try to get the color from:
|
// Configure the color palette. Try to get the color from:
|
||||||
@ -511,7 +520,9 @@ void terminal_destroy(Terminal *term)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!term->refcount) {
|
if (!term->refcount) {
|
||||||
if (pmap_has(ptr_t)(invalidated_terminals, term)) {
|
// might be destroyed after terminal_teardown is invoked
|
||||||
|
if (invalidated_terminals
|
||||||
|
&& pmap_has(ptr_t)(invalidated_terminals, term)) {
|
||||||
// flush any pending changes to the buffer
|
// flush any pending changes to the buffer
|
||||||
block_autocmds();
|
block_autocmds();
|
||||||
refresh_terminal(term);
|
refresh_terminal(term);
|
||||||
@ -1324,6 +1335,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
|
|||||||
// focused) of a invalidated terminal
|
// focused) of a invalidated terminal
|
||||||
static void refresh_screen(Terminal *term, buf_T *buf)
|
static void refresh_screen(Terminal *term, buf_T *buf)
|
||||||
{
|
{
|
||||||
|
assert(buf == curbuf); // TODO(bfredl): remove this condition
|
||||||
int changed = 0;
|
int changed = 0;
|
||||||
int added = 0;
|
int added = 0;
|
||||||
int height;
|
int height;
|
||||||
|
@ -2087,4 +2087,67 @@ describe('API', function()
|
|||||||
eq("", meths.exec("messages", true))
|
eq("", meths.exec("messages", true))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
describe('nvim_open_term', function()
|
||||||
|
local screen
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
screen = Screen.new(100, 35)
|
||||||
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[0] = {bold=true, foreground=Screen.colors.Blue},
|
||||||
|
[1] = {background = Screen.colors.Plum1};
|
||||||
|
[2] = {background = tonumber('0xffff40'), bg_indexed = true};
|
||||||
|
[3] = {background = Screen.colors.Plum1, fg_indexed = true, foreground = tonumber('0x00e000')};
|
||||||
|
[4] = {bold = true, reverse = true, background = Screen.colors.Plum1};
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can batch process sequences', function()
|
||||||
|
local b = meths.create_buf(true,true)
|
||||||
|
meths.open_win(b, false, {width=79, height=31, row=1, col=1, relative='editor'})
|
||||||
|
local t = meths.open_term(b, {})
|
||||||
|
|
||||||
|
meths.chan_send(t, io.open("test/functional/fixtures/smile2.cat", "r"):read("*a"))
|
||||||
|
screen:expect{grid=[[
|
||||||
|
^ |
|
||||||
|
{0:~}{1::smile }{0: }|
|
||||||
|
{0:~}{1: }{2:oooo$$$$$$$$$$$$oooo}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:o$}{1: }{2:$$}{1: }{2:o$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:o}{1: }{2:$}{1: }{2:oo}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o}{1: }{2:$$}{1: }{2:$$}{1: }{2:$$o$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:oo}{1: }{2:$}{1: }{2:$}{1: "}{2:$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$o}{1: }{2:$$$o$$o$}{1: }{0: }|
|
||||||
|
{0:~}{1: "}{2:$$$$$$o$}{1: }{2:o$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$o}{1: }{2:$$$$$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$}{1: }{2:$$$$$$$$$$$$$$}{1: """}{2:$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: "}{2:$$$}{1:""""}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$}{1: }{2:o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: "}{2:$$$o}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:o$$}{1:" }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$o}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" "}{2:$$$$$$ooooo$$$$o}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:o$$$oooo$$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:o$$$$$$$$$$$$$$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$$$$$$}{1:"}{2:$$$$}{1: }{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1: }{2:$$$$}{1:"""""""" }{0: }|
|
||||||
|
{0:~}{1: """" }{2:$$$$}{1: "}{2:$$$$$$$$$$$$$$$$$$$$$$$$$$$$}{1:" }{2:o$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: "}{2:$$$o}{1: """}{2:$$$$$$$$$$$$$$$$$$}{1:"}{2:$$}{1:" }{2:$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$o}{1: "}{2:$$}{1:""}{2:$$$$$$}{1:"""" }{2:o$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$$o}{1: }{2:o$$$}{1:" }{0: }|
|
||||||
|
{0:~}{1: "}{2:$$$$o}{1: }{2:o$$$$$$o}{1:"}{2:$$$$o}{1: }{2:o$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: "}{2:$$$$$oo}{1: ""}{2:$$$$o$$$$$o}{1: }{2:o$$$$}{1:"" }{0: }|
|
||||||
|
{0:~}{1: ""}{2:$$$$$oooo}{1: "}{2:$$$o$$$$$$$$$}{1:""" }{0: }|
|
||||||
|
{0:~}{1: ""}{2:$$$$$$$oo}{1: }{2:$$$$$$$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: """"}{2:$$$$$$$$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$$$$$$$$$$}{1: }{0: }|
|
||||||
|
{0:~}{1: }{2:$$$$$$$$$$}{1:" }{0: }|
|
||||||
|
{0:~}{1: "}{2:$$$}{1:"""" }{0: }|
|
||||||
|
{0:~}{1: }{0: }|
|
||||||
|
{0:~}{3:Press ENTER or type command to continue}{1: }{0: }|
|
||||||
|
{0:~}{4:term://~/config2/docs/pres//32693:vim --clean +smile 29,39 All}{0: }|
|
||||||
|
{0:~}{1::call nvim__screenshot("smile2.cat") }{0: }|
|
||||||
|
{0:~ }|
|
||||||
|
{0:~ }|
|
||||||
|
|
|
||||||
|
]]}
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
32
test/functional/fixtures/smile2.cat
Normal file
32
test/functional/fixtures/smile2.cat
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
31,79
|
||||||
|
[?25l[H[2J:smile
|
||||||
|
[103moooo$$$$$$$$$$$$oooo(B[m
|
||||||
|
[103moo$$$$$$$$$$$$$$$$$$$$$$$$o(B[m
|
||||||
|
[103moo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o(B[m [103mo$(B[m [103m$$(B[m [103mo$(B[m
|
||||||
|
[103mo(B[m [103m$(B[m [103moo(B[m [103mo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o(B[m [103m$$(B[m [103m$$(B[m [103m$$o$(B[m
|
||||||
|
[103moo(B[m [103m$(B[m [103m$(B[m "[103m$(B[m [103mo$$$$$$$$$(B[m [103m$$$$$$$$$$$$$(B[m [103m$$$$$$$$$o(B[m [103m$$$o$$o$(B[m
|
||||||
|
"[103m$$$$$$o$(B[m [103mo$$$$$$$$$(B[m [103m$$$$$$$$$$$(B[m [103m$$$$$$$$$$o(B[m [103m$$$$$$$$(B[m
|
||||||
|
[103m$$$$$$$(B[m [103m$$$$$$$$$$$(B[m [103m$$$$$$$$$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$(B[m
|
||||||
|
[103m$$$$$$$$$$$$$$$$$$$$$$$(B[m [103m$$$$$$$$$$$$$(B[m [103m$$$$$$$$$$$$$$(B[m """[103m$$$(B[m
|
||||||
|
"[103m$$$(B[m""""[103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m "[103m$$$(B[m
|
||||||
|
[103m$$$(B[m [103mo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m "[103m$$$o(B[m
|
||||||
|
[103mo$$(B[m" [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m [103m$$$o(B[m
|
||||||
|
[103m$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m" "[103m$$$$$$ooooo$$$$o(B[m
|
||||||
|
[103mo$$$oooo$$$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m [103mo$$$$$$$$$$$$$$$$$(B[m
|
||||||
|
[103m$$$$$$$$(B[m"[103m$$$$(B[m [103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m [103m$$$$(B[m""""""""
|
||||||
|
"""" [103m$$$$(B[m "[103m$$$$$$$$$$$$$$$$$$$$$$$$$$$$(B[m" [103mo$$$(B[m
|
||||||
|
"[103m$$$o(B[m """[103m$$$$$$$$$$$$$$$$$$(B[m"[103m$$(B[m" [103m$$$(B[m
|
||||||
|
[103m$$$o(B[m "[103m$$(B[m""[103m$$$$$$(B[m"""" [103mo$$$(B[m
|
||||||
|
[103m$$$$o(B[m [103mo$$$(B[m"
|
||||||
|
"[103m$$$$o(B[m [103mo$$$$$$o(B[m"[103m$$$$o(B[m [103mo$$$$(B[m
|
||||||
|
"[103m$$$$$oo(B[m ""[103m$$$$o$$$$$o(B[m [103mo$$$$(B[m""
|
||||||
|
""[103m$$$$$oooo(B[m "[103m$$$o$$$$$$$$$(B[m"""
|
||||||
|
""[103m$$$$$$$oo(B[m [103m$$$$$$$$$$(B[m
|
||||||
|
""""[103m$$$$$$$$$$$(B[m
|
||||||
|
[103m$$$$$$$$$$$$(B[m
|
||||||
|
[103m$$$$$$$$$$(B[m"
|
||||||
|
"[103m$$$(B[m""""
|
||||||
|
|
||||||
|
[32mPress ENTER or type command to continue(B[m
|
||||||
|
(B[0;1;7mterm://~/config2/docs/pres//32693:vim --clean +smile 29,39 All
|
||||||
|
(B[m:call nvim__screenshot("smile2.cat") [?25h
|
Loading…
Reference in New Issue
Block a user