diff options
65 files changed, 1356 insertions, 419 deletions
diff --git a/.github/scripts/install_deps.sh b/.github/scripts/install_deps.sh index 0533bd7fae..9a782e9698 100755 --- a/.github/scripts/install_deps.sh +++ b/.github/scripts/install_deps.sh @@ -30,7 +30,7 @@ if [[ $os == Linux ]]; then fi if [[ -n $TEST ]]; then - sudo apt-get install -y locales-all cpanminus attr libattr1-dev + sudo apt-get install -y locales-all cpanminus attr libattr1-dev gdb fi elif [[ $os == Darwin ]]; then brew update --quiet diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1ebb26d5c0..acf0f195b9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,6 @@ env: LOG_DIR: ${{ github.workspace }}/build/log NVIM_LOG_FILE: ${{ github.workspace }}/build/.nvimlog TSAN_OPTIONS: log_path=${{ github.workspace }}/build/log/tsan - UBSAN_OPTIONS: log_path=${{ github.workspace }}/build/log/ubsan VALGRIND_LOG: ${{ github.workspace }}/build/log/valgrind-%p.log # TEST_FILE: test/functional/core/startup_spec.lua # TEST_FILTER: foo diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 35c94d6c74..bc4d1f30c9 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -4133,7 +4133,8 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* When {dict} is omitted or zero: Return the rhs of mapping {name} in mode {mode}. The returned String has special characters translated like in the output of the ":map" command - listing. + listing. When {dict} is TRUE a dictionary is returned, see + below. To get a list of all mappings see |maplist()|. When there is no mapping for {name}, an empty String is returned if {dict} is FALSE, otherwise returns an empty Dict. @@ -4161,7 +4162,7 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* When {dict} is there and it is |TRUE| return a dictionary containing all the information of the mapping with the - following items: + following items: *mapping-dict* "lhs" The {lhs} of the mapping as it would be typed "lhsraw" The {lhs} of the mapping as raw bytes "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate @@ -4180,9 +4181,16 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* (|mapmode-ic|) "sid" The script local ID, used for <sid> mappings (|<SID>|). Negative for special contexts. + "scriptversion" The version of the script, always 1. "lnum" The line number in "sid", zero if unknown. "nowait" Do not wait for other, longer mappings. (|:map-<nowait>|). + "abbr" True if this is an |abbreviation|. + "mode_bits" Nvim's internal binary representation of "mode". + |mapset()| ignores this; only "mode" is used. + See |maplist()| for usage examples. The values + are from src/nvim/vim.h and may change in the + future. The dictionary can be used to restore a mapping with |mapset()|. @@ -4226,6 +4234,37 @@ mapcheck({name} [, {mode} [, {abbr}]]) *mapcheck()* < This avoids adding the "_vv" mapping when there already is a mapping for "_v" or for "_vvv". +maplist([{abbr}]) *maplist()* + Returns a |List| of all mappings. Each List item is a |Dict|, + the same as what is returned by |maparg()|, see + |mapping-dict|. When {abbr} is there and it is |TRUE| use + abbreviations instead of mappings. + + Example to show all mappings with "MultiMatch" in rhs: >vim + echo maplist()->filter({_, m -> + \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 + \ }) +< It can be tricky to find mappings for particular |:map-modes|. + |mapping-dict|'s "mode_bits" can simplify this. For example, + the mode_bits for Normal, Insert or Command-line modes are + 0x19. To find all the mappings available in those modes you + can do: >vim + let saved_maps = [] + for m in maplist() + if and(m.mode_bits, 0x19) != 0 + eval saved_maps->add(m) + endif + endfor + echo saved_maps->mapnew({_, m -> m.lhs}) +< The values of the mode_bits are defined in Nvim's + src/nvim/vim.h file and they can be discovered at runtime + using |:map-commands| and "maplist()". Example: >vim + omap xyzzy <Nop> + let op_bit = maplist()->filter( + \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits + ounmap xyzzy + echo printf("Operator-pending mode bit: 0x%x", op_bit) + mapnew({expr1}, {expr2}) *mapnew()* Like |map()| but instead of replacing items in {expr1} a new List or Dictionary is created and returned. {expr1} remains @@ -4233,9 +4272,17 @@ mapnew({expr1}, {expr2}) *mapnew()* don't want that use |deepcopy()| first. mapset({mode}, {abbr}, {dict}) *mapset()* - Restore a mapping from a dictionary returned by |maparg()|. - {mode} and {abbr} should be the same as for the call to - |maparg()|. *E460* + Restore a mapping from a dictionary, possibly returned by + |maparg()| or |maplist()|. A buffer mapping, when dict.buffer + is true, is set on the current buffer; it is up to the caller + to ensure that the intended buffer is the current buffer. This + feature allows copying mappings from one buffer to another. + The dict.mode value may restore a single mapping that covers + more than one mode, like with mode values of '!', ' ', "nox", + or 'v'. *E1276* + + In the first form, {mode} and {abbr} should be the same as + for the call to |maparg()|. *E460* {mode} is used to define the mode in which the mapping is set, not the "mode" entry in {dict}. Example for saving and restoring a mapping: >vim @@ -4244,8 +4291,21 @@ mapset({mode}, {abbr}, {dict}) *mapset()* " ... call mapset('n', 0, save_map) < Note that if you are going to replace a map in several modes, - e.g. with `:map!`, you need to save the mapping for all of - them, since they can differ. + e.g. with `:map!`, you need to save/restore the mapping for + all of them, when they might differ. + + In the second form, with {dict} as the only argument, mode + and abbr are taken from the dict. + Example: >vim + let save_maps = maplist()->filter( + \ {_, m -> m.lhs == 'K'}) + nnoremap K somethingelse + cnoremap K somethingelse2 + " ... + unmap K + for d in save_maps + call mapset(d) + endfor match({expr}, {pat} [, {start} [, {count}]]) *match()* When {expr} is a |List| then this returns the index of the diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 56e58baad8..6f61259af0 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -955,7 +955,7 @@ operator to add quotes around text in the current line: > \ ->setline(".")}'<CR>g@ ============================================================================== -2. Abbreviations *abbreviations* *Abbreviations* +2. Abbreviations *abbreviation* *abbreviations* *Abbreviations* Abbreviations are used in Insert mode, Replace mode and Command-line mode. If you enter a word that is an abbreviation, it is replaced with the word it diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index 2f7197e2ef..f038163ec2 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -399,18 +399,22 @@ Prompt mode can be used with: >vim If there is no g:termdebug_config you can use: >vim let g:termdebug_use_prompt = 1 < - *termdebug_map_K* -The K key is normally mapped to |:Evaluate|. If you do not want this use: >vim +Mappings ~ + *termdebug_map_K* *termdebug-mappings* +The K key is normally mapped to |:Evaluate| unless a buffer local (|:map-local|) +mapping to K already exists. If you do not want this use: >vim let g:termdebug_config['map_K'] = 0 If there is no g:termdebug_config you can use: >vim let g:termdebug_map_K = 0 < *termdebug_map_minus* -The - key is normally mapped to |:Down|. If you do not want this use: >vim +The - key is normally mapped to |:Down| unless a buffer local mapping to the - +key already exists. If you do not want this use: >vim let g:termdebug_config['map_minus'] = 0 < *termdebug_map_plus* -The + key is normally mapped to |:Up|. If you do not want this use: >vim +The + key is normally mapped to |:Up| unless a buffer local mapping to the + +key already exists. If you do not want this use: >vim let g:termdebug_config['map_plus'] = 0 < *termdebug_disasm_window* @@ -421,6 +425,9 @@ If you want the Asm window shown by default, set the "disasm_window" flag to If there is no g:termdebug_config you can use: >vim let g:termdebug_disasm_window = 15 Any value greater than 1 will set the Asm window height to that value. +If the current window has enough horizontal space, it will be vertically split +and the Asm window will be shown side by side with the source code window (and +the height option won't be used). *termdebug_variables_window* If you want the Var window shown by default, set the "variables_window" flag @@ -431,6 +438,9 @@ height: >vim If there is no g:termdebug_config you can use: >vim let g:termdebug_variables_window = 15 Any value greater than 1 will set the Var window height to that value. +If the current window has enough horizontal space, it will be vertically split +and the Var window will be shown side by side with the source code window (and +the height options won't be used). Communication ~ *termdebug-communication* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b2a020cf9d..d92e528214 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2521,10 +2521,8 @@ A jump table for the options with a short description can be found at |Q_op|. default to single-byte alternatives. Example: > - :set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- -< This is similar to the default, except that these characters will also - be used when there is highlighting. - + :set fillchars=stl:\ ,stlnc:\ ,vert:│,fold:·,diff:- +< For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items single-byte and multibyte characters are supported. But double-width characters are not supported. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index c8b31b2d5b..e206a804f4 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1015,6 +1015,7 @@ Mappings and Menus: *mapping-functions* hasmapto() check if a mapping exists mapcheck() check if a matching mapping exists maparg() get rhs of a mapping + maplist() get list of all mappings mapset() restore a mapping menu_info() get information about a menu item wildmenumode() check if the wildmode is active diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index efebf46d85..1e12ccd7cc 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -550,10 +550,6 @@ Vimscript compatibility: `shell_error` does not alias to |v:shell_error| `this_session` does not alias to |v:this_session| -UI/Display: - 'statusline' always uses the "stl" and "stlnc" fields of 'fillchars', even - if they are the same and |hl-StatusLine| and |hl-StatusLineNC| are equal. - Working directory (Vim implemented some of these after Nvim): - |DirChanged| and |DirChangedPre| can be triggered when switching to another window or tab. diff --git a/runtime/ftplugin/wget.vim b/runtime/ftplugin/wget.vim index 7a10221824..db3b62191e 100644 --- a/runtime/ftplugin/wget.vim +++ b/runtime/ftplugin/wget.vim @@ -1,5 +1,5 @@ " Vim filetype plugin file -" Language: Wget configuration file (/etc/wgetrc ~/.wgetrc) +" Language: Wget configuration file (/etc/wgetrc ~/.wgetrc) " Maintainer: Doug Kearns <dougkearns@gmail.com> " Last Change: 2022 Apr 28 @@ -18,7 +18,7 @@ setlocal formatoptions-=t formatoptions+=croql let b:undo_ftplugin = "setl fo< com< cms<" if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") - let b:browsefilter = "Wget Configuration File (wgetrc, .wgetrc)\twgetrc;.wgetrc\n" . + let b:browsefilter = "Wget Configuration File (wgetrc, .wgetrc)\twgetrc;.wgetrc\n" .. \ "All Files (*.*)\t*.*\n" let b:undo_ftplugin ..= " | unlet! b:browsefilter" endif diff --git a/runtime/ftplugin/wget2.vim b/runtime/ftplugin/wget2.vim index a6845f6df5..8cfcd514e5 100644 --- a/runtime/ftplugin/wget2.vim +++ b/runtime/ftplugin/wget2.vim @@ -1,5 +1,5 @@ " Vim filetype plugin file -" Language: Wget2 configuration file (/etc/wget2rc ~/.wget2rc) +" Language: Wget2 configuration file (/etc/wget2rc ~/.wget2rc) " Maintainer: Doug Kearns <dougkearns@gmail.com> " Last Change: 2022 Apr 28 @@ -18,7 +18,7 @@ setlocal formatoptions-=t formatoptions+=croql let b:undo_ftplugin = "setl fo< com< cms<" if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") - let b:browsefilter = "Wget2 Configuration File (wget2rc, .wget2rc)\twget2rc;.wget2rc\n" . + let b:browsefilter = "Wget2 Configuration File (wget2rc, .wget2rc)\twget2rc;.wget2rc\n" .. \ "All Files (*.*)\t*.*\n" let b:undo_ftplugin ..= " | unlet! b:browsefilter" endif diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index a986cd752c..53f7166b3d 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -2217,10 +2217,8 @@ vim.bo.ft = vim.bo.filetype --- --- Example: --- ``` ---- :set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- +--- :set fillchars=stl:\ ,stlnc:\ ,vert:│,fold:·,diff:- --- ``` ---- This is similar to the default, except that these characters will also ---- be used when there is highlighting. --- --- For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items --- single-byte and multibyte characters are supported. But double-width diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 0563f9dd5f..d1e676ef70 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -4994,7 +4994,8 @@ function vim.fn.map(expr1, expr2) end --- When {dict} is omitted or zero: Return the rhs of mapping --- {name} in mode {mode}. The returned String has special --- characters translated like in the output of the ":map" command ---- listing. +--- listing. When {dict} is TRUE a dictionary is returned, see +--- below. To get a list of all mappings see |maplist()|. --- --- When there is no mapping for {name}, an empty String is --- returned if {dict} is FALSE, otherwise returns an empty Dict. @@ -5022,7 +5023,7 @@ function vim.fn.map(expr1, expr2) end --- --- When {dict} is there and it is |TRUE| return a dictionary --- containing all the information of the mapping with the ---- following items: +--- following items: *mapping-dict* --- "lhs" The {lhs} of the mapping as it would be typed --- "lhsraw" The {lhs} of the mapping as raw bytes --- "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate @@ -5041,9 +5042,16 @@ function vim.fn.map(expr1, expr2) end --- (|mapmode-ic|) --- "sid" The script local ID, used for <sid> mappings --- (|<SID>|). Negative for special contexts. +--- "scriptversion" The version of the script, always 1. --- "lnum" The line number in "sid", zero if unknown. --- "nowait" Do not wait for other, longer mappings. --- (|:map-<nowait>|). +--- "abbr" True if this is an |abbreviation|. +--- "mode_bits" Nvim's internal binary representation of "mode". +--- |mapset()| ignores this; only "mode" is used. +--- See |maplist()| for usage examples. The values +--- are from src/nvim/vim.h and may change in the +--- future. --- --- The dictionary can be used to restore a mapping with --- |mapset()|. @@ -5099,6 +5107,39 @@ function vim.fn.maparg(name, mode, abbr, dict) end --- @return any function vim.fn.mapcheck(name, mode, abbr) end +--- Returns a |List| of all mappings. Each List item is a |Dict|, +--- the same as what is returned by |maparg()|, see +--- |mapping-dict|. When {abbr} is there and it is |TRUE| use +--- abbreviations instead of mappings. +--- +--- Example to show all mappings with "MultiMatch" in rhs: >vim +--- echo maplist()->filter({_, m -> +--- \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 +--- \ }) +--- <It can be tricky to find mappings for particular |:map-modes|. +--- |mapping-dict|'s "mode_bits" can simplify this. For example, +--- the mode_bits for Normal, Insert or Command-line modes are +--- 0x19. To find all the mappings available in those modes you +--- can do: >vim +--- let saved_maps = [] +--- for m in maplist() +--- if and(m.mode_bits, 0x19) != 0 +--- eval saved_maps->add(m) +--- endif +--- endfor +--- echo saved_maps->mapnew({_, m -> m.lhs}) +--- <The values of the mode_bits are defined in Nvim's +--- src/nvim/vim.h file and they can be discovered at runtime +--- using |:map-commands| and "maplist()". Example: >vim +--- omap xyzzy <Nop> +--- let op_bit = maplist()->filter( +--- \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits +--- ounmap xyzzy +--- echo printf("Operator-pending mode bit: 0x%x", op_bit) +--- +--- @return any +function vim.fn.maplist() end + --- Like |map()| but instead of replacing items in {expr1} a new --- List or Dictionary is created and returned. {expr1} remains --- unchanged. Items can still be changed by {expr2}, if you @@ -5109,9 +5150,17 @@ function vim.fn.mapcheck(name, mode, abbr) end --- @return any function vim.fn.mapnew(expr1, expr2) end ---- Restore a mapping from a dictionary returned by |maparg()|. ---- {mode} and {abbr} should be the same as for the call to ---- |maparg()|. *E460* +--- Restore a mapping from a dictionary, possibly returned by +--- |maparg()| or |maplist()|. A buffer mapping, when dict.buffer +--- is true, is set on the current buffer; it is up to the caller +--- to ensure that the intended buffer is the current buffer. This +--- feature allows copying mappings from one buffer to another. +--- The dict.mode value may restore a single mapping that covers +--- more than one mode, like with mode values of '!', ' ', "nox", +--- or 'v'. *E1276* +--- +--- In the first form, {mode} and {abbr} should be the same as +--- for the call to |maparg()|. *E460* --- {mode} is used to define the mode in which the mapping is set, --- not the "mode" entry in {dict}. --- Example for saving and restoring a mapping: >vim @@ -5120,12 +5169,25 @@ function vim.fn.mapnew(expr1, expr2) end --- " ... --- call mapset('n', 0, save_map) --- <Note that if you are going to replace a map in several modes, ---- e.g. with `:map!`, you need to save the mapping for all of ---- them, since they can differ. +--- e.g. with `:map!`, you need to save/restore the mapping for +--- all of them, when they might differ. +--- +--- In the second form, with {dict} as the only argument, mode +--- and abbr are taken from the dict. +--- Example: >vim +--- let save_maps = maplist()->filter( +--- \ {_, m -> m.lhs == 'K'}) +--- nnoremap K somethingelse +--- cnoremap K somethingelse2 +--- " ... +--- unmap K +--- for d in save_maps +--- call mapset(d) +--- endfor --- --- @param mode string ---- @param abbr any ---- @param dict any +--- @param abbr? any +--- @param dict? any --- @return any function vim.fn.mapset(mode, abbr, dict) end diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index a0710476a9..8beca8ffb3 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2023 Aug 23 +" Last Change: 2023 Nov 02 " " WORK IN PROGRESS - The basics works stable, more to come " Note: In general you need at least GDB 7.12 because this provides the @@ -894,7 +894,9 @@ func s:ParseVarinfo(varinfo) let nameIdx = matchstrpos(a:varinfo, '{name="\([^"]*\)"') let dict['name'] = a:varinfo[nameIdx[1] + 7 : nameIdx[2] - 2] let typeIdx = matchstrpos(a:varinfo, ',type="\([^"]*\)"') - let dict['type'] = a:varinfo[typeIdx[1] + 7 : typeIdx[2] - 2] + " 'type' maybe is a url-like string, + " try to shorten it and show only the /tail + let dict['type'] = (a:varinfo[typeIdx[1] + 7 : typeIdx[2] - 2])->fnamemodify(':t') let valueIdx = matchstrpos(a:varinfo, ',value="\(.*\)"}') if valueIdx[1] == -1 let dict['value'] = 'Complex value' @@ -1022,7 +1024,9 @@ func s:InstallCommands() endif if map let s:k_map_saved = maparg('K', 'n', 0, 1) - nnoremap K :Evaluate<CR> + if !empty(s:k_map_saved) && !s:k_map_saved.buffer || empty(s:k_map_saved) + nnoremap K :Evaluate<CR> + endif endif let map = 1 @@ -1031,7 +1035,9 @@ func s:InstallCommands() endif if map let s:plus_map_saved = maparg('+', 'n', 0, 1) - nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>' + if !empty(s:plus_map_saved) && !s:plus_map_saved.buffer || empty(s:plus_map_saved) + nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>' + endif endif let map = 1 @@ -1040,7 +1046,9 @@ func s:InstallCommands() endif if map let s:minus_map_saved = maparg('-', 'n', 0, 1) - nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>' + if !empty(s:minus_map_saved) && !s:minus_map_saved.buffer || empty(s:minus_map_saved) + nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>' + endif endif @@ -1106,29 +1114,29 @@ func s:DeleteCommands() delcommand Winbar if exists('s:k_map_saved') - if empty(s:k_map_saved) + if !empty(s:k_map_saved) && !s:k_map_saved.buffer + nunmap K + call mapset(s:k_map_saved) + elseif empty(s:k_map_saved) nunmap K - else - " call mapset(s:k_map_saved) - call mapset('n', 0, s:k_map_saved) endif unlet s:k_map_saved endif if exists('s:plus_map_saved') - if empty(s:plus_map_saved) + if !empty(s:plus_map_saved) && !s:plus_map_saved.buffer + nunmap + + call mapset(s:plus_map_saved) + elseif empty(s:plus_map_saved) nunmap + - else - " call mapset(s:plus_map_saved) - call mapset('n', 0, s:plus_map_saved) endif unlet s:plus_map_saved endif if exists('s:minus_map_saved') - if empty(s:minus_map_saved) + if !empty(s:minus_map_saved) && !s:minus_map_saved.buffer + nunmap - + call mapset(s:minus_map_saved) + elseif empty(s:minus_map_saved) nunmap - - else - " call mapset(s:minus_map_saved) - call mapset('n', 0, s:minus_map_saved) endif unlet s:minus_map_saved endif @@ -1556,8 +1564,15 @@ endfunc func s:GotoAsmwinOrCreateIt() if !win_gotoid(s:asmwin) + let mdf = '' if win_gotoid(s:sourcewin) - exe 'rightbelow new' + " 60 is approx spaceBuffer * 3 + if winwidth(0) > (78 + 60) + let mdf = 'vert' + exe mdf .. ' ' .. 60 .. 'new' + else + exe 'rightbelow new' + endif else exe 'new' endif @@ -1579,7 +1594,7 @@ func s:GotoAsmwinOrCreateIt() let s:asmbuf = bufnr('Termdebug-asm-listing') endif - if s:GetDisasmWindowHeight() > 0 + if mdf != 'vert' && s:GetDisasmWindowHeight() > 0 exe 'resize ' .. s:GetDisasmWindowHeight() endif endif @@ -1619,8 +1634,15 @@ endfunc func s:GotoVariableswinOrCreateIt() if !win_gotoid(s:varwin) + let mdf = '' if win_gotoid(s:sourcewin) - exe 'rightbelow new' + " 60 is approx spaceBuffer * 3 + if winwidth(0) > (78 + 60) + let mdf = 'vert' + exe mdf .. ' ' .. 60 .. 'new' + else + exe 'rightbelow new' + endif else exe 'new' endif @@ -1641,7 +1663,7 @@ func s:GotoVariableswinOrCreateIt() let s:varbuf = bufnr('Termdebug-variables-listing') endif - if s:GetVariablesWindowHeight() > 0 + if mdf != 'vert' && s:GetVariablesWindowHeight() > 0 exe 'resize ' .. s:GetVariablesWindowHeight() endif endif @@ -1910,3 +1932,5 @@ call s:InitAutocmd() let &cpo = s:keepcpo unlet s:keepcpo + +" vim: sw=2 sts=2 et diff --git a/runtime/syntax/wget.vim b/runtime/syntax/wget.vim index 8178d02bad..93206c2005 100644 --- a/runtime/syntax/wget.vim +++ b/runtime/syntax/wget.vim @@ -1,7 +1,7 @@ " Vim syntax file -" Language: Wget configuration file (/etc/wgetrc ~/.wgetrc) +" Language: Wget configuration file (/etc/wgetrc ~/.wgetrc) " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2022 Apr 28 +" Last Change: 2023 Nov 05 " GNU Wget 1.21 built on linux-gnu. @@ -12,7 +12,7 @@ endif let s:cpo_save = &cpo set cpo&vim -syn match wgetComment "#.*$" contains=wgetTodo contained +syn match wgetComment "#.*" contains=wgetTodo contained syn keyword wgetTodo TODO NOTE FIXME XXX contained @@ -21,208 +21,206 @@ syn region wgetString start=+'+ skip=+\\\\\|\\'+ end=+'+ contained oneline syn case ignore -syn keyword wgetBoolean on off yes no contained -syn keyword wgetNumber inf contained - -syn match wgetNumber "\<\d\+>" contained -syn match wgetQuota "\<\d\+[kmgt]\>" contained -syn match wgetTime "\<\d\+[smhdw]\>" contained +syn keyword wgetBoolean on off yes no contained +syn keyword wgetNumber inf contained +syn match wgetNumber "\<\d\+>" contained +syn match wgetQuota "\<\d\+[kmgt]\>" contained +syn match wgetTime "\<\d\+[smhdw]\>" contained "{{{ Commands let s:commands =<< trim EOL accept - accept_regex - add_host_dir - adjust_extension - always_rest - ask_password - auth_no_challenge + accept-regex + add-host-dir + adjust-extension + always-rest + ask-password + auth-no-challenge background - backup_converted + backup-converted backups base - bind_address - bind_dns_address - body_data - body_file - ca_certificate - ca_directory + bind-address + bind-dns-address + body-data + body-file + ca-certificate + ca-directory cache certificate - certificate_type - check_certificate - choose_config + certificate-type + check-certificate + choose-config ciphers compression - connect_timeout - content_disposition - content_on_error + connect-timeout + content-disposition + content-on-error continue - convert_file_only - convert_links + convert-file-only + convert-links cookies - crl_file - cut_dirs + crl-file + cut-dirs debug - default_page - delete_after - dns_cache - dns_servers - dns_timeout - dir_prefix - dir_struct + default-page + delete-after + dns-cache + dns-servers + dns-timeout + dir-prefix + dir-struct domains - dot_bytes - dots_in_line - dot_spacing - dot_style - egd_file - exclude_directories - exclude_domains - follow_ftp - follow_tags - force_html - ftp_passwd - ftp_password - ftp_user - ftp_proxy - ftps_clear_data_connection - ftps_fallback_to_ftp - ftps_implicit - ftps_resume_ssl + dot-bytes + dots-in-line + dot-spacing + dot-style + egd-file + exclude-directories + exclude-domains + follow-ftp + follow-tags + force-html + ftp-passwd + ftp-password + ftp-user + ftp-proxy + ftps-clear-data-connection + ftps-fallback-to-ftp + ftps-implicit + ftps-resume-ssl hsts - hsts_file - ftp_stmlf + hsts-file + ftp-stmlf glob header - html_extension + html-extension htmlify - http_keep_alive - http_passwd - http_password - http_proxy - https_proxy - https_only - http_user - if_modified_since - ignore_case - ignore_length - ignore_tags - include_directories - inet4_only - inet6_only + http-keep-alive + http-passwd + http-password + http-proxy + https-proxy + https-only + http-user + if-modified-since + ignore-case + ignore-length + ignore-tags + include-directories + inet4-only + inet6-only input - input_meta_link + input-meta-link iri - keep_bad_hash - keep_session_cookies - kill_longer - limit_rate - load_cookies + keep-bad-hash + keep-session-cookies + kill-longer + limit-rate + load-cookies locale - local_encoding + local-encoding logfile login - max_redirect - metalink_index - metalink_over_http + max-redirect + metalink-index + metalink-over-http method mirror netrc - no_clobber - no_config - no_parent - no_proxy + no-clobber + no-config + no-parent + no-proxy numtries - output_document - page_requisites - passive_ftp + output-document + page-requisites + passive-ftp passwd password - pinned_pubkey - post_data - post_file - prefer_family - preferred_location - preserve_permissions - private_key - private_key_type + pinned-pubkey + post-data + post-file + prefer-family + preferred-location + preserve-permissions + private-key + private-key-type progress - protocol_directories - proxy_passwd - proxy_password - proxy_user + protocol-directories + proxy-passwd + proxy-password + proxy-user quiet quota - random_file - random_wait - read_timeout - rec_level + random-file + random-wait + read-timeout + rec-level recursive referer - regex_type + regex-type reject - rejected_log - reject_regex - relative_only - remote_encoding - remove_listing - report_speed - restrict_file_names - retr_symlinks - retry_connrefused - retry_on_host_error - retry_on_http_error + rejected-log + reject-regex + relative-only + remote-encoding + remove-listing + report-speed + restrict-file-names + retr-symlinks + retry-connrefused + retry-on-host-error + retry-on-http-error robots - save_cookies - save_headers - secure_protocol - server_response - show_all_dns_entries - show_progress - simple_host_check - span_hosts + save-cookies + save-headers + secure-protocol + server-response + show-all-dns-entries + show-progress + simple-host-check + span-hosts spider - start_pos - strict_comments + start-pos + strict-comments sslcertfile sslcertkey timeout timestamping - use_server_timestamps + use-server-timestamps tries - trust_server_names + trust-server-names unlink - use_askpass + use-askpass user - use_proxy - user_agent + use-proxy + user-agent verbose wait - wait_retry - warc_cdx - warc_cdx_dedup - warc_compression - warc_digests - warc_file - warc_header - warc_keep_log - warc_max_size - warc_temp_dir + wait-retry + warc-cdx + warc-cdx-dedup + warc-compression + warc-digests + warc-file + warc-header + warc-keep-log + warc-max-size + warc-temp-dir wdebug xattr EOL "}}} -call map(s:commands, "substitute(v:val, '_', '[-_]\\\\=', 'g')") - for cmd in s:commands - exe 'syn match wgetCommand "\<' . cmd . '\>" nextgroup=wgetAssignmentOperator skipwhite contained' + exe 'syn match wgetCommand "\<' .. substitute(cmd, '-', '[-_]\\=', "g") .. '\>" nextgroup=wgetAssignmentOperator skipwhite contained' endfor +unlet s:commands syn case match -syn match wgetStart "^" nextgroup=wgetCommand,wgetComment skipwhite +syn match wgetLineStart "^" nextgroup=wgetCommand,wgetComment skipwhite syn match wgetAssignmentOperator "=" nextgroup=wgetString,wgetBoolean,wgetNumber,wgetQuota,wgetTime skipwhite contained hi def link wgetAssignmentOperator Special diff --git a/runtime/syntax/wget2.vim b/runtime/syntax/wget2.vim index a63c336f06..3e9abdf23d 100644 --- a/runtime/syntax/wget2.vim +++ b/runtime/syntax/wget2.vim @@ -1,9 +1,9 @@ " Vim syntax file -" Language: Wget2 configuration file (/etc/wget2rc ~/.wget2rc) +" Language: Wget2 configuration file (/etc/wget2rc ~/.wget2rc) " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2022 Apr 28 +" Last Change: 2023 Nov 05 -" GNU Wget2 2.0.0 - multithreaded metalink/file/website downloader +" GNU Wget2 2.1.0 - multithreaded metalink/file/website downloader if exists("b:current_syntax") finish @@ -12,21 +12,20 @@ endif let s:cpo_save = &cpo set cpo&vim -syn match wgetComment "#.*$" contains=wgetTodo contained +syn match wget2Comment "#.*" contains=wget2Todo contained -syn keyword wgetTodo TODO NOTE FIXME XXX contained +syn keyword wget2Todo TODO NOTE FIXME XXX contained -syn region wgetString start=+"+ skip=+\\\\\|\\"+ end=+"+ contained oneline -syn region wgetString start=+'+ skip=+\\\\\|\\'+ end=+'+ contained oneline +syn region wget2String start=+"+ skip=+\\\\\|\\"+ end=+"+ contained oneline +syn region wget2String start=+'+ skip=+\\\\\|\\'+ end=+'+ contained oneline syn case ignore -syn keyword wgetBoolean on off yes no y n contained -syn keyword wgetNumber infinity inf contained - -syn match wgetNumber "\<\d\+>" contained -syn match wgetQuota "\<\d\+[kmgt]\>" contained -syn match wgetTime "\<\d\+[smhd]\>" contained +syn keyword wget2Boolean on off yes no y n contained +syn keyword wget2Number infinity inf contained +syn match wget2Number "\<\d\+>" contained +syn match wget2Quota "\<\d\+[kmgt]\>" contained +syn match wget2Time "\<\d\+[smhd]\>" contained "{{{ Commands let s:commands =<< trim EOL @@ -67,6 +66,7 @@ let s:commands =<< trim EOL cut-dirs cut-file-get-vars cut-url-get-vars + dane debug default-http-port default-https-port @@ -85,6 +85,7 @@ let s:commands =<< trim EOL execute filter-mime-type filter-urls + follow-sitemaps follow-tags force-atom force-css @@ -221,28 +222,27 @@ let s:commands =<< trim EOL EOL "}}} -call map(s:commands, "substitute(v:val, '_', '[-_]\\\\=', 'g')") - for cmd in s:commands - exe 'syn match wgetCommand "\<' . cmd . '\>" nextgroup=wgetAssignmentOperator skipwhite contained' + exe 'syn match wget2Command "\<' .. substitute(cmd, '-', '[-_]\\=', "g") .. '\>" nextgroup=wget2AssignmentOperator skipwhite contained' endfor +unlet s:commands syn case match -syn match wgetStart "^" nextgroup=wgetCommand,wgetComment skipwhite -syn match wgetAssignmentOperator "=" nextgroup=wgetString,wgetBoolean,wgetNumber,wgetQuota,wgetTime skipwhite contained +syn match wget2LineStart "^" nextgroup=wget2Command,wget2Comment skipwhite +syn match wget2AssignmentOperator "=" nextgroup=wget2String,wget2Boolean,wget2Number,wget2Quota,wget2Time skipwhite contained -hi def link wgetAssignmentOperator Special -hi def link wgetBoolean Boolean -hi def link wgetCommand Identifier -hi def link wgetComment Comment -hi def link wgetNumber Number -hi def link wgetQuota Number -hi def link wgetString String -hi def link wgetTime Number -hi def link wgetTodo Todo +hi def link wget2AssignmentOperator Special +hi def link wget2Boolean Boolean +hi def link wget2Command Identifier +hi def link wget2Comment Comment +hi def link wget2Number Number +hi def link wget2Quota Number +hi def link wget2String String +hi def link wget2Time Number +hi def link wget2Todo Todo -let b:current_syntax = "wget" +let b:current_syntax = "wget2" let &cpo = s:cpo_save unlet s:cpo_save diff --git a/src/man/nvim.1 b/src/man/nvim.1 index b846ed4ba6..4dc099f98c 100644 --- a/src/man/nvim.1 +++ b/src/man/nvim.1 @@ -339,6 +339,11 @@ Print version information and exit. .El .Sh ENVIRONMENT .Bl -tag -width Fl +.It Ev NVIM_APPNAME +The name of sub-directories used within each XDG user directory. +Defaults to +.Cm nvim . +:help $NVIM_APPNAME .It Ev NVIM_LOG_FILE Low-level log file, usually found at ~/.local/state/nvim/log. :help $NVIM_LOG_FILE diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index f4c6f646eb..9d51f2a4dc 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -419,7 +419,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error VALIDATE_EXP(!string_iswhite(elem.data.string), "command arg", "non-whitespace", NULL, { goto end; }); - data_str = xstrndup(elem.data.string.data, elem.data.string.size); + data_str = string_to_cstr(elem.data.string); break; default: VALIDATE_EXP(false, "command arg", "valid type", api_typename(elem.type), { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 9cf4620acd..8911e145e7 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -75,8 +75,6 @@ #define PUT_C(dict, k, v) \ kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) -#define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); - #define ADD(array, item) \ kv_push(array, item) diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index bdb3983ab3..1a83dd30c0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1031,7 +1031,7 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int } ap->refcount = 0; - ap->pat = xstrnsave(pat, (size_t)patlen); + ap->pat = xmemdupz(pat, (size_t)patlen); ap->patlen = patlen; // need to initialize last_mode for the first ModeChanged autocmd @@ -2514,7 +2514,7 @@ static int arg_augroup_get(char **argp) return AUGROUP_ALL; } - char *group_name = xstrnsave(arg, (size_t)(p - arg)); + char *group_name = xmemdupz(arg, (size_t)(p - arg)); int group = augroup_find(group_name); if (group == AUGROUP_ERROR) { group = AUGROUP_ALL; // no match, use all groups diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 496c047a4a..6b83664339 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -804,7 +804,7 @@ static char *find_longest_match(expand_T *xp, int options) } } - return xstrndup(xp->xp_files[0], len); + return xmemdupz(xp->xp_files[0], len); } /// Do wildcard expansion on the string "str". @@ -3156,11 +3156,11 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re if (match) { if (!fuzzy) { - GA_APPEND(char *, &ga, xstrnsave(s, (size_t)(e - s))); + GA_APPEND(char *, &ga, xmemdupz(s, (size_t)(e - s))); } else { GA_APPEND(fuzmatch_str_T, &ga, ((fuzmatch_str_T){ .idx = ga.ga_len, - .str = xstrnsave(s, (size_t)(e - s)), + .str = xmemdupz(s, (size_t)(e - s)), .score = score, })); } diff --git a/src/nvim/context.c b/src/nvim/context.c index fe5a49faa2..9b1eee56c5 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -330,7 +330,7 @@ static inline msgpack_sbuffer array_to_sbuf(Array array) typval_T list_tv; Error err = ERROR_INIT; - object_to_vim(ARRAY_OBJ(array), &list_tv, &err); + (void)object_to_vim(ARRAY_OBJ(array), &list_tv, &err); if (!encode_vim_list_to_buf(list_tv.vval.v_list, &sbuf.size, &sbuf.data)) { emsg(_("E474: Failed to convert list to msgpack string buffer")); diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 5f0dfb381d..de76d55977 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -2110,10 +2110,10 @@ void ex_loadkeymap(exarg_T *eap) if ((*p != '"') && (*p != NUL)) { kmap_T *kp = GA_APPEND_VIA_PTR(kmap_T, &curbuf->b_kmap_ga); s = skiptowhite(p); - kp->from = xstrnsave(p, (size_t)(s - p)); + kp->from = xmemdupz(p, (size_t)(s - p)); p = skipwhite(s); s = skiptowhite(p); - kp->to = xstrnsave(p, (size_t)(s - p)); + kp->to = xmemdupz(p, (size_t)(s - p)); if ((strlen(kp->from) + strlen(kp->to) >= KMAP_LLEN) || (*kp->from == NUL) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a3ea5169fd..390cdbb377 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1633,7 +1633,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (len == -1) { lp->ll_newkey = xstrdup(key); } else { - lp->ll_newkey = xstrnsave(key, (size_t)len); + lp->ll_newkey = xmemdupz(key, (size_t)len); } tv_clear(&var1); break; @@ -1966,7 +1966,7 @@ bool next_for_item(void *fi_void, char *arg) typval_T tv; tv.v_type = VAR_STRING; tv.v_lock = VAR_FIXED; - tv.vval.v_string = xstrnsave(fi->fi_string + fi->fi_byte_idx, (size_t)len); + tv.vval.v_string = xmemdupz(fi->fi_string + fi->fi_byte_idx, (size_t)len); fi->fi_byte_idx += len; const int result = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; @@ -4893,7 +4893,7 @@ static int get_literal_key(char **arg, typval_T *tv) } for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {} tv->v_type = VAR_STRING; - tv->vval.v_string = xstrnsave(*arg, (size_t)(p - *arg)); + tv->vval.v_string = xmemdupz(*arg, (size_t)(p - *arg)); *arg = skipwhite(p); return OK; @@ -5242,7 +5242,7 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T * typval_T tv = { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrnsave(p, (size_t)len), + .vval.v_string = xmemdupz(p, (size_t)len), }; vimvars[VV_KEY].vv_nr = idx; @@ -7483,7 +7483,7 @@ char *char_from_string(const char *str, varnumber_T index) if (nbyte >= slen) { return NULL; } - return xstrnsave(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); + return xmemdupz(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); } /// Get the byte index for character index "idx" in string "str" with length @@ -7544,7 +7544,7 @@ char *string_slice(const char *str, varnumber_T first, varnumber_T last, bool ex if (start_byte >= (ssize_t)slen || end_byte <= start_byte) { return NULL; } - return xstrnsave(str + start_byte, (size_t)(end_byte - start_byte)); + return xmemdupz(str + start_byte, (size_t)(end_byte - start_byte)); } /// Handle: @@ -8586,13 +8586,13 @@ repeat: // find end of pattern p = vim_strchr(s, sep); if (p != NULL) { - char *const pat = xstrnsave(s, (size_t)(p - s)); + char *const pat = xmemdupz(s, (size_t)(p - s)); s = p + 1; // find end of substitution p = vim_strchr(s, sep); if (p != NULL) { - char *const sub = xstrnsave(s, (size_t)(p - s)); - char *const str = xstrnsave(*fnamep, *fnamelen); + char *const sub = xmemdupz(s, (size_t)(p - s)); + char *const str = xmemdupz(*fnamep, *fnamelen); *usedlen = (size_t)(p + 1 - src); s = do_string_sub(str, pat, sub, NULL, flags); *fnamep = s; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 221be492af..a2ba0f0189 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6142,7 +6142,8 @@ M.funcs = { When {dict} is omitted or zero: Return the rhs of mapping {name} in mode {mode}. The returned String has special characters translated like in the output of the ":map" command - listing. + listing. When {dict} is TRUE a dictionary is returned, see + below. To get a list of all mappings see |maplist()|. When there is no mapping for {name}, an empty String is returned if {dict} is FALSE, otherwise returns an empty Dict. @@ -6170,7 +6171,7 @@ M.funcs = { When {dict} is there and it is |TRUE| return a dictionary containing all the information of the mapping with the - following items: + following items: *mapping-dict* "lhs" The {lhs} of the mapping as it would be typed "lhsraw" The {lhs} of the mapping as raw bytes "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate @@ -6189,9 +6190,16 @@ M.funcs = { (|mapmode-ic|) "sid" The script local ID, used for <sid> mappings (|<SID>|). Negative for special contexts. + "scriptversion" The version of the script, always 1. "lnum" The line number in "sid", zero if unknown. "nowait" Do not wait for other, longer mappings. (|:map-<nowait>|). + "abbr" True if this is an |abbreviation|. + "mode_bits" Nvim's internal binary representation of "mode". + |mapset()| ignores this; only "mode" is used. + See |maplist()| for usage examples. The values + are from src/nvim/vim.h and may change in the + future. The dictionary can be used to restore a mapping with |mapset()|. @@ -6254,6 +6262,43 @@ M.funcs = { params = { { 'name', 'string' }, { 'mode', 'string' }, { 'abbr', 'any' } }, signature = 'mapcheck({name} [, {mode} [, {abbr}]])', }, + maplist = { + args = { 0, 1 }, + desc = [[ + Returns a |List| of all mappings. Each List item is a |Dict|, + the same as what is returned by |maparg()|, see + |mapping-dict|. When {abbr} is there and it is |TRUE| use + abbreviations instead of mappings. + + Example to show all mappings with "MultiMatch" in rhs: >vim + echo maplist()->filter({_, m -> + \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 + \ }) + <It can be tricky to find mappings for particular |:map-modes|. + |mapping-dict|'s "mode_bits" can simplify this. For example, + the mode_bits for Normal, Insert or Command-line modes are + 0x19. To find all the mappings available in those modes you + can do: >vim + let saved_maps = [] + for m in maplist() + if and(m.mode_bits, 0x19) != 0 + eval saved_maps->add(m) + endif + endfor + echo saved_maps->mapnew({_, m -> m.lhs}) + <The values of the mode_bits are defined in Nvim's + src/nvim/vim.h file and they can be discovered at runtime + using |:map-commands| and "maplist()". Example: >vim + omap xyzzy <Nop> + let op_bit = maplist()->filter( + \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits + ounmap xyzzy + echo printf("Operator-pending mode bit: 0x%x", op_bit) + ]], + name = 'maplist', + params = {}, + signature = 'maplist([{abbr}])' + }, mapnew = { args = 2, base = 1, @@ -6268,12 +6313,20 @@ M.funcs = { signature = 'mapnew({expr1}, {expr2})', }, mapset = { - args = 3, + args = { 1, 3 }, base = 1, desc = [=[ - Restore a mapping from a dictionary returned by |maparg()|. - {mode} and {abbr} should be the same as for the call to - |maparg()|. *E460* + Restore a mapping from a dictionary, possibly returned by + |maparg()| or |maplist()|. A buffer mapping, when dict.buffer + is true, is set on the current buffer; it is up to the caller + to ensure that the intended buffer is the current buffer. This + feature allows copying mappings from one buffer to another. + The dict.mode value may restore a single mapping that covers + more than one mode, like with mode values of '!', ' ', "nox", + or 'v'. *E1276* + + In the first form, {mode} and {abbr} should be the same as + for the call to |maparg()|. *E460* {mode} is used to define the mode in which the mapping is set, not the "mode" entry in {dict}. Example for saving and restoring a mapping: >vim @@ -6282,8 +6335,21 @@ M.funcs = { " ... call mapset('n', 0, save_map) <Note that if you are going to replace a map in several modes, - e.g. with `:map!`, you need to save the mapping for all of - them, since they can differ. + e.g. with `:map!`, you need to save/restore the mapping for + all of them, when they might differ. + + In the second form, with {dict} as the only argument, mode + and abbr are taken from the dict. + Example: >vim + let save_maps = maplist()->filter( + \ {_, m -> m.lhs == 'K'}) + nnoremap K somethingelse + cnoremap K somethingelse2 + " ... + unmap K + for d in save_maps + call mapset(d) + endfor ]=], name = 'mapset', params = { { 'mode', 'string' }, { 'abbr', 'any' }, { 'dict', 'any' } }, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 550d296093..2d8ad458f1 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2911,6 +2911,9 @@ static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool error = false; const int called_emsg_before = called_emsg; + // Flush screen updates before blocking. + ui_flush(); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, timeout, eval_expr_typval(&expr, false, &argv, 0, &exprval) != OK || tv_get_number_chk(&exprval, &error) @@ -5669,7 +5672,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } if (prevlen == 0) { assert(len < INT_MAX); - s = xstrnsave(start, len); + s = xmemdupz(start, len); } else { // Change "prev" buffer to be the right size. This way // the bytes are only copied once, and very long lines are @@ -6305,7 +6308,7 @@ static void reduce_string(typval_T *argvars, typval_T *expr, typval_T *rettv) *rettv = (typval_T){ .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrnsave(p, (size_t)len), + .vval.v_string = xmemdupz(p, (size_t)len), }; p += len; } else if (tv_check_for_string_arg(argvars, 2) == FAIL) { @@ -6321,7 +6324,7 @@ static void reduce_string(typval_T *argvars, typval_T *expr, typval_T *rettv) argv[1] = (typval_T){ .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrnsave(p, (size_t)len), + .vval.v_string = xmemdupz(p, (size_t)len), }; const int r = eval_expr_typval(expr, true, argv, 2, rettv); diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 6e7b1e4d67..4b50710649 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1625,7 +1625,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t if (fp == NULL) { // Make a copy of the name, if it comes from a funcref variable it could // be changed or deleted in the called function. - name = xstrnsave(funcname, (size_t)len); + name = xmemdupz(funcname, (size_t)len); fname = fname_trans_sid(name, fname_buf, &tofree, &error); } @@ -2089,7 +2089,7 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi) if (strncmp(p, "<lambda>", 8) == 0) { p += 8; (void)getdigits(&p, false, 0); - saved = xstrndup(*name, (size_t)(p - *name)); + saved = xmemdupz(*name, (size_t)(p - *name)); if (fudi != NULL) { CLEAR_POINTER(fudi); } @@ -2573,12 +2573,12 @@ void ex_function(exarg_T *eap) if (strncmp(p, "trim", 4) == 0) { // Ignore leading white space. p = skipwhite(p + 4); - heredoc_trimmed = xstrnsave(theline, (size_t)(skipwhite(theline) - theline)); + heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline)); } if (*p == NUL) { skip_until = xstrdup("."); } else { - skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p)); + skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p)); } do_concat = false; is_heredoc = true; @@ -2598,7 +2598,7 @@ void ex_function(exarg_T *eap) if (strncmp(p, "trim", 4) == 0) { // Ignore leading white space. p = skipwhite(p + 4); - heredoc_trimmed = xstrnsave(theline, (size_t)(skipwhite(theline) - theline)); + heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline)); continue; } if (strncmp(p, "eval", 4) == 0) { @@ -2608,7 +2608,7 @@ void ex_function(exarg_T *eap) } break; } - skip_until = xstrnsave(p, (size_t)(skiptowhite(p) - p)); + skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p)); do_concat = false; is_heredoc = true; } diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 33256b78e1..ed79d8a681 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -274,7 +274,7 @@ list_T *heredoc_get(exarg_T *eap, char *cmd, bool script_get) p++; text_indent_len++; } - text_indent = xstrnsave(theline, (size_t)text_indent_len); + text_indent = xmemdupz(theline, (size_t)text_indent_len); } // with "trim": skip the indent matching the first line if (text_indent != NULL) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2857465db0..bb3284a6b8 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2816,7 +2816,7 @@ void ex_append(exarg_T *eap) if (p == NULL) { p = eap->nextcmd + strlen(eap->nextcmd); } - theline = xstrnsave(eap->nextcmd, (size_t)(p - eap->nextcmd)); + theline = xmemdupz(eap->nextcmd, (size_t)(p - eap->nextcmd)); if (*p != NUL) { p++; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c83b00f77f..ad09ee6bb1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2033,7 +2033,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter while (ASCII_ISALNUM(*p)) { p++; } - p = xstrnsave(ea.cmd, (size_t)(p - ea.cmd)); + p = xmemdupz(ea.cmd, (size_t)(p - ea.cmd)); int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL); xfree(p); // If the autocommands did something and didn't cause an error, try @@ -7121,7 +7121,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum } result = NULL; } else { - result = xstrnsave(result, resultlen); + result = xmemdupz(result, resultlen); } xfree(resultbuf); return result; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 796769d7ff..5bdaaa880e 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -293,7 +293,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i xstrlcpy(ff_expand_buffer, rel_fname, len + 1); search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, false); } else { - search_ctx->ffsc_start_dir = xstrnsave(rel_fname, len); + search_ctx->ffsc_start_dir = xmemdupz(rel_fname, len); } if (*++path != NUL) { path++; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 804c9cec11..424bd33b6c 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1993,7 +1993,7 @@ static char *next_fenc(char **pp, bool *alloced) r = enc_canonize(*pp); *pp += strlen(*pp); } else { - r = xstrnsave(*pp, (size_t)(p - *pp)); + r = xmemdupz(*pp, (size_t)(p - *pp)); *pp = p + 1; p = enc_canonize(r); xfree(r); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 8b1e2f9105..2370df916c 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1723,7 +1723,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) // getchar(): blocking wait. // TODO(bfredl): deduplicate shared logic with state_enter ? if (!char_avail()) { - // flush output before waiting + // Flush screen updates before blocking. ui_flush(); (void)os_inchar(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events); if (!multiqueue_empty(main_loop.events)) { diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 65eabd72c3..ff21b11abb 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -960,7 +960,7 @@ void ex_retab(exarg_T *eap) return; } while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') { - (eap->arg)++; + eap->arg++; } // This ensures that either new_vts_array and new_ts_str are freshly @@ -970,7 +970,7 @@ void ex_retab(exarg_T *eap) new_vts_array = curbuf->b_p_vts_array; new_ts_str = NULL; } else { - new_ts_str = xstrnsave(new_ts_str, (size_t)(eap->arg - new_ts_str)); + new_ts_str = xmemdupz(new_ts_str, (size_t)(eap->arg - new_ts_str)); } for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) { char *ptr = ml_get(lnum); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3333c73bfd..876a5c34bd 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -463,7 +463,7 @@ static int nlua_wait(lua_State *lstate) int pcall_status = 0; bool callback_result = false; - // Flush UI before blocking + // Flush screen updates before blocking. ui_flush(); LOOP_PROCESS_EVENTS_UNTIL(&main_loop, diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index da98f5e6c0..b54b07fc20 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -123,6 +123,8 @@ static const char e_mapping_already_exists_for_str[] = N_("E227: Mapping already exists for %s"); static const char e_entries_missing_in_mapset_dict_argument[] = N_("E460: Entries missing in mapset() dict argument"); +static const char e_illegal_map_mode_string_str[] + = N_("E1276: Illegal map mode string: '%s'"); /// Get the start of the hashed map list for "state" and first character "c". mapblock_T *get_maphash_list(int state, int c) @@ -1652,7 +1654,7 @@ char *eval_map_expr(mapblock_T *mp, int c) Array args = ARRAY_DICT_INIT; Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err); if (ret.type == kObjectTypeString) { - p = xstrndup(ret.data.string.data, ret.data.string.size); + p = string_to_cstr(ret.data.string); } api_free_object(ret); if (err.type != kErrorTypeNone) { @@ -2070,17 +2072,18 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @param mp The maphash that contains the mapping information /// @param buffer_value The "buffer" value +/// @param abbr True if abbreviation /// @param compatible True for compatible with old maparg() dict /// /// @return A Dictionary. static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt, - const int buffer_value, const bool compatible) + const int buffer_value, const bool abbr, const bool compatible) FUNC_ATTR_NONNULL_ARG(1) { Dictionary dict = ARRAY_DICT_INIT; char *const lhs = str2special_save(mp->m_keys, compatible, !compatible); char *const mapmode = map_mode_to_chars(mp->m_mode); - varnumber_T noremap_value; + int noremap_value; if (compatible) { // Keep old compatible behavior @@ -2112,14 +2115,17 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); - PUT(dict, "sid", INTEGER_OBJ((varnumber_T)mp->m_script_ctx.sc_sid)); - PUT(dict, "lnum", INTEGER_OBJ((varnumber_T)mp->m_script_ctx.sc_lnum)); - PUT(dict, "buffer", INTEGER_OBJ((varnumber_T)buffer_value)); + PUT(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); + PUT(dict, "scriptversion", INTEGER_OBJ(1)); + PUT(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); + PUT(dict, "buffer", INTEGER_OBJ(buffer_value)); PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); if (mp->m_replace_keycodes) { PUT(dict, "replace_keycodes", INTEGER_OBJ(1)); } PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); + PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); + PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); return dict; } @@ -2192,7 +2198,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_simplified : NULL, - buffer_local, true); + buffer_local, abbr, true); (void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL); api_free_dictionary(dict); } else { @@ -2205,21 +2211,99 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) xfree(alt_keys_buf); } +/// Get the mapping mode from the mode string. +/// It may contain multiple characters, eg "nox", or "!", or ' ' +/// Return 0 if there is an error. +static int get_map_mode_string(const char *const mode_string, const bool abbr) +{ + const char *p = mode_string; + const int MASK_V = MODE_VISUAL | MODE_SELECT; + const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING; + const int MASK_BANG = MODE_INSERT | MODE_CMDLINE; + + if (*p == NUL) { + p = " "; // compatibility + } + int mode = 0; + int modec; + while ((modec = (uint8_t)(*p++))) { + int tmode; + switch (modec) { + case 'i': + tmode = MODE_INSERT; break; + case 'l': + tmode = MODE_LANGMAP; break; + case 'c': + tmode = MODE_CMDLINE; break; + case 'n': + tmode = MODE_NORMAL; break; + case 'x': + tmode = MODE_VISUAL; break; + case 's': + tmode = MODE_SELECT; break; + case 'o': + tmode = MODE_OP_PENDING; break; + case 't': + tmode = MODE_TERMINAL; break; + case 'v': + tmode = MASK_V; break; + case '!': + tmode = MASK_BANG; break; + case ' ': + tmode = MASK_MAP; break; + default: + return 0; // error, unknown mode character + } + mode |= tmode; + } + if ((abbr && (mode & ~MASK_BANG) != 0) + || (!abbr && (mode & (mode - 1)) != 0 // more than one bit set + && ( + // false if multiple bits set in mode and mode is fully + // contained in one mask + !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0) + || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0))))) { + return 0; + } + + return mode; +} + /// "mapset()" function void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { + const char *which; char buf[NUMBUFLEN]; - const char *which = tv_get_string_buf_chk(&argvars[0], buf); - if (which == NULL) { - return; + int is_abbr; + dict_T *d; + + // If first arg is a dict, then that's the only arg permitted. + const bool dict_only = argvars[0].v_type == VAR_DICT; + + if (dict_only) { + d = argvars[0].vval.v_dict; + which = tv_dict_get_string(d, "mode", false); + is_abbr = (int)tv_dict_get_bool(d, "abbr", -1); + if (which == NULL || is_abbr < 0) { + emsg(_(e_entries_missing_in_mapset_dict_argument)); + return; + } + } else { + which = tv_get_string_buf_chk(&argvars[0], buf); + if (which == NULL) { + return; + } + is_abbr = (int)tv_get_bool(&argvars[1]); + if (tv_check_for_dict_arg(argvars, 2) == FAIL) { + return; + } + d = argvars[2].vval.v_dict; } - const int mode = get_map_mode((char **)&which, 0); - const bool is_abbr = tv_get_number(&argvars[1]) != 0; - - if (tv_check_for_dict_arg(argvars, 2) == FAIL) { + const int mode = get_map_mode_string(which, is_abbr); + if (mode == 0) { + semsg(_(e_illegal_map_mode_string_str), which); return; } - dict_T *d = argvars[2].vval.v_dict; // Get the values in the same order as above in get_maparg(). char *lhs = tv_dict_get_string(d, "lhs", false); @@ -2280,6 +2364,59 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) sid, lnum, false); } +/// "maplist()" function +void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; + const bool abbr = argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]); + + tv_list_alloc_ret(rettv, kListLenUnknown); + + // Do it twice: once for global maps and once for local maps. + for (int buffer_local = 0; buffer_local <= 1; buffer_local++) { + for (int hash = 0; hash < 256; hash++) { + mapblock_T *mp; + if (abbr) { + if (hash > 0) { // there is only one abbr list + break; + } + if (buffer_local) { + mp = curbuf->b_first_abbr; + } else { + mp = first_abbr; + } + } else if (buffer_local) { + mp = curbuf->b_maphash[hash]; + } else { + mp = maphash[hash]; + } + for (; mp; mp = mp->m_next) { + if (mp->m_simplified) { + continue; + } + + char *keys_buf = NULL; + bool did_simplify = false; + + char *lhs = str2special_save(mp->m_keys, true, false); + (void)replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify, + CPO_TO_CPO_FLAGS); + xfree(lhs); + + Dictionary dict = mapblock_fill_dict(mp, + did_simplify ? keys_buf : NULL, + buffer_local, abbr, true); + typval_T d = TV_INITIAL_VALUE; + (void)object_to_vim(DICTIONARY_OBJ(dict), &d, NULL); + assert(d.v_type == VAR_DICT); + tv_list_append_dict(rettv->vval.v_list, d.vval.v_dict); + api_free_dictionary(dict); + xfree(keys_buf); + } + } + } +} + /// "maparg()" function void f_maparg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -2593,30 +2730,21 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod goto fail_and_free; } - bool is_abbrev = false; - if (mode.size > 2) { - api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data); + char *p = mode.size > 0 ? mode.data : "m"; + bool forceit = *p == '!'; + // integer value of the mapping mode, to be passed to do_map() + int mode_val = get_map_mode(&p, forceit); + if (forceit) { + assert(p == mode.data); + p++; + } + bool is_abbrev = (mode_val & (MODE_INSERT | MODE_CMDLINE)) != 0 && *p == 'a'; + if (is_abbrev) { + p++; + } + if (mode.size > 0 && (size_t)(p - mode.data) != mode.size) { + api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", mode.data); goto fail_and_free; - } else if (mode.size == 2) { - if ((mode.data[0] != '!' && mode.data[0] != 'i' && mode.data[0] != 'c') - || mode.data[1] != 'a') { - api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data); - goto fail_and_free; - } - is_abbrev = true; - } - int mode_val; // integer value of the mapping mode, to be passed to do_map() - char *p = (mode.size) ? mode.data : "m"; - if (*p == '!') { - mode_val = get_map_mode(&p, true); // mapmode-ic - } else { - mode_val = get_map_mode(&p, false); - if (mode_val == (MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING) && mode.size > 0) { - // get_map_mode() treats unrecognized mode shortnames as ":map". - // This is an error unless the given shortname was empty string "". - api_set_error(err, kErrorTypeValidation, "Invalid mode shortname: \"%s\"", p); - goto fail_and_free; - } } if (parsed_args.lhs_len == 0) { @@ -2706,7 +2834,8 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) // Check for correct mode if (int_mode & current_maphash->m_mode) { ADD(mappings, - DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, false))); + DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, + buffer_value, false, false))); } } } diff --git a/src/nvim/match.c b/src/nvim/match.c index e087d4f45d..d153ea042d 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -1216,7 +1216,7 @@ void ex_match(exarg_T *eap) } else { p = skiptowhite(eap->arg); if (!eap->skip) { - g = xstrnsave(eap->arg, (size_t)(p - eap->arg)); + g = xmemdupz(eap->arg, (size_t)(p - eap->arg)); } p = skipwhite(p); if (*p == NUL) { diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 5eb8b41015..5fa731141e 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1313,7 +1313,7 @@ static char *menu_text(const char *str, int *mnemonic, char **actext) *actext = xstrdup(p + 1); } assert(p >= str); - text = xstrnsave(str, (size_t)(p - str)); + text = xmemdupz(str, (size_t)(p - str)); } else { text = xstrdup(str); } @@ -1736,7 +1736,7 @@ void ex_menutranslate(exarg_T *eap) from = xstrdup(from); from_noamp = menu_text(from, NULL, NULL); assert(arg >= to); - to = xstrnsave(to, (size_t)(arg - to)); + to = xmemdupz(to, (size_t)(arg - to)); menu_translate_tab_and_shift(from); menu_translate_tab_and_shift(to); menu_unescape_name(from); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 54e1e49d97..418097a82a 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4232,7 +4232,7 @@ static void nv_brackets(cmdarg_T *cap) clearop(cap->oap); } else { // Make a copy, if the line was changed it will be freed. - ptr = xstrnsave(ptr, len); + ptr = xmemdupz(ptr, len); find_pattern_in_path(ptr, 0, len, true, cap->count0 == 0 ? !isupper(cap->nchar) : false, (((cap->nchar & 0xf) == ('d' & 0xf)) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 9dbeed8658..b56d5e11ea 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1366,7 +1366,7 @@ bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg) cnt = find_ident_under_cursor(argp, (regname == Ctrl_W ? (FIND_IDENT|FIND_STRING) : FIND_STRING)); - *argp = cnt ? xstrnsave(*argp, cnt) : NULL; + *argp = cnt ? xmemdupz(*argp, cnt) : NULL; *allocated = true; return true; @@ -2404,7 +2404,7 @@ void op_insert(oparg_T *oap, int count1) } int ins_len = (int)strlen(firstline) - pre_textlen - offset; if (pre_textlen >= 0 && ins_len > 0) { - char *ins_text = xstrnsave(firstline, (size_t)ins_len); + char *ins_text = xmemdupz(firstline, (size_t)ins_len); // block handled here if (u_save(oap->start.lnum, (linenr_T)(oap->end.lnum + 1)) == OK) { block_insert(oap, ins_text, (oap->op_type == OP_INSERT), &bd); @@ -3118,7 +3118,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) if (dir == FORWARD && *p != NUL) { MB_PTR_ADV(p); } - ptr = xstrnsave(oldp, (size_t)(p - oldp)); + ptr = xmemdupz(oldp, (size_t)(p - oldp)); ml_replace(curwin->w_cursor.lnum, ptr, false); nr_lines++; dir = FORWARD; diff --git a/src/nvim/option.c b/src/nvim/option.c index b44dda67d6..7005b63cac 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -630,7 +630,7 @@ void set_init_3(void) size_t len = 0; char *p = (char *)invocation_path_tail(p_sh, &len); - p = xstrnsave(p, len); + p = xmemdupz(p, len); { // @@ -5749,7 +5749,7 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c // If more than one flags, split the flags up and expose each // character as individual choice. for (char *flag = option_val; *flag != NUL; flag++) { - (*matches)[count++] = xstrnsave(flag, 1); + (*matches)[count++] = xmemdupz(flag, 1); } } diff --git a/src/nvim/options.lua b/src/nvim/options.lua index c244b83c9e..47cb8e7912 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2851,10 +2851,8 @@ return { default to single-byte alternatives. Example: > - :set fillchars=stl:^,stlnc:=,vert:│,fold:·,diff:- - < This is similar to the default, except that these characters will also - be used when there is highlighting. - + :set fillchars=stl:\ ,stlnc:\ ,vert:│,fold:·,diff:- + < For the "stl", "stlnc", "foldopen", "foldclose" and "foldsep" items single-byte and multibyte characters are supported. But double-width characters are not supported. diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index b868c90108..be76688e92 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -635,7 +635,7 @@ static int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatch // existing flag. Just skip it to avoid duplicate. continue; } - (*matches)[count++] = xstrnsave(flag, 1); + (*matches)[count++] = xmemdupz(flag, 1); } } @@ -1569,6 +1569,9 @@ const char *did_set_iconstring(optset_T *args) /// The 'inccommand' option is changed. const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED) { + if (cmdpreview) { + return e_invarg; + } return did_set_opt_strings(p_icm, p_icm_values, false); } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 7de7168d62..dbea6f01df 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -292,12 +292,11 @@ char *os_getenvname_at_index(size_t index) // Some Windows env vars start with =, so skip over that to find the // separator between name/value - const char * const end = strchr(utf8_str + (utf8_str[0] == '=' ? 1 : 0), - '='); + const char *const end = strchr(utf8_str + (utf8_str[0] == '=' ? 1 : 0), '='); assert(end != NULL); ptrdiff_t len = end - utf8_str; assert(len > 0); - name = xstrndup(utf8_str, (size_t)len); + name = xmemdupz(utf8_str, (size_t)len); xfree(utf8_str); break; } @@ -328,7 +327,7 @@ char *os_getenvname_at_index(size_t index) assert(end != NULL); ptrdiff_t len = end - str; assert(len > 0); - return xstrndup(str, (size_t)len); + return xmemdupz(str, (size_t)len); #endif } @@ -960,7 +959,7 @@ char *vim_getenv(const char *name) // check that the result is a directory name assert(vim_path_end >= vim_path); - vim_path = xstrndup(vim_path, (size_t)(vim_path_end - vim_path)); + vim_path = xmemdupz(vim_path, (size_t)(vim_path_end - vim_path)); if (!os_isdir(vim_path)) { xfree(vim_path); diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index fa474b67dd..8ea30ff21e 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -127,7 +127,7 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) ret = "/tmp/"; } size_t len = strlen(ret); - ret = xstrndup(ret, len >= 2 ? len - 1 : 0); // Trim trailing slash. + ret = xmemdupz(ret, len >= 2 ? len - 1 : 0); // Trim trailing slash. } return ret; diff --git a/src/nvim/path.c b/src/nvim/path.c index 15a8762da1..8cc824337c 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1374,7 +1374,7 @@ static int expand_backtick(garray_T *gap, char *pat, int flags) int cnt = 0; // Create the command: lop off the backticks. - char *cmd = xstrnsave(pat + 1, strlen(pat) - 2); + char *cmd = xmemdupz(pat + 1, strlen(pat) - 2); if (*cmd == '=') { // `={expr}`: Expand expression buffer = eval_to_string(cmd + 1, true); diff --git a/src/nvim/search.c b/src/nvim/search.c index e253abaa41..cee526d416 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1629,7 +1629,7 @@ static bool find_rawstring_end(char *linep, pos_T *startpos, pos_T *endpos) for (p = linep + startpos->col + 1; *p && *p != '('; p++) {} size_t delim_len = (size_t)((p - linep) - startpos->col - 1); - char *delim_copy = xstrnsave(linep + startpos->col + 1, delim_len); + char *delim_copy = xmemdupz(linep + startpos->col + 1, delim_len); bool found = false; for (lnum = startpos->lnum; lnum <= endpos->lnum; lnum++) { char *line = ml_get(lnum); diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 97164f2234..6a6adbd866 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -1209,27 +1209,27 @@ static void sign_define_cmd(char *sign_name, char *cmdline) if (strncmp(arg, "icon=", 5) == 0) { arg += 5; XFREE_CLEAR(icon); - icon = xstrnsave(arg, (size_t)(p - arg)); + icon = xmemdupz(arg, (size_t)(p - arg)); } else if (strncmp(arg, "text=", 5) == 0) { arg += 5; XFREE_CLEAR(text); - text = xstrnsave(arg, (size_t)(p - arg)); + text = xmemdupz(arg, (size_t)(p - arg)); } else if (strncmp(arg, "linehl=", 7) == 0) { arg += 7; XFREE_CLEAR(linehl); - linehl = xstrnsave(arg, (size_t)(p - arg)); + linehl = xmemdupz(arg, (size_t)(p - arg)); } else if (strncmp(arg, "texthl=", 7) == 0) { arg += 7; XFREE_CLEAR(texthl); - texthl = xstrnsave(arg, (size_t)(p - arg)); + texthl = xmemdupz(arg, (size_t)(p - arg)); } else if (strncmp(arg, "culhl=", 6) == 0) { arg += 6; XFREE_CLEAR(culhl); - culhl = xstrnsave(arg, (size_t)(p - arg)); + culhl = xmemdupz(arg, (size_t)(p - arg)); } else if (strncmp(arg, "numhl=", 6) == 0) { arg += 6; XFREE_CLEAR(numhl); - numhl = xstrnsave(arg, (size_t)(p - arg)); + numhl = xmemdupz(arg, (size_t)(p - arg)); } else { semsg(_(e_invarg2), arg); failed = true; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 4b1f8afb15..864a55b12b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2257,7 +2257,7 @@ static void use_midword(slang_T *lp, win_T *wp) wp->w_s->b_spell_ismw[c] = true; } else if (wp->w_s->b_spell_ismw_mb == NULL) { // First multi-byte char in "b_spell_ismw_mb". - wp->w_s->b_spell_ismw_mb = xstrnsave(p, (size_t)l); + wp->w_s->b_spell_ismw_mb = xmemdupz(p, (size_t)l); } else { // Append multi-byte chars to "b_spell_ismw_mb". const int n = (int)strlen(wp->w_s->b_spell_ismw_mb); diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 938e8cec8f..15ec652859 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -3146,7 +3146,7 @@ static void add_suggestion(suginfo_T *su, garray_T *gap, const char *goodword, i if (i < 0) { // Add a suggestion. stp = GA_APPEND_VIA_PTR(suggest_T, gap); - stp->st_word = xstrnsave(goodword, (size_t)goodlen); + stp->st_word = xmemdupz(goodword, (size_t)goodlen); stp->st_wordlen = goodlen; stp->st_score = score; stp->st_altscore = altscore; diff --git a/src/nvim/state.c b/src/nvim/state.c index 61e8d8d2ff..d2466e4248 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -70,7 +70,7 @@ getkey: update_screen(); setcursor(); // put cursor back where it belongs } - // Flush screen updates before blocking + // 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 diff --git a/src/nvim/strings.c b/src/nvim/strings.c index af82f5e578..26da38f284 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2646,7 +2646,7 @@ void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } rettv->v_type = VAR_STRING; - rettv->vval.v_string = xstrndup(p + nbyte, (size_t)len); + rettv->vval.v_string = xmemdupz(p + nbyte, (size_t)len); } /// "strpart()" function diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 1d4c85de49..7a175926d2 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -707,7 +707,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose) && tagp2.user_data) { XFREE_CLEAR(tagstack[tagstackidx].user_data); tagstack[tagstackidx].user_data = - xstrnsave(tagp2.user_data, (size_t)(tagp2.user_data_end - tagp2.user_data)); + xmemdupz(tagp2.user_data, (size_t)(tagp2.user_data_end - tagp2.user_data)); } tagstackidx++; diff --git a/src/nvim/window.c b/src/nvim/window.c index eee500f695..d1b8d7c7b5 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -586,7 +586,7 @@ wingotofile: } // Make a copy, if the line was changed it will be freed. - ptr = xstrnsave(ptr, len); + ptr = xmemdupz(ptr, len); find_pattern_in_path(ptr, 0, len, true, Prenum == 0, type, Prenum1, ACTION_SPLIT, 1, MAXLNUM); diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 953402ada3..3af1faaece 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -19,6 +19,20 @@ local sleep = helpers.sleep local sid_api_client = -9 local sid_lua = -8 +local mode_bits_map = { + ['n'] = 0x01, + ['x'] = 0x02, + ['o'] = 0x04, + ['c'] = 0x08, + ['i'] = 0x10, + ['l'] = 0x20, + ['s'] = 0x40, + ['t'] = 0x80, + [' '] = 0x47, + ['v'] = 0x42, + ['!'] = 0x18, +} + describe('nvim_get_keymap', function() before_each(clear) @@ -32,9 +46,12 @@ describe('nvim_get_keymap', function() rhs='bar', expr=0, sid=0, + scriptversion=1, buffer=0, nowait=0, mode='n', + mode_bits=0x01, + abbr=0, noremap=1, lnum=0, } @@ -81,6 +98,7 @@ describe('nvim_get_keymap', function() -- The table will be the same except for the mode local insert_table = shallowcopy(foo_bar_map_table) insert_table['mode'] = 'i' + insert_table['mode_bits'] = 0x10 eq({insert_table}, meths.get_keymap('i')) end) @@ -258,8 +276,10 @@ describe('nvim_get_keymap', function() silent=0, expr=0, sid=0, + scriptversion=1, buffer=0, nowait=0, + abbr=0, noremap=1, lnum=0, } @@ -268,6 +288,7 @@ describe('nvim_get_keymap', function() ret.lhs = lhs ret.rhs = rhs ret.mode = mode + ret.mode_bits = mode_bits_map[mode] return ret end @@ -323,10 +344,13 @@ describe('nvim_get_keymap', function() lhsraw='| |', rhs='| |', mode='n', + mode_bits=0x01, + abbr=0, script=0, silent=0, expr=0, sid=0, + scriptversion=1, buffer=0, nowait=0, noremap=1, @@ -365,15 +389,18 @@ describe('nvim_get_keymap', function() silent=0, expr=0, sid=sid_lua, + scriptversion=1, buffer=0, nowait=0, mode='n', + mode_bits=0x01, + abbr=0, noremap=0, lnum=0, }, mapargs[1]) end) - it ('can handle map descriptions', function() + it('can handle map descriptions', function() meths.set_keymap('n', 'lhs', 'rhs', {desc="map description"}) eq({ lhs='lhs', @@ -383,9 +410,12 @@ describe('nvim_get_keymap', function() silent=0, expr=0, sid=sid_api_client, + scriptversion=1, buffer=0, nowait=0, mode='n', + mode_bits=0x01, + abbr=0, noremap=0, lnum=0, desc='map description' @@ -420,7 +450,10 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end local to_return = {} - to_return.mode = normalize_mapmode(mode, true) + local expected_mode = normalize_mapmode(mode, true) + to_return.mode = expected_mode + to_return.mode_bits = mode_bits_map[expected_mode] + to_return.abbr = mode:sub(-1) == 'a' and 1 or 0 to_return.noremap = not opts.noremap and 0 or 1 to_return.lhs = lhs to_return.rhs = rhs @@ -429,6 +462,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() to_return.nowait = not opts.nowait and 0 or 1 to_return.expr = not opts.expr and 0 or 1 to_return.sid = not opts.sid and sid_api_client or opts.sid + to_return.scriptversion = 1 to_return.buffer = not opts.buffer and 0 or opts.buffer to_return.lnum = not opts.lnum and 0 or opts.lnum to_return.desc = opts.desc @@ -487,35 +521,33 @@ describe('nvim_set_keymap, nvim_del_keymap', function() get_mapargs('', 'lhs')) end) - it('throws errors when given too-long mode shortnames', function() - eq('Shortname is too long: map', - pcall_err(meths.set_keymap, 'map', 'lhs', 'rhs', {})) - - eq('Shortname is too long: vmap', - pcall_err(meths.set_keymap, 'vmap', 'lhs', 'rhs', {})) - - eq('Shortname is too long: xnoremap', - pcall_err(meths.set_keymap, 'xnoremap', 'lhs', 'rhs', {})) - - eq('Shortname is too long: map', pcall_err(meths.del_keymap, 'map', 'lhs')) - eq('Shortname is too long: vmap', pcall_err(meths.del_keymap, 'vmap', 'lhs')) - eq('Shortname is too long: xnoremap', pcall_err(meths.del_keymap, 'xnoremap', 'lhs')) - end) - it('error on invalid mode shortname', function() - eq('Invalid mode shortname: " "', - pcall_err(meths.set_keymap, ' ', 'lhs', 'rhs', {})) - eq('Invalid mode shortname: "m"', - pcall_err(meths.set_keymap, 'm', 'lhs', 'rhs', {})) - eq('Invalid mode shortname: "?"', - pcall_err(meths.set_keymap, '?', 'lhs', 'rhs', {})) - eq('Invalid mode shortname: "y"', - pcall_err(meths.set_keymap, 'y', 'lhs', 'rhs', {})) - eq('Invalid mode shortname: "p"', - pcall_err(meths.set_keymap, 'p', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: " "', pcall_err(meths.set_keymap, ' ', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "m"', pcall_err(meths.set_keymap, 'm', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "?"', pcall_err(meths.set_keymap, '?', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "y"', pcall_err(meths.set_keymap, 'y', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "p"', pcall_err(meths.set_keymap, 'p', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "a"', pcall_err(meths.set_keymap, 'a', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "oa"', pcall_err(meths.set_keymap, 'oa', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "!o"', pcall_err(meths.set_keymap, '!o', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "!i"', pcall_err(meths.set_keymap, '!i', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "!!"', pcall_err(meths.set_keymap, '!!', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "map"', pcall_err(meths.set_keymap, 'map', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "vmap"', pcall_err(meths.set_keymap, 'vmap', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: "xnoremap"', pcall_err(meths.set_keymap, 'xnoremap', 'lhs', 'rhs', {})) + eq('Invalid mode shortname: " "', pcall_err(meths.del_keymap, ' ', 'lhs')) + eq('Invalid mode shortname: "m"', pcall_err(meths.del_keymap, 'm', 'lhs')) eq('Invalid mode shortname: "?"', pcall_err(meths.del_keymap, '?', 'lhs')) eq('Invalid mode shortname: "y"', pcall_err(meths.del_keymap, 'y', 'lhs')) eq('Invalid mode shortname: "p"', pcall_err(meths.del_keymap, 'p', 'lhs')) + eq('Invalid mode shortname: "a"', pcall_err(meths.del_keymap, 'a', 'lhs')) + eq('Invalid mode shortname: "oa"', pcall_err(meths.del_keymap, 'oa', 'lhs')) + eq('Invalid mode shortname: "!o"', pcall_err(meths.del_keymap, '!o', 'lhs')) + eq('Invalid mode shortname: "!i"', pcall_err(meths.del_keymap, '!i', 'lhs')) + eq('Invalid mode shortname: "!!"', pcall_err(meths.del_keymap, '!!', 'lhs')) + eq('Invalid mode shortname: "map"', pcall_err(meths.del_keymap, 'map', 'lhs')) + eq('Invalid mode shortname: "vmap"', pcall_err(meths.del_keymap, 'vmap', 'lhs')) + eq('Invalid mode shortname: "xnoremap"', pcall_err(meths.del_keymap, 'xnoremap', 'lhs')) end) it('error on invalid optnames', function() @@ -823,7 +855,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() eq(1, exec_lua[[return GlobalCount]]) end) - it (':map command shows lua mapping correctly', function() + it(':map command shows lua mapping correctly', function() exec_lua [[ vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end }) ]] @@ -835,7 +867,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() ) end) - it ('mapcheck() returns lua mapping correctly', function() + it('mapcheck() returns lua mapping correctly', function() exec_lua [[ vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() print('jkl;') end }) ]] @@ -843,7 +875,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() "^<Lua %d+>")) end) - it ('maparg() returns lua mapping correctly', function() + it('maparg() returns lua mapping correctly', function() eq(0, exec_lua([[ GlobalCount = 0 vim.api.nvim_set_keymap('n', 'asdf', '', {callback = function() GlobalCount = GlobalCount + 1 end }) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index befbd4bc3b..038368c387 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -875,25 +875,41 @@ describe('jobs', function() end) end) - it('hides cursor when waiting', function() - local screen = Screen.new(30, 3) + it('hides cursor and flushes messages before blocking', function() + local screen = Screen.new(50, 6) screen:set_default_attr_ids({ - [0] = {foreground = Screen.colors.Blue1, bold = true}; + [0] = {foreground = Screen.colors.Blue, bold = true}; -- NonText + [1] = {bold = true, reverse = true}; -- MsgSeparator + [2] = {bold = true, foreground = Screen.colors.SeaGreen}; -- MoreMsg }) screen:attach() command([[let g:id = jobstart([v:progpath, '--clean', '--headless'])]]) - feed_command('call jobwait([g:id], 300)') + source([[ + func PrintAndWait() + echon "aaa\nbbb" + call jobwait([g:id], 300) + echon "\nccc" + endfunc + ]]) + feed_command('call PrintAndWait()') screen:expect{grid=[[ - | - {0:~ }| - :call jobwait([g:id], 300) | + | + {0:~ }| + {0:~ }| + {1: }| + aaa | + bbb | ]], timeout=100} - funcs.jobstop(meths.get_var('id')) screen:expect{grid=[[ - ^ | - {0:~ }| - :call jobwait([g:id], 300) | + | + {1: }| + aaa | + bbb | + ccc | + {2:Press ENTER or type command to continue}^ | ]]} + feed('<CR>') + funcs.jobstop(meths.get_var('id')) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 1ebfa9dd1d..a8a72f20c9 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2559,7 +2559,6 @@ describe('lua stdlib', function() ]]) end) - it('should not block other events', function() eq({time = true, wait_result = true}, exec_lua[[ start_time = get_time() @@ -2601,6 +2600,7 @@ describe('lua stdlib', function() } ]]) end) + it('should work with vim.defer_fn', function() eq({time = true, wait_result = true}, exec_lua[[ start_time = get_time() diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index c9e004289d..3ee67a710c 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -3167,3 +3167,31 @@ it("with 'inccommand' typing :filter doesn't segfault or leak memory #19057", fu feed('i') assert_alive() end) + +it("'inccommand' cannot be changed during preview #23136", function() + clear() + local screen = Screen.new(30, 6) + common_setup(screen, 'nosplit', 'foo\nbar\nbaz') + source([[ + function! IncCommandToggle() + let prev = &inccommand + + if &inccommand ==# 'split' + set inccommand=nosplit + elseif &inccommand ==# 'nosplit' + set inccommand=split + elseif &inccommand ==# '' + set inccommand=nosplit + else + throw 'unknown inccommand' + endif + + return " \<BS>" + endfun + + cnoremap <expr> <C-E> IncCommandToggle() + ]]) + + feed(':%s/foo/bar<C-E><C-E><C-E>') + assert_alive() +end) diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index a5b2474bc5..215e763fd1 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1312,17 +1312,54 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim ]]) end) - it('echo messages are shown correctly when getchar() immediately follows', function() - feed([[:echo 'foo' | echo 'bar' | call getchar()<CR>]]) - screen:expect([[ - | - {1:~ }| - {1:~ }| - {1:~ }| - {3: }| - foo | - bar^ | - ]]) + describe('echo messages are shown when immediately followed by', function() + --- @param to_block string command to cause a blocking wait + --- @param to_unblock number|string number: timeout for blocking screen + --- string: keys to stop the blocking wait + local function test_flush_before_block(to_block, to_unblock) + local timeout = type(to_unblock) == 'number' and to_unblock or nil + exec(([[ + func PrintAndWait() + echon "aaa\nbbb" + %s + echon "\nccc" + endfunc + ]]):format(to_block)) + feed(':call PrintAndWait()<CR>') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {1:~ }| + {3: }| + aaa | + bbb^ | + ]], timeout=timeout} + if type(to_unblock) == 'string' then + feed(to_unblock) + end + screen:expect{grid=[[ + | + {1:~ }| + {3: }| + aaa | + bbb | + ccc | + {4:Press ENTER or type command to continue}^ | + ]]} + end + + it('getchar()', function() + test_flush_before_block([[call getchar()]], 'k') + end) + + it('wait()', function() + test_flush_before_block([[call wait(300, '0')]], 100) + end) + + it('lua vim.wait()', function() + test_flush_before_block([[lua vim.wait(300, function() end)]], 100) + end) end) it('consecutive calls to win_move_statusline() work after multiline message #21014',function() diff --git a/test/functional/vimscript/map_functions_spec.lua b/test/functional/vimscript/map_functions_spec.lua index 2c8fe69428..acba5e9708 100644 --- a/test/functional/vimscript/map_functions_spec.lua +++ b/test/functional/vimscript/map_functions_spec.lua @@ -26,9 +26,12 @@ describe('maparg()', function() rhs='bar', expr=0, sid=0, + scriptversion=1, buffer=0, nowait=0, mode='n', + mode_bits=0x01, + abbr=0, noremap=1, lnum=0, } @@ -155,10 +158,13 @@ describe('maparg()', function() buffer = 0, expr = 0, mode = 'n', + mode_bits = 0x01, + abbr = 0, noremap = 1, nowait = 0, - script=0, + script = 0, sid = 0, + scriptversion = 1, silent = 0, lnum = 0, } diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim index b0c2a15a3f..7546b342e6 100644 --- a/test/old/testdir/setup.vim +++ b/test/old/testdir/setup.vim @@ -58,6 +58,11 @@ func Ntest_setmouse(row, col) endif endfunc +" roughly equivalent to term_wait() in Vim +func Nterm_wait(buf, time = 10) + execute $'sleep {a:time}m' +endfunc + " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/test/old/testdir/test_maparg.vim b/test/old/testdir/test_map_functions.vim index 1837511990..2aeb470a0d 100644 --- a/test/old/testdir/test_maparg.vim +++ b/test/old/testdir/test_map_functions.vim @@ -1,4 +1,4 @@ -" Tests for maparg(), mapcheck() and mapset(). +" Tests for maparg(), mapcheck(), mapset(), maplist() " Also test utf8 map with a 0x80 byte. source shared.vim @@ -19,28 +19,41 @@ func Test_maparg() call assert_equal("is<F4>foo", maparg('foo<C-V>')) call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo<C-V>', \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16", - \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, - \ 'rhs': 'is<F4>foo', 'buffer': 0}, + \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, + \ 'rhs': 'is<F4>foo', 'buffer': 0, 'abbr': 0, 'mode_bits': 0x47}, \ maparg('foo<C-V>', '', 0, 1)) call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', \ 'lhsraw': 'bar', 'mode': 'v', - \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2, - \ 'rhs': 'isbar', 'buffer': 1}, + \ 'nowait': 0, 'expr': 1, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 2, + \ 'rhs': 'isbar', 'buffer': 1, 'abbr': 0, 'mode_bits': 0x42}, \ 'bar'->maparg('', 0, 1)) let lnum = expand('<sflnum>') map <buffer> <nowait> foo bar call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', \ 'lhsraw': 'foo', 'mode': ' ', - \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar', - \ 'buffer': 1}, + \ 'nowait': 1, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, 'rhs': 'bar', + \ 'buffer': 1, 'abbr': 0, 'mode_bits': 0x47}, \ maparg('foo', '', 0, 1)) let lnum = expand('<sflnum>') tmap baz foo call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', \ 'lhsraw': 'baz', 'mode': 't', - \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo', - \ 'buffer': 0}, + \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, 'rhs': 'foo', + \ 'buffer': 0, 'abbr': 0, 'mode_bits': 0x80}, \ maparg('baz', 't', 0, 1)) + let lnum = expand('<sflnum>') + iab A B + call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'A', + \ 'lhsraw': 'A', 'mode': 'i', + \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, 'rhs': 'B', + \ 'buffer': 0, 'abbr': 1, 'mode_bits': 0x0010}, + \ maparg('A', 'i', 1, 1)) + iuna A map abc x<char-114>x call assert_equal("xrx", maparg('abc')) @@ -274,6 +287,152 @@ func Test_mapset() call assert_fails('call mapset("i", 0, {})', 'E460:') endfunc +func Test_mapset_arg1_dir() + " This test is mostly about get_map_mode_string. + " Once the code gets past that, it's common with the 3 arg mapset. + + " GetModes() return list of modes for 'XZ' lhs using maplist. + " There is one list item per mapping + func s:GetModes(abbr = v:false) + return maplist(a:abbr)->filter({_, m -> m.lhs == 'XZ'}) + \ ->mapnew({_, m -> m.mode}) + endfunc + + func s:UnmapAll(lhs) + const unmap_cmds = [ 'unmap', 'unmap!', 'tunmap', 'lunmap' ] + for cmd in unmap_cmds + try | call execute(cmd .. ' ' .. a:lhs) | catch /E31/ | endtry + endfor + endfunc + + let tmap = {} + + " some mapset(mode, abbr, dict) tests using get_map_mode_str + map XZ x + let tmap = maplist()->filter({_, m -> m.lhs == 'XZ'})[0]->copy() + " this splits the mapping into 2 mappings + call mapset('ox', v:false, tmap) + call assert_equal(2, len(s:GetModes())) + call mapset('o', v:false, tmap) + call assert_equal(3, len(s:GetModes())) + " test that '' acts like ' ', and that the 3 mappings become 1 + call mapset('', v:false, tmap) + call assert_equal([' '], s:GetModes()) + " dict's mode/abbr are ignored + call s:UnmapAll('XZ') + let tmap.mode = '!' + let tmap.abbr = v:true + call mapset('o', v:false, tmap) + call assert_equal(['o'], s:GetModes()) + + " test the 3 arg version handles bad mode string, dict not used + call assert_fails("call mapset('vi', v:false, {})", 'E1276:') + + + " get the abbreviations out of the way + abbreviate XZ ZX + let tmap = maplist(v:true)->filter({_, m -> m.lhs == 'XZ'})[0]->copy() + + abclear + " 'ic' is the default ab command, shows up as '!' + let tmap.mode = 'ic' + call mapset(tmap) + call assert_equal(['!'], s:GetModes(v:true)) + + abclear + let tmap.mode = 'i' + call mapset(tmap) + call assert_equal(['i'], s:GetModes(v:true)) + + abclear + let tmap.mode = 'c' + call mapset(tmap) + call assert_equal(['c'], s:GetModes(v:true)) + + abclear + let tmap.mode = '!' + call mapset(tmap) + call assert_equal(['!'], s:GetModes(v:true)) + + call assert_fails("call mapset(#{mode: ' !', abbr: 1})", 'E1276:') + call assert_fails("call mapset(#{mode: 'cl', abbr: 1})", 'E1276:') + call assert_fails("call mapset(#{mode: 'in', abbr: 1})", 'E1276:') + + " the map commands + map XZ x + let tmap = maplist()->filter({_, m -> m.lhs == 'XZ'})[0]->copy() + + " try the combos + call s:UnmapAll('XZ') + " 'nxso' is ' ', the unadorned :map + let tmap.mode = 'nxso' + call mapset(tmap) + call assert_equal([' '], s:GetModes()) + + cal s:UnmapAll('XZ') + " 'ic' is '!' + let tmap.mode = 'ic' + call mapset(tmap) + call assert_equal(['!'], s:GetModes()) + + call s:UnmapAll('XZ') + " 'xs' is really 'v' + let tmap.mode = 'xs' + call mapset(tmap) + call assert_equal(['v'], s:GetModes()) + + " try the individual modes + call s:UnmapAll('XZ') + let tmap.mode = 'n' + call mapset(tmap) + call assert_equal(['n'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 'x' + call mapset(tmap) + call assert_equal(['x'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 's' + call mapset(tmap) + call assert_equal(['s'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 'o' + call mapset(tmap) + call assert_equal(['o'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 'i' + call mapset(tmap) + call assert_equal(['i'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 'c' + call mapset(tmap) + call assert_equal(['c'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 't' + call mapset(tmap) + call assert_equal(['t'], s:GetModes()) + + call s:UnmapAll('XZ') + let tmap.mode = 'l' + call mapset(tmap) + call assert_equal(['l'], s:GetModes()) + + call s:UnmapAll('XZ') + + " get errors for modes that can't be in one mapping + call assert_fails("call mapset(#{mode: 'nxsoi', abbr: 0})", 'E1276:') + call assert_fails("call mapset(#{mode: ' !', abbr: 0})", 'E1276:') + call assert_fails("call mapset(#{mode: 'ix', abbr: 0})", 'E1276:') + call assert_fails("call mapset(#{mode: 'tl', abbr: 0})", 'E1276:') + call assert_fails("call mapset(#{mode: ' l', abbr: 0})", 'E1276:') + call assert_fails("call mapset(#{mode: ' t', abbr: 0})", 'E1276:') +endfunc + func Check_ctrlb_map(d, check_alt) call assert_equal('<C-B>', a:d.lhs) if a:check_alt @@ -368,4 +527,92 @@ func Test_map_restore_negative_sid() call delete('Xresult') endfunc +func Test_maplist() + new + func s:ClearMappingsAbbreviations() + mapclear | nmapclear | vmapclear | xmapclear | smapclear | omapclear + mapclear! | imapclear | lmapclear | cmapclear | tmapclear + mapclear <buffer> | nmapclear <buffer> | vmapclear <buffer> + xmapclear <buffer> | smapclear <buffer> | omapclear <buffer> + mapclear! <buffer> | imapclear <buffer> | lmapclear <buffer> + cmapclear <buffer> | tmapclear <buffer> + abclear | abclear <buffer> + endfunc + + func s:AddMaps(new, accum) + if len(a:new) > 0 && a:new[0] != "No mapping found" + eval a:accum->extend(a:new) + endif + endfunc + + call s:ClearMappingsAbbreviations() + call assert_equal(0, len(maplist())) + call assert_equal(0, len(maplist(v:true))) + + " Set up some mappings. + map dup bar + map <buffer> dup bufbar + map foo<C-V> is<F4>foo + vnoremap <script> <buffer> <expr> <silent> bar isbar + tmap baz foo + omap h w + lmap i w + nmap j w + xmap k w + smap l w + map abc <Nop> + nmap <M-j> x + nmap <M-Space> y + " And abbreviations + abbreviate xy he + abbreviate xx she + abbreviate <buffer> x they + + " Get a list of the mappings with the ':map' commands. + " Check maplist() return a list of the same size. + call assert_equal(13, len(maplist())) + call assert_equal(3, len(maplist(v:true))) + call assert_equal(13, len(maplist(v:false))) + + " collect all the current maps using :map commands + let maps_command = [] + call s:AddMaps(split(execute('map'), '\n'), maps_command) + call s:AddMaps(split(execute('map!'), '\n'), maps_command) + call s:AddMaps(split(execute('tmap'), '\n'), maps_command) + call s:AddMaps(split(execute('lmap'), '\n'), maps_command) + + " Use maplist to get all the maps + let maps_maplist = maplist() + call assert_equal(len(maps_command), len(maps_maplist)) + + " make sure all the mode-lhs are unique, no duplicates + let map_set = {} + for d in maps_maplist + let map_set[d.mode .. "-" .. d.lhs .. "-" .. d.buffer] = 0 + endfor + call assert_equal(len(maps_maplist), len(map_set)) + + " For everything returned by maplist, should be the same as from maparg. + " Except for "map dup", bacause maparg returns the <buffer> version + for d in maps_maplist + if d.lhs == 'dup' && d.buffer == 0 + continue + endif + let d_maparg = maparg(d.lhs, d.mode, v:false, v:true) + call assert_equal(d_maparg, d) + endfor + + " Check abbr matches maparg + for d in maplist(v:true) + " Note, d.mode is '!', but can't use that with maparg + let d_maparg = maparg(d.lhs, 'i', v:true, v:true) + call assert_equal(d_maparg, d) + endfor + + call s:ClearMappingsAbbreviations() + call assert_equal(0, len(maplist())) + call assert_equal(0, len(maplist(v:true))) +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_mapping.vim b/test/old/testdir/test_mapping.vim index 40d0b3e6f7..36a46684c1 100644 --- a/test/old/testdir/test_mapping.vim +++ b/test/old/testdir/test_mapping.vim @@ -1051,7 +1051,6 @@ func Test_map_cmdkey() call assert_equal('t', m) call assert_equal('t', mode(1)) call StopShellInTerminal(buf) - call TermWait(buf) close! tunmap <F3> endif diff --git a/test/old/testdir/test_modeline.vim b/test/old/testdir/test_modeline.vim index 613722fdbd..487a89e038 100644 --- a/test/old/testdir/test_modeline.vim +++ b/test/old/testdir/test_modeline.vim @@ -172,6 +172,8 @@ func Test_modeline_colon() endfunc func s:modeline_fails(what, text, error) + " Don't use CheckOption(), it would skip the whole test + " just for a single un-supported option if !exists('+' .. a:what) return endif diff --git a/test/old/testdir/test_statusline.vim b/test/old/testdir/test_statusline.vim index fccfd46966..c48bac12b4 100644 --- a/test/old/testdir/test_statusline.vim +++ b/test/old/testdir/test_statusline.vim @@ -617,4 +617,25 @@ func Test_statusline_showcmd() call StopVimInTerminal(buf) endfunc +func Test_statusline_highlight_group_cleared() + CheckScreendump + + " the laststatus option is there to prevent + " the code-style test from complaining about + " trailing whitespace + let lines =<< trim END + set fillchars=stl:\ ,stlnc:\ laststatus=2 + split + hi clear StatusLine + hi clear StatusLineNC + END + call writefile(lines, 'XTest_statusline_stl', 'D') + + let buf = RunVimInTerminal('-S XTest_statusline_stl', {}) + + call VerifyScreenDump(buf, 'Test_statusline_stl_1', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_termdebug.vim b/test/old/testdir/test_termdebug.vim new file mode 100644 index 0000000000..ab1a76000a --- /dev/null +++ b/test/old/testdir/test_termdebug.vim @@ -0,0 +1,197 @@ +" Test for the termdebug plugin + +source shared.vim +source check.vim + +CheckUnix +" CheckFeature terminal +CheckExecutable gdb +CheckExecutable gcc + +let g:GDB = exepath('gdb') +if g:GDB->empty() + throw 'Skipped: gdb is not found in $PATH' +endif + +let g:GCC = exepath('gcc') +if g:GCC->empty() + throw 'Skipped: gcc is not found in $PATH' +endif + +packadd termdebug + +func Test_termdebug_basic() + let lines =<< trim END + #include <stdio.h> + #include <stdlib.h> + + int isprime(int n) + { + if (n <= 1) + return 0; + + for (int i = 2; i <= n / 2; i++) + if (n % i == 0) + return 0; + + return 1; + } + + int main(int argc, char *argv[]) + { + int n = 7; + + printf("%d is %s prime\n", n, isprime(n) ? "a" : "not a"); + + return 0; + } + END + call writefile(lines, 'XTD_basic.c', 'D') + call system($'{g:GCC} -g -o XTD_basic XTD_basic.c') + + edit XTD_basic.c + Termdebug ./XTD_basic + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + Break 9 + call Nterm_wait(gdb_buf) + redraw! + call assert_equal([ + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + Run + call Nterm_wait(gdb_buf, 400) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 9, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + "\ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs->reverse())}) + Finish + call Nterm_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}, + \ {'lnum': 20, 'id': 12, 'name': 'debugPC', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + Continue + + let cn = 0 + " 60 is approx spaceBuffer * 3 + if winwidth(0) <= 78 + 60 + Var + call assert_equal(winnr(), winnr('$')) + call assert_equal(winlayout(), ['col', [['leaf', 1002], ['leaf', 1001], ['leaf', 1000], ['leaf', 1003 + cn]]]) + let cn += 1 + bw! + Asm + call assert_equal(winnr(), winnr('$')) + call assert_equal(winlayout(), ['col', [['leaf', 1002], ['leaf', 1001], ['leaf', 1000], ['leaf', 1003 + cn]]]) + let cn += 1 + bw! + endif + set columns=160 + Var + call assert_equal(winnr(), winnr('$') - 1) + call assert_equal(winlayout(), ['col', [['leaf', 1002], ['leaf', 1001], ['row', [['leaf', 1003 + cn], ['leaf', 1000]]]]]) + let cn += 1 + bw! + Asm + call assert_equal(winnr(), winnr('$') - 1) + call assert_equal(winlayout(), ['col', [['leaf', 1002], ['leaf', 1001], ['row', [['leaf', 1003 + cn], ['leaf', 1000]]]]]) + let cn += 1 + bw! + set columns& + + wincmd t + " Nvim: stop GDB process and process pending events + call chanclose(&channel) + call wait(0, '0') + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal([], sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + call delete('XTD_basic') + %bw! +endfunc + +func Test_termdebug_mapping() + %bw! + call assert_equal(maparg('K', 'n', 0, 1)->empty(), 1) + call assert_equal(maparg('-', 'n', 0, 1)->empty(), 1) + call assert_equal(maparg('+', 'n', 0, 1)->empty(), 1) + Termdebug + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd b + call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('K', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('-', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('+', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('K', 'n', 0, 1).rhs, ':Evaluate<CR>') + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal(maparg('K', 'n', 0, 1)->empty(), 1) + call assert_equal(maparg('-', 'n', 0, 1)->empty(), 1) + call assert_equal(maparg('+', 'n', 0, 1)->empty(), 1) + + %bw! + nnoremap K :echom "K"<cr> + nnoremap - :echom "-"<cr> + nnoremap + :echom "+"<cr> + Termdebug + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd b + call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('K', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('-', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('+', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('K', 'n', 0, 1).rhs, ':Evaluate<CR>') + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0) + call assert_equal(maparg('K', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('-', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('+', 'n', 0, 1).buffer, 0) + call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "K"<cr>') + + %bw! + nnoremap <buffer> K :echom "bK"<cr> + nnoremap <buffer> - :echom "b-"<cr> + nnoremap <buffer> + :echom "b+"<cr> + Termdebug + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + wincmd b + call assert_equal(maparg('K', 'n', 0, 1).buffer, 1) + call assert_equal(maparg('-', 'n', 0, 1).buffer, 1) + call assert_equal(maparg('+', 'n', 0, 1).buffer, 1) + call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>') + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal(maparg('K', 'n', 0, 1).buffer, 1) + call assert_equal(maparg('-', 'n', 0, 1).buffer, 1) + call assert_equal(maparg('+', 'n', 0, 1).buffer, 1) + call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>') + + %bw! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab |