diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 0517c160b6..45b82eb733 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1379,6 +1379,7 @@ nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) • Lua can use |vim.keycode()| instead. Attributes: ~ + |api-fast| Since: 0.1.0 Parameters: ~ diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index c183a7e0cd..22b1e9e416 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1273,7 +1273,7 @@ vim.inspect() *vim.inspect()* vim.keycode({keys}, {info}) *vim.keycode()* Converts keys from |key-notation| to the internal encoding. Optionally - returns structured key-chord info as retval 2. + returns structured *key-chord* info as retval 2. Inverse of |keytrans()|, which converts the internal encoding back to |key-notation|. @@ -1347,33 +1347,36 @@ vim.notify_once({msg}, {level}, {opts}) *vim.notify_once()* (`boolean`) true if message was displayed, else false vim.on_key({fn}, {ns_id}, {opts}) *vim.on_key()* - Adds Lua function {fn} with namespace id {ns_id} as a listener to every, - yes every, input key. + Registers function {fn} with |namespace| {ns_id} as a listener to every, + yes EVERY, input key. - The Nvim command-line option |-w| is related but does not support - callbacks and cannot be toggled dynamically. + To parse |key-chord|s, see |vim.keycode()|. Example: >lua + local keychords = vim.keycode(vim.fn.keytrans(key), true) +< + + The |-w| command-line option is related but does not support callbacks and + cannot be toggled dynamically. Note: ~ + • If {fn} returns an empty string, {key} is discarded/ignored; if {key} + is then the "" sequence is discarded as a whole. + • Non-recursive: if {fn} itself consumes input, it won't be invoked for + those keys. + • To UNregister a given {ns_id}, pass `nil` {fn}. • {fn} will be removed on error. - • {fn} won't be invoked recursively, i.e. if {fn} itself consumes input, - it won't be invoked for those keys. • {fn} will not be cleared by |nvim_buf_clear_namespace()| Parameters: ~ • {fn} (`fun(key: string, typed: string): string??`) Function invoked for every input key, after mappings have been applied but before further processing. Arguments {key} and {typed} - are raw keycodes, where {key} is the key after mappings are - applied, and {typed} is the key(s) before mappings are - applied. {typed} may be empty if {key} is produced by - non-typed key(s) or by the same typed key(s) that produced a - previous {key}. If {fn} returns an empty string, {key} is - discarded/ignored, and if {key} is then the - "" sequence is discarded as a whole. When {fn} is - `nil`, the callback associated with namespace {ns_id} is - removed. - • {ns_id} (`integer?`) Namespace ID. If nil or 0, generates and returns - a new |nvim_create_namespace()| id. + are raw input (use |keytrans()| to get |keycodes|). + • {key} is the key after mappings are applied. + • {typed} is the input before mappings are applied; may be + empty if {key} was produced by non-typed key(s) or by the + same typed key(s) that produced a previous {key}. + • {ns_id} (`integer?`) Namespace ID. If nil or 0, returns a new + |namespace| id. • {opts} (`table?`) Optional parameters Return: ~ @@ -1382,6 +1385,7 @@ vim.on_key({fn}, {ns_id}, {opts}) *vim.on_key()* See also: ~ • |keytrans()| + • |vim.keycode()| vim.paste({lines}, {phase}) *vim.paste()* Paste handler, invoked by |nvim_paste()|. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 1259391921..4e5ba1b508 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -386,6 +386,11 @@ VIMSCRIPT • |v:starttime| is the process start time (nanoseconds since UNIX epoch). • |v:useractive| indicates user activity. • |serverlist()| with `info=true` returns details for each server (own + peers). +• These Vimscript functions may be called from |api-fast| context (e.g. + |vim.uv| timer/handle callbacks): |keytrans()|, |char2nr()|, |nr2char()|, + |str2list()|, |strpart()|, |strcharpart()|, |strgetchar()|, |strchars()|, + |strcharlen()|, |strlen()|, |strdisplaywidth()|, |byteidx()|, |charidx()|, + |utf16idx()|, |tr()|, |trim()|. ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/vimfn.txt b/runtime/doc/vimfn.txt index c0915ee20d..722ffe8a8c 100644 --- a/runtime/doc/vimfn.txt +++ b/runtime/doc/vimfn.txt @@ -99,6 +99,9 @@ api_info() *api_info()* lua vim.print(vim.fn.api_info()) < + Attributes: ~ + |api-fast| + Return: ~ (`table`) @@ -856,6 +859,9 @@ byteidx({expr}, {nr} [, {utf16}]) *byteidx()* echo byteidx('a😊😊', 3, 1) " returns 5 < + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`any`) • {nr} (`integer`) @@ -878,6 +884,9 @@ byteidxcomp({expr}, {nr} [, {utf16}]) *byteidxcomp()* character is 3 bytes), the second echo results in 1 ('e' is one byte). + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`any`) • {nr} (`integer`) @@ -999,6 +1008,9 @@ char2nr({string} [, {utf8}]) *char2nr()* Returns 0 if {string} is not a |String|. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {utf8} (`any?`) @@ -1083,6 +1095,9 @@ charidx({string}, {idx} [, {countcc} [, {utf16}]]) *charidx()* echo charidx('a😊😊', 4, 0, 1) " returns 2 < + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {idx} (`integer`) @@ -1731,6 +1746,9 @@ did_filetype() *did_filetype()* editing another buffer to set 'filetype' and load a syntax file. + Attributes: ~ + |api-fast| + Return: ~ (`integer`) @@ -1887,6 +1905,9 @@ environ() *environ()* echo index(keys(environ()), 'HOME', 0, 1) != -1 < + Attributes: ~ + |api-fast| + Return: ~ (`any`) @@ -1898,6 +1919,9 @@ escape({string}, {chars}) *escape()* c:\\program\ files\\vim < Also see |shellescape()| and |fnameescape()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {chars} (`string`) @@ -1961,6 +1985,9 @@ executable({expr}) *executable()* 0 does not exist |exepath()| can be used to get the full path of an executable. + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`string`) @@ -2010,6 +2037,9 @@ exepath({expr}) *exepath()* Returns empty string otherwise. If {expr} starts with "./" the |current-directory| is used. + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`string`) @@ -2424,6 +2454,9 @@ filereadable({file}) *filereadable()* 1 < + Attributes: ~ + |api-fast| + Parameters: ~ • {file} (`string`) @@ -2439,6 +2472,9 @@ filewritable({file}) *filewritable()* is not writable, the result is 0. If {file} is a directory, and we can write to it, the result is 2. + Attributes: ~ + |api-fast| + Parameters: ~ • {file} (`string`) @@ -2696,6 +2732,9 @@ fnameescape({filepath}) *fnameescape()* edit \+some\ str\%nge\|name < + Attributes: ~ + |api-fast| + Parameters: ~ • {filepath} (`string`) @@ -2723,6 +2762,9 @@ fnamemodify({fname}, {mods}) *fnamemodify()* Note: Environment variables don't work in {fname}, use |expand()| first then. + Attributes: ~ + |api-fast| + Parameters: ~ • {fname} (`string`) • {mods} (`string`) @@ -3779,6 +3821,9 @@ getfperm({fname}) *getfperm()* For setting permissions use |setfperm()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {fname} (`string`) @@ -3794,6 +3839,9 @@ getfsize({fname}) *getfsize()* If the size of {fname} is too big to fit in a Number then -2 is returned. + Attributes: ~ + |api-fast| + Parameters: ~ • {fname} (`string`) @@ -3809,6 +3857,9 @@ getftime({fname}) *getftime()* |localtime()| and |strftime()|. If the file {fname} can't be found -1 is returned. + Attributes: ~ + |api-fast| + Parameters: ~ • {fname} (`string`) @@ -3837,6 +3888,9 @@ getftype({fname}) *getftype()* systems that support it. On some systems only "dir" and "file" are returned. + Attributes: ~ + |api-fast| + Parameters: ~ • {fname} (`string`) @@ -4051,6 +4105,9 @@ getpid() *getpid()* Return a Number which is the process ID of the Vim process. This is a unique number, until Vim exits. + Attributes: ~ + |api-fast| + Return: ~ (`integer`) @@ -4908,6 +4965,9 @@ has({feature}) *has()* endif < + Attributes: ~ + |api-fast| + Parameters: ~ • {feature} (`string`) @@ -5135,6 +5195,9 @@ hostname() *hostname()* Returns the hostname of the machine on which the Nvim server (not the UI client) is currently running. + Attributes: ~ + |api-fast| + Return: ~ (`string`) @@ -5152,6 +5215,9 @@ iconv({string}, {from}, {to}) *iconv()* from/to UCS-2 is automatically changed to use UTF-8. You cannot use UCS-2 in a string anyway, because of the NUL bytes. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {from} (`string`) @@ -5562,6 +5628,9 @@ isabsolutepath({path}) *isabsolutepath()* echo isabsolutepath('\\remote\file') " 1 < + Attributes: ~ + |api-fast| + Parameters: ~ • {path} (`string`) @@ -5575,6 +5644,9 @@ isdirectory({directory}) *isdirectory()* exist or isn't a directory. {directory} is any expression, which is used as a String. + Attributes: ~ + |api-fast| + Parameters: ~ • {directory} (`string`) @@ -5924,6 +5996,9 @@ keytrans({string}) *keytrans()* echo keytrans(xx) < + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) @@ -7408,6 +7483,9 @@ nr2char({expr} [, {utf8}]) *nr2char()* characters. nr2char(0) is a real NUL and terminates the string, thus results in an empty string. + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`integer`) • {utf8} (`boolean?`) @@ -8310,6 +8388,9 @@ reltime({start}, {end}) Note: |localtime()| returns the current (non-relative) time. + Attributes: ~ + |api-fast| + Parameters: ~ • {start} (`any?`) • {end} (`any?`) @@ -8331,6 +8412,9 @@ reltimefloat({time}) *reltimefloat()* Also see |profiling|. If there is an error an empty string is returned + Attributes: ~ + |api-fast| + Parameters: ~ • {time} (`any`) @@ -8351,6 +8435,9 @@ reltimestr({time}) *reltimestr()* < Also see |profiling|. If there is an error an empty string is returned + Attributes: ~ + |api-fast| + Parameters: ~ • {time} (`any`) @@ -8452,6 +8539,9 @@ repeat({expr}, {count}) *repeat()* let longlist = repeat(['a', 'b'], 3) < Results in ['a', 'b', 'a', 'b', 'a', 'b']. + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`any`) • {count} (`integer`) @@ -8472,6 +8562,9 @@ resolve({filename}) *resolve()* *E65 current directory (provided the result is still a relative path name) and also keeps a trailing path separator. + Attributes: ~ + |api-fast| + Parameters: ~ • {filename} (`string`) @@ -10780,6 +10873,9 @@ state([{what}]) *state()* recursiveness up to "ccc") s screen has scrolled for messages + Attributes: ~ + |api-fast| + Parameters: ~ • {what} (`string?`) @@ -10837,6 +10933,9 @@ stdpath({what}) *stdpath()* *E610 echo stdpath("config") < + Attributes: ~ + |api-fast| + Parameters: ~ • {what} (`'cache'|'config'|'config_dirs'|'data'|'data_dirs'|'log'|'run'|'state'`) @@ -10886,6 +10985,9 @@ str2list({string} [, {utf8}]) *str2list()* echo str2list("á") " returns [97, 769] < + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {utf8} (`boolean?`) @@ -10931,6 +11033,9 @@ strcharlen({string}) *strcharlen()* Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) @@ -10952,6 +11057,9 @@ strcharpart({src}, {start} [, {len} [, {skipcc}]]) *strcharpart()* Returns an empty string on error. + Attributes: ~ + |api-fast| + Parameters: ~ • {src} (`string`) • {start} (`integer`) @@ -10992,6 +11100,9 @@ strchars({string} [, {skipcc}]) *strchars()* endif < + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {skipcc} (`0|1|boolean?`) @@ -11019,6 +11130,9 @@ strdisplaywidth({string} [, {col}]) *strdisplaywidth()* Also see |strlen()|, |strwidth()| and |strchars()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {col} (`integer?`) @@ -11061,6 +11175,9 @@ strgetchar({str}, {index}) *strgetchar()* Returns -1 if {index} is invalid. Also see |strcharpart()| and |strchars()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {str} (`string`) • {index} (`integer`) @@ -11091,6 +11208,9 @@ stridx({haystack}, {needle} [, {start}]) *stridx()* stridx() works similar to the C function strstr(). When used with a single character it works similar to strchr(). + Attributes: ~ + |api-fast| + Parameters: ~ • {haystack} (`string`) • {needle} (`string`) @@ -11145,6 +11265,9 @@ strlen({string}) *strlen()* |strchars()|. Also see |len()|, |strdisplaywidth()| and |strwidth()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) @@ -11180,6 +11303,9 @@ strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* strpart(getline("."), col(".") - 1, 1, v:true) < + Attributes: ~ + |api-fast| + Parameters: ~ • {src} (`string`) • {start} (`integer`) @@ -11258,6 +11384,9 @@ strtrans({string}) *strtrans()* Returns an empty string on error. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) @@ -11306,6 +11435,9 @@ strwidth({string}) *strwidth()* Returns zero on error. Also see |strlen()|, |strdisplaywidth()| and |strchars()|. + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) @@ -12004,6 +12136,9 @@ tolower({expr}) *tolower()* Converts a String to lowercase (like applying |gu|). Returns empty string on error. + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`string`) @@ -12016,6 +12151,9 @@ toupper({expr}) *toupper()* Converts a String to uppercase (like applying |gU|). Returns empty string on error. + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`string`) @@ -12036,6 +12174,9 @@ tr({src}, {fromstr}, {tostr}) *tr()* echo tr("", "<>", "{}") < returns "{blob}" + Attributes: ~ + |api-fast| + Parameters: ~ • {src} (`string`) • {fromstr} (`string`) @@ -12074,6 +12215,9 @@ trim({text} [, {mask} [, {dir}]]) *trim()* echo trim(" vim ", " ", 2) < returns " vim" + Attributes: ~ + |api-fast| + Parameters: ~ • {text} (`string`) • {mask} (`string?`) @@ -12134,6 +12278,9 @@ type({expr}) *type()* if exists('v:t_number') | endif < + Attributes: ~ + |api-fast| + Parameters: ~ • {expr} (`any`) @@ -12261,6 +12408,9 @@ utf16idx({string}, {idx} [, {countcc} [, {charidx}]]) *utf16idx()* echo utf16idx('a😊😊', 9) " returns -1 < + Attributes: ~ + |api-fast| + Parameters: ~ • {string} (`string`) • {idx} (`integer`) @@ -12697,6 +12847,9 @@ windowsversion() *windowsversion()* Lua: see |uv.os_uname()|. + Attributes: ~ + |api-fast| + Return: ~ (`string`) diff --git a/runtime/lua/vim/_core/editor.lua b/runtime/lua/vim/_core/editor.lua index 8e1971402f..98cd7b5ad6 100644 --- a/runtime/lua/vim/_core/editor.lua +++ b/runtime/lua/vim/_core/editor.lua @@ -713,31 +713,34 @@ end local on_key_cbs = {} --- @type table ---- Adds Lua function {fn} with namespace id {ns_id} as a listener to every, ---- yes every, input key. +--- Registers function {fn} with [namespace] {ns_id} as a listener to every, yes EVERY, input key. --- ---- The Nvim command-line option |-w| is related but does not support callbacks ---- and cannot be toggled dynamically. +--- To parse [key-chord]s, see |vim.keycode()|. Example: +--- ```lua +--- local keychords = vim.keycode(vim.fn.keytrans(key), true) +--- ``` --- +--- The |-w| command-line option is related but does not support callbacks and cannot be toggled +--- dynamically. +--- +---@note If {fn} returns an empty string, {key} is discarded/ignored; if {key} is [] then the +--- "[]…[]" sequence is discarded as a whole. +---@note Non-recursive: if {fn} itself consumes input, it won't be invoked for those keys. +---@note To UNregister a given {ns_id}, pass `nil` {fn}. ---@note {fn} will be removed on error. ----@note {fn} won't be invoked recursively, i.e. if {fn} itself consumes input, ---- it won't be invoked for those keys. ---@note {fn} will not be cleared by |nvim_buf_clear_namespace()| --- ---@param fn nil|fun(key: string, typed: string): string? Function invoked for every input key, ---- after mappings have been applied but before further processing. Arguments ---- {key} and {typed} are raw keycodes, where {key} is the key after mappings ---- are applied, and {typed} is the key(s) before mappings are applied. ---- {typed} may be empty if {key} is produced by non-typed key(s) or by the ---- same typed key(s) that produced a previous {key}. ---- If {fn} returns an empty string, {key} is discarded/ignored, and if {key} ---- is [] then the "[]…[]" sequence is discarded as a whole. ---- When {fn} is `nil`, the callback associated with namespace {ns_id} is removed. ----@param ns_id integer? Namespace ID. If nil or 0, generates and returns a ---- new |nvim_create_namespace()| id. +--- after mappings have been applied but before further processing. Arguments {key} and +--- {typed} are raw input (use [keytrans()] to get [keycodes]). +--- - {key} is the key after mappings are applied. +--- - {typed} is the input before mappings are applied; may be empty if {key} was produced +--- by non-typed key(s) or by the same typed key(s) that produced a previous {key}. +---@param ns_id integer? Namespace ID. If nil or 0, returns a new |namespace| id. ---@param opts table? Optional parameters --- ---@see |keytrans()| +---@see |vim.keycode()| --- ---@return integer Namespace id associated with {fn}. Or count of all callbacks ---if on_key() is called without arguments. @@ -1277,8 +1280,8 @@ end --- A list of single character modifiers of the key. --- @field mod ('M'|'T'|'C'|'S'|'2'|'3'|'4'|'D')[] ---- Converts keys from [key-notation] to the internal encoding. Optionally returns ---- structured key-chord info as retval 2. +--- Converts keys from [key-notation] to the internal encoding. Optionally returns structured +--- [key-chord]() info as retval 2. --- --- Inverse of [keytrans()], which converts the internal encoding back to [key-notation]. --- diff --git a/src/gen/gen_eval_files.lua b/src/gen/gen_eval_files.lua index cd6df9a891..76f450b8c6 100755 --- a/src/gen/gen_eval_files.lua +++ b/src/gen/gen_eval_files.lua @@ -571,6 +571,12 @@ local function render_eval_doc(f, fun, write) write('') end + if fun.fast then + write(util.md_to_vimdoc('Attributes: ~', 16, 16, TEXT_WIDTH)) + write(util.md_to_vimdoc('|api-fast|', 18, 18, TEXT_WIDTH)) + write('') + end + if #params > 0 then write(util.md_to_vimdoc('Parameters: ~', 16, 16, TEXT_WIDTH)) for i, param in ipairs(params) do diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 6b5ed59e07..389cc379b5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -493,7 +493,7 @@ error: /// @param do_lt Also translate []. Ignored if `special` is false. /// @param special Replace |keycodes|, e.g. [] becomes a "\r" char. String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special) - FUNC_API_SINCE(1) FUNC_API_RET_ALLOC + FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_RET_ALLOC { if (str.size == 0) { // Empty string diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index a07e3be0c3..f966f48da4 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -1233,6 +1233,7 @@ M.funcs = { echo charidx('a😊😊', 4, 0, 1) " returns 2 < ]=], + fast = true, name = 'charidx', params = { { 'string', 'string' }, @@ -6506,6 +6507,7 @@ M.funcs = { < ]=], + fast = true, name = 'keytrans', params = { { 'string', 'string' } }, returns = 'string', @@ -8099,6 +8101,7 @@ M.funcs = { string, thus results in an empty string. ]=], + fast = true, name = 'nr2char', params = { { 'expr', 'integer' }, { 'utf8', 'boolean' } }, returns = 'string', @@ -11818,6 +11821,7 @@ M.funcs = { echo str2list("á") " returns [97, 769] < ]=], + fast = true, name = 'str2list', params = { { 'string', 'string' }, { 'utf8', 'boolean' } }, signature = 'str2list({string} [, {utf8}])', @@ -11865,6 +11869,7 @@ M.funcs = { Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. ]=], + fast = true, name = 'strcharlen', params = { { 'string', 'string' } }, returns = 'integer', @@ -11931,6 +11936,7 @@ M.funcs = { endif < ]=], + fast = true, name = 'strchars', params = { { 'string', 'string' }, { 'skipcc', '0|1|boolean' } }, returns = 'integer', @@ -11958,6 +11964,7 @@ M.funcs = { Also see |strlen()|, |strwidth()| and |strchars()|. ]=], + fast = true, name = 'strdisplaywidth', params = { { 'string', 'string' }, { 'col', 'integer' } }, returns = 'integer', @@ -12005,6 +12012,7 @@ M.funcs = { Also see |strcharpart()| and |strchars()|. ]=], + fast = true, name = 'strgetchar', params = { { 'str', 'string' }, { 'index', 'integer' } }, returns = 'integer', @@ -12091,6 +12099,7 @@ M.funcs = { Also see |len()|, |strdisplaywidth()| and |strwidth()|. ]=], + fast = true, name = 'strlen', params = { { 'string', 'string' } }, returns = 'integer', @@ -13085,6 +13094,7 @@ M.funcs = { '), + keytrans = vim.fn.keytrans(vim.keycode('')), + nr2char = vim.fn.nr2char(65), + replace_termcodes = vim.api.nvim_replace_termcodes('', true, true, true), + str2list = vim.fn.str2list('AB'), + strcharlen = vim.fn.strcharlen('abc'), + strcharpart = vim.fn.strcharpart('héllo', 1, 2), + strchars = vim.fn.strchars('abc'), + strdisplaywidth = vim.fn.strdisplaywidth('ab'), + strgetchar = vim.fn.strgetchar('abc', 1), + strlen = vim.fn.strlen('abc'), + strpart = vim.fn.strpart('abcdef', 1, 3), + strtrans = vim.fn.strtrans('a\001b'), + tr = vim.fn.tr('abc', 'ab', 'AB'), + trim = vim.fn.trim(' x '), + utf16idx = vim.fn.utf16idx('abc', 2), + } + end) + end) + vim.wait(2000, function() + return result ~= nil + end) + return result + end) + eq({ + byteidx = 3, + char2nr = 65, + charidx = 2, + in_fast = true, + keycode = '\1', -- + keytrans = '', + nr2char = 'A', + replace_termcodes = '\27', -- + str2list = { 65, 66 }, + strcharlen = 3, + strcharpart = 'él', + strchars = 3, + strdisplaywidth = 2, + strgetchar = 98, + strlen = 3, + strpart = 'bcd', + strtrans = 'a^Ab', + tr = 'ABc', + trim = 'x', + utf16idx = 2, + }, out) + end) + + it('do not trigger os_breakcheck()', function() + local res = exec_lua(function() + local polls = 0 + -- os_breakcheck() polls the event loop (loop_poll_events), which fires a uv_check handle. + local chk = assert(vim.uv.new_check()) + chk:start(function() + polls = polls + 1 + end) + local function polled(fn) + polls = 0 + fn() + return polls ~= 0 + end + + -- Big (multibyte) input => per-char breakcheck would exceed BREAKCHECK_SKIP (1000, or 100_000 + -- for `veryfast_breakcheck`). Small input could pass (false negative). + local big = ('héllo wörld '):rep(100000) -- 1.2M chars. + + local cases = { + byteidx = function() + vim.fn.byteidx(big, 500000) + end, + char2nr = function() + vim.fn.char2nr('A') + end, + charidx = function() + vim.fn.charidx(big, 500000) + end, + keycode = function() + vim.keycode('') + end, + keytrans = function() + vim.fn.keytrans(vim.keycode('')) + end, + nr2char = function() + vim.fn.nr2char(65) + end, + replace_termcodes = function() + vim.api.nvim_replace_termcodes('', true, true, true) + end, + str2list = function() + vim.fn.str2list(big) + end, + strcharlen = function() + vim.fn.strcharlen(big) + end, + strcharpart = function() + vim.fn.strcharpart(big, 1, 2) + end, + strchars = function() + vim.fn.strchars(big) + end, + strdisplaywidth = function() + vim.fn.strdisplaywidth(big) + end, + strgetchar = function() + vim.fn.strgetchar(big, 500000) + end, + strlen = function() + vim.fn.strlen(big) + end, + strpart = function() + vim.fn.strpart(big, 1, 3) + end, + strtrans = function() + vim.fn.strtrans(big) + end, + tr = function() + vim.fn.tr(big, 'ho', 'HO') + end, + trim = function() + vim.fn.trim(big) + end, + utf16idx = function() + vim.fn.utf16idx(big, 500000) + end, + } + + local bad = {} + for name, fn in pairs(cases) do + if polled(fn) then + bad[#bad + 1] = name + end + end + table.sort(bad) + + -- Positive control: a big Vimscript loop calls line_breakcheck() every iteration. + local control = polled(function() + vim.api.nvim_exec2('for i in range(1500000)|endfor', {}) + end) + + chk:stop() + chk:close() + return { bad = bad, control = control } + end) + + eq(true, res.control) -- Confirm our detector actually works: breakcheck was seen. + eq({}, res.bad) -- No api-fast function polled the event-loop. + end) +end)