mirror of
https://github.com/neovim/neovim
synced 2025-07-15 16:51:49 +00:00
feat(ui): emit "msg_clear" event after clearing the screen (#34035)
Problem: ext_messages cannot tell when the screen was cleared, which is needed to clear visible messages. An empty message is also never emitted, but clears messages from the message grid. Solution: Repurpose the "msg_clear" event to be emitted when the screen was cleared. Emit an empty message with the `empty` kind to hint to a UI to clear the cmdline area.
This commit is contained in:
@ -67,10 +67,12 @@ EDITOR
|
||||
|
||||
EVENTS
|
||||
|
||||
• |ui-messages| no longer emits the `msg_show.return_prompt`, `msg_clear` and
|
||||
`msg_history_clear` events. These events arbitrarily assume a message UI
|
||||
mimicking the legacy message grid. Benefit: reduced UI event traffic and
|
||||
more flexibility for UIs.
|
||||
• |ui-messages| no longer emits the `msg_show.return_prompt`, and
|
||||
`msg_history_clear` events. The `msg_clear` event was repurposed and is now
|
||||
emitted after the screen is cleared. These events arbitrarily assumed a
|
||||
message UI that mimicks the legacy message grid. Benefit: reduced UI event
|
||||
traffic and more flexibility for UIs.
|
||||
• A new `empty` message kind is emitted for an empty (e.g. `:echo ""`) message.
|
||||
|
||||
HIGHLIGHTS
|
||||
|
||||
|
@ -824,6 +824,9 @@ must handle.
|
||||
kind
|
||||
Name indicating the message kind:
|
||||
"" (empty) Unknown (consider a |feature-request|)
|
||||
"empty" Empty message (`:echo ""`), with empty `content`.
|
||||
Should clear messages sharing the 'cmdheight'
|
||||
area if it is the only message in a batch.
|
||||
"bufwrite" |:write| message
|
||||
"confirm" Message preceding a prompt (|:confirm|,
|
||||
|confirm()|, |inputlist()|, |z=|, …)
|
||||
@ -872,8 +875,9 @@ must handle.
|
||||
rather than started on a new line. Is set for |:echon|.
|
||||
|
||||
["msg_clear"] ~
|
||||
Clear all messages currently displayed by "msg_show". (Messages sent
|
||||
by other "msg_" events below will not be affected).
|
||||
Clear all messages currently displayed by "msg_show", emitted after
|
||||
clearing the screen (messages sent by other "msg_" events below should
|
||||
not be affected).
|
||||
|
||||
["msg_showmode", content] ~
|
||||
Shows 'showmode' and |recording| messages. `content` has the same
|
||||
|
@ -56,9 +56,14 @@ end
|
||||
---@param level integer
|
||||
---@param hl_id integer
|
||||
function M.cmdline_show(content, pos, firstc, prompt, indent, level, hl_id)
|
||||
M.level, M.indent, M.prompt = level, indent, #prompt > 0
|
||||
M.level, M.indent, M.prompt = level, indent, M.prompt or #prompt > 0
|
||||
-- Only enable TS highlighter for Ex commands (not search or filter commands).
|
||||
M.highlighter.active[ext.bufs.cmd] = firstc == ':' and M.highlighter or nil
|
||||
if ext.msg.cmd.msg_row ~= -1 then
|
||||
ext.msg.msg_clear()
|
||||
end
|
||||
ext.msg.virt.last = { {}, {}, {}, {} }
|
||||
|
||||
set_text(content, ('%s%s%s'):format(firstc, prompt, (' '):rep(indent)))
|
||||
if promptlen > 0 and hl_id > 0 then
|
||||
api.nvim_buf_set_extmark(ext.bufs.cmd, ext.ns, 0, 0, { hl_group = hl_id, end_col = promptlen })
|
||||
@ -67,14 +72,6 @@ function M.cmdline_show(content, pos, firstc, prompt, indent, level, hl_id)
|
||||
local height = math.max(ext.cmdheight, api.nvim_win_text_height(ext.wins.cmd, {}).all)
|
||||
win_config(ext.wins.cmd, false, height)
|
||||
M.cmdline_pos(pos)
|
||||
|
||||
-- Clear message cmdline state; should not be shown during, and reset after cmdline.
|
||||
if ext.cfg.msg.target == 'cmd' and ext.msg.cmd.msg_row ~= -1 then
|
||||
ext.msg.prev_msg, ext.msg.dupe, ext.msg.cmd.msg_row = '', 0, -1
|
||||
api.nvim_buf_clear_namespace(ext.bufs.cmd, ext.ns, 0, -1)
|
||||
ext.msg.virt.msg = { {}, {} }
|
||||
end
|
||||
ext.msg.virt.last = { {}, {}, {}, {} }
|
||||
end
|
||||
|
||||
--- Insert special character at cursor position.
|
||||
|
@ -334,18 +334,22 @@ function M.show_msg(tar, content, replace_last, append, pager)
|
||||
end
|
||||
end
|
||||
|
||||
local replace_bufwrite = false
|
||||
--- Route the message to the appropriate sink.
|
||||
---
|
||||
---@param kind string
|
||||
---@alias MsgChunk [integer, string, integer]
|
||||
---@alias MsgContent MsgChunk[]
|
||||
---@param content MsgContent
|
||||
--@param replace_last boolean
|
||||
---@param replace_last boolean
|
||||
--@param history boolean
|
||||
---@param append boolean
|
||||
function M.msg_show(kind, content, _, _, append)
|
||||
if kind == 'search_count' then
|
||||
function M.msg_show(kind, content, replace_last, _, append)
|
||||
if kind == 'empty' then
|
||||
-- A sole empty message clears the cmdline.
|
||||
if ext.cfg.msg.target == 'cmd' and M.cmd.count == 0 then
|
||||
M.msg_clear()
|
||||
end
|
||||
elseif kind == 'search_count' then
|
||||
-- Extract only the search_count, not the entered search command.
|
||||
-- Match any of search.c:cmdline_search_stat():' [(x | >x | ?)/(y | >y | ??)]'
|
||||
content = { content[#content] }
|
||||
@ -353,16 +357,18 @@ function M.msg_show(kind, content, _, _, append)
|
||||
M.virt.last[M.virt.idx.search] = content
|
||||
M.virt.last[M.virt.idx.cmd] = { { 0, (' '):rep(11) } }
|
||||
set_virttext('last')
|
||||
elseif kind == 'return_prompt' then
|
||||
-- Bypass hit enter prompt.
|
||||
vim.api.nvim_feedkeys(vim.keycode('<CR>'), 'n', false)
|
||||
elseif kind == 'verbose' then
|
||||
-- Verbose messages are sent too often to be meaningful in the cmdline:
|
||||
-- Verbose messages are sent too often to be meaningful in the cmdline.
|
||||
-- always route to message window regardless of cfg.msg.target.
|
||||
M.show_msg('msg', content, false, append)
|
||||
elseif ext.cmd.prompt then
|
||||
elseif ext.cmd.prompt or kind == 'wildlist' then
|
||||
-- Route to dialog that stays open so long as the cmdline prompt is active.
|
||||
M.show_msg('dialog', content, api.nvim_win_get_config(ext.wins.dialog).hide, append)
|
||||
replace_last = api.nvim_win_get_config(ext.wins.dialog).hide or kind == 'wildlist'
|
||||
if kind == 'wildlist' then
|
||||
api.nvim_buf_set_lines(ext.bufs.dialog, 0, -1, false, {})
|
||||
ext.cmd.prompt = true -- Ensure dialog is closed when cmdline is hidden.
|
||||
end
|
||||
M.show_msg('dialog', content, replace_last, append)
|
||||
M.set_pos('dialog')
|
||||
else
|
||||
-- Set the entered search command in the cmdline (if available).
|
||||
@ -381,9 +387,7 @@ function M.msg_show(kind, content, _, _, append)
|
||||
-- Typed "inspection" messages should be routed to the pager.
|
||||
local inspect = { 'echo', 'echomsg', 'lua_print' }
|
||||
local pager = kind == 'list_cmd' or (ext.cmd.level >= 0 and vim.tbl_contains(inspect, kind))
|
||||
M.show_msg(tar, content, replace_bufwrite, append, pager)
|
||||
-- Replace message for every second bufwrite message.
|
||||
replace_bufwrite = not replace_bufwrite and kind == 'bufwrite'
|
||||
M.show_msg(tar, content, replace_last, append, pager)
|
||||
-- Don't remember search_cmd message as actual message.
|
||||
if kind == 'search_cmd' then
|
||||
M.cmd.lines, M.cmd.count, M.prev_msg = 0, 0, ''
|
||||
@ -391,7 +395,14 @@ function M.msg_show(kind, content, _, _, append)
|
||||
end
|
||||
end
|
||||
|
||||
function M.msg_clear() end
|
||||
---Clear currently visible messages.
|
||||
function M.msg_clear()
|
||||
api.nvim_buf_set_lines(ext.bufs.cmd, 0, -1, false, {})
|
||||
api.nvim_buf_set_lines(ext.bufs.msg, 0, -1, false, {})
|
||||
api.nvim_win_set_config(ext.wins.msg, { hide = true })
|
||||
M.dupe, M[ext.cfg.msg.target].count, M.cmd.msg_row, M.cmd.lines, M.msg.width = 0, 0, -1, 1, 1
|
||||
M.prev_msg, M.virt.msg = '', { {}, {} }
|
||||
end
|
||||
|
||||
--- Place the mode text in the cmdline.
|
||||
---
|
||||
@ -437,8 +448,6 @@ function M.msg_history_show(entries)
|
||||
M.set_pos('pager')
|
||||
end
|
||||
|
||||
function M.msg_history_clear() end
|
||||
|
||||
--- Adjust dimensions of the message windows after certain events.
|
||||
---
|
||||
---@param type? 'cmd'|'dialog'|'msg'|'pager' Type of to be positioned window (nil for all).
|
||||
|
@ -264,6 +264,9 @@ void screenclear(void)
|
||||
msg_grid_invalid = false;
|
||||
clear_cmdline = true;
|
||||
}
|
||||
if (ui_has(kUIMessages)) {
|
||||
ui_call_msg_clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Unlike cmdline "one_key" prompts, the message part of the prompt is not stored
|
||||
|
@ -7863,10 +7863,8 @@ void ex_echo(exarg_T *eap)
|
||||
msg_puts_hl(" ", echo_hl_id, false);
|
||||
}
|
||||
char *tofree = encode_tv2echo(&rettv, NULL);
|
||||
if (*tofree != NUL) {
|
||||
msg_ext_append = eap->cmdidx == CMD_echon;
|
||||
msg_multiline(cstr_as_string(tofree), echo_hl_id, true, false, &need_clear);
|
||||
}
|
||||
msg_ext_append = eap->cmdidx == CMD_echon;
|
||||
msg_multiline(cstr_as_string(tofree), echo_hl_id, true, false, &need_clear);
|
||||
xfree(tofree);
|
||||
}
|
||||
tv_clear(&rettv);
|
||||
|
@ -1405,10 +1405,16 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first)
|
||||
static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len,
|
||||
const VarType type, const char *string, int *first)
|
||||
{
|
||||
msg_ext_set_kind("list_cmd");
|
||||
if (*first) {
|
||||
msg_ext_set_kind("list_cmd");
|
||||
msg_start();
|
||||
} else {
|
||||
msg_putchar('\n');
|
||||
}
|
||||
// don't use msg() to avoid overwriting "v:statusmsg"
|
||||
msg_start();
|
||||
msg_puts(prefix);
|
||||
if (*prefix != NUL) {
|
||||
msg_puts(prefix);
|
||||
}
|
||||
if (name != NULL) { // "a:" vars don't have a name stored
|
||||
msg_puts_len(name, name_len, 0, false);
|
||||
}
|
||||
|
@ -283,9 +283,7 @@ void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_
|
||||
}
|
||||
|
||||
// Print the rest of the message
|
||||
if (*chunk != NUL) {
|
||||
msg_outtrans_len(chunk, (int)(str.size - (size_t)(chunk - str.data)), hl_id, hist);
|
||||
}
|
||||
msg_outtrans_len(chunk, (int)(str.size - (size_t)(chunk - str.data)), hl_id, hist);
|
||||
}
|
||||
|
||||
// Avoid starting a new message for each chunk and adding message to history in msg_keep().
|
||||
@ -1632,7 +1630,7 @@ static void msg_home_replace_hl(const char *fname, int hl_id)
|
||||
/// @return the number of characters it takes on the screen.
|
||||
int msg_outtrans(const char *str, int hl_id, bool hist)
|
||||
{
|
||||
return msg_outtrans_len(str, (int)strlen(str), hl_id, hist);
|
||||
return *str == NUL ? 0 : msg_outtrans_len(str, (int)strlen(str), hl_id, hist);
|
||||
}
|
||||
|
||||
/// Output one character at "p".
|
||||
@ -1714,8 +1712,8 @@ int msg_outtrans_len(const char *msgstr, int len, int hl_id, bool hist)
|
||||
}
|
||||
}
|
||||
|
||||
if (str > plain_start && !got_int) {
|
||||
// Print the printable chars at the end.
|
||||
if ((str > plain_start || plain_start == msgstr) && !got_int) {
|
||||
// Print the printable chars at the end (or emit empty string).
|
||||
msg_puts_len(plain_start, str - plain_start, hl_id, hist);
|
||||
}
|
||||
|
||||
@ -2155,6 +2153,9 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
|
||||
|
||||
// Don't print anything when using ":silent cmd" or empty message.
|
||||
if (msg_silent != 0 || *str == NUL) {
|
||||
if (*str == NUL && ui_has(kUIMessages)) {
|
||||
ui_call_msg_show(cstr_as_string("empty"), (Array)ARRAY_DICT_INIT, false, false, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ describe('vim.ui_attach', function()
|
||||
pos = 0,
|
||||
} },
|
||||
})
|
||||
feed('version<CR><CR>v<Esc>')
|
||||
feed('version<CR>')
|
||||
screen:expect({
|
||||
grid = [[
|
||||
^2 |
|
||||
@ -208,7 +208,7 @@ describe('vim.ui_attach', function()
|
||||
screen.messages = {} -- Ignore the build dependent :version content
|
||||
end,
|
||||
})
|
||||
feed([[:call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
|
||||
feed([[v<Esc>:call confirm("Save changes?", "&Yes\n&No\n&Cancel")<CR>]])
|
||||
screen:expect({
|
||||
grid = [[
|
||||
^4 |
|
||||
|
@ -7,95 +7,145 @@ local clear, command, exec_lua, feed = n.clear, n.command, n.exec_lua, n.feed
|
||||
|
||||
describe('messages2', function()
|
||||
local screen
|
||||
describe('target=cmd', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = Screen.new()
|
||||
screen:add_extra_attr_ids({
|
||||
[100] = { foreground = Screen.colors.Magenta1, bold = true },
|
||||
})
|
||||
exec_lua(function()
|
||||
require('vim._extui').enable({})
|
||||
end)
|
||||
end)
|
||||
|
||||
it('multiline messages and pager', function()
|
||||
command('echo "foo\nbar"')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
foo[+1] |
|
||||
]])
|
||||
command('set ruler showcmd noshowmode')
|
||||
feed('g<lt>')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*9
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:fo^o }|
|
||||
{4:bar }|
|
||||
foo[+1] 1,3 All|
|
||||
]])
|
||||
-- New message clears spill indicator.
|
||||
feed('Q')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*9
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:fo^o }|
|
||||
{4:bar }|
|
||||
{9:E354: Invalid register name: '^@'} 1,3 All|
|
||||
]])
|
||||
-- Multiple messages in same event loop iteration are appended.
|
||||
feed([[q:echo "foo\nbar" | echo "baz"<CR>]])
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*8
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:^foo }|
|
||||
{4:bar }|
|
||||
{4:baz }|
|
||||
1,1 All|
|
||||
]])
|
||||
-- No error for ruler virt_text msg_row exceeding buffer length.
|
||||
command([[map Q <cmd>echo "foo\nbar" <bar> ls<CR>]])
|
||||
feed('qQ')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*7
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:^foo }|
|
||||
{4:bar }|
|
||||
{4: }|
|
||||
{4: 1 %a "[No Name]" line 1 }|
|
||||
1,1 All|
|
||||
]])
|
||||
-- edit_unputchar() does not clear already updated screen #34515.
|
||||
feed('qix<Esc>dwi<C-r>')
|
||||
screen:expect([[
|
||||
{18:^"} |
|
||||
{1:~ }|*12
|
||||
^R 1,1 All|
|
||||
]])
|
||||
feed('-')
|
||||
screen:expect([[
|
||||
x^ |
|
||||
{1:~ }|*12
|
||||
1,2 All|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('new buffer, window and options after closing a buffer', function()
|
||||
command('set nomodifiable | echom "foo" | messages')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*10
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:fo^o }|
|
||||
foo |
|
||||
]])
|
||||
command('bdelete | messages')
|
||||
screen:expect_unchanged()
|
||||
before_each(function()
|
||||
clear()
|
||||
screen = Screen.new()
|
||||
screen:add_extra_attr_ids({
|
||||
[100] = { foreground = Screen.colors.Magenta1, bold = true },
|
||||
})
|
||||
exec_lua(function()
|
||||
require('vim._extui').enable({})
|
||||
end)
|
||||
end)
|
||||
|
||||
it('multiline messages and pager', function()
|
||||
command('echo "foo\nbar"')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
foo[+1] |
|
||||
]])
|
||||
command('set ruler showcmd noshowmode')
|
||||
feed('g<lt>')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*9
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:fo^o }|
|
||||
{4:bar }|
|
||||
foo[+1] 1,3 All|
|
||||
]])
|
||||
-- New message clears spill indicator.
|
||||
feed('Q')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*9
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:fo^o }|
|
||||
{4:bar }|
|
||||
{9:E354: Invalid register name: '^@'} 1,3 All|
|
||||
]])
|
||||
-- Multiple messages in same event loop iteration are appended.
|
||||
feed([[q:echo "foo\nbar" | echo "baz"<CR>]])
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*8
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:^foo }|
|
||||
{4:bar }|
|
||||
{4:baz }|
|
||||
1,1 All|
|
||||
]])
|
||||
-- No error for ruler virt_text msg_row exceeding buffer length.
|
||||
command([[map Q <cmd>echo "foo\nbar" <bar> ls<CR>]])
|
||||
feed('qQ')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*7
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:^foo }|
|
||||
{4:bar }|
|
||||
{4: }|
|
||||
{4: 1 %a "[No Name]" line 1 }|
|
||||
1,1 All|
|
||||
]])
|
||||
-- edit_unputchar() does not clear already updated screen #34515.
|
||||
feed('qix<Esc>dwi<C-r>')
|
||||
screen:expect([[
|
||||
{18:^"} |
|
||||
{1:~ }|*12
|
||||
^R 1,1 All|
|
||||
]])
|
||||
feed('-')
|
||||
screen:expect([[
|
||||
x^ |
|
||||
{1:~ }|*12
|
||||
1,2 All|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('new buffer, window and options after closing a buffer', function()
|
||||
command('set nomodifiable | echom "foo" | messages')
|
||||
screen:expect([[
|
||||
|
|
||||
{1:~ }|*10
|
||||
─{100:Pager}───────────────────────────────────────────────|
|
||||
{4:fo^o }|
|
||||
foo |
|
||||
]])
|
||||
command('bdelete | messages')
|
||||
screen:expect_unchanged()
|
||||
end)
|
||||
|
||||
it('screenclear and empty message clears messages', function()
|
||||
command('echo "foo"')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
foo |
|
||||
]])
|
||||
command('mode')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
|
|
||||
]])
|
||||
command('echo "foo"')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
foo |
|
||||
]])
|
||||
command('echo ""')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*12
|
||||
|
|
||||
]])
|
||||
command('set cmdheight=0')
|
||||
command('echo "foo"')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*10
|
||||
{1:~ }┌───┐|
|
||||
{1:~ }│{4:foo}│|
|
||||
{1:~ }└───┘|
|
||||
]])
|
||||
command('mode')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*13
|
||||
]])
|
||||
-- But not with target='msg'
|
||||
command('echo "foo"')
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*10
|
||||
{1:~ }┌───┐|
|
||||
{1:~ }│{4:foo}│|
|
||||
{1:~ }└───┘|
|
||||
]])
|
||||
command('echo ""')
|
||||
screen:expect_unchanged()
|
||||
end)
|
||||
end)
|
||||
|
@ -547,6 +547,22 @@ describe('ui/ext_messages', function()
|
||||
screen.messages = {}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Empty messages
|
||||
feed(':echo "foo" | echo "" | lua print()<CR>')
|
||||
screen:expect({
|
||||
grid = [[
|
||||
line 1 |
|
||||
^line |
|
||||
{1:~ }|*3
|
||||
]],
|
||||
cmdline = { { abort = false } },
|
||||
messages = {
|
||||
{ content = { { 'foo' } }, kind = 'echo' },
|
||||
{ content = {}, kind = 'empty' },
|
||||
{ content = {}, kind = 'empty' },
|
||||
},
|
||||
})
|
||||
end)
|
||||
|
||||
it(':echoerr', function()
|
||||
@ -735,17 +751,19 @@ describe('ui/ext_messages', function()
|
||||
it("doesn't crash with column adjustment #10069", function()
|
||||
feed(':let [x,y] = [1,2]<cr>')
|
||||
feed(':let x y<cr>')
|
||||
screen:expect {
|
||||
screen:expect({
|
||||
grid = [[
|
||||
^ |
|
||||
{1:~ }|*4
|
||||
]],
|
||||
cmdline = { { abort = false } },
|
||||
messages = {
|
||||
{ content = { { 'x #1' } }, kind = 'list_cmd' },
|
||||
{ content = { { 'y #2' } }, kind = 'list_cmd' },
|
||||
{
|
||||
content = { { 'x #1\ny #2' } },
|
||||
kind = 'list_cmd',
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
end)
|
||||
|
||||
it('&showmode', function()
|
||||
|
Reference in New Issue
Block a user