mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
Compare commits
21 Commits
cc323f19bf
...
bf9ed8dbb3
Author | SHA1 | Date | |
---|---|---|---|
|
bf9ed8dbb3 | ||
|
4c5bce9cb4 | ||
|
5d7853f229 | ||
|
f279d1ae33 | ||
|
9ddfcb64bf | ||
|
8e81212e15 | ||
|
648d6426c8 | ||
|
f0334c2c71 | ||
|
c8e3618e0e | ||
|
ed832b9ddf | ||
|
8a2aec9974 | ||
|
3a88113246 | ||
|
08153ddd1c | ||
|
003b8a251d | ||
|
0cfbc6eaff | ||
|
95b65a7554 | ||
|
e37404f7fe | ||
|
b40ec083ae | ||
|
d338ec9cb2 | ||
|
5ddf2ab768 | ||
|
3d1110674e |
@ -2230,6 +2230,12 @@ vim.tbl_count({t}) *vim.tbl_count()*
|
||||
vim.tbl_deep_extend({behavior}, {...}) *vim.tbl_deep_extend()*
|
||||
Merges recursively two or more tables.
|
||||
|
||||
Only values that are empty tables or tables that are not |lua-list|s
|
||||
(indexed by consecutive integers starting from 1) are merged recursively.
|
||||
This is useful for merging nested tables like default and user
|
||||
configurations where lists should be treated as literals (i.e., are
|
||||
overwritten instead of merged).
|
||||
|
||||
Parameters: ~
|
||||
• {behavior} (`'error'|'keep'|'force'`) Decides what to do if a key is
|
||||
found in more than one map:
|
||||
|
@ -214,9 +214,6 @@ These existing features changed their behavior.
|
||||
more emoji characters than before, including those encoded with multiple
|
||||
emoji codepoints combined with ZWJ (zero width joiner) codepoints.
|
||||
|
||||
• |vim.tbl_deep_extend()| no longer ignores any values for which |vim.isarray()|
|
||||
returns `true`.
|
||||
|
||||
==============================================================================
|
||||
REMOVED FEATURES *news-removed*
|
||||
|
||||
|
@ -1129,9 +1129,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
list:{n} Adds an additional indent for lines that match a
|
||||
numbered or bulleted list (using the
|
||||
'formatlistpat' setting).
|
||||
list:-1 Uses the length of a match with 'formatlistpat'
|
||||
for indentation.
|
||||
(default: 0)
|
||||
list:-1 Uses the width of a match with 'formatlistpat' for
|
||||
indentation.
|
||||
column:{n} Indent at column {n}. Will overrule the other
|
||||
sub-options. Note: an additional indent may be
|
||||
added for the 'showbreak' setting.
|
||||
|
@ -53,11 +53,17 @@ active yes yes 'a'
|
||||
hidden no yes 'h'
|
||||
inactive no no ' '
|
||||
|
||||
Note: All CTRL-W commands can also be executed with |:wincmd|, for those
|
||||
places where a Normal mode command can't be used or is inconvenient.
|
||||
*buffer-reuse*
|
||||
Each buffer has a unique number and the number will not change within a Vim
|
||||
session. The |bufnr()| and |bufname()| functions can be used to convert
|
||||
between a buffer name and the buffer number. There is one exception: if a new
|
||||
empty buffer is created and it is not modified, the buffer will be re-used
|
||||
when loading another file into that buffer. This also means the buffer number
|
||||
will not change.
|
||||
|
||||
The main Vim window can hold several split windows. There are also tab pages
|
||||
|tab-page|, each of which can hold multiple windows.
|
||||
|
||||
*window-ID* *winid* *windowid*
|
||||
Each window has a unique identifier called the window ID. This identifier
|
||||
will not change within a Vim session. The |win_getid()| and |win_id2tabwin()|
|
||||
@ -69,9 +75,6 @@ across tabs. For most functions that take a window ID or a window number, the
|
||||
window number only applies to the current tab, while the window ID can refer
|
||||
to a window in any tab.
|
||||
|
||||
Each buffer has a unique number and the number will not change within a Vim
|
||||
session. The |bufnr()| and |bufname()| functions can be used to convert
|
||||
between a buffer name and the buffer number.
|
||||
|
||||
==============================================================================
|
||||
2. Starting Vim *windows-starting*
|
||||
@ -468,6 +471,10 @@ These commands can also be executed with ":wincmd":
|
||||
:exe nr .. "wincmd w"
|
||||
< This goes to window "nr".
|
||||
|
||||
Note: All CTRL-W commands can also be executed with |:wincmd|, for those
|
||||
places where a Normal mode command can't be used or is inconvenient (e.g.
|
||||
in a browser-based terminal).
|
||||
|
||||
==============================================================================
|
||||
5. Moving windows around *window-moving*
|
||||
|
||||
|
3
runtime/ftplugin/dot.lua
Normal file
3
runtime/ftplugin/dot.lua
Normal file
@ -0,0 +1,3 @@
|
||||
vim.bo.commentstring = '// %s'
|
||||
|
||||
vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n setl commentstring<'
|
3
runtime/ftplugin/faust.lua
Normal file
3
runtime/ftplugin/faust.lua
Normal file
@ -0,0 +1,3 @@
|
||||
vim.bo.commentstring = '// %s'
|
||||
|
||||
vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n setl commentstring<'
|
3
runtime/ftplugin/stata.lua
Normal file
3
runtime/ftplugin/stata.lua
Normal file
@ -0,0 +1,3 @@
|
||||
vim.bo.commentstring = '// %s'
|
||||
|
||||
vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n setl commentstring<'
|
3
runtime/ftplugin/supercollider.lua
Normal file
3
runtime/ftplugin/supercollider.lua
Normal file
@ -0,0 +1,3 @@
|
||||
vim.bo.commentstring = '// %s'
|
||||
|
||||
vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n setl commentstring<'
|
3
runtime/ftplugin/swift.lua
Normal file
3
runtime/ftplugin/swift.lua
Normal file
@ -0,0 +1,3 @@
|
||||
vim.bo.commentstring = '// %s'
|
||||
|
||||
vim.b.undo_ftplugin = vim.b.undo_ftplugin .. ' | setl commentstring<'
|
@ -1293,9 +1293,25 @@ local function opt_to_global_state(opt, title)
|
||||
local fonts = {}
|
||||
if opt.font then
|
||||
fonts = type(opt.font) == 'string' and { opt.font } or opt.font --[[@as (string[])]]
|
||||
for i, v in pairs(fonts) do
|
||||
fonts[i] = ('"%s"'):format(v)
|
||||
end
|
||||
elseif vim.o.guifont:match('^[^:]+') then
|
||||
table.insert(fonts, vim.o.guifont:match('^[^:]+'))
|
||||
-- Example:
|
||||
-- Input: "Font,Escape\,comma, Ignore space after comma"
|
||||
-- Output: { "Font","Escape,comma","Ignore space after comma" }
|
||||
local prev = ''
|
||||
for name in vim.gsplit(vim.o.guifont:match('^[^:]+'), ',', { trimempty = true }) do
|
||||
if vim.endswith(name, '\\') then
|
||||
prev = prev .. vim.trim(name:sub(1, -2) .. ',')
|
||||
elseif vim.trim(name) ~= '' then
|
||||
table.insert(fonts, ('"%s%s"'):format(prev, vim.trim(name)))
|
||||
prev = ''
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Generic family names (monospace here) must not be quoted
|
||||
-- because the browser recognizes them as font families.
|
||||
table.insert(fonts, 'monospace')
|
||||
--- @type vim.tohtml.state.global
|
||||
local state = {
|
||||
|
4
runtime/lua/vim/_meta/options.lua
generated
4
runtime/lua/vim/_meta/options.lua
generated
@ -558,9 +558,9 @@ vim.wo.bri = vim.wo.breakindent
|
||||
--- list:{n} Adds an additional indent for lines that match a
|
||||
--- numbered or bulleted list (using the
|
||||
--- 'formatlistpat' setting).
|
||||
--- list:-1 Uses the length of a match with 'formatlistpat'
|
||||
--- for indentation.
|
||||
--- (default: 0)
|
||||
--- list:-1 Uses the width of a match with 'formatlistpat' for
|
||||
--- indentation.
|
||||
--- column:{n} Indent at column {n}. Will overrule the other
|
||||
--- sub-options. Note: an additional indent may be
|
||||
--- added for the 'showbreak' setting.
|
||||
|
@ -77,12 +77,7 @@ function M.on_inlayhint(err, result, ctx, _)
|
||||
local col = position.character
|
||||
if col > 0 then
|
||||
local line = lines[position.line + 1] or ''
|
||||
local ok, convert_result
|
||||
ok, convert_result = pcall(util._str_byteindex_enc, line, col, client.offset_encoding)
|
||||
if ok then
|
||||
return convert_result
|
||||
end
|
||||
return math.min(#line, col)
|
||||
return util._str_byteindex_enc(line, col, client.offset_encoding)
|
||||
end
|
||||
return col
|
||||
end
|
||||
|
@ -140,12 +140,7 @@ local function tokens_to_ranges(data, bufnr, client, request)
|
||||
local function _get_byte_pos(col)
|
||||
if col > 0 then
|
||||
local buf_line = lines[line + 1] or ''
|
||||
local ok, result
|
||||
ok, result = pcall(util._str_byteindex_enc, buf_line, col, client.offset_encoding)
|
||||
if ok then
|
||||
return result
|
||||
end
|
||||
return math.min(#buf_line, col)
|
||||
return util._str_byteindex_enc(buf_line, col, client.offset_encoding)
|
||||
end
|
||||
return col
|
||||
end
|
||||
|
@ -119,6 +119,7 @@ end
|
||||
---@param encoding string|nil utf-8|utf-16|utf-32|nil defaults to utf-16
|
||||
---@return integer `encoding` index of `index` in `line`
|
||||
function M._str_utfindex_enc(line, index, encoding)
|
||||
local len32, len16 = vim.str_utfindex(line)
|
||||
if not encoding then
|
||||
encoding = 'utf-16'
|
||||
end
|
||||
@ -129,9 +130,15 @@ function M._str_utfindex_enc(line, index, encoding)
|
||||
return #line
|
||||
end
|
||||
elseif encoding == 'utf-16' then
|
||||
if not index or index > len16 then
|
||||
return len16
|
||||
end
|
||||
local _, col16 = vim.str_utfindex(line, index)
|
||||
return col16
|
||||
elseif encoding == 'utf-32' then
|
||||
if not index or index > len32 then
|
||||
return len32
|
||||
end
|
||||
local col32, _ = vim.str_utfindex(line, index)
|
||||
return col32
|
||||
else
|
||||
@ -147,6 +154,12 @@ end
|
||||
---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16
|
||||
---@return integer byte (utf-8) index of `encoding` index `index` in `line`
|
||||
function M._str_byteindex_enc(line, index, encoding)
|
||||
local len = vim.fn.strlen(line)
|
||||
if index > len then
|
||||
-- LSP spec: if character > line length, default to the line length.
|
||||
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
|
||||
return len
|
||||
end
|
||||
if not encoding then
|
||||
encoding = 'utf-16'
|
||||
end
|
||||
@ -165,9 +178,6 @@ function M._str_byteindex_enc(line, index, encoding)
|
||||
end
|
||||
end
|
||||
|
||||
local _str_utfindex_enc = M._str_utfindex_enc
|
||||
local _str_byteindex_enc = M._str_byteindex_enc
|
||||
|
||||
--- Replaces text in a range with new text.
|
||||
---
|
||||
--- CAUTION: Changes in-place!
|
||||
@ -334,12 +344,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding)
|
||||
-- character
|
||||
if col > 0 then
|
||||
local line = get_line(bufnr, position.line) or ''
|
||||
local ok, result
|
||||
ok, result = pcall(_str_byteindex_enc, line, col, offset_encoding)
|
||||
if ok then
|
||||
return result
|
||||
end
|
||||
return math.min(#line, col)
|
||||
return M._str_byteindex_enc(line, col, offset_encoding or 'utf-16')
|
||||
end
|
||||
return col
|
||||
end
|
||||
@ -436,14 +441,15 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding)
|
||||
e.end_col = last_line_len
|
||||
has_eol_text_edit = true
|
||||
else
|
||||
-- If the replacement is over the end of a line (i.e. e.end_col is out of bounds and the
|
||||
-- If the replacement is over the end of a line (i.e. e.end_col is equal to the line length and the
|
||||
-- replacement text ends with a newline We can likely assume that the replacement is assumed
|
||||
-- to be meant to replace the newline with another newline and we need to make sure this
|
||||
-- doesn't add an extra empty line. E.g. when the last line to be replaced contains a '\r'
|
||||
-- in the file some servers (clangd on windows) will include that character in the line
|
||||
-- while nvim_buf_set_text doesn't count it as part of the line.
|
||||
if
|
||||
e.end_col > last_line_len
|
||||
e.end_col >= last_line_len
|
||||
and text_edit.range['end'].character > e.end_col
|
||||
and #text_edit.newText > 0
|
||||
and string.sub(text_edit.newText, -1) == '\n'
|
||||
then
|
||||
@ -1795,17 +1801,9 @@ function M.locations_to_items(locations, offset_encoding)
|
||||
local row = pos.line
|
||||
local end_row = end_pos.line
|
||||
local line = lines[row] or ''
|
||||
local line_len = vim.fn.strcharlen(line)
|
||||
local end_line = lines[end_row] or ''
|
||||
local end_line_len = vim.fn.strcharlen(end_line)
|
||||
-- LSP spec: if character > line length, default to the line length.
|
||||
-- https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#position
|
||||
local col = pos.character <= line_len
|
||||
and M._str_byteindex_enc(line, pos.character, offset_encoding)
|
||||
or line_len
|
||||
local end_col = end_pos.character <= end_line_len
|
||||
and M._str_byteindex_enc(end_line, end_pos.character, offset_encoding)
|
||||
or end_line_len
|
||||
local col = M._str_byteindex_enc(line, pos.character, offset_encoding)
|
||||
local end_col = M._str_byteindex_enc(end_line, end_pos.character, offset_encoding)
|
||||
|
||||
table.insert(items, {
|
||||
filename = filename,
|
||||
@ -1935,7 +1933,7 @@ local function make_position_param(window, offset_encoding)
|
||||
return { line = 0, character = 0 }
|
||||
end
|
||||
|
||||
col = _str_utfindex_enc(line, col, offset_encoding)
|
||||
col = M._str_utfindex_enc(line, col, offset_encoding)
|
||||
|
||||
return { line = row, character = col }
|
||||
end
|
||||
@ -2117,11 +2115,7 @@ function M.character_offset(buf, row, col, offset_encoding)
|
||||
)
|
||||
offset_encoding = vim.lsp.get_clients({ bufnr = buf })[1].offset_encoding
|
||||
end
|
||||
-- If the col is past the EOL, use the line length.
|
||||
if col > #line then
|
||||
return _str_utfindex_enc(line, nil, offset_encoding)
|
||||
end
|
||||
return _str_utfindex_enc(line, col, offset_encoding)
|
||||
return M._str_utfindex_enc(line, col, offset_encoding)
|
||||
end
|
||||
|
||||
--- Helper function to return nested values in language server settings
|
||||
|
@ -354,6 +354,12 @@ function vim.tbl_isempty(t)
|
||||
return next(t) == nil
|
||||
end
|
||||
|
||||
--- We only merge empty tables or tables that are not list-like (indexed by consecutive integers
|
||||
--- starting from 1)
|
||||
local function can_merge(v)
|
||||
return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.islist(v))
|
||||
end
|
||||
|
||||
--- Recursive worker for tbl_extend
|
||||
--- @param behavior 'error'|'keep'|'force'
|
||||
--- @param deep_extend boolean
|
||||
@ -368,7 +374,7 @@ local function tbl_extend_rec(behavior, deep_extend, ...)
|
||||
local tbl = select(i, ...) --[[@as table<any,any>]]
|
||||
if tbl then
|
||||
for k, v in pairs(tbl) do
|
||||
if deep_extend and type(v) == 'table' and type(ret[k]) == 'table' then
|
||||
if deep_extend and can_merge(v) and can_merge(ret[k]) then
|
||||
ret[k] = tbl_extend_rec(behavior, true, ret[k], v)
|
||||
elseif behavior ~= 'force' and ret[k] ~= nil then
|
||||
if behavior == 'error' then
|
||||
@ -421,6 +427,11 @@ end
|
||||
|
||||
--- Merges recursively two or more tables.
|
||||
---
|
||||
--- Only values that are empty tables or tables that are not |lua-list|s (indexed by consecutive
|
||||
--- integers starting from 1) are merged recursively. This is useful for merging nested tables
|
||||
--- like default and user configurations where lists should be treated as literals (i.e., are
|
||||
--- overwritten instead of merged).
|
||||
---
|
||||
---@see |vim.tbl_extend()|
|
||||
---
|
||||
---@generic T1: table
|
||||
|
@ -152,14 +152,14 @@ function M.open(path)
|
||||
else
|
||||
return nil, 'vim.ui.open: rundll32 not found'
|
||||
end
|
||||
elseif vim.fn.executable('wslview') == 1 then
|
||||
cmd = { 'wslview', path }
|
||||
elseif vim.fn.executable('explorer.exe') == 1 then
|
||||
cmd = { 'explorer.exe', path }
|
||||
elseif vim.fn.executable('xdg-open') == 1 then
|
||||
cmd = { 'xdg-open', path }
|
||||
opts.stdout = false
|
||||
opts.stderr = false
|
||||
elseif vim.fn.executable('wslview') == 1 then
|
||||
cmd = { 'wslview', path }
|
||||
elseif vim.fn.executable('explorer.exe') == 1 then
|
||||
cmd = { 'explorer.exe', path }
|
||||
else
|
||||
return nil, 'vim.ui.open: no handler found (tried: wslview, explorer.exe, xdg-open)'
|
||||
end
|
||||
|
@ -7641,7 +7641,7 @@ static void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
|
||||
return;
|
||||
}
|
||||
const void *iter = NULL;
|
||||
const char *appname = get_appname();
|
||||
const char *appname = get_appname(false);
|
||||
do {
|
||||
size_t dir_len;
|
||||
const char *dir;
|
||||
|
@ -16,15 +16,14 @@ struct loop {
|
||||
uv_loop_t uv;
|
||||
MultiQueue *events;
|
||||
MultiQueue *thread_events;
|
||||
// Immediate events:
|
||||
// "Processed after exiting uv_run() (to avoid recursion), but before
|
||||
// returning from loop_poll_events()." 502aee690c98
|
||||
// Practical consequence (for main_loop): these events are processed by
|
||||
// state_enter()..os_inchar()
|
||||
// whereas "regular" events (main_loop.events) are processed by
|
||||
// state_enter()..VimState.execute()
|
||||
// But state_enter()..os_inchar() can be "too early" if you want the event
|
||||
// to trigger UI updates and other user-activity-related side-effects.
|
||||
// Immediate events.
|
||||
// - "Processed after exiting `uv_run()` (to avoid recursion), but before returning from
|
||||
// `loop_poll_events()`." 502aee690c98
|
||||
// - Practical consequence (for `main_loop`):
|
||||
// - these are processed by `state_enter()..input_get()` whereas "regular" events
|
||||
// (`main_loop.events`) are processed by `state_enter()..VimState.execute()`
|
||||
// - `state_enter()..input_get()` can be "too early" if you want the event to trigger UI
|
||||
// updates and other user-activity-related side-effects.
|
||||
MultiQueue *fast_events;
|
||||
|
||||
// used by process/job-control subsystem
|
||||
|
@ -3264,18 +3264,12 @@ static void vim_mktempdir(void)
|
||||
char tmp[TEMP_FILE_PATH_MAXLEN];
|
||||
char path[TEMP_FILE_PATH_MAXLEN];
|
||||
char user[40] = { 0 };
|
||||
char appname[40] = { 0 };
|
||||
|
||||
os_get_username(user, sizeof(user));
|
||||
// Usernames may contain slashes! #19240
|
||||
memchrsub(user, '/', '_', sizeof(user));
|
||||
memchrsub(user, '\\', '_', sizeof(user));
|
||||
|
||||
// Appname may be a relative path, replace slashes to make it name-like.
|
||||
xstrlcpy(appname, get_appname(), sizeof(appname));
|
||||
memchrsub(appname, '/', '%', sizeof(appname));
|
||||
memchrsub(appname, '\\', '%', sizeof(appname));
|
||||
|
||||
// Make sure the umask doesn't remove the executable bit.
|
||||
// "repl" has been reported to use "0177".
|
||||
mode_t umask_save = umask(0077);
|
||||
@ -3283,14 +3277,15 @@ static void vim_mktempdir(void)
|
||||
// Expand environment variables, leave room for "/tmp/nvim.<user>/XXXXXX/999999999".
|
||||
expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64);
|
||||
if (!os_isdir(tmp)) {
|
||||
if (strequal("$TMPDIR", temp_dirs[i])) {
|
||||
WLOG("$TMPDIR tempdir not a directory (or does not exist): %s", tmp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// "/tmp/" exists, now try to create "/tmp/nvim.<user>/".
|
||||
add_pathsep(tmp);
|
||||
|
||||
xstrlcat(tmp, appname, sizeof(tmp));
|
||||
xstrlcat(tmp, ".", sizeof(tmp));
|
||||
xstrlcat(tmp, "nvim.", sizeof(tmp));
|
||||
xstrlcat(tmp, user, sizeof(tmp));
|
||||
os_mkdir(tmp, 0700); // Always create, to avoid a race.
|
||||
bool owned = os_file_owned(tmp);
|
||||
|
@ -1858,7 +1858,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
|
||||
if (!char_avail()) {
|
||||
// Flush screen updates before blocking.
|
||||
ui_flush();
|
||||
os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
|
||||
input_get(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
|
||||
if (!multiqueue_empty(main_loop.events)) {
|
||||
state_handle_k_event();
|
||||
continue;
|
||||
@ -2981,7 +2981,7 @@ int inchar(uint8_t *buf, int maxlen, long wait_time)
|
||||
uint8_t dum[DUM_LEN + 1];
|
||||
|
||||
while (true) {
|
||||
len = os_inchar(dum, DUM_LEN, 0, 0, NULL);
|
||||
len = input_get(dum, DUM_LEN, 0, 0, NULL);
|
||||
if (len == 0 || (len == 1 && dum[0] == Ctrl_C)) {
|
||||
break;
|
||||
}
|
||||
@ -2997,7 +2997,7 @@ int inchar(uint8_t *buf, int maxlen, long wait_time)
|
||||
|
||||
// Fill up to a third of the buffer, because each character may be
|
||||
// tripled below.
|
||||
len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt, NULL);
|
||||
len = input_get(buf, maxlen / 3, (int)wait_time, tb_change_cnt, NULL);
|
||||
}
|
||||
|
||||
// If the typebuf was changed further down, it is like nothing was added by
|
||||
|
@ -370,12 +370,15 @@ void update_window_hl(win_T *wp, bool invalid)
|
||||
|
||||
// determine window specific background set in 'winhighlight'
|
||||
bool float_win = wp->w_floating && !wp->w_config.external;
|
||||
if (float_win && hl_def[HLF_NFLOAT] != 0) {
|
||||
if (float_win && hl_def[HLF_NFLOAT] != 0 && ns_id > 0) {
|
||||
wp->w_hl_attr_normal = hl_def[HLF_NFLOAT];
|
||||
} else if (hl_def[HLF_COUNT] > 0) {
|
||||
wp->w_hl_attr_normal = hl_def[HLF_COUNT];
|
||||
} else if (float_win) {
|
||||
wp->w_hl_attr_normal = HL_ATTR(HLF_NFLOAT) > 0
|
||||
? HL_ATTR(HLF_NFLOAT) : highlight_attr[HLF_NFLOAT];
|
||||
} else {
|
||||
wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
|
||||
wp->w_hl_attr_normal = 0;
|
||||
}
|
||||
|
||||
if (wp->w_floating) {
|
||||
|
@ -891,7 +891,17 @@ int get_breakindent_win(win_T *wp, char *line)
|
||||
if (wp->w_briopt_list > 0) {
|
||||
prev_list += wp->w_briopt_list;
|
||||
} else {
|
||||
prev_indent = (int)(*regmatch.endp - *regmatch.startp);
|
||||
char *ptr = *regmatch.startp;
|
||||
char *end_ptr = *regmatch.endp;
|
||||
int indent = 0;
|
||||
// Compute the width of the matched text.
|
||||
// Use win_chartabsize() so that TAB size is correct,
|
||||
// while wrapping is ignored.
|
||||
while (ptr < end_ptr) {
|
||||
indent += win_chartabsize(wp, ptr, indent);
|
||||
MB_PTR_ADV(ptr);
|
||||
}
|
||||
prev_indent = indent;
|
||||
}
|
||||
}
|
||||
vim_regfree(regmatch.regprog);
|
||||
|
@ -37,7 +37,7 @@
|
||||
/// @param[in] str Prompt: question to ask user. Is always followed by
|
||||
/// " (y/n)?".
|
||||
/// @param[in] direct Determines what function to use to get user input. If
|
||||
/// true then os_inchar() will be used, otherwise vgetc().
|
||||
/// true then input_get() will be used, otherwise vgetc().
|
||||
/// I.e. when direct is true then characters are obtained
|
||||
/// directly from the user without buffers involved.
|
||||
///
|
||||
@ -111,7 +111,7 @@ int get_keystroke(MultiQueue *events)
|
||||
|
||||
// First time: blocking wait. Second time: wait up to 100ms for a
|
||||
// terminal code to complete.
|
||||
n = os_inchar(buf + len, maxlen, len == 0 ? -1 : 100, 0, events);
|
||||
n = input_get(buf + len, maxlen, len == 0 ? -1 : 100, 0, events);
|
||||
if (n > 0) {
|
||||
// Replace zero and K_SPECIAL by a special key code.
|
||||
n = fix_input_buffer(buf + len, n);
|
||||
|
@ -266,7 +266,7 @@ int main(int argc, char **argv)
|
||||
|
||||
if (argc > 1 && STRICMP(argv[1], "-ll") == 0) {
|
||||
if (argc == 2) {
|
||||
print_mainerr(err_arg_missing, argv[1]);
|
||||
print_mainerr(err_arg_missing, argv[1], NULL);
|
||||
exit(1);
|
||||
}
|
||||
nlua_run_script(argv, argc, 3);
|
||||
@ -357,10 +357,8 @@ int main(int argc, char **argv)
|
||||
assert(!ui_client_channel_id && !use_builtin_ui);
|
||||
// Nvim server...
|
||||
|
||||
int listen_rv = server_init(params.listen_addr);
|
||||
if (listen_rv != 0) {
|
||||
mainerr("Failed to --listen", listen_rv < 0
|
||||
? os_strerror(listen_rv) : (listen_rv == 1 ? "empty address" : NULL));
|
||||
if (!server_init(params.listen_addr)) {
|
||||
mainerr(IObuff, NULL, NULL);
|
||||
}
|
||||
|
||||
TIME_MSG("expanding arguments");
|
||||
@ -1053,7 +1051,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
// "+" or "+{number}" or "+/{pat}" or "+{command}" argument.
|
||||
if (argv[0][0] == '+' && !had_minmin) {
|
||||
if (parmp->n_commands >= MAX_ARG_CMDS) {
|
||||
mainerr(err_extra_cmd, NULL);
|
||||
mainerr(err_extra_cmd, NULL, NULL);
|
||||
}
|
||||
argv_idx = -1; // skip to next argument
|
||||
if (argv[0][1] == NUL) {
|
||||
@ -1074,7 +1072,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
parmp->no_swap_file = true;
|
||||
} else {
|
||||
if (parmp->edit_type > EDIT_STDIN) {
|
||||
mainerr(err_too_many_args, argv[0]);
|
||||
mainerr(err_too_many_args, argv[0], NULL);
|
||||
}
|
||||
parmp->had_stdin_file = true;
|
||||
parmp->edit_type = EDIT_STDIN;
|
||||
@ -1137,7 +1135,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
nlua_disable_preload = true;
|
||||
} else {
|
||||
if (argv[0][argv_idx]) {
|
||||
mainerr(err_opt_unknown, argv[0]);
|
||||
mainerr(err_opt_unknown, argv[0], NULL);
|
||||
}
|
||||
had_minmin = true;
|
||||
}
|
||||
@ -1211,7 +1209,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
break;
|
||||
case 'q': // "-q" QuickFix mode
|
||||
if (parmp->edit_type != EDIT_NONE) {
|
||||
mainerr(err_too_many_args, argv[0]);
|
||||
mainerr(err_too_many_args, argv[0], NULL);
|
||||
}
|
||||
parmp->edit_type = EDIT_QF;
|
||||
if (argv[0][argv_idx]) { // "-q{errorfile}"
|
||||
@ -1240,7 +1238,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
break;
|
||||
case 't': // "-t {tag}" or "-t{tag}" jump to tag
|
||||
if (parmp->edit_type != EDIT_NONE) {
|
||||
mainerr(err_too_many_args, argv[0]);
|
||||
mainerr(err_too_many_args, argv[0], NULL);
|
||||
}
|
||||
parmp->edit_type = EDIT_TAG;
|
||||
if (argv[0][argv_idx]) { // "-t{tag}"
|
||||
@ -1274,7 +1272,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
case 'c': // "-c{command}" or "-c {command}" exec command
|
||||
if (argv[0][argv_idx] != NUL) {
|
||||
if (parmp->n_commands >= MAX_ARG_CMDS) {
|
||||
mainerr(err_extra_cmd, NULL);
|
||||
mainerr(err_extra_cmd, NULL, NULL);
|
||||
}
|
||||
parmp->commands[parmp->n_commands++] = argv[0] + argv_idx;
|
||||
argv_idx = -1;
|
||||
@ -1291,19 +1289,19 @@ static void command_line_scan(mparm_T *parmp)
|
||||
break;
|
||||
|
||||
default:
|
||||
mainerr(err_opt_unknown, argv[0]);
|
||||
mainerr(err_opt_unknown, argv[0], NULL);
|
||||
}
|
||||
|
||||
// Handle option arguments with argument.
|
||||
if (want_argument) {
|
||||
// Check for garbage immediately after the option letter.
|
||||
if (argv[0][argv_idx] != NUL) {
|
||||
mainerr(err_opt_garbage, argv[0]);
|
||||
mainerr(err_opt_garbage, argv[0], NULL);
|
||||
}
|
||||
|
||||
argc--;
|
||||
if (argc < 1 && c != 'S') { // -S has an optional argument
|
||||
mainerr(err_arg_missing, argv[0]);
|
||||
mainerr(err_arg_missing, argv[0], NULL);
|
||||
}
|
||||
argv++;
|
||||
argv_idx = -1;
|
||||
@ -1312,7 +1310,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
case 'c': // "-c {command}" execute command
|
||||
case 'S': // "-S {file}" execute Vim script
|
||||
if (parmp->n_commands >= MAX_ARG_CMDS) {
|
||||
mainerr(err_extra_cmd, NULL);
|
||||
mainerr(err_extra_cmd, NULL, NULL);
|
||||
}
|
||||
if (c == 'S') {
|
||||
char *a;
|
||||
@ -1343,7 +1341,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
if (strequal(argv[-1], "--cmd")) {
|
||||
// "--cmd {command}" execute command
|
||||
if (parmp->n_pre_commands >= MAX_ARG_CMDS) {
|
||||
mainerr(err_extra_cmd, NULL);
|
||||
mainerr(err_extra_cmd, NULL, NULL);
|
||||
}
|
||||
parmp->pre_commands[parmp->n_pre_commands++] = argv[0];
|
||||
} else if (strequal(argv[-1], "--listen")) {
|
||||
@ -1425,7 +1423,7 @@ scripterror:
|
||||
|
||||
// Check for only one type of editing.
|
||||
if (parmp->edit_type > EDIT_STDIN) {
|
||||
mainerr(err_too_many_args, argv[0]);
|
||||
mainerr(err_too_many_args, argv[0], NULL);
|
||||
}
|
||||
parmp->edit_type = EDIT_FILE;
|
||||
|
||||
@ -1472,7 +1470,7 @@ scripterror:
|
||||
}
|
||||
|
||||
if (embedded_mode && (silent_mode || parmp->luaf)) {
|
||||
mainerr(_("--embed conflicts with -es/-Es/-l"), NULL);
|
||||
mainerr(_("--embed conflicts with -es/-Es/-l"), NULL, NULL);
|
||||
}
|
||||
|
||||
// If there is a "+123" or "-c" command, set v:swapcommand to the first one.
|
||||
@ -2135,28 +2133,30 @@ static int execute_env(char *env)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/// Prints the following then exits:
|
||||
/// - An error message `errstr`
|
||||
/// - A string `str` if not null
|
||||
/// Prints a message of the form "{msg1}: {msg2}: {msg3}", then exits with code 1.
|
||||
///
|
||||
/// @param errstr string containing an error message
|
||||
/// @param str string to append to the primary error message, or NULL
|
||||
static void mainerr(const char *errstr, const char *str)
|
||||
/// @param msg1 error message
|
||||
/// @param msg2 extra message, or NULL
|
||||
/// @param msg3 extra message, or NULL
|
||||
static void mainerr(const char *msg1, const char *msg2, const char *msg3)
|
||||
FUNC_ATTR_NORETURN
|
||||
{
|
||||
print_mainerr(errstr, str);
|
||||
print_mainerr(msg1, msg2, msg3);
|
||||
os_exit(1);
|
||||
}
|
||||
|
||||
static void print_mainerr(const char *errstr, const char *str)
|
||||
static void print_mainerr(const char *msg1, const char *msg2, const char *msg3)
|
||||
{
|
||||
char *prgname = path_tail(argv0);
|
||||
|
||||
signal_stop(); // kill us with CTRL-C here, if you like
|
||||
|
||||
fprintf(stderr, "%s: %s", prgname, _(errstr));
|
||||
if (str != NULL) {
|
||||
fprintf(stderr, ": \"%s\"", str);
|
||||
fprintf(stderr, "%s: %s", prgname, _(msg1));
|
||||
if (msg2 != NULL) {
|
||||
fprintf(stderr, ": \"%s\"", msg2);
|
||||
}
|
||||
if (msg3 != NULL) {
|
||||
fprintf(stderr, ": \"%s\"", msg3);
|
||||
}
|
||||
fprintf(stderr, _("\nMore info with \""));
|
||||
fprintf(stderr, "%s -h\"\n", prgname);
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
/// HACK: os/input.c drains this queue immediately before blocking for input.
|
||||
/// Events on this queue are async-safe, but they need the resolved state
|
||||
/// of os_inchar(), so they are processed "just-in-time".
|
||||
/// of input_get(), so they are processed "just-in-time".
|
||||
EXTERN MultiQueue *ch_before_blocking_events INIT( = NULL);
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
|
@ -11,12 +11,14 @@
|
||||
#include "nvim/event/socket.h"
|
||||
#include "nvim/garray.h"
|
||||
#include "nvim/garray_defs.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/msgpack_rpc/server.h"
|
||||
#include "nvim/os/os.h"
|
||||
#include "nvim/os/stdpaths_defs.h"
|
||||
#include "nvim/types_defs.h"
|
||||
|
||||
#define MAX_CONNECTIONS 32
|
||||
#define ENV_LISTEN "NVIM_LISTEN_ADDRESS" // deprecated
|
||||
@ -27,36 +29,30 @@ static garray_T watchers = GA_EMPTY_INIT_VALUE;
|
||||
# include "msgpack_rpc/server.c.generated.h"
|
||||
#endif
|
||||
|
||||
/// Initializes the module
|
||||
/// Initializes resources, handles `--listen`, starts the primary server at v:servername.
|
||||
///
|
||||
/// @returns 0: success, 1: validation error, 2: already listening, -errno: failed to bind/listen.
|
||||
int server_init(const char *listen_addr)
|
||||
/// @returns true on success, false on fatal error (message stored in IObuff)
|
||||
bool server_init(const char *listen_addr)
|
||||
{
|
||||
bool ok = true;
|
||||
bool must_free = false;
|
||||
TriState user_arg = kTrue; // User-provided --listen arg.
|
||||
ga_init(&watchers, sizeof(SocketWatcher *), 1);
|
||||
|
||||
// $NVIM_LISTEN_ADDRESS (deprecated)
|
||||
if ((!listen_addr || listen_addr[0] == '\0') && os_env_exists(ENV_LISTEN)) {
|
||||
user_arg = kFalse; // User-provided env var.
|
||||
listen_addr = os_getenv(ENV_LISTEN);
|
||||
}
|
||||
|
||||
if (!listen_addr || listen_addr[0] == '\0') {
|
||||
user_arg = kNone; // Autogenerated server address.
|
||||
listen_addr = server_address_new(NULL);
|
||||
must_free = true;
|
||||
}
|
||||
|
||||
if (!listen_addr) {
|
||||
abort(); // Cannot happen.
|
||||
}
|
||||
|
||||
int rv = server_start(listen_addr);
|
||||
|
||||
if (os_env_exists(ENV_LISTEN)) {
|
||||
// Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. It is "input only", it must not be
|
||||
// leaked to child jobs or :terminal.
|
||||
os_unsetenv(ENV_LISTEN);
|
||||
}
|
||||
|
||||
// TODO(justinmk): this is for logging_spec. Can remove this after nvim_log #7062 is merged.
|
||||
if (os_env_exists("__NVIM_TEST_LOG")) {
|
||||
ELOG("test log message");
|
||||
@ -66,7 +62,28 @@ int server_init(const char *listen_addr)
|
||||
xfree((char *)listen_addr);
|
||||
}
|
||||
|
||||
return rv;
|
||||
if (rv == 0 || user_arg == kNone) {
|
||||
// The autogenerated servername can fail if the user has a broken $XDG_RUNTIME_DIR. #30282
|
||||
// But that is not fatal (startup will continue, logged in $NVIM_LOGFILE, empty v:servername).
|
||||
goto end;
|
||||
}
|
||||
|
||||
(void)snprintf(IObuff, IOSIZE,
|
||||
user_arg ==
|
||||
kTrue ? "Failed to --listen: %s: \"%s\""
|
||||
: "Failed $NVIM_LISTEN_ADDRESS: %s: \"%s\"",
|
||||
rv < 0 ? os_strerror(rv) : (rv == 1 ? "empty address" : "?"),
|
||||
listen_addr);
|
||||
ok = false;
|
||||
|
||||
end:
|
||||
if (os_env_exists(ENV_LISTEN)) {
|
||||
// Unset $NVIM_LISTEN_ADDRESS, it's a liability hereafter. It is "input only", it must not be
|
||||
// leaked to child jobs or :terminal.
|
||||
os_unsetenv(ENV_LISTEN);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/// Teardown a single server
|
||||
@ -97,17 +114,19 @@ void server_teardown(void)
|
||||
/// - Windows: "\\.\pipe\<name>.<pid>.<counter>"
|
||||
/// - Other: "/tmp/nvim.user/xxx/<name>.<pid>.<counter>"
|
||||
char *server_address_new(const char *name)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
||||
{
|
||||
static uint32_t count = 0;
|
||||
char fmt[ADDRESS_MAX_SIZE];
|
||||
const char *appname = get_appname();
|
||||
#ifdef MSWIN
|
||||
(void)get_appname(true);
|
||||
int r = snprintf(fmt, sizeof(fmt), "\\\\.\\pipe\\%s.%" PRIu64 ".%" PRIu32,
|
||||
name ? name : appname, os_get_pid(), count++);
|
||||
name ? name : NameBuff, os_get_pid(), count++);
|
||||
#else
|
||||
char *dir = stdpaths_get_xdg_var(kXDGRuntimeDir);
|
||||
(void)get_appname(true);
|
||||
int r = snprintf(fmt, sizeof(fmt), "%s/%s.%" PRIu64 ".%" PRIu32,
|
||||
dir, name ? name : appname, os_get_pid(), count++);
|
||||
dir, name ? name : NameBuff, os_get_pid(), count++);
|
||||
xfree(dir);
|
||||
#endif
|
||||
if ((size_t)r >= sizeof(fmt)) {
|
||||
|
@ -6597,11 +6597,11 @@ static void nv_open(cmdarg_T *cap)
|
||||
static void nv_event(cmdarg_T *cap)
|
||||
{
|
||||
// Garbage collection should have been executed before blocking for events in
|
||||
// the `os_inchar` in `state_enter`, but we also disable it here in case the
|
||||
// `os_inchar` branch was not executed (!multiqueue_empty(loop.events), which
|
||||
// the `input_get` in `state_enter`, but we also disable it here in case the
|
||||
// `input_get` branch was not executed (!multiqueue_empty(loop.events), which
|
||||
// could have `may_garbage_collect` set to true in `normal_check`).
|
||||
//
|
||||
// That is because here we may run code that calls `os_inchar`
|
||||
// That is because here we may run code that calls `input_get`
|
||||
// later(`f_confirm` or `get_keystroke` for example), but in these cases it is
|
||||
// not safe to perform garbage collection because there could be unreferenced
|
||||
// lists or dicts being used.
|
||||
|
@ -761,9 +761,9 @@ return {
|
||||
list:{n} Adds an additional indent for lines that match a
|
||||
numbered or bulleted list (using the
|
||||
'formatlistpat' setting).
|
||||
list:-1 Uses the length of a match with 'formatlistpat'
|
||||
for indentation.
|
||||
(default: 0)
|
||||
list:-1 Uses the width of a match with 'formatlistpat' for
|
||||
indentation.
|
||||
column:{n} Indent at column {n}. Will overrule the other
|
||||
sub-options. Note: an additional indent may be
|
||||
added for the 'showbreak' setting.
|
||||
|
@ -33,12 +33,6 @@
|
||||
#define READ_BUFFER_SIZE 0xfff
|
||||
#define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN)
|
||||
|
||||
typedef enum {
|
||||
kInputNone,
|
||||
kInputAvail,
|
||||
kInputEof,
|
||||
} InbufPollResult;
|
||||
|
||||
static RStream read_stream = { .s.closed = true }; // Input before UI starts.
|
||||
static char input_buffer[INPUT_BUFFER_SIZE];
|
||||
static char *input_read_pos = input_buffer;
|
||||
@ -84,57 +78,76 @@ static void cursorhold_event(void **argv)
|
||||
static void create_cursorhold_event(bool events_enabled)
|
||||
{
|
||||
// If events are enabled and the queue has any items, this function should not
|
||||
// have been called (inbuf_poll would return kInputAvail).
|
||||
// have been called (`inbuf_poll` would return `kTrue`).
|
||||
// TODO(tarruda): Cursorhold should be implemented as a timer set during the
|
||||
// `state_check` callback for the states where it can be triggered.
|
||||
assert(!events_enabled || multiqueue_empty(main_loop.events));
|
||||
multiqueue_put(main_loop.events, cursorhold_event, NULL);
|
||||
}
|
||||
|
||||
static void restart_cursorhold_wait(int tb_change_cnt)
|
||||
static void reset_cursorhold_wait(int tb_change_cnt)
|
||||
{
|
||||
cursorhold_time = 0;
|
||||
cursorhold_tb_change_cnt = tb_change_cnt;
|
||||
}
|
||||
|
||||
/// Low level input function
|
||||
/// Reads OS input into `buf`, and consumes pending events while waiting (if `ms != 0`).
|
||||
///
|
||||
/// Wait until either the input buffer is non-empty or, if `events` is not NULL
|
||||
/// until `events` is non-empty.
|
||||
int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events)
|
||||
/// - Consumes available input received from the OS.
|
||||
/// - Consumes pending events.
|
||||
/// - Manages CursorHold events.
|
||||
/// - Handles EOF conditions.
|
||||
///
|
||||
/// Originally based on the Vim `mch_inchar` function.
|
||||
///
|
||||
/// @param buf Buffer to store read input.
|
||||
/// @param maxlen Maximum bytes to read into `buf`, or 0 to skip reading.
|
||||
/// @param ms Timeout in milliseconds. -1 for indefinite wait, 0 for no wait.
|
||||
/// @param tb_change_cnt Used to detect when typeahead changes.
|
||||
/// @param events (optional) Events to process.
|
||||
/// @return Bytes read into buf, or 0 if no input was read
|
||||
int input_get(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *events)
|
||||
{
|
||||
// This check is needed so that feeding typeahead from RPC can prevent CursorHold.
|
||||
if (tb_change_cnt != cursorhold_tb_change_cnt) {
|
||||
restart_cursorhold_wait(tb_change_cnt);
|
||||
reset_cursorhold_wait(tb_change_cnt);
|
||||
}
|
||||
|
||||
if (maxlen && input_available()) {
|
||||
restart_cursorhold_wait(tb_change_cnt);
|
||||
size_t to_read = MIN((size_t)maxlen, input_available());
|
||||
memcpy(buf, input_read_pos, to_read);
|
||||
input_read_pos += to_read;
|
||||
return (int)to_read;
|
||||
}
|
||||
#define TRY_READ() \
|
||||
do { \
|
||||
if (maxlen && input_available()) { \
|
||||
reset_cursorhold_wait(tb_change_cnt); \
|
||||
assert(maxlen >= 0); \
|
||||
size_t to_read = MIN((size_t)maxlen, input_available()); \
|
||||
memcpy(buf, input_read_pos, to_read); \
|
||||
input_read_pos += to_read; \
|
||||
/* This is safe because INPUT_BUFFER_SIZE fits in an int. */ \
|
||||
assert(to_read <= INT_MAX); \
|
||||
return (int)to_read; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
TRY_READ();
|
||||
|
||||
// No risk of a UI flood, so disable CTRL-C "interrupt" behavior if it's mapped.
|
||||
if ((mapped_ctrl_c | curbuf->b_mapped_ctrl_c) & get_real_state()) {
|
||||
ctrl_c_interrupts = false;
|
||||
}
|
||||
|
||||
InbufPollResult result;
|
||||
TriState result; ///< inbuf_poll result.
|
||||
if (ms >= 0) {
|
||||
if ((result = inbuf_poll(ms, events)) == kInputNone) {
|
||||
if ((result = inbuf_poll(ms, events)) == kFalse) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
uint64_t wait_start = os_hrtime();
|
||||
cursorhold_time = MIN(cursorhold_time, (int)p_ut);
|
||||
if ((result = inbuf_poll((int)p_ut - cursorhold_time, events)) == kInputNone) {
|
||||
if ((result = inbuf_poll((int)p_ut - cursorhold_time, events)) == kFalse) {
|
||||
if (read_stream.s.closed && silent_mode) {
|
||||
// Drained eventloop & initial input; exit silent/batch-mode (-es/-Es).
|
||||
read_error_exit();
|
||||
}
|
||||
restart_cursorhold_wait(tb_change_cnt);
|
||||
reset_cursorhold_wait(tb_change_cnt);
|
||||
if (trigger_cursorhold() && !typebuf_changed(tb_change_cnt)) {
|
||||
create_cursorhold_event(events == main_loop.events);
|
||||
} else {
|
||||
@ -153,32 +166,26 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (maxlen && input_available()) {
|
||||
restart_cursorhold_wait(tb_change_cnt);
|
||||
// Safe to convert `to_read` to int, it will never overflow since
|
||||
// INPUT_BUFFER_SIZE fits in an int
|
||||
size_t to_read = MIN((size_t)maxlen, input_available());
|
||||
memcpy(buf, input_read_pos, to_read);
|
||||
input_read_pos += to_read;
|
||||
return (int)to_read;
|
||||
}
|
||||
TRY_READ();
|
||||
|
||||
// If there are events, return the keys directly
|
||||
if (maxlen && pending_events(events)) {
|
||||
return push_event_key(buf, maxlen);
|
||||
}
|
||||
|
||||
if (result == kInputEof) {
|
||||
if (result == kNone) {
|
||||
read_error_exit();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef TRY_READ
|
||||
}
|
||||
|
||||
// Check if a character is available for reading
|
||||
bool os_char_avail(void)
|
||||
{
|
||||
return inbuf_poll(0, NULL) == kInputAvail;
|
||||
return inbuf_poll(0, NULL) == kTrue;
|
||||
}
|
||||
|
||||
/// Poll for fast events. `got_int` will be set to `true` if CTRL-C was typed.
|
||||
@ -463,15 +470,22 @@ bool input_blocking(void)
|
||||
return blocking;
|
||||
}
|
||||
|
||||
// This is a replacement for the old `WaitForChar` function in os_unix.c
|
||||
static InbufPollResult inbuf_poll(int ms, MultiQueue *events)
|
||||
/// Checks for (but does not read) available input, and consumes `main_loop.events` while waiting.
|
||||
///
|
||||
/// @param ms Timeout in milliseconds. -1 for indefinite wait, 0 for no wait.
|
||||
/// @param events (optional) Queue to check for pending events.
|
||||
/// @return TriState:
|
||||
/// - kTrue: Input/events available
|
||||
/// - kFalse: No input/events
|
||||
/// - kNone: EOF reached on the input stream
|
||||
static TriState inbuf_poll(int ms, MultiQueue *events)
|
||||
{
|
||||
if (os_input_ready(events)) {
|
||||
return kInputAvail;
|
||||
return kTrue;
|
||||
}
|
||||
|
||||
if (do_profiling == PROF_YES && ms) {
|
||||
prof_inchar_enter();
|
||||
prof_input_start();
|
||||
}
|
||||
|
||||
if ((ms == -1 || ms > 0) && events != main_loop.events && !input_eof) {
|
||||
@ -479,20 +493,18 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events)
|
||||
blocking = true;
|
||||
multiqueue_process_events(ch_before_blocking_events);
|
||||
}
|
||||
DLOG("blocking... events_enabled=%d events_pending=%d", events != NULL,
|
||||
events && !multiqueue_empty(events));
|
||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms,
|
||||
os_input_ready(events) || input_eof);
|
||||
DLOG("blocking... events=%d pending=%d", !!events, pending_events(events));
|
||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, os_input_ready(events) || input_eof);
|
||||
blocking = false;
|
||||
|
||||
if (do_profiling == PROF_YES && ms) {
|
||||
prof_inchar_exit();
|
||||
prof_input_end();
|
||||
}
|
||||
|
||||
if (os_input_ready(events)) {
|
||||
return kInputAvail;
|
||||
return kTrue;
|
||||
}
|
||||
return input_eof ? kInputEof : kInputNone;
|
||||
return input_eof ? kNone : kFalse;
|
||||
}
|
||||
|
||||
static size_t input_read_cb(RStream *stream, const char *buf, size_t c, void *data, bool at_eof)
|
||||
@ -533,8 +545,8 @@ static void process_ctrl_c(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function used to push bytes from the 'event' key sequence partially
|
||||
// between calls to os_inchar when maxlen < 3
|
||||
/// Pushes bytes from the "event" key sequence (KE_EVENT) partially between calls to input_get when
|
||||
/// `maxlen < 3`.
|
||||
static int push_event_key(uint8_t *buf, int maxlen)
|
||||
{
|
||||
static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT };
|
||||
|
@ -63,22 +63,32 @@ static const char *const xdg_defaults[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Get the value of $NVIM_APPNAME or "nvim" if not set.
|
||||
/// Gets the value of $NVIM_APPNAME, or "nvim" if not set.
|
||||
///
|
||||
/// @param namelike Write "name-like" value (no path separators) in `NameBuff`.
|
||||
///
|
||||
/// @return $NVIM_APPNAME value
|
||||
const char *get_appname(void)
|
||||
const char *get_appname(bool namelike)
|
||||
{
|
||||
const char *env_val = os_getenv("NVIM_APPNAME");
|
||||
if (env_val == NULL || *env_val == NUL) {
|
||||
env_val = "nvim";
|
||||
}
|
||||
|
||||
if (namelike) {
|
||||
// Appname may be a relative path, replace slashes to make it name-like.
|
||||
xstrlcpy(NameBuff, env_val, sizeof(NameBuff));
|
||||
memchrsub(NameBuff, '/', '-', sizeof(NameBuff));
|
||||
memchrsub(NameBuff, '\\', '-', sizeof(NameBuff));
|
||||
}
|
||||
|
||||
return env_val;
|
||||
}
|
||||
|
||||
/// Ensure that APPNAME is valid. Must be a name or relative path.
|
||||
bool appname_is_valid(void)
|
||||
{
|
||||
const char *appname = get_appname();
|
||||
const char *appname = get_appname(false);
|
||||
if (path_is_absolute(appname)
|
||||
// TODO(justinmk): on Windows, path_is_absolute says "/" is NOT absolute. Should it?
|
||||
|| strequal(appname, "/")
|
||||
@ -193,7 +203,7 @@ char *get_xdg_home(const XDGVarType idx)
|
||||
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
{
|
||||
char *dir = stdpaths_get_xdg_var(idx);
|
||||
const char *appname = get_appname();
|
||||
const char *appname = get_appname(false);
|
||||
size_t appname_len = strlen(appname);
|
||||
assert(appname_len < (IOSIZE - sizeof("-data")));
|
||||
|
||||
|
@ -383,19 +383,19 @@ void set_context_in_profile_cmd(expand_T *xp, const char *arg)
|
||||
xp->xp_context = EXPAND_NOTHING;
|
||||
}
|
||||
|
||||
static proftime_T inchar_time;
|
||||
static proftime_T wait_time;
|
||||
|
||||
/// Called when starting to wait for the user to type a character.
|
||||
void prof_inchar_enter(void)
|
||||
void prof_input_start(void)
|
||||
{
|
||||
inchar_time = profile_start();
|
||||
wait_time = profile_start();
|
||||
}
|
||||
|
||||
/// Called when finished waiting for the user to type a character.
|
||||
void prof_inchar_exit(void)
|
||||
void prof_input_end(void)
|
||||
{
|
||||
inchar_time = profile_end(inchar_time);
|
||||
profile_set_wait(profile_add(profile_get_wait(), inchar_time));
|
||||
wait_time = profile_end(wait_time);
|
||||
profile_set_wait(profile_add(profile_get_wait(), wait_time));
|
||||
}
|
||||
|
||||
/// @return true when a function defined in the current script should be
|
||||
|
@ -1559,7 +1559,7 @@ static inline char *add_env_sep_dirs(char *dest, const char *const val, const ch
|
||||
return dest;
|
||||
}
|
||||
const void *iter = NULL;
|
||||
const char *appname = get_appname();
|
||||
const char *appname = get_appname(false);
|
||||
const size_t appname_len = strlen(appname);
|
||||
do {
|
||||
size_t dir_len;
|
||||
@ -1626,7 +1626,7 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_
|
||||
if (!after_pathsep(dest - 1, dest)) {
|
||||
*dest++ = PATHSEP;
|
||||
}
|
||||
const char *appname = get_appname();
|
||||
const char *appname = get_appname(false);
|
||||
size_t appname_len = strlen(appname);
|
||||
assert(appname_len < (IOSIZE - sizeof("-data")));
|
||||
xmemcpyz(IObuff, appname, appname_len);
|
||||
@ -1697,7 +1697,7 @@ char *runtimepath_default(bool clean_arg)
|
||||
size_t config_len = 0;
|
||||
size_t vimruntime_len = 0;
|
||||
size_t libdir_len = 0;
|
||||
const char *appname = get_appname();
|
||||
const char *appname = get_appname(false);
|
||||
size_t appname_len = strlen(appname);
|
||||
if (data_home != NULL) {
|
||||
data_len = strlen(data_home);
|
||||
|
@ -74,10 +74,9 @@ getkey:
|
||||
}
|
||||
// Flush screen updates before blocking.
|
||||
ui_flush();
|
||||
// Call `os_inchar` directly to block for events or user input without
|
||||
// consuming anything from `input_buffer`(os/input.c) or calling the
|
||||
// mapping engine.
|
||||
os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
|
||||
// Call `input_get` directly to block for events or user input without consuming anything from
|
||||
// `os/input.c:input_buffer` or calling the mapping engine.
|
||||
input_get(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
|
||||
// If an event was put into the queue, we send K_EVENT directly.
|
||||
if (!input_available() && !multiqueue_empty(main_loop.events)) {
|
||||
key = K_EVENT;
|
||||
|
@ -3201,7 +3201,7 @@ describe('API', function()
|
||||
end)
|
||||
|
||||
describe('nvim_get_runtime_file', function()
|
||||
local p = n.alter_slashes
|
||||
local p = t.fix_slashes
|
||||
it('can find files', function()
|
||||
eq({}, api.nvim_get_runtime_file('bork.borkbork', false))
|
||||
eq({}, api.nvim_get_runtime_file('bork.borkbork', true))
|
||||
@ -3210,36 +3210,36 @@ describe('API', function()
|
||||
local val = api.nvim_get_runtime_file('autoload/remote/*.vim', true)
|
||||
eq(2, #val)
|
||||
if endswith(val[1], 'define.vim') then
|
||||
ok(endswith(val[1], p 'autoload/remote/define.vim'))
|
||||
ok(endswith(val[2], p 'autoload/remote/host.vim'))
|
||||
ok(endswith(p(val[1]), 'autoload/remote/define.vim'))
|
||||
ok(endswith(p(val[2]), 'autoload/remote/host.vim'))
|
||||
else
|
||||
ok(endswith(val[1], p 'autoload/remote/host.vim'))
|
||||
ok(endswith(val[2], p 'autoload/remote/define.vim'))
|
||||
ok(endswith(p(val[1]), 'autoload/remote/host.vim'))
|
||||
ok(endswith(p(val[2]), 'autoload/remote/define.vim'))
|
||||
end
|
||||
val = api.nvim_get_runtime_file('autoload/remote/*.vim', false)
|
||||
eq(1, #val)
|
||||
ok(
|
||||
endswith(val[1], p 'autoload/remote/define.vim')
|
||||
or endswith(val[1], p 'autoload/remote/host.vim')
|
||||
endswith(p(val[1]), 'autoload/remote/define.vim')
|
||||
or endswith(p(val[1]), 'autoload/remote/host.vim')
|
||||
)
|
||||
|
||||
val = api.nvim_get_runtime_file('lua', true)
|
||||
eq(1, #val)
|
||||
ok(endswith(val[1], p 'lua'))
|
||||
ok(endswith(p(val[1]), 'lua'))
|
||||
|
||||
val = api.nvim_get_runtime_file('lua/vim', true)
|
||||
eq(1, #val)
|
||||
ok(endswith(val[1], p 'lua/vim'))
|
||||
ok(endswith(p(val[1]), 'lua/vim'))
|
||||
end)
|
||||
|
||||
it('can find directories', function()
|
||||
local val = api.nvim_get_runtime_file('lua/', true)
|
||||
eq(1, #val)
|
||||
ok(endswith(val[1], p 'lua/'))
|
||||
ok(endswith(p(val[1]), 'lua/'))
|
||||
|
||||
val = api.nvim_get_runtime_file('lua/vim/', true)
|
||||
eq(1, #val)
|
||||
ok(endswith(val[1], p 'lua/vim/'))
|
||||
ok(endswith(p(val[1]), 'lua/vim/'))
|
||||
|
||||
eq({}, api.nvim_get_runtime_file('foobarlang/', true))
|
||||
end)
|
||||
|
@ -9,7 +9,7 @@ local request = n.request
|
||||
local is_os = t.is_os
|
||||
|
||||
describe('autocmd DirChanged and DirChangedPre', function()
|
||||
local curdir = vim.uv.cwd():gsub('\\', '/')
|
||||
local curdir = t.fix_slashes(vim.uv.cwd())
|
||||
local dirs = {
|
||||
curdir .. '/Xtest-functional-autocmd-dirchanged.dir1',
|
||||
curdir .. '/Xtest-functional-autocmd-dirchanged.dir2',
|
||||
|
@ -321,11 +321,11 @@ end)
|
||||
describe('tmpdir', function()
|
||||
local tmproot_pat = [=[.*[/\\]nvim%.[^/\\]+]=]
|
||||
local testlog = 'Xtest_tmpdir_log'
|
||||
local os_tmpdir
|
||||
local os_tmpdir ---@type string
|
||||
|
||||
before_each(function()
|
||||
-- Fake /tmp dir so that we can mess it up.
|
||||
os_tmpdir = vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX')
|
||||
os_tmpdir = assert(vim.uv.fs_mkdtemp(vim.fs.dirname(t.tmpname(false)) .. '/nvim_XXXXXXXXXX'))
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
@ -414,15 +414,4 @@ describe('tmpdir', function()
|
||||
rm_tmpdir()
|
||||
eq('E5431: tempdir disappeared (3 times)', api.nvim_get_vvar('errmsg'))
|
||||
end)
|
||||
|
||||
it('$NVIM_APPNAME relative path', function()
|
||||
clear({
|
||||
env = {
|
||||
NVIM_APPNAME = 'a/b',
|
||||
NVIM_LOG_FILE = testlog,
|
||||
TMPDIR = os_tmpdir,
|
||||
},
|
||||
})
|
||||
matches([=[.*[/\\]a%%b%.[^/\\]+]=], fn.tempname())
|
||||
end)
|
||||
end)
|
||||
|
@ -41,6 +41,21 @@ describe('server', function()
|
||||
end
|
||||
end)
|
||||
|
||||
it('broken $XDG_RUNTIME_DIR is not fatal #30282', function()
|
||||
clear {
|
||||
args_rm = { '--listen' },
|
||||
env = { NVIM_LOG_FILE = testlog, XDG_RUNTIME_DIR = '/non-existent-dir/subdir//' },
|
||||
}
|
||||
|
||||
if is_os('win') then
|
||||
-- Windows pipes have a special namespace and thus aren't decided by $XDG_RUNTIME_DIR.
|
||||
matches('nvim', api.nvim_get_vvar('servername'))
|
||||
else
|
||||
eq('', api.nvim_get_vvar('servername'))
|
||||
t.assert_log('Failed to start server%: no such file or directory', testlog, 100)
|
||||
end
|
||||
end)
|
||||
|
||||
it('serverstart(), serverstop() does not set $NVIM', function()
|
||||
clear()
|
||||
local s = eval('serverstart()')
|
||||
@ -139,7 +154,7 @@ describe('server', function()
|
||||
clear_serverlist()
|
||||
|
||||
-- Address without slashes is a "name" which is appended to a generated path. #8519
|
||||
matches([[.*[/\\]xtest1%.2%.3%.4[^/\\]*]], fn.serverstart('xtest1.2.3.4'))
|
||||
matches([[[/\\]xtest1%.2%.3%.4[^/\\]*]], fn.serverstart('xtest1.2.3.4'))
|
||||
clear_serverlist()
|
||||
|
||||
eq('Vim:Failed to start server: invalid argument', pcall_err(fn.serverstart, '127.0.0.1:65536')) -- invalid port
|
||||
@ -175,56 +190,79 @@ describe('server', function()
|
||||
end)
|
||||
|
||||
describe('startup --listen', function()
|
||||
-- Tests Nvim output when failing to start, with and without "--headless".
|
||||
-- TODO(justinmk): clear() should have a way to get stdout if Nvim fails to start.
|
||||
local function _test(args, env, expected)
|
||||
local function run(cmd)
|
||||
return n.exec_lua(function(cmd_, env_)
|
||||
return vim
|
||||
.system(cmd_, {
|
||||
text = true,
|
||||
env = vim.tbl_extend(
|
||||
'force',
|
||||
-- Avoid noise in the logs; we expect failures for these tests.
|
||||
{ NVIM_LOG_FILE = testlog },
|
||||
env_ or {}
|
||||
),
|
||||
})
|
||||
:wait()
|
||||
end, cmd, env) --[[@as vim.SystemCompleted]]
|
||||
end
|
||||
|
||||
local cmd = vim.list_extend({ n.nvim_prog, '+qall!', '--headless' }, args)
|
||||
local r = run(cmd)
|
||||
eq(1, r.code)
|
||||
matches(expected, (r.stderr .. r.stdout):gsub('\\n', ' '))
|
||||
|
||||
if is_os('win') then
|
||||
return -- On Windows, output without --headless is garbage.
|
||||
end
|
||||
table.remove(cmd, 3) -- Remove '--headless'.
|
||||
assert(not vim.tbl_contains(cmd, '--headless'))
|
||||
r = run(cmd)
|
||||
eq(1, r.code)
|
||||
matches(expected, (r.stderr .. r.stdout):gsub('\\n', ' '))
|
||||
end
|
||||
|
||||
it('validates', function()
|
||||
clear { env = { NVIM_LOG_FILE = testlog } }
|
||||
|
||||
-- Tests args with and without "--headless".
|
||||
local function _test(args, expected)
|
||||
local function run(cmd)
|
||||
return n.exec_lua(function(cmd_)
|
||||
return vim
|
||||
.system(cmd_, {
|
||||
text = true,
|
||||
env = {
|
||||
-- Avoid noise in the logs; we expect failures for these tests.
|
||||
NVIM_LOG_FILE = testlog,
|
||||
},
|
||||
})
|
||||
:wait()
|
||||
end, cmd) --[[@as vim.SystemCompleted]]
|
||||
end
|
||||
|
||||
local cmd = vim.list_extend({ n.nvim_prog, '+qall!', '--headless' }, args)
|
||||
local r = run(cmd)
|
||||
eq(1, r.code)
|
||||
matches(expected, (r.stderr .. r.stdout):gsub('\\n', ' '))
|
||||
|
||||
if is_os('win') then
|
||||
return -- On Windows, output without --headless is garbage.
|
||||
end
|
||||
table.remove(cmd, 3) -- Remove '--headless'.
|
||||
assert(not vim.tbl_contains(cmd, '--headless'))
|
||||
r = run(cmd)
|
||||
eq(1, r.code)
|
||||
matches(expected, (r.stderr .. r.stdout):gsub('\\n', ' '))
|
||||
end
|
||||
local in_use = n.eval('v:servername') ---@type string Address already used by another server.
|
||||
|
||||
t.assert_nolog('Failed to start server', testlog, 100)
|
||||
t.assert_nolog('Host lookup failed', testlog, 100)
|
||||
|
||||
_test({ '--listen' }, 'nvim.*: Argument missing after: "%-%-listen"')
|
||||
_test({ '--listen2' }, 'nvim.*: Garbage after option argument: "%-%-listen2"')
|
||||
_test({ '--listen', n.eval('v:servername') }, 'nvim.*: Failed to %-%-listen: ".* already .*"')
|
||||
_test({ '--listen', '/' }, 'nvim.*: Failed to %-%-listen: ".*"')
|
||||
_test({ '--listen' }, nil, 'nvim.*: Argument missing after: "%-%-listen"')
|
||||
_test({ '--listen2' }, nil, 'nvim.*: Garbage after option argument: "%-%-listen2"')
|
||||
_test(
|
||||
{ '--listen', in_use },
|
||||
nil,
|
||||
('nvim.*: Failed to %%-%%-listen: [^:]+ already [^:]+: "%s"'):format(vim.pesc(in_use))
|
||||
)
|
||||
_test({ '--listen', '/' }, nil, 'nvim.*: Failed to %-%-listen: [^:]+: "/"')
|
||||
_test(
|
||||
{ '--listen', 'https://example.com' },
|
||||
('nvim.*: Failed to %%-%%-listen: "%s"'):format(
|
||||
nil,
|
||||
('nvim.*: Failed to %%-%%-listen: %s: "https://example.com"'):format(
|
||||
is_os('mac') and 'unknown node or service' or 'service not available for socket type'
|
||||
)
|
||||
)
|
||||
|
||||
t.assert_log('Failed to start server', testlog, 100)
|
||||
t.assert_log('Host lookup failed', testlog, 100)
|
||||
|
||||
_test(
|
||||
{},
|
||||
{ NVIM_LISTEN_ADDRESS = in_use },
|
||||
('nvim.*: Failed $NVIM_LISTEN_ADDRESS: [^:]+ already [^:]+: "%s"'):format(vim.pesc(in_use))
|
||||
)
|
||||
_test({}, { NVIM_LISTEN_ADDRESS = '/' }, 'nvim.*: Failed $NVIM_LISTEN_ADDRESS: [^:]+: "/"')
|
||||
_test(
|
||||
{},
|
||||
{ NVIM_LISTEN_ADDRESS = 'https://example.com' },
|
||||
('nvim.*: Failed $NVIM_LISTEN_ADDRESS: %s: "https://example.com"'):format(
|
||||
is_os('mac') and 'unknown node or service' or 'service not available for socket type'
|
||||
)
|
||||
)
|
||||
end)
|
||||
|
||||
it('sets v:servername, overrides $NVIM_LISTEN_ADDRESS', function()
|
||||
@ -235,6 +273,6 @@ describe('startup --listen', function()
|
||||
|
||||
-- Address without slashes is a "name" which is appended to a generated path. #8519
|
||||
clear({ args = { '--listen', 'test-name' } })
|
||||
matches([[.*[/\\]test%-name[^/\\]*]], api.nvim_get_vvar('servername'))
|
||||
matches([[[/\\]test%-name[^/\\]*]], api.nvim_get_vvar('servername'))
|
||||
end)
|
||||
end)
|
@ -27,7 +27,6 @@ local sleep = vim.uv.sleep
|
||||
local startswith = vim.startswith
|
||||
local write_file = t.write_file
|
||||
local api = n.api
|
||||
local alter_slashes = n.alter_slashes
|
||||
local is_os = t.is_os
|
||||
local dedent = t.dedent
|
||||
local tbl_map = vim.tbl_map
|
||||
@ -40,22 +39,15 @@ local testlog = 'Xtest-startupspec-log'
|
||||
describe('startup', function()
|
||||
it('--clean', function()
|
||||
clear()
|
||||
ok(
|
||||
string.find(
|
||||
alter_slashes(api.nvim_get_option_value('runtimepath', {})),
|
||||
fn.stdpath('config'),
|
||||
1,
|
||||
true
|
||||
) ~= nil
|
||||
matches(
|
||||
vim.pesc(t.fix_slashes(fn.stdpath('config'))),
|
||||
t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
)
|
||||
|
||||
clear('--clean')
|
||||
ok(
|
||||
string.find(
|
||||
alter_slashes(api.nvim_get_option_value('runtimepath', {})),
|
||||
fn.stdpath('config'),
|
||||
1,
|
||||
true
|
||||
) == nil
|
||||
not t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
:match(vim.pesc(t.fix_slashes(fn.stdpath('config'))))
|
||||
)
|
||||
end)
|
||||
|
||||
|
@ -170,7 +170,7 @@ describe(':mksession', function()
|
||||
skip(is_os('win'), 'causes rmdir() to fail')
|
||||
|
||||
local cwd_dir = fn.fnamemodify('.', ':p:~'):gsub([[[\/]*$]], '')
|
||||
cwd_dir = cwd_dir:gsub([[\]], '/') -- :mksession always uses unix slashes.
|
||||
cwd_dir = t.fix_slashes(cwd_dir) -- :mksession always uses unix slashes.
|
||||
local session_path = cwd_dir .. '/' .. session_file
|
||||
|
||||
command('cd ' .. tab_dir)
|
||||
|
@ -217,7 +217,7 @@ describe('URI methods', function()
|
||||
]],
|
||||
file
|
||||
)
|
||||
local expected_uri = 'file:///' .. file:gsub('\\', '/')
|
||||
local expected_uri = 'file:///' .. t.fix_slashes(file)
|
||||
eq(expected_uri, exec_lua(test_case))
|
||||
os.remove(file)
|
||||
end)
|
||||
|
@ -1071,42 +1071,28 @@ describe('lua stdlib', function()
|
||||
]])
|
||||
)
|
||||
|
||||
-- Fix github issue #23654
|
||||
ok(exec_lua([[
|
||||
local a = { sub = { [1] = 'a' } }
|
||||
local b = { sub = { b = 'a' } }
|
||||
local a = { sub = { 'a', 'b' } }
|
||||
local b = { sub = { 'b', 'c' } }
|
||||
local c = vim.tbl_deep_extend('force', a, b)
|
||||
return vim.deep_equal(c, { sub = { [1] = 'a', b = 'a' } })
|
||||
return vim.deep_equal(c, { sub = { 'b', 'c' } })
|
||||
]]))
|
||||
|
||||
matches(
|
||||
'invalid "behavior": nil',
|
||||
pcall_err(
|
||||
exec_lua,
|
||||
[[
|
||||
return vim.tbl_deep_extend()
|
||||
]]
|
||||
)
|
||||
)
|
||||
matches('invalid "behavior": nil', pcall_err(exec_lua, [[return vim.tbl_deep_extend()]]))
|
||||
|
||||
matches(
|
||||
'wrong number of arguments %(given 1, expected at least 3%)',
|
||||
pcall_err(
|
||||
exec_lua,
|
||||
[[
|
||||
return vim.tbl_deep_extend("keep")
|
||||
]]
|
||||
)
|
||||
pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep")]])
|
||||
)
|
||||
|
||||
matches(
|
||||
'wrong number of arguments %(given 2, expected at least 3%)',
|
||||
pcall_err(
|
||||
exec_lua,
|
||||
[[
|
||||
return vim.tbl_deep_extend("keep", {})
|
||||
]]
|
||||
)
|
||||
pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep", {})]])
|
||||
)
|
||||
|
||||
matches(
|
||||
'after the second argument%: expected table, got number',
|
||||
pcall_err(exec_lua, [[return vim.tbl_deep_extend("keep", {}, 42)]])
|
||||
)
|
||||
end)
|
||||
|
||||
|
@ -22,7 +22,7 @@ describe("'autochdir'", function()
|
||||
end)
|
||||
|
||||
it('is not overwritten by getwinvar() call #17609', function()
|
||||
local curdir = vim.uv.cwd():gsub('\\', '/')
|
||||
local curdir = t.fix_slashes(vim.uv.cwd())
|
||||
local dir_a = curdir .. '/Xtest-functional-options-autochdir.dir_a'
|
||||
local dir_b = curdir .. '/Xtest-functional-options-autochdir.dir_b'
|
||||
mkdir(dir_a)
|
||||
|
@ -23,7 +23,6 @@ local insert = n.insert
|
||||
local neq = t.neq
|
||||
local mkdir = t.mkdir
|
||||
local rmdir = n.rmdir
|
||||
local alter_slashes = n.alter_slashes
|
||||
local tbl_contains = vim.tbl_contains
|
||||
local expect_exit = n.expect_exit
|
||||
local check_close = n.check_close
|
||||
@ -262,7 +261,7 @@ describe('startup defaults', function()
|
||||
NVIM_LOG_FILE = '', -- Empty is invalid.
|
||||
},
|
||||
})
|
||||
eq(xdgstatedir .. '/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
|
||||
eq(xdgstatedir .. '/log', t.fix_slashes(eval('$NVIM_LOG_FILE')))
|
||||
end)
|
||||
|
||||
it('defaults to stdpath("log")/log if invalid', function()
|
||||
@ -273,7 +272,7 @@ describe('startup defaults', function()
|
||||
NVIM_LOG_FILE = '.', -- Any directory is invalid.
|
||||
},
|
||||
})
|
||||
eq(xdgstatedir .. '/log', string.gsub(eval('$NVIM_LOG_FILE'), '\\', '/'))
|
||||
eq(xdgstatedir .. '/log', t.fix_slashes(eval('$NVIM_LOG_FILE')))
|
||||
-- Avoid "failed to open $NVIM_LOG_FILE" noise in test output.
|
||||
expect_exit(command, 'qall!')
|
||||
end)
|
||||
@ -383,69 +382,69 @@ describe('XDG defaults', function()
|
||||
|
||||
eq(
|
||||
(
|
||||
(
|
||||
t.fix_slashes(
|
||||
root_path
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. (',' .. root_path .. '/c/nvim')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. (',' .. root_path .. '/C/nvim/site')
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. (',' .. root_path .. '/C/nvim/site/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. (',' .. root_path .. '/c/nvim/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim/after'
|
||||
):gsub('\\', '/')
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. (',' .. root_path .. '/c/nvim')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. (',' .. root_path .. '/C/nvim/site')
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. (',' .. root_path .. '/C/nvim/site/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. (',' .. root_path .. '/c/nvim/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim/after'
|
||||
)
|
||||
),
|
||||
(api.nvim_get_option_value('runtimepath', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
)
|
||||
command('set runtimepath&')
|
||||
command('set backupdir&')
|
||||
@ -454,85 +453,85 @@ describe('XDG defaults', function()
|
||||
command('set viewdir&')
|
||||
eq(
|
||||
(
|
||||
(
|
||||
t.fix_slashes(
|
||||
root_path
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. (',' .. root_path .. '/c/nvim')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. (',' .. root_path .. '/C/nvim/site')
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. (',' .. root_path .. '/C/nvim/site/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. (',' .. root_path .. '/c/nvim/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim/after'
|
||||
):gsub('\\', '/')
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim'
|
||||
.. (',' .. root_path .. '/c/nvim')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site'
|
||||
.. (',' .. root_path .. '/C/nvim/site')
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. (',' .. root_path .. '/C/nvim/site/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/B'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/A'):rep(2048)
|
||||
.. '/nvim/site/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/X'):rep(4096)
|
||||
.. '/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. (',' .. root_path .. '/c/nvim/after')
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/b'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/a'):rep(2048)
|
||||
.. '/nvim/after'
|
||||
.. ','
|
||||
.. root_path
|
||||
.. ('/x'):rep(4096)
|
||||
.. '/nvim/after'
|
||||
)
|
||||
),
|
||||
(api.nvim_get_option_value('runtimepath', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
)
|
||||
eq(
|
||||
'.,' .. root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/backup//',
|
||||
(api.nvim_get_option_value('backupdir', {}):gsub('\\', '/'))
|
||||
t.fix_slashes(api.nvim_get_option_value('backupdir', {}))
|
||||
)
|
||||
eq(
|
||||
root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/swap//',
|
||||
(api.nvim_get_option_value('directory', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('directory', {}))
|
||||
)
|
||||
eq(
|
||||
root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/undo//',
|
||||
(api.nvim_get_option_value('undodir', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('undodir', {}))
|
||||
)
|
||||
eq(
|
||||
root_path .. ('/X'):rep(4096) .. '/' .. state_dir .. '/view//',
|
||||
(api.nvim_get_option_value('viewdir', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('viewdir', {}))
|
||||
)
|
||||
end)
|
||||
end)
|
||||
@ -574,26 +573,26 @@ describe('XDG defaults', function()
|
||||
local vimruntime, libdir = vimruntime_and_libdir()
|
||||
eq(
|
||||
(
|
||||
(
|
||||
t.fix_slashes(
|
||||
'$XDG_DATA_HOME/nvim'
|
||||
.. ',$XDG_DATA_DIRS/nvim'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site'
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. ',$XDG_DATA_DIRS/nvim/after'
|
||||
.. ',$XDG_DATA_HOME/nvim/after'
|
||||
):gsub('\\', '/')
|
||||
.. ',$XDG_DATA_DIRS/nvim'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site'
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. ',$XDG_DATA_DIRS/nvim/after'
|
||||
.. ',$XDG_DATA_HOME/nvim/after'
|
||||
)
|
||||
),
|
||||
(api.nvim_get_option_value('runtimepath', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
)
|
||||
command('set runtimepath&')
|
||||
command('set backupdir&')
|
||||
@ -602,8 +601,47 @@ describe('XDG defaults', function()
|
||||
command('set viewdir&')
|
||||
eq(
|
||||
(
|
||||
(
|
||||
t.fix_slashes(
|
||||
'$XDG_DATA_HOME/nvim'
|
||||
.. ',$XDG_DATA_DIRS/nvim'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site'
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. ',$XDG_DATA_DIRS/nvim/after'
|
||||
.. ',$XDG_DATA_HOME/nvim/after'
|
||||
)
|
||||
),
|
||||
t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
)
|
||||
eq(
|
||||
('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
|
||||
t.fix_slashes(api.nvim_get_option_value('backupdir', {}))
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
|
||||
t.fix_slashes(api.nvim_get_option_value('directory', {}))
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
|
||||
t.fix_slashes(api.nvim_get_option_value('undodir', {}))
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
|
||||
t.fix_slashes(api.nvim_get_option_value('viewdir', {}))
|
||||
)
|
||||
command('set all&')
|
||||
eq(
|
||||
t.fix_slashes(
|
||||
'$XDG_DATA_HOME/nvim'
|
||||
.. ',$XDG_DATA_DIRS/nvim'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
@ -619,63 +657,24 @@ describe('XDG defaults', function()
|
||||
.. '/site/after'
|
||||
.. ',$XDG_DATA_DIRS/nvim/after'
|
||||
.. ',$XDG_DATA_HOME/nvim/after'
|
||||
):gsub('\\', '/')
|
||||
),
|
||||
(api.nvim_get_option_value('runtimepath', {})):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('runtimepath', {}))
|
||||
)
|
||||
eq(
|
||||
('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
|
||||
api.nvim_get_option_value('backupdir', {}):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('backupdir', {}))
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
|
||||
api.nvim_get_option_value('directory', {}):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('directory', {}))
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
|
||||
api.nvim_get_option_value('undodir', {}):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('undodir', {}))
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
|
||||
api.nvim_get_option_value('viewdir', {}):gsub('\\', '/')
|
||||
)
|
||||
command('set all&')
|
||||
eq(
|
||||
(
|
||||
'$XDG_DATA_HOME/nvim'
|
||||
.. ',$XDG_DATA_DIRS/nvim'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site'
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site'
|
||||
.. ','
|
||||
.. vimruntime
|
||||
.. ','
|
||||
.. libdir
|
||||
.. ',$XDG_CONFIG_DIRS/nvim/site/after'
|
||||
.. ',$XDG_CONFIG_HOME/'
|
||||
.. data_dir
|
||||
.. '/site/after'
|
||||
.. ',$XDG_DATA_DIRS/nvim/after'
|
||||
.. ',$XDG_DATA_HOME/nvim/after'
|
||||
):gsub('\\', '/'),
|
||||
(api.nvim_get_option_value('runtimepath', {})):gsub('\\', '/')
|
||||
)
|
||||
eq(
|
||||
('.,$XDG_CONFIG_HOME/' .. state_dir .. '/backup//'),
|
||||
api.nvim_get_option_value('backupdir', {}):gsub('\\', '/')
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/swap//'),
|
||||
api.nvim_get_option_value('directory', {}):gsub('\\', '/')
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/undo//'),
|
||||
api.nvim_get_option_value('undodir', {}):gsub('\\', '/')
|
||||
)
|
||||
eq(
|
||||
('$XDG_CONFIG_HOME/' .. state_dir .. '/view//'),
|
||||
api.nvim_get_option_value('viewdir', {}):gsub('\\', '/')
|
||||
t.fix_slashes(api.nvim_get_option_value('viewdir', {}))
|
||||
)
|
||||
eq(nil, (fn.tempname()):match('XDG_RUNTIME_DIR'))
|
||||
end)
|
||||
@ -915,7 +914,7 @@ describe('stdpath()', function()
|
||||
assert_alive() -- Check for crash. #8393
|
||||
end)
|
||||
|
||||
it('supports $NVIM_APPNAME', function()
|
||||
it('$NVIM_APPNAME', function()
|
||||
local appname = 'NVIM_APPNAME_TEST' .. ('_'):rep(106)
|
||||
clear({ env = { NVIM_APPNAME = appname, NVIM_LOG_FILE = testlog } })
|
||||
eq(appname, fn.fnamemodify(fn.stdpath('config'), ':t'))
|
||||
@ -939,7 +938,7 @@ describe('stdpath()', function()
|
||||
local child = vim.fn.jobstart({ vim.v.progpath, '--clean', '--headless', '--listen', 'x', '+qall!' }, { env = { NVIM_APPNAME = %q } })
|
||||
return vim.fn.jobwait({ child }, %d)[1]
|
||||
]],
|
||||
alter_slashes(testAppname),
|
||||
testAppname,
|
||||
3000
|
||||
)
|
||||
eq(expected_exitcode, exec_lua(lua_code))
|
||||
@ -957,24 +956,43 @@ describe('stdpath()', function()
|
||||
test_appname('a/b\\c', 0)
|
||||
end)
|
||||
|
||||
it('$NVIM_APPNAME relative path', function()
|
||||
local tmpdir = t.tmpname(false)
|
||||
t.mkdir(tmpdir)
|
||||
|
||||
clear({
|
||||
args_rm = { '--listen' },
|
||||
env = {
|
||||
NVIM_APPNAME = 'relative/appname',
|
||||
NVIM_LOG_FILE = testlog,
|
||||
TMPDIR = tmpdir,
|
||||
},
|
||||
})
|
||||
|
||||
t.matches(vim.pesc(tmpdir), t.fix_slashes(fn.tempname()))
|
||||
t.assert_nolog('tempdir', testlog, 100)
|
||||
t.assert_nolog('TMPDIR', testlog, 100)
|
||||
t.matches([=[[/\\]relative%-appname.[^/\\]+]=], api.nvim_get_vvar('servername'))
|
||||
end)
|
||||
|
||||
describe('returns a String', function()
|
||||
describe('with "config"', function()
|
||||
it('knows XDG_CONFIG_HOME', function()
|
||||
clear({
|
||||
env = {
|
||||
XDG_CONFIG_HOME = alter_slashes('/home/docwhat/.config'),
|
||||
XDG_CONFIG_HOME = '/home/docwhat/.config',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('/home/docwhat/.config/nvim'), fn.stdpath('config'))
|
||||
eq('/home/docwhat/.config/nvim', t.fix_slashes(fn.stdpath('config')))
|
||||
end)
|
||||
|
||||
it('handles changes during runtime', function()
|
||||
clear({ env = {
|
||||
XDG_CONFIG_HOME = alter_slashes('/home/original'),
|
||||
XDG_CONFIG_HOME = '/home/original',
|
||||
} })
|
||||
eq(alter_slashes('/home/original/nvim'), fn.stdpath('config'))
|
||||
command("let $XDG_CONFIG_HOME='" .. alter_slashes('/home/new') .. "'")
|
||||
eq(alter_slashes('/home/new/nvim'), fn.stdpath('config'))
|
||||
eq('/home/original/nvim', t.fix_slashes(fn.stdpath('config')))
|
||||
command("let $XDG_CONFIG_HOME='/home/new'")
|
||||
eq('/home/new/nvim', t.fix_slashes(fn.stdpath('config')))
|
||||
end)
|
||||
|
||||
it("doesn't expand $VARIABLES", function()
|
||||
@ -984,32 +1002,32 @@ describe('stdpath()', function()
|
||||
VARIABLES = 'this-should-not-happen',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('$VARIABLES/nvim'), fn.stdpath('config'))
|
||||
eq('$VARIABLES/nvim', t.fix_slashes(fn.stdpath('config')))
|
||||
end)
|
||||
|
||||
it("doesn't expand ~/", function()
|
||||
clear({ env = {
|
||||
XDG_CONFIG_HOME = alter_slashes('~/frobnitz'),
|
||||
XDG_CONFIG_HOME = '~/frobnitz',
|
||||
} })
|
||||
eq(alter_slashes('~/frobnitz/nvim'), fn.stdpath('config'))
|
||||
eq('~/frobnitz/nvim', t.fix_slashes(fn.stdpath('config')))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('with "data"', function()
|
||||
it('knows XDG_DATA_HOME', function()
|
||||
clear({ env = {
|
||||
XDG_DATA_HOME = alter_slashes('/home/docwhat/.local'),
|
||||
XDG_DATA_HOME = '/home/docwhat/.local',
|
||||
} })
|
||||
eq(alter_slashes('/home/docwhat/.local/' .. datadir), fn.stdpath('data'))
|
||||
eq('/home/docwhat/.local/' .. datadir, t.fix_slashes(fn.stdpath('data')))
|
||||
end)
|
||||
|
||||
it('handles changes during runtime', function()
|
||||
clear({ env = {
|
||||
XDG_DATA_HOME = alter_slashes('/home/original'),
|
||||
XDG_DATA_HOME = '/home/original',
|
||||
} })
|
||||
eq(alter_slashes('/home/original/' .. datadir), fn.stdpath('data'))
|
||||
command("let $XDG_DATA_HOME='" .. alter_slashes('/home/new') .. "'")
|
||||
eq(alter_slashes('/home/new/' .. datadir), fn.stdpath('data'))
|
||||
eq('/home/original/' .. datadir, t.fix_slashes(fn.stdpath('data')))
|
||||
command("let $XDG_DATA_HOME='/home/new'")
|
||||
eq('/home/new/' .. datadir, t.fix_slashes(fn.stdpath('data')))
|
||||
end)
|
||||
|
||||
it("doesn't expand $VARIABLES", function()
|
||||
@ -1019,14 +1037,14 @@ describe('stdpath()', function()
|
||||
VARIABLES = 'this-should-not-happen',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('$VARIABLES/' .. datadir), fn.stdpath('data'))
|
||||
eq('$VARIABLES/' .. datadir, t.fix_slashes(fn.stdpath('data')))
|
||||
end)
|
||||
|
||||
it("doesn't expand ~/", function()
|
||||
clear({ env = {
|
||||
XDG_DATA_HOME = alter_slashes('~/frobnitz'),
|
||||
XDG_DATA_HOME = '~/frobnitz',
|
||||
} })
|
||||
eq(alter_slashes('~/frobnitz/' .. datadir), fn.stdpath('data'))
|
||||
eq('~/frobnitz/' .. datadir, t.fix_slashes(fn.stdpath('data')))
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -1034,19 +1052,19 @@ describe('stdpath()', function()
|
||||
it('knows XDG_STATE_HOME', function()
|
||||
clear({
|
||||
env = {
|
||||
XDG_STATE_HOME = alter_slashes('/home/docwhat/.local'),
|
||||
XDG_STATE_HOME = '/home/docwhat/.local',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('/home/docwhat/.local/' .. statedir), fn.stdpath('state'))
|
||||
eq('/home/docwhat/.local/' .. statedir, t.fix_slashes(fn.stdpath('state')))
|
||||
end)
|
||||
|
||||
it('handles changes during runtime', function()
|
||||
clear({ env = {
|
||||
XDG_STATE_HOME = alter_slashes('/home/original'),
|
||||
XDG_STATE_HOME = '/home/original',
|
||||
} })
|
||||
eq(alter_slashes('/home/original/' .. statedir), fn.stdpath('state'))
|
||||
command("let $XDG_STATE_HOME='" .. alter_slashes('/home/new') .. "'")
|
||||
eq(alter_slashes('/home/new/' .. statedir), fn.stdpath('state'))
|
||||
eq('/home/original/' .. statedir, t.fix_slashes(fn.stdpath('state')))
|
||||
command("let $XDG_STATE_HOME='" .. '/home/new' .. "'")
|
||||
eq('/home/new/' .. statedir, t.fix_slashes(fn.stdpath('state')))
|
||||
end)
|
||||
|
||||
it("doesn't expand $VARIABLES", function()
|
||||
@ -1056,14 +1074,14 @@ describe('stdpath()', function()
|
||||
VARIABLES = 'this-should-not-happen',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('$VARIABLES/' .. statedir), fn.stdpath('state'))
|
||||
eq('$VARIABLES/' .. statedir, t.fix_slashes(fn.stdpath('state')))
|
||||
end)
|
||||
|
||||
it("doesn't expand ~/", function()
|
||||
clear({ env = {
|
||||
XDG_STATE_HOME = alter_slashes('~/frobnitz'),
|
||||
XDG_STATE_HOME = '~/frobnitz',
|
||||
} })
|
||||
eq(alter_slashes('~/frobnitz/' .. statedir), fn.stdpath('state'))
|
||||
eq('~/frobnitz/' .. statedir, t.fix_slashes(fn.stdpath('state')))
|
||||
end)
|
||||
end)
|
||||
|
||||
@ -1071,19 +1089,19 @@ describe('stdpath()', function()
|
||||
it('knows XDG_CACHE_HOME', function()
|
||||
clear({
|
||||
env = {
|
||||
XDG_CACHE_HOME = alter_slashes('/home/docwhat/.cache'),
|
||||
XDG_CACHE_HOME = '/home/docwhat/.cache',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('/home/docwhat/.cache/nvim'), fn.stdpath('cache'))
|
||||
eq('/home/docwhat/.cache/nvim', t.fix_slashes(fn.stdpath('cache')))
|
||||
end)
|
||||
|
||||
it('handles changes during runtime', function()
|
||||
clear({ env = {
|
||||
XDG_CACHE_HOME = alter_slashes('/home/original'),
|
||||
XDG_CACHE_HOME = '/home/original',
|
||||
} })
|
||||
eq(alter_slashes('/home/original/nvim'), fn.stdpath('cache'))
|
||||
command("let $XDG_CACHE_HOME='" .. alter_slashes('/home/new') .. "'")
|
||||
eq(alter_slashes('/home/new/nvim'), fn.stdpath('cache'))
|
||||
eq('/home/original/nvim', t.fix_slashes(fn.stdpath('cache')))
|
||||
command("let $XDG_CACHE_HOME='" .. '/home/new' .. "'")
|
||||
eq('/home/new/nvim', t.fix_slashes(fn.stdpath('cache')))
|
||||
end)
|
||||
|
||||
it("doesn't expand $VARIABLES", function()
|
||||
@ -1093,14 +1111,14 @@ describe('stdpath()', function()
|
||||
VARIABLES = 'this-should-not-happen',
|
||||
},
|
||||
})
|
||||
eq(alter_slashes('$VARIABLES/nvim'), fn.stdpath('cache'))
|
||||
eq('$VARIABLES/nvim', t.fix_slashes(fn.stdpath('cache')))
|
||||
end)
|
||||
|
||||
it("doesn't expand ~/", function()
|
||||
clear({ env = {
|
||||
XDG_CACHE_HOME = alter_slashes('~/frobnitz'),
|
||||
XDG_CACHE_HOME = '~/frobnitz',
|
||||
} })
|
||||
eq(alter_slashes('~/frobnitz/nvim'), fn.stdpath('cache'))
|
||||
eq('~/frobnitz/nvim', t.fix_slashes(fn.stdpath('cache')))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
@ -1114,6 +1132,7 @@ describe('stdpath()', function()
|
||||
HOMEDRIVE = 'C:',
|
||||
HOMEPATH = '\\Users\\docwhat',
|
||||
LOCALAPPDATA = 'C:\\Users\\docwhat\\AppData\\Local',
|
||||
NVIM_LOG_FILE = testlog,
|
||||
TEMP = 'C:\\Users\\docwhat\\AppData\\Local\\Temp',
|
||||
TMPDIR = 'C:\\Users\\docwhat\\AppData\\Local\\Temp',
|
||||
TMP = 'C:\\Users\\docwhat\\AppData\\Local\\Temp',
|
||||
@ -1124,6 +1143,7 @@ describe('stdpath()', function()
|
||||
HOMEDRIVE = 'HOMEDRIVE-should-be-ignored',
|
||||
HOMEPATH = 'HOMEPATH-should-be-ignored',
|
||||
LOCALAPPDATA = 'LOCALAPPDATA-should-be-ignored',
|
||||
NVIM_LOG_FILE = testlog,
|
||||
TEMP = 'TEMP-should-be-ignored',
|
||||
TMPDIR = 'TMPDIR-should-be-ignored',
|
||||
TMP = 'TMP-should-be-ignored',
|
||||
@ -1147,12 +1167,18 @@ describe('stdpath()', function()
|
||||
describe(msg, function()
|
||||
it('set via system', function()
|
||||
set_paths_via_system(env_var_name, paths)
|
||||
eq(expected_paths, fn.stdpath(stdpath_arg))
|
||||
eq(expected_paths, t.fix_slashes(fn.stdpath(stdpath_arg)))
|
||||
if not is_os('win') then
|
||||
assert_log('$TMPDIR tempdir not a directory.*TMPDIR%-should%-be%-ignored', testlog, 100)
|
||||
end
|
||||
end)
|
||||
|
||||
it('set at runtime', function()
|
||||
set_paths_at_runtime(env_var_name, paths)
|
||||
eq(expected_paths, fn.stdpath(stdpath_arg))
|
||||
eq(expected_paths, t.fix_slashes(fn.stdpath(stdpath_arg)))
|
||||
if not is_os('win') then
|
||||
assert_log('$TMPDIR tempdir not a directory.*TMPDIR%-should%-be%-ignored', testlog, 100)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@ -1163,10 +1189,10 @@ describe('stdpath()', function()
|
||||
'config_dirs',
|
||||
'XDG_CONFIG_DIRS',
|
||||
{
|
||||
alter_slashes('/home/docwhat/.config'),
|
||||
t.fix_slashes('/home/docwhat/.config'),
|
||||
},
|
||||
{
|
||||
alter_slashes('/home/docwhat/.config/nvim'),
|
||||
t.fix_slashes('/home/docwhat/.config/nvim'),
|
||||
}
|
||||
)
|
||||
|
||||
@ -1175,12 +1201,12 @@ describe('stdpath()', function()
|
||||
'config_dirs',
|
||||
'XDG_CONFIG_DIRS',
|
||||
{
|
||||
alter_slashes('/home/docwhat/.config'),
|
||||
alter_slashes('/etc/config'),
|
||||
t.fix_slashes('/home/docwhat/.config'),
|
||||
t.fix_slashes('/etc/config'),
|
||||
},
|
||||
{
|
||||
alter_slashes('/home/docwhat/.config/nvim'),
|
||||
alter_slashes('/etc/config/nvim'),
|
||||
t.fix_slashes('/home/docwhat/.config/nvim'),
|
||||
t.fix_slashes('/etc/config/nvim'),
|
||||
}
|
||||
)
|
||||
|
||||
@ -1190,25 +1216,25 @@ describe('stdpath()', function()
|
||||
'XDG_CONFIG_DIRS',
|
||||
{ '$HOME', '$TMP' },
|
||||
{
|
||||
alter_slashes('$HOME/nvim'),
|
||||
alter_slashes('$TMP/nvim'),
|
||||
t.fix_slashes('$HOME/nvim'),
|
||||
t.fix_slashes('$TMP/nvim'),
|
||||
}
|
||||
)
|
||||
|
||||
behaves_like_dir_list_env("doesn't expand ~/", 'config_dirs', 'XDG_CONFIG_DIRS', {
|
||||
alter_slashes('~/.oldconfig'),
|
||||
alter_slashes('~/.olderconfig'),
|
||||
t.fix_slashes('~/.oldconfig'),
|
||||
t.fix_slashes('~/.olderconfig'),
|
||||
}, {
|
||||
alter_slashes('~/.oldconfig/nvim'),
|
||||
alter_slashes('~/.olderconfig/nvim'),
|
||||
t.fix_slashes('~/.oldconfig/nvim'),
|
||||
t.fix_slashes('~/.olderconfig/nvim'),
|
||||
})
|
||||
end)
|
||||
|
||||
describe('with "data_dirs"', function()
|
||||
behaves_like_dir_list_env('knows XDG_DATA_DIRS with one path', 'data_dirs', 'XDG_DATA_DIRS', {
|
||||
alter_slashes('/home/docwhat/.data'),
|
||||
t.fix_slashes('/home/docwhat/.data'),
|
||||
}, {
|
||||
alter_slashes('/home/docwhat/.data/nvim'),
|
||||
t.fix_slashes('/home/docwhat/.data/nvim'),
|
||||
})
|
||||
|
||||
behaves_like_dir_list_env(
|
||||
@ -1216,12 +1242,12 @@ describe('stdpath()', function()
|
||||
'data_dirs',
|
||||
'XDG_DATA_DIRS',
|
||||
{
|
||||
alter_slashes('/home/docwhat/.data'),
|
||||
alter_slashes('/etc/local'),
|
||||
t.fix_slashes('/home/docwhat/.data'),
|
||||
t.fix_slashes('/etc/local'),
|
||||
},
|
||||
{
|
||||
alter_slashes('/home/docwhat/.data/nvim'),
|
||||
alter_slashes('/etc/local/nvim'),
|
||||
t.fix_slashes('/home/docwhat/.data/nvim'),
|
||||
t.fix_slashes('/etc/local/nvim'),
|
||||
}
|
||||
)
|
||||
|
||||
@ -1231,17 +1257,17 @@ describe('stdpath()', function()
|
||||
'XDG_DATA_DIRS',
|
||||
{ '$HOME', '$TMP' },
|
||||
{
|
||||
alter_slashes('$HOME/nvim'),
|
||||
alter_slashes('$TMP/nvim'),
|
||||
t.fix_slashes('$HOME/nvim'),
|
||||
t.fix_slashes('$TMP/nvim'),
|
||||
}
|
||||
)
|
||||
|
||||
behaves_like_dir_list_env("doesn't expand ~/", 'data_dirs', 'XDG_DATA_DIRS', {
|
||||
alter_slashes('~/.oldconfig'),
|
||||
alter_slashes('~/.olderconfig'),
|
||||
t.fix_slashes('~/.oldconfig'),
|
||||
t.fix_slashes('~/.olderconfig'),
|
||||
}, {
|
||||
alter_slashes('~/.oldconfig/nvim'),
|
||||
alter_slashes('~/.olderconfig/nvim'),
|
||||
t.fix_slashes('~/.oldconfig/nvim'),
|
||||
t.fix_slashes('~/.olderconfig/nvim'),
|
||||
})
|
||||
end)
|
||||
end)
|
||||
|
@ -3607,21 +3607,21 @@ describe('LSP', function()
|
||||
range = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
},
|
||||
selectionRange = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
},
|
||||
uri = 'file:///home/jiangyinzuo/hello.cpp',
|
||||
@ -3651,21 +3651,21 @@ describe('LSP', function()
|
||||
range = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
},
|
||||
selectionRange = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
},
|
||||
uri = 'file:///home/jiangyinzuo/hello.cpp',
|
||||
@ -3679,7 +3679,15 @@ describe('LSP', function()
|
||||
})
|
||||
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
||||
local handler = require 'vim.lsp.handlers'['typeHierarchy/subtypes']
|
||||
handler(nil, clangd_response, { client_id = client_id, bufnr = 1 })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
'class B : public A{};',
|
||||
'class C : public B{};',
|
||||
'class D1 : public C{};',
|
||||
'class D2 : public C{};',
|
||||
'class E : public D1, D2 {};',
|
||||
})
|
||||
handler(nil, clangd_response, { client_id = client_id, bufnr = bufnr })
|
||||
return vim.fn.getqflist()
|
||||
end)
|
||||
|
||||
@ -3689,7 +3697,7 @@ describe('LSP', function()
|
||||
col = 7,
|
||||
end_col = 0,
|
||||
end_lnum = 0,
|
||||
lnum = 10,
|
||||
lnum = 4,
|
||||
module = '',
|
||||
nr = 0,
|
||||
pattern = '',
|
||||
@ -3703,7 +3711,7 @@ describe('LSP', function()
|
||||
col = 7,
|
||||
end_col = 0,
|
||||
end_lnum = 0,
|
||||
lnum = 9,
|
||||
lnum = 3,
|
||||
module = '',
|
||||
nr = 0,
|
||||
pattern = '',
|
||||
@ -3763,7 +3771,15 @@ describe('LSP', function()
|
||||
})
|
||||
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
||||
local handler = require 'vim.lsp.handlers'['typeHierarchy/subtypes']
|
||||
handler(nil, jdtls_response, { client_id = client_id, bufnr = 1 })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
'package mylist;',
|
||||
'',
|
||||
'public class MyList {',
|
||||
' static class Inner extends MyList{}',
|
||||
'~}',
|
||||
})
|
||||
handler(nil, jdtls_response, { client_id = client_id, bufnr = bufnr })
|
||||
return vim.fn.getqflist()
|
||||
end)
|
||||
|
||||
@ -3840,21 +3856,21 @@ describe('LSP', function()
|
||||
range = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
},
|
||||
selectionRange = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 9,
|
||||
line = 3,
|
||||
},
|
||||
},
|
||||
uri = 'file:///home/jiangyinzuo/hello.cpp',
|
||||
@ -3884,21 +3900,21 @@ describe('LSP', function()
|
||||
range = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
},
|
||||
selectionRange = {
|
||||
['end'] = {
|
||||
character = 8,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
start = {
|
||||
character = 6,
|
||||
line = 8,
|
||||
line = 2,
|
||||
},
|
||||
},
|
||||
uri = 'file:///home/jiangyinzuo/hello.cpp',
|
||||
@ -3912,7 +3928,16 @@ describe('LSP', function()
|
||||
})
|
||||
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
||||
local handler = require 'vim.lsp.handlers'['typeHierarchy/supertypes']
|
||||
handler(nil, clangd_response, { client_id = client_id, bufnr = 1 })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
'class B : public A{};',
|
||||
'class C : public B{};',
|
||||
'class D1 : public C{};',
|
||||
'class D2 : public C{};',
|
||||
'class E : public D1, D2 {};',
|
||||
})
|
||||
|
||||
handler(nil, clangd_response, { client_id = client_id, bufnr = bufnr })
|
||||
return vim.fn.getqflist()
|
||||
end)
|
||||
|
||||
@ -3922,7 +3947,7 @@ describe('LSP', function()
|
||||
col = 7,
|
||||
end_col = 0,
|
||||
end_lnum = 0,
|
||||
lnum = 10,
|
||||
lnum = 4,
|
||||
module = '',
|
||||
nr = 0,
|
||||
pattern = '',
|
||||
@ -3936,7 +3961,7 @@ describe('LSP', function()
|
||||
col = 7,
|
||||
end_col = 0,
|
||||
end_lnum = 0,
|
||||
lnum = 9,
|
||||
lnum = 3,
|
||||
module = '',
|
||||
nr = 0,
|
||||
pattern = '',
|
||||
@ -3996,7 +4021,15 @@ describe('LSP', function()
|
||||
})
|
||||
local client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd })
|
||||
local handler = require 'vim.lsp.handlers'['typeHierarchy/supertypes']
|
||||
handler(nil, jdtls_response, { client_id = client_id, bufnr = 1 })
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {
|
||||
'package mylist;',
|
||||
'',
|
||||
'public class MyList {',
|
||||
' static class Inner extends MyList{}',
|
||||
'~}',
|
||||
})
|
||||
handler(nil, jdtls_response, { client_id = client_id, bufnr = bufnr })
|
||||
return vim.fn.getqflist()
|
||||
end)
|
||||
|
||||
|
@ -136,6 +136,50 @@ local function run_tohtml_and_assert(screen, func)
|
||||
screen:expect({ grid = expected.grid, attr_ids = expected.attr_ids })
|
||||
end
|
||||
|
||||
---@param guifont boolean
|
||||
local function test_generates_html(guifont, expect_font)
|
||||
insert([[line]])
|
||||
exec('set termguicolors')
|
||||
local bg = fn.synIDattr(fn.hlID('Normal'), 'bg#', 'gui')
|
||||
local fg = fn.synIDattr(fn.hlID('Normal'), 'fg#', 'gui')
|
||||
local tmpfile = t.tmpname()
|
||||
|
||||
exec_lua(
|
||||
[[
|
||||
local guifont, outfile = ...
|
||||
local html = (guifont
|
||||
and require('tohtml').tohtml(0,{title="title"})
|
||||
or require('tohtml').tohtml(0,{title="title",font={ "dumyfont","anotherfont" }}))
|
||||
vim.fn.writefile(html, outfile)
|
||||
vim.cmd.split(outfile)
|
||||
]],
|
||||
guifont,
|
||||
tmpfile
|
||||
)
|
||||
|
||||
local out_file = api.nvim_buf_get_name(api.nvim_get_current_buf())
|
||||
eq({
|
||||
'<!DOCTYPE html>',
|
||||
'<html>',
|
||||
'<head>',
|
||||
'<meta charset="UTF-8">',
|
||||
'<title>title</title>',
|
||||
('<meta name="colorscheme" content="%s"></meta>'):format(api.nvim_get_var('colors_name')),
|
||||
'<style>',
|
||||
('* {font-family: %s,monospace}'):format(expect_font),
|
||||
('body {background-color: %s; color: %s}'):format(bg, fg),
|
||||
'</style>',
|
||||
'</head>',
|
||||
'<body style="display: flex">',
|
||||
'<pre>',
|
||||
'line',
|
||||
'',
|
||||
'</pre>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
}, fn.readfile(out_file))
|
||||
end
|
||||
|
||||
describe(':TOhtml', function()
|
||||
--- @type test.functional.ui.screen
|
||||
local screen
|
||||
@ -146,41 +190,16 @@ describe(':TOhtml', function()
|
||||
exec('colorscheme default')
|
||||
end)
|
||||
|
||||
it('expected internal html generated', function()
|
||||
insert([[line]])
|
||||
exec('set termguicolors')
|
||||
local bg = fn.synIDattr(fn.hlID('Normal'), 'bg#', 'gui')
|
||||
local fg = fn.synIDattr(fn.hlID('Normal'), 'fg#', 'gui')
|
||||
exec_lua [[
|
||||
local outfile = vim.fn.tempname() .. '.html'
|
||||
local html = require('tohtml').tohtml(0,{title="title",font="dumyfont"})
|
||||
vim.fn.writefile(html, outfile)
|
||||
vim.cmd.split(outfile)
|
||||
]]
|
||||
local out_file = api.nvim_buf_get_name(api.nvim_get_current_buf())
|
||||
eq({
|
||||
'<!DOCTYPE html>',
|
||||
'<html>',
|
||||
'<head>',
|
||||
'<meta charset="UTF-8">',
|
||||
'<title>title</title>',
|
||||
('<meta name="colorscheme" content="%s"></meta>'):format(api.nvim_get_var('colors_name')),
|
||||
'<style>',
|
||||
'* {font-family: dumyfont,monospace}',
|
||||
('body {background-color: %s; color: %s}'):format(bg, fg),
|
||||
'</style>',
|
||||
'</head>',
|
||||
'<body style="display: flex">',
|
||||
'<pre>',
|
||||
'line',
|
||||
'',
|
||||
'</pre>',
|
||||
'</body>',
|
||||
'</html>',
|
||||
}, fn.readfile(out_file))
|
||||
it('generates html with given font', function()
|
||||
test_generates_html(false, '"dumyfont","anotherfont"')
|
||||
end)
|
||||
|
||||
it('expected internal html generated from range', function()
|
||||
it("generates html, respects 'guifont'", function()
|
||||
exec_lua [[vim.o.guifont='Font,Escape\\,comma, Ignore space after comma']]
|
||||
test_generates_html(true, '"Font","Escape,comma","Ignore space after comma"')
|
||||
end)
|
||||
|
||||
it('generates html from range', function()
|
||||
insert([[
|
||||
line1
|
||||
line2
|
||||
@ -218,7 +237,7 @@ describe(':TOhtml', function()
|
||||
}, fn.readfile(out_file))
|
||||
end)
|
||||
|
||||
it('highlight attributes generated', function()
|
||||
it('generates highlight attributes', function()
|
||||
--Make sure to uncomment the attribute in `html_syntax_match()`
|
||||
exec('hi LINE guisp=#00ff00 gui=' .. table.concat({
|
||||
'bold',
|
||||
|
@ -342,7 +342,7 @@ describe(':terminal buffer', function()
|
||||
command('wincmd p')
|
||||
|
||||
-- cwd will be inserted in a file URI, which cannot contain backs
|
||||
local cwd = fn.getcwd():gsub('\\', '/')
|
||||
local cwd = t.fix_slashes(fn.getcwd())
|
||||
local parent = cwd:match('^(.+/)')
|
||||
local expected = '\027]7;file://host' .. parent
|
||||
api.nvim_chan_send(term, string.format('%s\027\\', expected))
|
||||
|
@ -14,8 +14,7 @@ local is_os = t.is_os
|
||||
local ok = t.ok
|
||||
local sleep = uv.sleep
|
||||
|
||||
--- This module uses functions from the context of the test session, i.e. in the context of the
|
||||
--- nvim being tests.
|
||||
--- Functions executing in the current nvim session/process being tested.
|
||||
local M = {}
|
||||
|
||||
local runtime_set = 'set runtimepath^=./build/lib/nvim/'
|
||||
@ -903,26 +902,6 @@ function M.missing_provider(provider)
|
||||
assert(false, 'Unknown provider: ' .. provider)
|
||||
end
|
||||
|
||||
--- @param obj string|table
|
||||
--- @return any
|
||||
function M.alter_slashes(obj)
|
||||
if not is_os('win') then
|
||||
return obj
|
||||
end
|
||||
if type(obj) == 'string' then
|
||||
local ret = obj:gsub('/', '\\')
|
||||
return ret
|
||||
elseif type(obj) == 'table' then
|
||||
--- @cast obj table<any,any>
|
||||
local ret = {} --- @type table<any,any>
|
||||
for k, v in pairs(obj) do
|
||||
ret[k] = M.alter_slashes(v)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
assert(false, 'expected string or table of strings, got ' .. type(obj))
|
||||
end
|
||||
|
||||
local load_factor = 1
|
||||
if t.is_ci() then
|
||||
-- Compute load factor only once (but outside of any tests).
|
||||
|
@ -1344,6 +1344,53 @@ describe('float window', function()
|
||||
|
|
||||
]])
|
||||
end
|
||||
|
||||
--
|
||||
-- floating windows inherit NormalFloat from global-ns.
|
||||
--
|
||||
command('fclose')
|
||||
command('hi NormalFloat guibg=LightRed')
|
||||
api.nvim_open_win(0, false, { relative = 'win', row = 3, col = 3, width = 12, height = 3, style = 'minimal' })
|
||||
api.nvim_set_hl_ns(api.nvim_create_namespace('test1'))
|
||||
if multigrid then
|
||||
screen:expect({
|
||||
grid = [[
|
||||
## grid 1
|
||||
[2:----------------------------------------]|*6
|
||||
[3:----------------------------------------]|
|
||||
## grid 2
|
||||
{14: 1 }^x |
|
||||
{14: 2 }y |
|
||||
{14: 3 } |
|
||||
{0:~ }|*3
|
||||
## grid 3
|
||||
|
|
||||
## grid 5
|
||||
{22:x }|
|
||||
{22:y }|
|
||||
{22: }|
|
||||
]], float_pos={
|
||||
[5] = {1002, "NW", 2, 3, 3, true, 50};
|
||||
}, win_viewport={
|
||||
[2] = {win = 1000, topline = 0, botline = 4, curline = 0, curcol = 0, linecount = 3, sum_scroll_delta = 0};
|
||||
[5] = {win = 1002, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3, sum_scroll_delta = 0};
|
||||
}, win_viewport_margins={
|
||||
[2] = { bottom = 0, left = 0, right = 0, top = 0, win = 1000 },
|
||||
[5] = { bottom = 0, left = 0, right = 0, top = 0, win = 1002 }
|
||||
}})
|
||||
else
|
||||
screen:expect({
|
||||
grid = [[
|
||||
{14: 1 }^x |
|
||||
{14: 2 }y |
|
||||
{14: 3 } |
|
||||
{0:~ }{22:x }{0: }|
|
||||
{0:~ }{22:y }{0: }|
|
||||
{0:~ }{22: }{0: }|
|
||||
|
|
||||
]]
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
it("can use 'minimal' style", function()
|
||||
|
@ -7,11 +7,10 @@ local fnamemodify = n.fn.fnamemodify
|
||||
local getcwd = n.fn.getcwd
|
||||
local command = n.command
|
||||
local write_file = t.write_file
|
||||
local alter_slashes = n.alter_slashes
|
||||
local is_os = t.is_os
|
||||
|
||||
local function eq_slashconvert(expected, got)
|
||||
eq(alter_slashes(expected), alter_slashes(got))
|
||||
eq(t.fix_slashes(expected), t.fix_slashes(got))
|
||||
end
|
||||
|
||||
describe('fnamemodify()', function()
|
||||
|
@ -837,18 +837,73 @@ func Test_breakindent20_list()
|
||||
\ ]
|
||||
let lines = s:screen_lines2(1, 9, 20)
|
||||
call s:compare_lines(expect, lines)
|
||||
|
||||
" check with TABs
|
||||
call setline(1, ["\t1.\tCongress shall make no law",
|
||||
\ "\t2.) Congress shall make no law",
|
||||
\ "\t3.] Congress shall make no law"])
|
||||
setl tabstop=4 list listchars=tab:<->
|
||||
norm! 1gg
|
||||
redraw!
|
||||
let expect = [
|
||||
\ "<-->1.<>Congress ",
|
||||
\ " shall make ",
|
||||
\ " no law ",
|
||||
\ "<-->2.) Congress ",
|
||||
\ " shall make ",
|
||||
\ " no law ",
|
||||
\ "<-->3.] Congress ",
|
||||
\ " shall make ",
|
||||
\ " no law ",
|
||||
\ ]
|
||||
let lines = s:screen_lines2(1, 9, 20)
|
||||
call s:compare_lines(expect, lines)
|
||||
|
||||
setl tabstop=2 nolist
|
||||
redraw!
|
||||
let expect = [
|
||||
\ " 1. Congress ",
|
||||
\ " shall make no ",
|
||||
\ " law ",
|
||||
\ " 2.) Congress ",
|
||||
\ " shall make no ",
|
||||
\ " law ",
|
||||
\ " 3.] Congress ",
|
||||
\ " shall make no ",
|
||||
\ " law ",
|
||||
\ ]
|
||||
let lines = s:screen_lines2(1, 9, 20)
|
||||
call s:compare_lines(expect, lines)
|
||||
|
||||
setl tabstop& list listchars=space:_
|
||||
redraw!
|
||||
let expect = [
|
||||
\ "^I1.^ICongress_ ",
|
||||
\ " shall_make_no_",
|
||||
\ " law ",
|
||||
\ "^I2.)_Congress_ ",
|
||||
\ " shall_make_no_",
|
||||
\ " law ",
|
||||
\ "^I3.]_Congress_ ",
|
||||
\ " shall_make_no_",
|
||||
\ " law ",
|
||||
\ ]
|
||||
let lines = s:screen_lines2(1, 9, 20)
|
||||
call s:compare_lines(expect, lines)
|
||||
|
||||
" check formatlistpat indent with different list levels
|
||||
let &l:flp = '^\s*\*\+\s\+'
|
||||
let &l:flp = '^\s*\(\*\|•\)\+\s\+'
|
||||
setl list&vim listchars&vim
|
||||
%delete _
|
||||
call setline(1, ['* Congress shall make no law',
|
||||
\ '*** Congress shall make no law',
|
||||
\ '••• Congress shall make no law',
|
||||
\ '**** Congress shall make no law'])
|
||||
norm! 1gg
|
||||
redraw!
|
||||
let expect = [
|
||||
\ "* Congress shall ",
|
||||
\ " make no law ",
|
||||
\ "*** Congress shall ",
|
||||
\ "••• Congress shall ",
|
||||
\ " make no law ",
|
||||
\ "**** Congress shall ",
|
||||
\ " make no law ",
|
||||
@ -864,7 +919,7 @@ func Test_breakindent20_list()
|
||||
let expect = [
|
||||
\ "* Congress shall ",
|
||||
\ "> make no law ",
|
||||
\ "*** Congress shall ",
|
||||
\ "••• Congress shall ",
|
||||
\ "> make no law ",
|
||||
\ "**** Congress shall ",
|
||||
\ "> make no law ",
|
||||
@ -880,7 +935,7 @@ func Test_breakindent20_list()
|
||||
let expect = [
|
||||
\ "* Congress shall ",
|
||||
\ "> make no law ",
|
||||
\ "*** Congress shall ",
|
||||
\ "••• Congress shall ",
|
||||
\ "> make no law ",
|
||||
\ "**** Congress shall ",
|
||||
\ "> make no law ",
|
||||
|
@ -16,7 +16,7 @@ local function shell_quote(str)
|
||||
return str
|
||||
end
|
||||
|
||||
--- This module uses functions from the context of the test runner.
|
||||
--- Functions executing in the context of the test runner (not the current nvim test session).
|
||||
--- @class test.testutil
|
||||
local M = {
|
||||
paths = Paths,
|
||||
@ -42,6 +42,29 @@ function M.isdir(path)
|
||||
return stat.type == 'directory'
|
||||
end
|
||||
|
||||
--- (Only on Windows) Replaces yucky "\\" slashes with delicious "/" slashes in a string, or all
|
||||
--- string values in a table (recursively).
|
||||
---
|
||||
--- @param obj string|table
|
||||
--- @return any
|
||||
function M.fix_slashes(obj)
|
||||
if not M.is_os('win') then
|
||||
return obj
|
||||
end
|
||||
if type(obj) == 'string' then
|
||||
local ret = obj:gsub('\\', '/')
|
||||
return ret
|
||||
elseif type(obj) == 'table' then
|
||||
--- @cast obj table<any,any>
|
||||
local ret = {} --- @type table<any,any>
|
||||
for k, v in pairs(obj) do
|
||||
ret[k] = M.fix_slashes(v)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
assert(false, 'expected string or table of strings, got ' .. type(obj))
|
||||
end
|
||||
|
||||
--- @param ... string|string[]
|
||||
--- @return string
|
||||
function M.argss_to_cmd(...)
|
||||
|
975
test/unit/termkey_spec.lua
Normal file
975
test/unit/termkey_spec.lua
Normal file
@ -0,0 +1,975 @@
|
||||
local t = require('test.unit.testutil')
|
||||
local itp = t.gen_itp(it)
|
||||
local bit = require('bit')
|
||||
|
||||
--- @alias TermKeyKey {utf8: string, type: integer, modifiers: integer, code: {codepoint: integer, sym: any, number: integer}}
|
||||
|
||||
--- @class termkey
|
||||
--- @field TERMKEY_CANON_SPACESYMBOL integer
|
||||
--- @field TERMKEY_FLAG_SPACESYMBOL integer
|
||||
--- @field TERMKEY_FLAG_UTF8 integer
|
||||
--- @field TERMKEY_FORMAT_ALTISMETA integer
|
||||
--- @field TERMKEY_FORMAT_CARETCTRL integer
|
||||
--- @field TERMKEY_FORMAT_LONGMOD integer
|
||||
--- @field TERMKEY_FORMAT_LOWERMOD integer
|
||||
--- @field TERMKEY_FORMAT_LOWERSPACE integer
|
||||
--- @field TERMKEY_FORMAT_MOUSE_POS integer
|
||||
--- @field TERMKEY_FORMAT_SPACEMOD integer
|
||||
--- @field TERMKEY_FORMAT_WRAPBRACKET integer
|
||||
--- @field TERMKEY_KEYMOD_ALT integer
|
||||
--- @field TERMKEY_KEYMOD_CTRL integer
|
||||
--- @field TERMKEY_MOUSE_DRAG integer
|
||||
--- @field TERMKEY_MOUSE_PRESS integer
|
||||
--- @field TERMKEY_MOUSE_RELEASE integer
|
||||
--- @field TERMKEY_RES_AGAIN integer
|
||||
--- @field TERMKEY_RES_KEY integer
|
||||
--- @field TERMKEY_RES_NONE integer
|
||||
--- @field TERMKEY_SYM_DOWN integer
|
||||
--- @field TERMKEY_SYM_PAGEUP integer
|
||||
--- @field TERMKEY_SYM_SPACE integer
|
||||
--- @field TERMKEY_SYM_UNKNOWN integer
|
||||
--- @field TERMKEY_SYM_UP integer
|
||||
--- @field TERMKEY_TYPE_DCS integer
|
||||
--- @field TERMKEY_TYPE_FUNCTION integer
|
||||
--- @field TERMKEY_TYPE_KEYSYM integer
|
||||
--- @field TERMKEY_TYPE_MODEREPORT integer
|
||||
--- @field TERMKEY_TYPE_MOUSE integer
|
||||
--- @field TERMKEY_TYPE_OSC integer
|
||||
--- @field TERMKEY_TYPE_POSITION integer
|
||||
--- @field TERMKEY_TYPE_UNICODE integer
|
||||
--- @field TERMKEY_TYPE_UNKNOWN_CSI integer
|
||||
--- @field termkey_canonicalise fun(any, any):any
|
||||
--- @field termkey_destroy fun(any)
|
||||
--- @field termkey_get_buffer_remaining fun(any):integer
|
||||
--- @field termkey_get_buffer_size fun(any):integer
|
||||
--- @field termkey_get_canonflags fun(any):any
|
||||
--- @field termkey_get_keyname fun(any, any):any
|
||||
--- @field termkey_getkey fun(any, any):any
|
||||
--- @field termkey_getkey_force fun(any, any):any
|
||||
--- @field termkey_interpret_csi fun(any, any, any, any, any):any
|
||||
--- @field termkey_interpret_modereport fun(any, any, any, any, any):any
|
||||
--- @field termkey_interpret_mouse fun(any, any, TermKeyKey, integer, integer, integer):any
|
||||
--- @field termkey_interpret_position fun(any, any, any, any):any
|
||||
--- @field termkey_interpret_string fun(any, TermKeyKey, any):any
|
||||
--- @field termkey_lookup_keyname fun(any, any, any):any
|
||||
--- @field termkey_new_abstract fun(string, integer):any
|
||||
--- @field termkey_push_bytes fun(any, string, integer):integer
|
||||
--- @field termkey_set_buffer_size fun(any, integer):integer
|
||||
--- @field termkey_set_canonflags fun(any, any):any
|
||||
--- @field termkey_set_flags fun(any, integer)
|
||||
--- @field termkey_start fun(any):integer
|
||||
--- @field termkey_stop fun(any):integer
|
||||
--- @field termkey_strfkey fun(any, string, integer, any, any):integer
|
||||
local termkey = t.cimport(
|
||||
'./src/nvim/tui/termkey/termkey.h',
|
||||
'./src/nvim/tui/termkey/termkey-internal.h',
|
||||
'./src/nvim/tui/termkey/termkey_defs.h',
|
||||
'./src/nvim/tui/termkey/driver-csi.h'
|
||||
)
|
||||
|
||||
describe('termkey', function()
|
||||
itp('01base', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
t.neq(tk, nil)
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_size(tk), 256)
|
||||
t.eq(tk.is_started, 1) -- tk->is_started true after construction
|
||||
|
||||
termkey.termkey_stop(tk)
|
||||
t.neq(tk.is_started, 1) -- tk->is_started false after termkey_stop()
|
||||
|
||||
termkey.termkey_start(tk)
|
||||
t.eq(tk.is_started, 1) -- tk->is_started true after termkey_start()
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('02getkey', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_remaining(tk), 256) -- buffer free initially 256
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey yields RES_NONE when empty
|
||||
|
||||
t.eq(termkey.termkey_push_bytes(tk, 'h', 1), 1) -- push_bytes returns 1
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_remaining(tk), 255) -- buffer free 255 after push_bytes
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after h
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after h
|
||||
t.eq(key.code.codepoint, string.byte('h')) -- key.code.codepoint after h
|
||||
t.eq(key.modifiers, 0) -- key.modifiers after h
|
||||
t.eq(t.ffi.string(key.utf8), 'h') -- key.utf8 after h
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_remaining(tk), 256) -- buffer free 256 after getkey
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey yields RES_NONE a second time
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x01', 1)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after C-a
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after C-a
|
||||
t.eq(key.code.codepoint, string.byte('a')) -- key.code.codepoint after C-a
|
||||
t.eq(key.modifiers, termkey.TERMKEY_KEYMOD_CTRL) -- key.modifiers after C-a
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\033OA', 3)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Up
|
||||
|
||||
-- is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Up");
|
||||
-- is_int(key.code.sym, TERMKEY_SYM_UP, "key.code.sym after Up");
|
||||
t.eq(key.modifiers, 0) -- key.modifiers after Up
|
||||
|
||||
t.eq(termkey.termkey_push_bytes(tk, '\033O', 2), 2) -- push_bytes returns 2
|
||||
|
||||
-- is_int(termkey_get_buffer_remaining(tk), 254, "buffer free 254 after partial write");
|
||||
|
||||
-- is_int(termkey_getkey(tk, &key), TERMKEY_RES_AGAIN, "getkey yields RES_AGAIN after partial write");
|
||||
|
||||
termkey.termkey_push_bytes(tk, 'C', 1)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Right completion
|
||||
|
||||
-- is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Right");
|
||||
-- is_int(key.code.sym, TERMKEY_SYM_RIGHT, "key.code.sym after Right");
|
||||
-- is_int(key.modifiers, 0, "key.modifiers after Right");
|
||||
|
||||
-- is_int(termkey_get_buffer_remaining(tk), 256, "buffer free 256 after completion");
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\033[27;5u', 7)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Ctrl-Escape
|
||||
|
||||
-- is_int(key.type, TERMKEY_TYPE_KEYSYM, "key.type after Ctrl-Escape");
|
||||
-- is_int(key.code.sym, TERMKEY_SYM_ESCAPE, "key.code.sym after Ctrl-Escape");
|
||||
-- is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after Ctrl-Escape");
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\0', 1)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after Ctrl-Space
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after Ctrl-Space
|
||||
-- t.eq(key.code.codepoint, string.byte(' ')) -- key.code.codepoint after Ctrl-Space
|
||||
-- is_int(key.modifiers, TERMKEY_KEYMOD_CTRL, "key.modifiers after Ctrl-Space");
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('03utf8', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', termkey.TERMKEY_FLAG_UTF8)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
|
||||
termkey.termkey_push_bytes(tk, 'a', 1)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY low ASCII
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type low ASCII
|
||||
t.eq(key.code.codepoint, string.byte('a')) -- key.code.codepoint low ASCII
|
||||
|
||||
-- 2-byte UTF-8 range is U+0080 to U+07FF (0xDF 0xBF)
|
||||
-- However, we'd best avoid the C1 range, so we'll start at U+00A0 (0xC2 0xA0)
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xC2\xA0', 2)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 low
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 2 low
|
||||
t.eq(key.code.codepoint, 0x00A0) -- key.code.codepoint UTF-8 2 low
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xDF\xBF', 2)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 high
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 2 high
|
||||
t.eq(key.code.codepoint, 0x07FF) -- key.code.codepoint UTF-8 2 high
|
||||
|
||||
-- 3-byte UTF-8 range is U+0800 (0xE0 0xA0 0x80) to U+FFFD (0xEF 0xBF 0xBD)
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xE0\xA0\x80', 3)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 low
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 3 low
|
||||
t.eq(key.code.codepoint, 0x0800) -- key.code.codepoint UTF-8 3 low
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xEF\xBF\xBD', 3)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 high
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 3 high
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 3 high
|
||||
|
||||
-- 4-byte UTF-8 range is U+10000 (0xF0 0x90 0x80 0x80) to U+10FFFF (0xF4 0x8F 0xBF 0xBF)
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xF0\x90\x80\x80', 4)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 low
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 4 low
|
||||
t.eq(key.code.codepoint, 0x10000) -- key.code.codepoint UTF-8 4 low
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xF4\x8F\xBF\xBF', 4)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 high
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type UTF-8 4 high
|
||||
t.eq(key.code.codepoint, 0x10FFFF) -- key.code.codepoint UTF-8 4 high
|
||||
|
||||
-- Invalid continuations
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xC2!', 2)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 invalid cont
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 2 invalid cont
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 invalid after
|
||||
t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 2 invalid after
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xE0!', 2)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid cont
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 3 invalid cont
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid after
|
||||
t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 3 invalid after
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xE0\xA0!', 3)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid cont 2
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 3 invalid cont 2
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 invalid after
|
||||
t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 3 invalid after
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xF0!', 2)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid cont
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 4 invalid cont
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid after
|
||||
t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 4 invalid after
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xF0\x90!', 3)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid cont 2
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 4 invalid cont 2
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid after
|
||||
t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 4 invalid after
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xF0\x90\x80!', 4)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid cont 3
|
||||
t.eq(key.code.codepoint, 0xFFFD) -- key.code.codepoint UTF-8 4 invalid cont 3
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 invalid after
|
||||
t.eq(key.code.codepoint, string.byte('!')) -- key.code.codepoint UTF-8 4 invalid after
|
||||
|
||||
-- Partials
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xC2', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 2 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xA0', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 2 partial
|
||||
t.eq(key.code.codepoint, 0x00A0) -- key.code.codepoint UTF-8 2 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xE0', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 3 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xA0', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 3 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x80', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 3 partial
|
||||
t.eq(key.code.codepoint, 0x0800) -- key.code.codepoint UTF-8 3 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\xF0', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 4 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x90', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 4 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x80', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN UTF-8 4 partial
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x80', 1)
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY UTF-8 4 partial
|
||||
t.eq(key.code.codepoint, 0x10000) -- key.code.codepoint UTF-8 4 partial
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('04flags', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
|
||||
termkey.termkey_push_bytes(tk, ' ', 1)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after space
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type after space
|
||||
t.eq(key.code.codepoint, string.byte(' ')) -- key.code.codepoint after space
|
||||
t.eq(key.modifiers, 0) -- key.modifiers after space
|
||||
|
||||
termkey.termkey_set_flags(tk, termkey.TERMKEY_FLAG_SPACESYMBOL)
|
||||
|
||||
termkey.termkey_push_bytes(tk, ' ', 1)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY after space
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_KEYSYM) -- key.type after space with FLAG_SPACESYMBOL
|
||||
t.eq(key.code.sym, termkey.TERMKEY_SYM_SPACE) -- key.code.sym after space with FLAG_SPACESYMBOL
|
||||
t.eq(key.modifiers, 0) -- key.modifiers after space with FLAG_SPACESYMBOL
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('06buffer', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_remaining(tk), 256) -- buffer free initially 256
|
||||
t.eq(termkey.termkey_get_buffer_size(tk), 256) -- buffer size initially 256
|
||||
|
||||
t.eq(termkey.termkey_push_bytes(tk, 'h', 1), 1) -- push_bytes returns 1
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_remaining(tk), 255) -- buffer free 255 after push_bytes
|
||||
t.eq(termkey.termkey_get_buffer_size(tk), 256) -- buffer size 256 after push_bytes
|
||||
|
||||
t.eq(not not termkey.termkey_set_buffer_size(tk, 512), true) -- buffer set size OK
|
||||
|
||||
t.eq(termkey.termkey_get_buffer_remaining(tk), 511) -- buffer free 511 after push_bytes
|
||||
t.eq(termkey.termkey_get_buffer_size(tk), 512) -- buffer size 512 after push_bytes
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- buffered key still useable after resize
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
local function termkey_keyname2sym(tk, keyname)
|
||||
local sym = t.ffi.new('TermKeySym[1]')
|
||||
local endp = termkey.termkey_lookup_keyname(tk, keyname, sym)
|
||||
if endp == nil then
|
||||
return termkey.TERMKEY_SYM_UNKNOWN
|
||||
end
|
||||
return sym
|
||||
end
|
||||
|
||||
itp('10keyname', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
|
||||
local sym = termkey_keyname2sym(tk, 'SomeUnknownKey')
|
||||
t.eq(sym, termkey.TERMKEY_SYM_UNKNOWN) -- keyname2sym SomeUnknownKey
|
||||
|
||||
sym = termkey_keyname2sym(tk, 'Space')
|
||||
t.eq(sym[0], termkey.TERMKEY_SYM_SPACE) -- keyname2sym Space
|
||||
|
||||
local _end = termkey.termkey_lookup_keyname(tk, 'Up', sym)
|
||||
t.neq(_end, nil) -- termkey_get_keyname Up returns non-NULL
|
||||
t.eq(t.ffi.string(_end), '') -- termkey_get_keyname Up return points at endofstring
|
||||
t.eq(sym[0], termkey.TERMKEY_SYM_UP) -- termkey_get_keyname Up yields Up symbol
|
||||
|
||||
_end = termkey.termkey_lookup_keyname(tk, 'DownMore', sym)
|
||||
t.neq(_end, nil) -- termkey_get_keyname DownMore returns non-NULL
|
||||
t.eq(t.ffi.string(_end), 'More') -- termkey_get_keyname DownMore return points at More
|
||||
t.eq(sym[0], termkey.TERMKEY_SYM_DOWN) -- termkey_get_keyname DownMore yields Down symbol
|
||||
|
||||
_end = termkey.termkey_lookup_keyname(tk, 'SomeUnknownKey', sym)
|
||||
t.eq(_end, nil) -- termkey_get_keyname SomeUnknownKey returns NULL
|
||||
|
||||
t.eq(t.ffi.string(termkey.termkey_get_keyname(tk, termkey.TERMKEY_SYM_SPACE)), 'Space') -- "get_keyname SPACE");
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('11strfkey', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
---@type TermKeyKey
|
||||
local key = t.ffi.new(
|
||||
'TermKeyKey',
|
||||
{ type = termkey.TERMKEY_TYPE_UNICODE, code = { codepoint = string.byte('A') } }
|
||||
)
|
||||
local buffer = t.ffi.new('char[16]')
|
||||
|
||||
local len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 1) -- length for unicode/A/0
|
||||
t.eq(t.ffi.string(buffer), 'A') -- buffer for unicode/A/0
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_WRAPBRACKET
|
||||
)
|
||||
t.eq(len, 1) -- length for unicode/A/0 wrapbracket
|
||||
t.eq(t.ffi.string(buffer), 'A') -- buffer for unicode/A/0 wrapbracket
|
||||
|
||||
---@type TermKeyKey
|
||||
key = t.ffi.new('TermKeyKey', {
|
||||
type = termkey.TERMKEY_TYPE_UNICODE,
|
||||
code = { codepoint = string.byte('b') },
|
||||
modifiers = termkey.TERMKEY_KEYMOD_CTRL,
|
||||
})
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 3) -- length for unicode/b/CTRL
|
||||
t.eq(t.ffi.string(buffer), 'C-b') -- buffer for unicode/b/CTRL
|
||||
|
||||
len =
|
||||
termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, termkey.TERMKEY_FORMAT_LONGMOD)
|
||||
t.eq(len, 6) -- length for unicode/b/CTRL longmod
|
||||
t.eq(t.ffi.string(buffer), 'Ctrl-b') -- buffer for unicode/b/CTRL longmod
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
bit.bor(termkey.TERMKEY_FORMAT_LONGMOD, termkey.TERMKEY_FORMAT_SPACEMOD)
|
||||
)
|
||||
t.eq(len, 6) -- length for unicode/b/CTRL longmod|spacemod
|
||||
t.eq(t.ffi.string(buffer), 'Ctrl b') -- buffer for unicode/b/CTRL longmod|spacemod
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
bit.bor(termkey.TERMKEY_FORMAT_LONGMOD, termkey.TERMKEY_FORMAT_LOWERMOD)
|
||||
)
|
||||
t.eq(len, 6) -- length for unicode/b/CTRL longmod|lowermod
|
||||
t.eq(t.ffi.string(buffer), 'ctrl-b') -- buffer for unicode/b/CTRL longmod|lowermod
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
bit.bor(
|
||||
termkey.TERMKEY_FORMAT_LONGMOD,
|
||||
termkey.TERMKEY_FORMAT_SPACEMOD,
|
||||
termkey.TERMKEY_FORMAT_LOWERMOD
|
||||
)
|
||||
)
|
||||
t.eq(len, 6) -- length for unicode/b/CTRL longmod|spacemod|lowermode
|
||||
t.eq(t.ffi.string(buffer), 'ctrl b') -- buffer for unicode/b/CTRL longmod|spacemod|lowermode
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_CARETCTRL
|
||||
)
|
||||
t.eq(len, 2) -- length for unicode/b/CTRL caretctrl
|
||||
t.eq(t.ffi.string(buffer), '^B') -- buffer for unicode/b/CTRL caretctrl
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_WRAPBRACKET
|
||||
)
|
||||
t.eq(len, 5) -- length for unicode/b/CTRL wrapbracket
|
||||
t.eq(t.ffi.string(buffer), '<C-b>') -- buffer for unicode/b/CTRL wrapbracket
|
||||
|
||||
---@type TermKeyKey
|
||||
key = t.ffi.new('TermKeyKey', {
|
||||
type = termkey.TERMKEY_TYPE_UNICODE,
|
||||
code = { codepoint = string.byte('c') },
|
||||
modifiers = termkey.TERMKEY_KEYMOD_ALT,
|
||||
})
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 3) -- length for unicode/c/ALT
|
||||
t.eq(t.ffi.string(buffer), 'A-c') -- buffer for unicode/c/ALT
|
||||
|
||||
len =
|
||||
termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, termkey.TERMKEY_FORMAT_LONGMOD)
|
||||
t.eq(len, 5) -- length for unicode/c/ALT longmod
|
||||
t.eq(t.ffi.string(buffer), 'Alt-c') -- buffer for unicode/c/ALT longmod
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_ALTISMETA
|
||||
)
|
||||
t.eq(len, 3) -- length for unicode/c/ALT altismeta
|
||||
t.eq(t.ffi.string(buffer), 'M-c') -- buffer for unicode/c/ALT altismeta
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
bit.bor(termkey.TERMKEY_FORMAT_LONGMOD, termkey.TERMKEY_FORMAT_ALTISMETA)
|
||||
)
|
||||
t.eq(len, 6) -- length for unicode/c/ALT longmod|altismeta
|
||||
t.eq(t.ffi.string(buffer), 'Meta-c') -- buffer for unicode/c/ALT longmod|altismeta
|
||||
|
||||
---@type TermKeyKey
|
||||
key = t.ffi.new(
|
||||
'TermKeyKey',
|
||||
{ type = termkey.TERMKEY_TYPE_KEYSYM, code = { sym = termkey.TERMKEY_SYM_UP } }
|
||||
)
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 2) -- length for sym/Up/0
|
||||
t.eq(t.ffi.string(buffer), 'Up') -- buffer for sym/Up/0
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_WRAPBRACKET
|
||||
)
|
||||
t.eq(len, 4) -- length for sym/Up/0 wrapbracket
|
||||
t.eq(t.ffi.string(buffer), '<Up>') -- buffer for sym/Up/0 wrapbracket
|
||||
|
||||
---@type TermKeyKey
|
||||
key = t.ffi.new(
|
||||
'TermKeyKey',
|
||||
{ type = termkey.TERMKEY_TYPE_KEYSYM, code = { sym = termkey.TERMKEY_SYM_PAGEUP } }
|
||||
)
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 6) -- length for sym/PageUp/0
|
||||
t.eq(t.ffi.string(buffer), 'PageUp') -- buffer for sym/PageUp/0
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_LOWERSPACE
|
||||
)
|
||||
t.eq(len, 7) -- length for sym/PageUp/0 lowerspace
|
||||
t.eq(t.ffi.string(buffer), 'page up') -- buffer for sym/PageUp/0 lowerspace
|
||||
|
||||
-- If size of buffer is too small, strfkey should return something consistent
|
||||
len = termkey.termkey_strfkey(tk, buffer, 4, key, 0)
|
||||
t.eq(len, 6) -- length for sym/PageUp/0
|
||||
t.eq(t.ffi.string(buffer), 'Pag') -- buffer of len 4 for sym/PageUp/0
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, 4, key, termkey.TERMKEY_FORMAT_LOWERSPACE)
|
||||
t.eq(len, 7) -- length for sym/PageUp/0 lowerspace
|
||||
t.eq(t.ffi.string(buffer), 'pag') -- buffer of len 4 for sym/PageUp/0 lowerspace
|
||||
|
||||
key = t.ffi.new('TermKeyKey', { type = termkey.TERMKEY_TYPE_FUNCTION, code = { number = 5 } }) ---@type TermKeyKey
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 2) -- length for func/5/0
|
||||
t.eq(t.ffi.string(buffer), 'F5') -- buffer for func/5/0
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_WRAPBRACKET
|
||||
)
|
||||
t.eq(len, 4) -- length for func/5/0 wrapbracket
|
||||
t.eq(t.ffi.string(buffer), '<F5>') -- buffer for func/5/0 wrapbracket
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_LOWERSPACE
|
||||
)
|
||||
t.eq(len, 2) -- length for func/5/0 lowerspace
|
||||
t.eq(t.ffi.string(buffer), 'f5') -- buffer for func/5/0 lowerspace
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('13cmpkey', function()
|
||||
local function termkey_keycmp(tk, key1, key2)
|
||||
termkey.termkey_canonicalise(tk, key1)
|
||||
termkey.termkey_canonicalise(tk, key2)
|
||||
|
||||
if key1.type ~= key2.type then
|
||||
return key1.type - key2.type
|
||||
end
|
||||
|
||||
if key1.type == termkey.TERMKEY_TYPE_UNICODE then
|
||||
if key1.code.codepoint ~= key2.code.codepoint then
|
||||
return key1.code.codepoint - key2.code.codepoint
|
||||
end
|
||||
end
|
||||
|
||||
return key1.modifiers - key2.modifiers
|
||||
end
|
||||
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
---@type TermKeyKey
|
||||
local key1 = t.ffi.new('TermKeyKey', {
|
||||
type = termkey.TERMKEY_TYPE_UNICODE,
|
||||
code = { codepoint = string.byte('A') },
|
||||
modifiers = 0,
|
||||
})
|
||||
---@type TermKeyKey
|
||||
local key2 = t.ffi.new('TermKeyKey', {
|
||||
type = termkey.TERMKEY_TYPE_UNICODE,
|
||||
code = { codepoint = string.byte('A') },
|
||||
modifiers = 0,
|
||||
})
|
||||
|
||||
t.eq(termkey_keycmp(tk, key1, key1), 0) -- cmpkey same structure
|
||||
t.eq(termkey_keycmp(tk, key1, key2), 0) -- cmpkey identical structure
|
||||
|
||||
key2.modifiers = termkey.TERMKEY_KEYMOD_CTRL
|
||||
|
||||
t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders CTRL after nomod
|
||||
t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders nomod before CTRL
|
||||
|
||||
key2.code.codepoint = string.byte('B')
|
||||
key2.modifiers = 0
|
||||
|
||||
t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders 'B' after 'A'
|
||||
t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders 'A' before 'B'
|
||||
|
||||
key1.modifiers = termkey.TERMKEY_KEYMOD_CTRL
|
||||
|
||||
t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders nomod 'B' after CTRL 'A'
|
||||
t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders CTRL 'A' before nomod 'B'
|
||||
|
||||
key2.type = termkey.TERMKEY_TYPE_KEYSYM
|
||||
key2.code.sym = termkey.TERMKEY_SYM_UP
|
||||
|
||||
t.eq(termkey_keycmp(tk, key1, key2) < 0, true) -- cmpkey orders KEYSYM after UNICODE
|
||||
t.eq(termkey_keycmp(tk, key2, key1) > 0, true) -- cmpkey orders UNICODE before KEYSYM
|
||||
|
||||
key1.type = termkey.TERMKEY_TYPE_KEYSYM
|
||||
key1.code.sym = termkey.TERMKEY_SYM_SPACE
|
||||
key1.modifiers = 0
|
||||
key2.type = termkey.TERMKEY_TYPE_UNICODE
|
||||
key2.code.codepoint = string.byte(' ')
|
||||
key2.modifiers = 0
|
||||
|
||||
t.eq(termkey_keycmp(tk, key1, key2), 0) -- cmpkey considers KEYSYM/SPACE and UNICODE/SP identical
|
||||
|
||||
termkey.termkey_set_canonflags(
|
||||
tk,
|
||||
bit.bor(termkey.termkey_get_canonflags(tk), termkey.TERMKEY_CANON_SPACESYMBOL)
|
||||
)
|
||||
t.eq(termkey_keycmp(tk, key1, key2), 0) -- "cmpkey considers KEYSYM/SPACE and UNICODE/SP identical under SPACESYMBOL");
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('30mouse', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey', { type = -1 }) ---@type TermKeyKey
|
||||
local ev = t.ffi.new('TermKeyMouseEvent[1]')
|
||||
local button = t.ffi.new('int[1]')
|
||||
local line = t.ffi.new('int[1]')
|
||||
local col = t.ffi.new('int[1]')
|
||||
local buffer = t.ffi.new('char[32]')
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[M !!', 6)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse press
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse press
|
||||
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for press
|
||||
t.eq(button[0], 1) -- mouse button for press
|
||||
t.eq(line[0], 1) -- mouse line for press
|
||||
t.eq(col[0], 1) -- mouse column for press
|
||||
t.eq(key.modifiers, 0) -- modifiers for press
|
||||
|
||||
local len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 13) -- string length for press
|
||||
t.eq(t.ffi.string(buffer), 'MousePress(1)') -- string buffer for press
|
||||
|
||||
len = termkey.termkey_strfkey(
|
||||
tk,
|
||||
buffer,
|
||||
t.ffi.sizeof(buffer),
|
||||
key,
|
||||
termkey.TERMKEY_FORMAT_MOUSE_POS
|
||||
)
|
||||
t.eq(len, 21) -- string length for press
|
||||
t.eq(t.ffi.string(buffer), 'MousePress(1) @ (1,1)') -- string buffer for press
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[M@"!', 6)
|
||||
|
||||
termkey.termkey_getkey(tk, key)
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_DRAG) -- mouse event for drag
|
||||
t.eq(button[0], 1) -- mouse button for drag
|
||||
t.eq(line[0], 1) -- mouse line for drag
|
||||
t.eq(col[0], 2) -- mouse column for drag
|
||||
t.eq(key.modifiers, 0) -- modifiers for press
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[M##!', 6)
|
||||
|
||||
termkey.termkey_getkey(tk, key)
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_RELEASE) -- mouse event for release
|
||||
t.eq(line[0], 1) -- mouse line for release
|
||||
t.eq(col[0], 3) -- mouse column for release
|
||||
t.eq(key.modifiers, 0) -- modifiers for press
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[M0++', 6)
|
||||
|
||||
termkey.termkey_getkey(tk, key)
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for Ctrl-press
|
||||
t.eq(button[0], 1) -- mouse button for Ctrl-press
|
||||
t.eq(line[0], 11) -- mouse line for Ctrl-press
|
||||
t.eq(col[0], 11) -- mouse column for Ctrl-press
|
||||
t.eq(key.modifiers, termkey.TERMKEY_KEYMOD_CTRL) -- modifiers for Ctrl-press
|
||||
|
||||
len = termkey.termkey_strfkey(tk, buffer, t.ffi.sizeof(buffer), key, 0)
|
||||
t.eq(len, 15) -- string length for Ctrl-press
|
||||
t.eq(t.ffi.string(buffer), 'C-MousePress(1)') -- string buffer for Ctrl-press
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[M`!!', 6)
|
||||
|
||||
termkey.termkey_getkey(tk, key)
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for wheel down
|
||||
t.eq(button[0], 4) -- mouse button for wheel down
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[Mb!!', 6)
|
||||
|
||||
termkey.termkey_getkey(tk, key)
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for wheel left
|
||||
t.eq(button[0], 6) -- mouse button for wheel left
|
||||
|
||||
-- rxvt protocol
|
||||
termkey.termkey_push_bytes(tk, '\x1b[0;20;20M', 10)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse press rxvt protocol
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse press rxvt protocol
|
||||
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for press rxvt protocol
|
||||
t.eq(button[0], 1) -- mouse button for press rxvt protocol
|
||||
t.eq(line[0], 20) -- mouse line for press rxvt protocol
|
||||
t.eq(col[0], 20) -- mouse column for press rxvt protocol
|
||||
t.eq(key.modifiers, 0) -- modifiers for press rxvt protocol
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[3;20;20M', 10)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse release rxvt protocol
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse release rxvt protocol
|
||||
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_RELEASE) -- mouse event for release rxvt protocol
|
||||
t.eq(line[0], 20) -- mouse line for release rxvt protocol
|
||||
t.eq(col[0], 20) -- mouse column for release rxvt protocol
|
||||
t.eq(key.modifiers, 0) -- modifiers for release rxvt protocol
|
||||
|
||||
-- SGR protocol
|
||||
termkey.termkey_push_bytes(tk, '\x1b[<0;30;30M', 11)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse press SGR encoding
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse press SGR encoding
|
||||
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_PRESS) -- mouse event for press SGR
|
||||
t.eq(button[0], 1) -- mouse button for press SGR
|
||||
t.eq(line[0], 30) -- mouse line for press SGR
|
||||
t.eq(col[0], 30) -- mouse column for press SGR
|
||||
t.eq(key.modifiers, 0) -- modifiers for press SGR
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[<0;30;30m', 11)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mouse release SGR encoding
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MOUSE) -- key.type for mouse release SGR encoding
|
||||
|
||||
t.eq(termkey.termkey_interpret_mouse(tk, key, ev, button, line, col), termkey.TERMKEY_RES_KEY) -- interpret_mouse yields RES_KEY
|
||||
|
||||
t.eq(ev[0], termkey.TERMKEY_MOUSE_RELEASE) -- mouse event for release SGR
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[<0;500;300M', 13)
|
||||
|
||||
termkey.termkey_getkey(tk, key)
|
||||
termkey.termkey_interpret_mouse(tk, key, ev, button, line, col)
|
||||
|
||||
t.eq(line[0], 300) -- mouse line for press SGR wide
|
||||
t.eq(col[0], 500) -- mouse column for press SGR wide
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('31position', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
local line = t.ffi.new('int[1]')
|
||||
local col = t.ffi.new('int[1]')
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[?15;7R', 8)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for position report
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_POSITION) -- key.type for position report
|
||||
|
||||
t.eq(termkey.termkey_interpret_position(tk, key, line, col), termkey.TERMKEY_RES_KEY) -- interpret_position yields RES_KEY
|
||||
|
||||
t.eq(line[0], 15) -- line for position report
|
||||
t.eq(col[0], 7) -- column for position report
|
||||
|
||||
-- A plain CSI R is likely to be <F3> though.
|
||||
-- This is tricky :/
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[R', 3)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for <F3>
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_FUNCTION) -- key.type for <F3>
|
||||
t.eq(key.code.number, 3) -- key.code.number for <F3>
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('32modereport', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
local initial = t.ffi.new('int[1]')
|
||||
local mode = t.ffi.new('int[1]')
|
||||
local value = t.ffi.new('int[1]')
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[?1;2$y', 8)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mode report
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MODEREPORT) -- key.type for mode report
|
||||
|
||||
t.eq(
|
||||
termkey.termkey_interpret_modereport(tk, key, initial, mode, value),
|
||||
termkey.TERMKEY_RES_KEY
|
||||
) -- interpret_modereoprt yields RES_KEY
|
||||
|
||||
t.eq(initial[0], 63) -- initial indicator from mode report
|
||||
t.eq(mode[0], 1) -- mode number from mode report
|
||||
t.eq(value[0], 2) -- mode value from mode report
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[4;1$y', 7)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for mode report
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_MODEREPORT) -- key.type for mode report
|
||||
|
||||
t.eq(
|
||||
termkey.termkey_interpret_modereport(tk, key, initial, mode, value),
|
||||
termkey.TERMKEY_RES_KEY
|
||||
) -- interpret_modereoprt yields RES_KEY
|
||||
|
||||
t.eq(initial[0], 0) -- initial indicator from mode report
|
||||
t.eq(mode[0], 4) -- mode number from mode report
|
||||
t.eq(value[0], 1) -- mode value from mode report
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('38csi', function()
|
||||
local tk = termkey.termkey_new_abstract('vt100', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
local args = t.ffi.new('TermKeyCsiParam[16]')
|
||||
local nargs = t.ffi.new('size_t[1]')
|
||||
local command = t.ffi.new('unsigned[1]')
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[5;25v', 7)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for CSI v
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNKNOWN_CSI) -- key.type for unknown CSI
|
||||
|
||||
t.eq(termkey.termkey_interpret_csi(tk, key, args, nargs, command), termkey.TERMKEY_RES_KEY) -- interpret_csi yields RES_KEY
|
||||
|
||||
t.eq(nargs[0], 2) -- nargs for unknown CSI
|
||||
-- t.eq(args[0], 5) -- args[0] for unknown CSI
|
||||
-- t.eq(args[1], 25) -- args[1] for unknown CSI
|
||||
t.eq(command[0], 118) -- command for unknown CSI
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[?w', 4)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for CSI ? w
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNKNOWN_CSI) -- key.type for unknown CSI
|
||||
t.eq(termkey.termkey_interpret_csi(tk, key, args, nargs, command), termkey.TERMKEY_RES_KEY) -- interpret_csi yields RES_KEY
|
||||
t.eq(command[0], bit.bor(bit.lshift(63, 8), 119)) -- command for unknown CSI
|
||||
|
||||
termkey.termkey_push_bytes(tk, '\x1b[?$x', 5)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for CSI ? $x
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNKNOWN_CSI) -- key.type for unknown CSI
|
||||
t.eq(termkey.termkey_interpret_csi(tk, key, args, nargs, command), termkey.TERMKEY_RES_KEY) -- interpret_csi yields RES_KEY
|
||||
t.eq(command[0], bit.bor(bit.lshift(36, 16), bit.lshift(63, 8), 120)) -- command for unknown CSI
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
|
||||
itp('39dcs', function()
|
||||
local tk = termkey.termkey_new_abstract('xterm', 0)
|
||||
local key = t.ffi.new('TermKeyKey') ---@type TermKeyKey
|
||||
|
||||
-- 7bit DCS
|
||||
termkey.termkey_push_bytes(tk, '\x1bP1$r1 q\x1b\\', 10)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for DCS
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_DCS) -- key.type for DCS
|
||||
t.eq(key.modifiers, 0) -- key.modifiers for DCS
|
||||
|
||||
local str = t.ffi.new('const char*[1]')
|
||||
t.eq(termkey.termkey_interpret_string(tk, key, str), termkey.TERMKEY_RES_KEY) -- termkey_interpret_string() gives string
|
||||
t.eq(t.ffi.string(str[0]), '1$r1 q') -- termkey_interpret_string() yields correct string
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey again yields RES_NONE
|
||||
|
||||
-- 8bit DCS
|
||||
termkey.termkey_push_bytes(tk, '\x901$r2 q\x9c', 8)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for DCS
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_DCS) -- key.type for DCS
|
||||
t.eq(key.modifiers, 0) -- key.modifiers for DCS
|
||||
|
||||
t.eq(termkey.termkey_interpret_string(tk, key, str), termkey.TERMKEY_RES_KEY) -- "termkey_interpret_string() gives string");
|
||||
t.eq(t.ffi.string(str[0]), '1$r2 q') -- "termkey_interpret_string() yields correct string");
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- "getkey again yields RES_NONE");
|
||||
|
||||
-- 7bit OSC
|
||||
termkey.termkey_push_bytes(tk, '\x1b]15;abc\x1b\\', 10)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_KEY) -- getkey yields RES_KEY for OSC
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_OSC) -- key.type for OSC
|
||||
t.eq(key.modifiers, 0) -- key.modifiers for OSC
|
||||
|
||||
t.eq(termkey.termkey_interpret_string(tk, key, str), termkey.TERMKEY_RES_KEY) -- "termkey_interpret_string() gives string");
|
||||
t.eq(t.ffi.string(str[0]), '15;abc') -- "termkey_interpret_string() yields correct string");
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_NONE) -- getkey again yields RES_NONE
|
||||
|
||||
-- False alarm
|
||||
termkey.termkey_push_bytes(tk, '\x1bP', 2)
|
||||
|
||||
t.eq(termkey.termkey_getkey(tk, key), termkey.TERMKEY_RES_AGAIN) -- getkey yields RES_AGAIN for false alarm
|
||||
|
||||
t.eq(termkey.termkey_getkey_force(tk, key), termkey.TERMKEY_RES_KEY) -- getkey_force yields RES_KEY for false alarm
|
||||
|
||||
t.eq(key.type, termkey.TERMKEY_TYPE_UNICODE) -- key.type for false alarm
|
||||
t.eq(key.code.codepoint, string.byte('P')) -- key.code.codepoint for false alarm
|
||||
t.eq(key.modifiers, termkey.TERMKEY_KEYMOD_ALT) -- key.modifiers for false alarm
|
||||
|
||||
termkey.termkey_destroy(tk)
|
||||
end)
|
||||
end)
|
Loading…
Reference in New Issue
Block a user