diff options
155 files changed, 3506 insertions, 1974 deletions
diff --git a/cmake.config/iwyu/mapping.imp b/cmake.config/iwyu/mapping.imp index 737b9b0e57..030f9520c0 100644 --- a/cmake.config/iwyu/mapping.imp +++ b/cmake.config/iwyu/mapping.imp @@ -192,6 +192,7 @@ # of its behavior. { include: [ '"nvim/arglist_defs.h"', public, '"nvim/arglist.h"', public ] }, { include: [ '"nvim/buffer_defs.h"', public, '"nvim/buffer.h"', public ] }, + { include: [ '"nvim/cmdexpand_defs.h"', public, '"nvim/cmdexpand.h"', public ] }, { include: [ '"nvim/eval/typval_defs.h"', public, '"nvim/eval/typval.h"', public ] }, { include: [ '"nvim/ex_cmds_defs.h"', public, '"nvim/ex_cmds.h"', public ] }, { include: [ '"nvim/ex_eval_defs.h"', public, '"nvim/ex_eval.h"', public ] }, diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index 2dd5cf45bf..dcaa075608 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -3469,6 +3469,11 @@ fun! s:NetrwBookHistHandler(chg,curdir) echo "bookmarked the current directory" endif + try + call s:NetrwBookHistSave() + catch + endtry + elseif a:chg == 1 " change to the bookmarked directory " call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>")) @@ -3613,6 +3618,11 @@ fun! s:NetrwBookHistHandler(chg,curdir) " call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) endif " call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) + + try + call s:NetrwBookHistSave() + catch + endtry endif call s:NetrwBookmarkMenu() call s:NetrwTgtMenu() @@ -7322,8 +7332,7 @@ fun! s:NetrwMarkFileDiff(islocal) exe "NetrwKeepj e ".fnameescape(fname) diffthis elseif cnt == 2 || cnt == 3 - vsplit - wincmd l + below vsplit " call Decho("diffthis: ".fname,'~'.expand("<slnum>")) exe "NetrwKeepj e ".fnameescape(fname) diffthis @@ -11934,9 +11943,9 @@ fun! s:NetrwBufRemover(bufid) " call Decho("buf#".a:bufid." has name <".bufname(a:bufid).">","~".expand("<slnum>")) " call Decho("buf#".a:bufid." has winid#".bufwinid(a:bufid),"~".expand("<slnum>")) - if a:bufid > 1 && !buflisted(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1 + if a:bufid > 1 && !buflisted(a:bufid) && bufloaded(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1 " call Decho("(s:NetrwBufRemover) removing buffer#".a:bufid,"~".expand("<slnum>")) - exe "bd! ".a:bufid + exe "sil! bd! ".a:bufid endif " call Dret("s:NetrwBufRemover") diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 6ba28dcbfc..05f6bdb871 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -93,9 +93,9 @@ function! provider#clipboard#Executable() abort let s:cache_enabled = 0 return 'pbcopy' elseif !empty($WAYLAND_DISPLAY) && executable('wl-copy') && executable('wl-paste') - let s:copy['+'] = ['wl-copy', '--foreground', '--type', 'text/plain'] + let s:copy['+'] = ['wl-copy', '--type', 'text/plain'] let s:paste['+'] = ['wl-paste', '--no-newline'] - let s:copy['*'] = ['wl-copy', '--foreground', '--primary', '--type', 'text/plain'] + let s:copy['*'] = ['wl-copy', '--primary', '--type', 'text/plain'] let s:paste['*'] = ['wl-paste', '--no-newline', '--primary'] return 'wl-copy' elseif !empty($WAYLAND_DISPLAY) && executable('waycopy') && executable('waypaste') diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 90211fc5db..7ca9996e15 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -118,7 +118,7 @@ manually. Mostly the screen will not scroll up, thus there is no hit-enter prompt. When one command outputs two messages this can happen anyway. ============================================================================== -3. Removing autocommands *autocmd-remove* +3. Removing autocommands *autocmd!* *autocmd-remove* :au[tocmd]! [group] {event} {aupat} [++once] [++nested] {cmd} Remove all autocommands associated with {event} and diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index d0866b1c42..48fa953954 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -925,7 +925,7 @@ cosh({expr}) *cosh()* echo cosh(-0.5) < -1.127626 -count({comp}, {expr} [, {ic} [, {start}]]) *count()* +count({comp}, {expr} [, {ic} [, {start}]]) *count()* *E706* Return the number of times an item with value {expr} appears in |String|, |List| or |Dictionary| {comp}. @@ -5115,7 +5115,6 @@ printf({fmt}, {expr1} ...) *printf()* precision, the argument(s) to be used must also be specified using a {n$} positional argument specifier. See |printf-$|. - The conversion specifiers and their meanings are: *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X* @@ -5306,6 +5305,13 @@ printf({fmt}, {expr1} ...) *printf()* < E1505: Invalid format specifier: %1$d at width %2$d is: %01$*2$.3$d + *E1507* + This internal error indicates that the logic to parse a + positional format argument ran into a problem that couldn't be + otherwise reported. Please file a bug against Vim if you run + into this, copying the exact format string and parameters that + were used. + prompt_getprompt({buf}) *prompt_getprompt()* Returns the effective prompt text for buffer {buf}. {buf} can be a buffer name or number. See |prompt-buffer|. @@ -7842,8 +7848,8 @@ swapinfo({fname}) *swapinfo()* user user name host host name fname original file name - pid PID of the Vim process that created the swap - file + pid PID of the Nvim process that created the swap + file, or zero if not running. mtime last modification time in seconds inode Optional: INODE number of the file dirty 1 if file was modified, 0 if not diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 5ee1a2af13..63967f84a1 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -493,16 +493,26 @@ example, to match only files that end in ".c": > :e *.c$ This will not match a file ending in ".cpp". Without the "$" it does match. -The old value of an option can be obtained by hitting 'wildchar' just after -the '='. For example, typing 'wildchar' after ":set dir=" will insert the -current value of 'dir'. This overrules file name completion for the options -that take a file name. - If you would like using <S-Tab> for CTRL-P in an xterm, put this command in your .cshrc: > xmodmap -e "keysym Tab = Tab Find" And this in your vimrc: > :cmap <Esc>[1~ <C-P> +< *complete-set-option* +When setting an option using |:set=|, the old value of an option can be +obtained by hitting 'wildchar' just after the '='. For example, typing +'wildchar' after ":set dir=" will insert the current value of 'dir'. This +overrules file name completion for the options that take a file name. + +When using |:set=|, |:set+=|, or |:set^=|, string options that have +pre-defined names or syntax (e.g. 'diffopt', 'listchars') or are a list of +single-character flags (e.g. 'shortmess') will also present a list of possible +values for completion when using 'wildchar'. + +When using |:set-=|, comma-separated options like 'diffopt' or 'backupdir' +will show each item separately. Flag list options like 'shortmess' will show +both the entire old value and the individual flags. Otherwise completion will +just fill in with the entire old value. ============================================================================== 3. Ex command-lines *cmdline-lines* @@ -732,7 +742,7 @@ An example for subtracting (which isn't very useful): > On this text: 1 one ~ 2 two ~ - 3 three FOLDED~ + 3 three FOLDED ~ 4 four FOLDED ~ 5 five FOLDED ~ 6 six FOLDED ~ diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 6e7963c066..7df2eb9742 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -169,33 +169,26 @@ If you want to keep the changed buffer without saving it, switch on the 2. Editing a file *edit-a-file* *:e* *:edit* *reload* -:e[dit] [++opt] [+cmd] Edit the current file. This is useful to re-edit the +:e[dit][!] [++opt] [+cmd] + Edit the current file. This is useful to re-edit the current file, when it has been changed outside of Vim. - This fails when changes have been made to the current - buffer and 'autowriteall' isn't set or the file can't - be written. - Also see |++opt| and |+cmd|. - *:edit!* *discard* -:e[dit]! [++opt] [+cmd] - Edit the current file always. Discard any changes to - the current buffer. This is useful if you want to - start all over again. + If [!] is given, unsaved changes in the current buffer + are discarded. Without [!] the command fails if there + are unsaved changes, unless 'autowriteall' is set and + the file can be written. Also see |++opt| and |+cmd|. *:edit_f* -:e[dit] [++opt] [+cmd] {file} +:e[dit][!] [++opt] [+cmd] {file} Edit {file}. - This fails when changes have been made to the current - buffer, unless 'hidden' is set or 'autowriteall' is - set and the file can be written. - Also see |++opt| and |+cmd|. - *:edit!_f* -:e[dit]! [++opt] [+cmd] {file} - Edit {file} always. Discard any changes to the - current buffer. + If [!] is given, unsaved changes in the current buffer + are discarded. Without [!] the command fails if there + are unsaved changes, unless 'hidden' is set or + 'autowriteall' is set and the file can be written. Also see |++opt| and |+cmd|. + *:edit_#* *:e#* :e[dit] [++opt] [+cmd] #[count] Edit the [count]th buffer (as shown by |:files|). @@ -357,8 +350,8 @@ is to use "path\[[]abc]", this matches the file "path\[abc]". *starstar-wildcard* Expanding "**" is possible on Unix, Win32, macOS and a few other systems (but -it may depend on your 'shell' setting. It's known to work correctly for zsh; for -bash this requires at least bash version >= 4.X). +it may depend on your 'shell' setting on Unix and macOS. It's known to work +correctly for zsh; for bash this requires at least bash version >= 4.X). This allows searching a directory tree. This goes up to 100 directories deep. Note there are some commands where this works slightly differently, see |file-searching|. @@ -1078,8 +1071,8 @@ will get the ACL info of the original file. The ACL info is also used to check if a file is read-only (when opening the file). - *xattr* *E1506* *E1507* *E1508* *E1509* -xattr stands for Extended Attributes It is an advanced way to save metadata + *xattr* *E1506* *E1508* *E1509* +xattr stands for Extended Attributes. It is an advanced way to save metadata alongside the file in the filesystem. It depends on the actual filesystem being used and Vim supports it only on a Linux system. Vim attempts to preserve the extended attribute info when writing a file. @@ -1224,10 +1217,10 @@ MULTIPLE WINDOWS AND BUFFERS *window-exit* *:confirm* *:conf* :conf[irm] {command} Execute {command}, and use a dialog when an operation has to be confirmed. Can be used on the - |:q|, |:qa| and |:w| commands (the latter to override - a read-only setting), and any other command that can - fail in such a way, such as |:only|, |:buffer|, - |:bdelete|, etc. + |:edit|, |:q|, |:qa| and |:w| commands (the latter to + override a read-only setting), and any commands that + can fail because of unsaved changes, such as |:only|, + |:buffer|, |:bdelete|, etc. Examples: > :confirm w foo diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 3cd5ea13f6..c41237b862 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2276,12 +2276,13 @@ v:stderr |channel-id| corresponding to stderr. The value is always 2; :call chansend(v:stderr, "error: toaster empty\n") < *v:swapname* *swapname-variable* -v:swapname Only valid when executing |SwapExists| autocommands: Name of - the swap file found. Read-only. +v:swapname Name of the swapfile found. + Only valid during |SwapExists| event. + Read-only. *v:swapchoice* *swapchoice-variable* v:swapchoice |SwapExists| autocommands can set this to the selected choice - for handling an existing swap file: + for handling an existing swapfile: 'o' Open read-only 'e' Edit anyway 'r' Recover diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 27ec92dbc6..ed21dc1c37 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -396,7 +396,7 @@ So to enable this only for ruby, set the following variable: > :let g:ruby_exec = 1 If both, the global `plugin_exec` and the `<filetype>_exec` specific variable -are set, the filetpe specific variable should have precedent. +are set, the filetype specific variable should have precedent. AWK *ft-awk-plugin* diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index b632cf0932..b0f3b76587 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -68,6 +68,10 @@ The following changes may require adaptations in user config or plugins. • Float window support hide and show by setting `hide` on `nvim_open_win` and `nvim_win_set_config`. +• |vim.lsp.util.parse_snippet()| will now strictly follow the snippet grammar + defined by LSP, and hence previously parsed snippets might now be considered + invalid input. + ============================================================================== NEW FEATURES *news-features* @@ -105,9 +109,17 @@ The following new APIs and features were added. • |nvim_set_keymap()| and |nvim_del_keymap()| now support abbreviations. +• Better cmdline completion for string option value. |complete-set-option| + • Builtin TUI can now recognize "super" (|<D-|) and "meta" (|<T-|) modifiers in a terminal emulator that supports |tui-csiu|. +• Editor + • By default, the swapfile "ATTENTION" |E325| dialog is skipped if the + swapfile is owned by a running Nvim process, instead of prompting. If you + always want the swapfile dialog, delete the default SwapExists handler: + `autocmd! nvim_swapfile`. |default-autocmds| + • LSP • LSP method names are available in |vim.lsp.protocol.Methods|. • Implemented LSP inlay hints: |vim.lsp.inlay_hint()| @@ -143,6 +155,8 @@ The following new APIs and features were added. • Added `vim.treesitter.query.edit()`, for live editing of treesitter queries. • Improved error messages for query parsing. + • Added |vim.treesitter.foldtext()| to apply treesitter highlighting to + foldtext. • |vim.ui.open()| opens URIs using the system default handler (macOS `open`, Windows `explorer`, Linux `xdg-open`, etc.) @@ -189,6 +203,8 @@ The following changes to existing APIs or features add new behavior. option, which allows for rendering e.g., diagnostic severities differently. • Defaults: + • On Windows 'isfname' does not include ":". Drive letters are handled + correctly without it. (Use |gF| for filepaths suffixed with ":line:col"). • 'comments' includes "fb:•". • 'shortmess' includes the "C" flag. • Automatic linting of treesitter query files (see |ft-query-plugin|). diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 84cff775f6..283c1e3612 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -52,14 +52,16 @@ achieve special effects. These options come in three forms: 'lines' Warning: This may have a lot of side effects. - *:set-args* *E487* *E521* + *:set-args* *:set=* *E487* *E521* :se[t] {option}={value} or :se[t] {option}:{value} Set string or number option to {value}. For numeric options the value can be given in decimal, hex (preceded with 0x) or octal (preceded with '0'). The old value can be inserted by typing 'wildchar' (by - default this is a <Tab>). See |cmdline-completion|. + default this is a <Tab>). Many string options with + fixed syntax also support completing known values. + See |cmdline-completion| and |complete-set-option|. White space between {option} and '=' is allowed and will be ignored. White space between '=' and {value} is not allowed. @@ -93,6 +95,9 @@ achieve special effects. These options come in three forms: When the option is a list of flags, {value} must be exactly as they appear in the option. Remove flags one by one to avoid problems. + The individual values from a comma separated list or + list of flags can be inserted by typing 'wildchar'. + See |complete-set-option|. Also see |:set-args| above. The {option} arguments to ":set" may be repeated. For example: > @@ -1961,7 +1966,8 @@ A jump table for the options with a short description can be found at |Q_op|. When omitted a context of six lines is used. When using zero the context is actually one, since folds require a line in between, also - for a deleted line. + for a deleted line. Set it to a very large + value (999999) to disable folding completely. See |fold-diff|. iblank Ignore changes where lines are all blank. Adds @@ -3447,7 +3453,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'isfname'* *'isf'* 'isfname' 'isf' string (default for Windows: - "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" + "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=" otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,=") global The characters specified by this option are included in file names and diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index d74757de4f..09119b0918 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1065,7 +1065,7 @@ match ASCII characters, as indicated by the range. \(\) A pattern enclosed by escaped parentheses. */\(* */\(\)* */\)* E.g., "\(^a\)" matches 'a' at the start of a line. - There can only be ten of these. You can use "\%(" to add more, but + There can only be nine of these. You can use "\%(" to add more, but not counting it as a sub-expression. *E51* *E54* *E55* *E872* *E873* diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt index b05bcd6bc8..e6b5b06744 100644 --- a/runtime/doc/recover.txt +++ b/runtime/doc/recover.txt @@ -83,6 +83,15 @@ Detecting an existing swap file ~ You can find this in the user manual, section |11.3|. + *W325* +The default |SwapExists| handler (|default-autocmds|) skips the |E325| prompt +(selects "(E)dit") if the swapfile owner process (1) is still running and (2) +was started by the current user. This presumes that you normally don't want +to be bothered with the |ATTENTION| message just because you happen to edit +the same file from multiple Nvim instances. In the worst case (a system +crash) there will be more than one swapfile for the file; use |:recover| to +inspect all of its swapfiles. + Updating the swapfile ~ diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 34971c7acf..e19fda8fd1 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -560,6 +560,16 @@ foldexpr({lnum}) *vim.treesitter.foldexpr()* Return: ~ (string) +foldtext() *vim.treesitter.foldtext()* + Returns the highlighted content of the first line of the fold or falls + back to |foldtext()| if no treesitter parser is found. Can be set directly + to 'foldtext': >lua + vim.wo.foldtext = 'v:lua.vim.treesitter.foldtext()' +< + + Return: ~ + `{ [1]: string, [2]: string[] }[]` | string + *vim.treesitter.get_captures_at_cursor()* get_captures_at_cursor({winnr}) Returns a list of highlight capture names under the cursor diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index e636746616..4161d3b21e 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -53,6 +53,8 @@ Defaults *nvim-defaults* - 'hlsearch' is enabled - 'include' defaults to "". The C ftplugin sets it to "^\\s*#\\s*include" - 'incsearch' is enabled +- 'isfname' does not include ":" (on Windows). Drive letters are handled + correctly without it. (Use |gF| for filepaths suffixed with ":line:col"). - 'joinspaces' is disabled - 'langnoremap' is enabled - 'langremap' is disabled @@ -139,6 +141,11 @@ nvim_terminal: nvim_cmdwin: - CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|. +nvim_swapfile: +- SwapExists: Skips the swapfile prompt (sets |v:swapchoice| to "e") when the + swapfile is owned by a running Nvim process. Shows |W325| "Ignoring + swapfile…" message. + ============================================================================== New Features *nvim-features* @@ -287,13 +294,12 @@ Options: 'diffopt' "linematch" feature 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The user is prompted whether to trust the file. - 'fillchars' flags: "msgsep", "horiz", "horizup", - "horizdown", "vertleft", "vertright", "verthoriz" + 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown", + "vertleft", "vertright", "verthoriz" 'foldcolumn' supports up to 9 dynamic/fixed columns 'guicursor' works in the terminal (TUI) 'inccommand' shows interactive results for |:substitute|-like commands and |:command-preview| commands - 'jumpoptions' "stack" behavior 'jumpoptions' "view" tries to restore the |mark-view| when moving through the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|. 'laststatus' global statusline support @@ -304,6 +310,7 @@ Options: 'signcolumn' supports up to 9 dynamic/fixed columns 'statuscolumn' full control of columns using 'statusline' format 'tabline' %@Func@foo%X can call any function on mouse-click + 'termpastefilter' 'ttimeout', 'ttimeoutlen' behavior was simplified 'winblend' pseudo-transparency in floating windows |api-floatwin| 'winhighlight' window-local highlights @@ -382,6 +389,7 @@ Upstreamed features *nvim-upstreamed* These Nvim features were later integrated into Vim. - 'fillchars' flags: "eob" +- 'jumpoptions' "stack" behavior - 'wildoptions' flags: "pum" enables popupmenu for wildmode completion - |<Cmd>| - |WinClosed| diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim index c227838d18..c1a6bc5ade 100644 --- a/runtime/ftplugin/sh.vim +++ b/runtime/ftplugin/sh.vim @@ -3,7 +3,8 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " Previous Maintainer: Dan Sharp " Contributor: Enno Nagel <ennonagel+vim@gmail.com> -" Last Change: 2023 Aug 29 +" Eisuke Kawashima +" Last Change: 2023 Sep 28 if exists("b:did_ftplugin") finish @@ -39,16 +40,16 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") let b:undo_ftplugin ..= " | unlet! b:browsefilter" endif -if (exists("b:is_bash") && (b:is_bash == 1)) +if get(b:, "is_bash", 0) if !has("gui_running") && executable("less") - command! -buffer -nargs=1 Help silent exe '!bash -c "{ help "<args>" 2>/dev/null || man "<args>"; } | LESS= less"' | redraw! - elseif has('terminal') - command! -buffer -nargs=1 Help silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""' + command! -buffer -nargs=1 ShKeywordPrg silent exe '!bash -c "{ help "<args>" 2>/dev/null || man "<args>"; } | LESS= less"' | redraw! + elseif has("terminal") + command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""' else - command! -buffer -nargs=1 Help echo system('bash -c "help <args>" 2>/dev/null || man "<args>"') + command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help <args>" 2>/dev/null || man "<args>"') endif - setlocal keywordprg=:Help - let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer Help" + setlocal keywordprg=:ShKeywordPrg + let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer ShKeywordPrg" endif let &cpo = s:save_cpo diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 7f09fc8038..bbe93bfbc8 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -548,6 +548,9 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) else c2 = #bufline + 1 end + elseif regtype == 'V' then -- linewise selection, always return whole line + c1 = 0 + c2 = -1 else c1 = (l == pos1[1]) and pos1[2] or 0 c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1 @@ -1147,11 +1150,28 @@ function vim._init_default_autocmds() end end, }) + vim.api.nvim_create_autocmd({ 'CmdwinEnter' }, { pattern = '[:>]', group = vim.api.nvim_create_augroup('nvim_cmdwin', {}), command = 'syntax sync minlines=1 maxlines=1', }) + + vim.api.nvim_create_autocmd({ 'SwapExists' }, { + pattern = '*', + group = vim.api.nvim_create_augroup('nvim_swapfile', {}), + callback = function() + local info = vim.fn.swapinfo(vim.v.swapname) + local user = vim.uv.os_get_passwd().username + local iswin = 1 == vim.fn.has('win32') + if info.error or info.pid <= 0 or (not iswin and info.user ~= user) then + vim.v.swapchoice = '' -- Show the prompt. + return + end + vim.v.swapchoice = 'e' -- Choose "(E)dit". + vim.notify(('W325: Ignoring swapfile from Nvim process %d'):format(info.pid)) + end, + }) end function vim._init_defaults() diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index ed0d844748..08ee1f97ba 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -1591,7 +1591,8 @@ vim.go.dex = vim.go.diffexpr --- When omitted a context of six lines is used. --- When using zero the context is actually one, --- since folds require a line in between, also ---- for a deleted line. +--- for a deleted line. Set it to a very large +--- value (999999) to disable folding completely. --- See `fold-diff`. --- --- iblank Ignore changes where lines are all blank. Adds diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 86e8781160..06de2fdd1c 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -6098,7 +6098,6 @@ function vim.fn.prevnonblank(lnum) end --- precision, the argument(s) to be used must also be specified --- using a {n$} positional argument specifier. See |printf-$|. --- ---- --- The conversion specifiers and their meanings are: --- --- *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X* @@ -6289,6 +6288,13 @@ function vim.fn.prevnonblank(lnum) end --- < E1505: Invalid format specifier: --- %1$d at width %2$d is: %01$*2$.3$d --- +--- *E1507* +--- This internal error indicates that the logic to parse a +--- positional format argument ran into a problem that couldn't be +--- otherwise reported. Please file a bug against Vim if you run +--- into this, copying the exact format string and parameters that +--- were used. +--- --- @param fmt any --- @param expr1? any --- @return any @@ -9311,8 +9317,8 @@ function vim.fn.swapfilelist() end --- user user name --- host host name --- fname original file name ---- pid PID of the Vim process that created the swap ---- file +--- pid PID of the Nvim process that created the swap +--- file, or zero if not running. --- mtime last modification time in seconds --- inode Optional: INODE number of the file --- dirty 1 if file was modified, 0 if not diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 5ae4e508ef..c412364461 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -565,6 +565,7 @@ local extension = { libsonnet = 'jsonnet', jsp = 'jsp', jl = 'julia', + just = 'just', kdl = 'kdl', kv = 'kivy', kix = 'kix', @@ -1331,6 +1332,7 @@ local filename = { ['.jsfmtrc'] = 'jsonc', ['.jshintrc'] = 'jsonc', ['.swrc'] = 'jsonc', + ['.justfile'] = 'just', Kconfig = 'kconfig', ['Kconfig.debug'] = 'kconfig', ['lftp.conf'] = 'lftp', @@ -1715,6 +1717,7 @@ local pattern = { ['org%.eclipse%..*%.prefs'] = 'jproperties', ['.*%.properties_.._.._.*'] = starsetf('jproperties'), ['[jt]sconfig.*%.json'] = 'jsonc', + ['[jJ]ustfile'] = 'just', ['Kconfig%..*'] = starsetf('kconfig'), ['.*%.[Ss][Uu][Bb]'] = 'krl', ['lilo%.conf.*'] = starsetf('lilo'), diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua deleted file mode 100644 index e7ada5415f..0000000000 --- a/runtime/lua/vim/lsp/_snippet.lua +++ /dev/null @@ -1,500 +0,0 @@ -local P = {} - ----Take characters until the target characters (The escape sequence is '\' + char) ----@param targets string[] The character list for stop consuming text. ----@param specials string[] If the character isn't contained in targets/specials, '\' will be left. -P.take_until = function(targets, specials) - targets = targets or {} - specials = specials or {} - - return function(input, pos) - local new_pos = pos - local raw = {} - local esc = {} - while new_pos <= #input do - local c = string.sub(input, new_pos, new_pos) - if c == '\\' then - table.insert(raw, '\\') - new_pos = new_pos + 1 - c = string.sub(input, new_pos, new_pos) - if not vim.list_contains(targets, c) and not vim.list_contains(specials, c) then - table.insert(esc, '\\') - end - table.insert(raw, c) - table.insert(esc, c) - new_pos = new_pos + 1 - else - if vim.list_contains(targets, c) then - break - end - table.insert(raw, c) - table.insert(esc, c) - new_pos = new_pos + 1 - end - end - - if new_pos == pos then - return P.unmatch(pos) - end - - return { - parsed = true, - value = { - raw = table.concat(raw, ''), - esc = table.concat(esc, ''), - }, - pos = new_pos, - } - end -end - -P.unmatch = function(pos) - return { - parsed = false, - value = nil, - pos = pos, - } -end - -P.map = function(parser, map) - return function(input, pos) - local result = parser(input, pos) - if result.parsed then - return { - parsed = true, - value = map(result.value), - pos = result.pos, - } - end - return P.unmatch(pos) - end -end - -P.lazy = function(factory) - return function(input, pos) - return factory()(input, pos) - end -end - -P.token = function(token) - return function(input, pos) - local maybe_token = string.sub(input, pos, pos + #token - 1) - if token == maybe_token then - return { - parsed = true, - value = maybe_token, - pos = pos + #token, - } - end - return P.unmatch(pos) - end -end - -P.pattern = function(p) - return function(input, pos) - local maybe_match = string.match(string.sub(input, pos), '^' .. p) - if maybe_match then - return { - parsed = true, - value = maybe_match, - pos = pos + #maybe_match, - } - end - return P.unmatch(pos) - end -end - -P.many = function(parser) - return function(input, pos) - local values = {} - local new_pos = pos - while new_pos <= #input do - local result = parser(input, new_pos) - if not result.parsed then - break - end - table.insert(values, result.value) - new_pos = result.pos - end - if #values > 0 then - return { - parsed = true, - value = values, - pos = new_pos, - } - end - return P.unmatch(pos) - end -end - -P.any = function(...) - local parsers = { ... } - return function(input, pos) - for _, parser in ipairs(parsers) do - local result = parser(input, pos) - if result.parsed then - return result - end - end - return P.unmatch(pos) - end -end - -P.opt = function(parser) - return function(input, pos) - local result = parser(input, pos) - return { - parsed = true, - value = result.value, - pos = result.pos, - } - end -end - -P.seq = function(...) - local parsers = { ... } - return function(input, pos) - local values = {} - local new_pos = pos - for i, parser in ipairs(parsers) do - local result = parser(input, new_pos) - if result.parsed then - values[i] = result.value - new_pos = result.pos - else - return P.unmatch(pos) - end - end - return { - parsed = true, - value = values, - pos = new_pos, - } - end -end - -local Node = {} - -Node.Type = { - SNIPPET = 0, - TABSTOP = 1, - PLACEHOLDER = 2, - VARIABLE = 3, - CHOICE = 4, - TRANSFORM = 5, - FORMAT = 6, - TEXT = 7, -} - -function Node:__tostring() - local insert_text = {} - if self.type == Node.Type.SNIPPET then - for _, c in ipairs(self.children) do - table.insert(insert_text, tostring(c)) - end - elseif self.type == Node.Type.CHOICE then - table.insert(insert_text, self.items[1]) - elseif self.type == Node.Type.PLACEHOLDER then - for _, c in ipairs(self.children or {}) do - table.insert(insert_text, tostring(c)) - end - elseif self.type == Node.Type.TEXT then - table.insert(insert_text, self.esc) - end - return table.concat(insert_text, '') -end - ---@see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar - -local S = {} -S.dollar = P.token('$') -S.open = P.token('{') -S.close = P.token('}') -S.colon = P.token(':') -S.slash = P.token('/') -S.comma = P.token(',') -S.pipe = P.token('|') -S.plus = P.token('+') -S.minus = P.token('-') -S.question = P.token('?') -S.int = P.map(P.pattern('[0-9]+'), function(value) - return tonumber(value, 10) -end) -S.var = P.pattern('[%a_][%w_]+') -S.text = function(targets, specials) - return P.map(P.take_until(targets, specials), function(value) - return setmetatable({ - type = Node.Type.TEXT, - raw = value.raw, - esc = value.esc, - }, Node) - end) -end - -S.toplevel = P.lazy(function() - return P.any(S.placeholder, S.tabstop, S.variable, S.choice) -end) - -S.format = P.any( - P.map(P.seq(S.dollar, S.int), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[2], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - }, Node) - end), - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - S.slash, - P.any( - P.token('upcase'), - P.token('downcase'), - P.token('capitalize'), - P.token('camelcase'), - P.token('pascalcase') - ), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - modifier = values[6], - }, Node) - end - ), - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - P.seq( - S.question, - P.opt(P.take_until({ ':' }, { '\\' })), - S.colon, - P.opt(P.take_until({ '}' }, { '\\' })) - ), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = values[5][2] and values[5][2].esc or '', - else_text = values[5][4] and values[5][4].esc or '', - }, Node) - end - ), - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = values[5][2] and values[5][2].esc or '', - else_text = '', - }, Node) - end - ), - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - S.minus, - P.opt(P.take_until({ '}' }, { '\\' })), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = '', - else_text = values[6] and values[6].esc or '', - }, Node) - end - ), - P.map( - P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), - function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - if_text = '', - else_text = values[5] and values[5].esc or '', - }, Node) - end - ) -) - -S.transform = P.map( - P.seq( - S.slash, - P.take_until({ '/' }, { '\\' }), - S.slash, - P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))), - S.slash, - P.opt(P.pattern('[ig]+')) - ), - function(values) - return setmetatable({ - type = Node.Type.TRANSFORM, - pattern = values[2].raw, - format = values[4], - option = values[6], - }, Node) - end -) - -S.tabstop = P.any( - P.map(P.seq(S.dollar, S.int), function(values) - return setmetatable({ - type = Node.Type.TABSTOP, - tabstop = values[2], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) - return setmetatable({ - type = Node.Type.TABSTOP, - tabstop = values[3], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.transform, S.close), function(values) - return setmetatable({ - type = Node.Type.TABSTOP, - tabstop = values[3], - transform = values[4], - }, Node) - end) -) - -S.placeholder = P.any( - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.PLACEHOLDER, - tabstop = values[3], - -- insert empty text if opt did not match. - children = values[5] or { - setmetatable({ - type = Node.Type.TEXT, - raw = '', - esc = '', - }, Node), - }, - }, Node) - end - ) -) - -S.choice = P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.pipe, - P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values) - return values[1].esc - end)), - S.pipe, - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.CHOICE, - tabstop = values[3], - items = values[5], - }, Node) - end -) - -S.variable = P.any( - P.map(P.seq(S.dollar, S.var), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[2], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.var, S.close), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.var, S.transform, S.close), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - transform = values[4], - }, Node) - end), - P.map( - P.seq( - S.dollar, - S.open, - S.var, - S.colon, - P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - children = values[5], - }, Node) - end - ) -) - -S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values) - return setmetatable({ - type = Node.Type.SNIPPET, - children = values, - }, Node) -end) - -local M = {} - ----The snippet node type enum ----@types table<string, integer> -M.NodeType = Node.Type - ----Parse snippet string and returns the AST ----@param input string ----@return table -function M.parse(input) - local result = S.snippet(input, 1) - if not result.parsed then - error('snippet parsing failed.') - end - return result.value -end - -return M diff --git a/runtime/lua/vim/lsp/_snippet_grammar.lua b/runtime/lua/vim/lsp/_snippet_grammar.lua new file mode 100644 index 0000000000..86f00fea51 --- /dev/null +++ b/runtime/lua/vim/lsp/_snippet_grammar.lua @@ -0,0 +1,143 @@ +--- Grammar for LSP snippets, based on https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax + +local lpeg = vim.lpeg +local P, S, R, V = lpeg.P, lpeg.S, lpeg.R, lpeg.V +local C, Cg, Ct = lpeg.C, lpeg.Cg, lpeg.Ct + +local M = {} + +local alpha = R('az', 'AZ') +local backslash = P('\\') +local colon = P(':') +local dollar = P('$') +local int = R('09') ^ 1 +local l_brace, r_brace = P('{'), P('}') +local pipe = P('|') +local slash = P('/') +local underscore = P('_') +local var = Cg((underscore + alpha) * ((underscore + alpha + int) ^ 0), 'name') +local format_capture = Cg(int / tonumber, 'capture') +local format_modifier = Cg(P('upcase') + P('downcase') + P('capitalize'), 'modifier') +local tabstop = Cg(int / tonumber, 'tabstop') + +--- Returns a function that unescapes occurrences of "special" characters. +--- +--- @param special string +--- @return fun(match: string): string +local function escape_text(special) + return function(match) + local escaped = match:gsub('\\(.)', function(c) + return special:find(c) and c or '\\' .. c + end) + return escaped + end +end + +-- Text nodes match "any character", but $, \, and } must be escaped. +local escapable = '$}\\' +local text = (backslash * S(escapable)) + (P(1) - S(escapable)) +local text_0, text_1 = (text ^ 0) / escape_text(escapable), text ^ 1 +-- Within choice nodes, \ also escapes comma and pipe characters. +local choice_text = C(((backslash * S(escapable .. ',|')) + (P(1) - S(escapable .. ',|'))) ^ 1) + / escape_text(escapable .. ',|') +local if_text, else_text = Cg(text_0, 'if_text'), Cg(text_0, 'else_text') +-- Within format nodes, make sure we stop at / +local format_text = C(((backslash * S(escapable)) + (P(1) - S(escapable .. '/'))) ^ 1) + / escape_text(escapable) +-- Within ternary condition format nodes, make sure we stop at : +local if_till_colon_text = Cg( + C(((backslash * S(escapable)) + (P(1) - S(escapable .. ':'))) ^ 1) / escape_text(escapable), + 'if_text' +) + +-- Matches the string inside //, allowing escaping of the closing slash. +local regex = Cg(((backslash * slash) + (P(1) - slash)) ^ 1, 'regex') + +-- Regex constructor flags (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp#parameters). +local options = Cg(S('dgimsuvy') ^ 0, 'options') + +--- @enum vim.snippet.Type +local Type = { + Tabstop = 1, + Placeholder = 2, + Choice = 3, + Variable = 4, + Format = 5, + Text = 6, + Snippet = 7, +} +M.NodeType = Type + +--- @class vim.snippet.Node<T>: { type: vim.snippet.Type, data: T } +--- @class vim.snippet.TabstopData: { tabstop: number } +--- @class vim.snippet.TextData: { text: string } +--- @class vim.snippet.PlaceholderData: { tabstop: vim.snippet.TabstopData, value: vim.snippet.Node<any> } +--- @class vim.snippet.ChoiceData: { tabstop: vim.snippet.TabstopData, values: string[] } +--- @class vim.snippet.VariableData: { name: string, default?: vim.snippet.Node<any>, regex?: string, format?: vim.snippet.Node<vim.snippet.FormatData|vim.snippet.TextData>[], options?: string } +--- @class vim.snippet.FormatData: { capture: number, modifier?: string, if_text?: string, else_text?: string } +--- @class vim.snippet.SnippetData: { children: vim.snippet.Node<any>[] } + +--- Returns a function that constructs a snippet node of the given type. +--- +--- @generic T +--- @param type vim.snippet.Type +--- @return fun(data: T): vim.snippet.Node<T> +local function node(type) + return function(data) + return { type = type, data = data } + end +end + +-- stylua: ignore +local G = P({ + 'snippet'; + snippet = Ct(Cg( + Ct(( + V('any') + + (Ct(Cg(text_1 / escape_text(escapable), 'text')) / node(Type.Text)) + ) ^ 1), 'children' + )) / node(Type.Snippet), + any_or_text = V('any') + (Ct(Cg(text_0 / escape_text(escapable), 'text')) / node(Type.Text)), + any = V('placeholder') + V('tabstop') + V('choice') + V('variable'), + tabstop = Ct(dollar * (tabstop + (l_brace * tabstop * r_brace))) / node(Type.Tabstop), + placeholder = Ct(dollar * l_brace * tabstop * colon * Cg(V('any_or_text'), 'value') * r_brace) / node(Type.Placeholder), + choice = Ct(dollar * + l_brace * + tabstop * + pipe * + Cg(Ct(choice_text * (P(',') * choice_text) ^ 0), 'values') * + pipe * + r_brace) / node(Type.Choice), + variable = Ct(dollar * ( + var + ( + l_brace * var * ( + r_brace + + (colon * Cg(V('any_or_text'), 'default') * r_brace) + + (slash * regex * slash * Cg(Ct((V('format') + (C(format_text) / node(Type.Text))) ^ 1), 'format') * slash * options * r_brace) + )) + )) / node(Type.Variable), + format = Ct(dollar * ( + format_capture + ( + l_brace * format_capture * ( + r_brace + + (colon * ( + (slash * format_modifier * r_brace) + + (P('+') * if_text * r_brace) + + (P('?') * if_till_colon_text * colon * else_text * r_brace) + + (P('-') * else_text * r_brace) + + (else_text * r_brace) + )) + )) + )) / node(Type.Format), +}) + +--- Parses the given input into a snippet tree. +--- @param input string +--- @return vim.snippet.Node<vim.snippet.SnippetData> +function M.parse(input) + local snippet = G:match(input) + assert(snippet, 'snippet parsing failed') + return snippet --- @type vim.snippet.Node<vim.snippet.SnippetData> +end + +return M diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 8a29fac2b5..a906512e24 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -652,7 +652,7 @@ local function on_code_action_results(results, ctx, options) -- arguments?: any[] -- ---@type lsp.Client - local client = vim.lsp.get_client_by_id(action_tuple[1]) + local client = assert(vim.lsp.get_client_by_id(action_tuple[1])) local action = action_tuple[2] local reg = client.dynamic_capabilities:get(ms.textDocument_codeAction, { bufnr = ctx.bufnr }) @@ -663,10 +663,14 @@ local function on_code_action_results(results, ctx, options) if not action.edit and client and supports_resolve then client.request(ms.codeAction_resolve, action, function(err, resolved_action) if err then - vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) - return + if action.command then + apply_action(action, client) + else + vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) + end + else + apply_action(resolved_action, client) end - apply_action(resolved_action, client) end, ctx.bufnr) else apply_action(action, client) diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 51ed87219c..a4c8959b99 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1,5 +1,5 @@ local protocol = require('vim.lsp.protocol') -local snippet = require('vim.lsp._snippet') +local snippet = require('vim.lsp._snippet_grammar') local validate = vim.validate local api = vim.api local list_extend = vim.list_extend @@ -610,12 +610,41 @@ end ---@return string parsed snippet function M.parse_snippet(input) local ok, parsed = pcall(function() - return tostring(snippet.parse(input)) + return snippet.parse(input) end) if not ok then return input end - return parsed + + --- @param node vim.snippet.Node<any> + --- @return string + local function node_to_string(node) + local insert_text = {} + if node.type == snippet.NodeType.Snippet then + for _, child in + ipairs((node.data --[[@as vim.snippet.SnippetData]]).children) + do + table.insert(insert_text, node_to_string(child)) + end + elseif node.type == snippet.NodeType.Choice then + table.insert(insert_text, (node.data --[[@as vim.snippet.ChoiceData]]).values[1]) + elseif node.type == snippet.NodeType.Placeholder then + table.insert( + insert_text, + node_to_string((node.data --[[@as vim.snippet.PlaceholderData]]).value) + ) + elseif node.type == snippet.NodeType.Text then + table.insert( + insert_text, + node + .data --[[@as vim.snippet.TextData]] + .text + ) + end + return table.concat(insert_text) + end + + return node_to_string(parsed) end --- Sorts by CompletionItem.sortText. diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 0e34cbcbcc..f863942d3b 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -508,4 +508,16 @@ function M.foldexpr(lnum) return require('vim.treesitter._fold').foldexpr(lnum) end +--- Returns the highlighted content of the first line of the fold or falls back to |foldtext()| +--- if no treesitter parser is found. Can be set directly to 'foldtext': +--- +--- ```lua +--- vim.wo.foldtext = 'v:lua.vim.treesitter.foldtext()' +--- ``` +--- +---@return { [1]: string, [2]: string[] }[] | string +function M.foldtext() + return require('vim.treesitter._fold').foldtext() +end + return M diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 8bc08c9c2e..5c1cc06908 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -361,4 +361,96 @@ function M.foldexpr(lnum) return foldinfos[bufnr].levels[lnum] or '0' end +---@package +---@return { [1]: string, [2]: string[] }[]|string +function M.foldtext() + local foldstart = vim.v.foldstart + local bufnr = api.nvim_get_current_buf() + + ---@type boolean, LanguageTree + local ok, parser = pcall(ts.get_parser, bufnr) + if not ok then + return vim.fn.foldtext() + end + + local query = ts.query.get(parser:lang(), 'highlights') + if not query then + return vim.fn.foldtext() + end + + local tree = parser:parse({ foldstart - 1, foldstart })[1] + + local line = api.nvim_buf_get_lines(bufnr, foldstart - 1, foldstart, false)[1] + if not line then + return vim.fn.foldtext() + end + + ---@type { [1]: string, [2]: string[], range: { [1]: integer, [2]: integer } }[] | { [1]: string, [2]: string[] }[] + local result = {} + + local line_pos = 0 + + for id, node, metadata in query:iter_captures(tree:root(), 0, foldstart - 1, foldstart) do + local name = query.captures[id] + local start_row, start_col, end_row, end_col = node:range() + + local priority = tonumber(metadata.priority or vim.highlight.priorities.treesitter) + + if start_row == foldstart - 1 and end_row == foldstart - 1 then + -- check for characters ignored by treesitter + if start_col > line_pos then + table.insert(result, { + line:sub(line_pos + 1, start_col), + {}, + range = { line_pos, start_col }, + }) + end + line_pos = end_col + + local text = line:sub(start_col + 1, end_col) + table.insert(result, { text, { { '@' .. name, priority } }, range = { start_col, end_col } }) + end + end + + local i = 1 + while i <= #result do + -- find first capture that is not in current range and apply highlights on the way + local j = i + 1 + while + j <= #result + and result[j].range[1] >= result[i].range[1] + and result[j].range[2] <= result[i].range[2] + do + for k, v in ipairs(result[i][2]) do + if not vim.tbl_contains(result[j][2], v) then + table.insert(result[j][2], k, v) + end + end + j = j + 1 + end + + -- remove the parent capture if it is split into children + if j > i + 1 then + table.remove(result, i) + else + -- highlights need to be sorted by priority, on equal prio, the deeper nested capture (earlier + -- in list) should be considered higher prio + if #result[i][2] > 1 then + table.sort(result[i][2], function(a, b) + return a[2] < b[2] + end) + end + + result[i][2] = vim.tbl_map(function(tbl) + return tbl[1] + end, result[i][2]) + result[i] = { result[i][1], result[i][2] } + + i = i + 1 + end + end + + return result +end + return M diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index eb32f46143..f4c6f646eb 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -17,6 +17,7 @@ #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/decoration.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 675aaf1006..b8e0934669 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -941,6 +941,12 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) goto fail; } + // Set last_changedtick to avoid triggering a TextChanged autocommand right + // after it was added. + buf->b_last_changedtick = buf_get_changedtick(buf); + buf->b_last_changedtick_i = buf_get_changedtick(buf); + buf->b_last_changedtick_pum = buf_get_changedtick(buf); + // Only strictly needed for scratch, but could just as well be consistent // and do this now. buffer is created NOW, not when it latter first happen // to reach a window or aucmd_prepbuf() .. diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index a4221a1562..fb8849541d 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -14,6 +14,7 @@ #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/window.h" diff --git a/src/nvim/arglist.h b/src/nvim/arglist.h index 52894b73a0..bd5cfb4a11 100644 --- a/src/nvim/arglist.h +++ b/src/nvim/arglist.h @@ -2,6 +2,7 @@ #define NVIM_ARGLIST_H #include "nvim/arglist_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 657760914f..a40f7d8c26 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2213,6 +2213,13 @@ char *expand_get_event_name(expand_T *xp, int idx) return event_names[idx - next_augroup_id].name; } +/// Function given to ExpandGeneric() to obtain the list of event names. Don't +/// include groups. +char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + return event_names[idx].name; +} + /// Check whether given autocommand is supported /// /// @param[in] event Event to check. diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index b3de57311e..324a31be47 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -6,6 +6,7 @@ #include "nvim/api/private/defs.h" #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/macros.h" diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 5025e86771..076cf63913 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3480,8 +3480,8 @@ void get_rel_pos(win_T *wp, char *buf, int buflen) return; } - long above; // number of lines above window - long below; // number of lines below window + linenr_T above; // number of lines above window + linenr_T below; // number of lines below window above = wp->w_topline - 1; above += win_get_fill(wp, wp->w_topline) - wp->w_topfill; @@ -3580,7 +3580,7 @@ void ex_buffer_all(exarg_T *eap) bool p_ea_save; int open_wins = 0; int r; - long count; // Maximum number of windows to open. + linenr_T count; // Maximum number of windows to open. int all; // When true also load inactive buffers. int had_tab = cmdmod.cmod_tab; tabpage_T *tpnext; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index a9ad0051ed..1a2e2fbdae 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -488,13 +488,13 @@ struct file_buffer { u_header_T *b_u_newhead; // pointer to newest header; may not be valid // if b_u_curhead is not NULL u_header_T *b_u_curhead; // pointer to current header - int b_u_numhead; // current number of headers - bool b_u_synced; // entry lists are synced - long b_u_seq_last; // last used undo sequence number - long b_u_save_nr_last; // counter for last file write - long b_u_seq_cur; // uh_seq of header below which we are now - time_t b_u_time_cur; // uh_time of header below which we are now - long b_u_save_nr_cur; // file write nr after which we are now + int b_u_numhead; // current number of headers + bool b_u_synced; // entry lists are synced + int b_u_seq_last; // last used undo sequence number + int b_u_save_nr_last; // counter for last file write + int b_u_seq_cur; // uh_seq of header below which we are now + time_t b_u_time_cur; // uh_time of header below which we are now + int b_u_save_nr_cur; // file write nr after which we are now // variables for "U" command in undo.c char *b_u_line_ptr; // saved line for "U" command diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c index db813a3ae1..2e4dda78fc 100644 --- a/src/nvim/bufwrite.c +++ b/src/nvim/bufwrite.c @@ -1368,7 +1368,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en } int no_eol = false; // no end-of-line written - long nchars; + int nchars; linenr_T lnum; int fileformat; int checking_conversion; @@ -1789,7 +1789,7 @@ restore_backup: if (msg_add_fileformat(fileformat)) { insert_space = true; } - msg_add_lines(insert_space, (long)lnum, nchars); // add line/char count + msg_add_lines(insert_space, lnum, nchars); // add line/char count if (!shortmess(SHM_WRITE)) { if (append) { xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"), IOSIZE); diff --git a/src/nvim/change.c b/src/nvim/change.c index 48dc02b65b..abbfe2505e 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1941,7 +1941,7 @@ void truncate_line(int fixpos) /// Delete "nlines" lines at the cursor. /// Saves the lines for undo first if "undo" is true. -void del_lines(long nlines, bool undo) +void del_lines(linenr_T nlines, bool undo) { int n; linenr_T first = curwin->w_cursor.lnum; diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index d733ffe6ab..c2469b6574 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -69,9 +69,6 @@ #include "nvim/vim.h" #include "nvim/window.h" -/// Type used by ExpandGeneric() -typedef char *(*CompleteListItemGetter)(expand_T *, int); - /// Type used by call_user_expand_func typedef void *(*user_expand_func_T)(const char *, int, typval_T *); @@ -107,6 +104,8 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp) && xp->xp_context != EXPAND_HELP && xp->xp_context != EXPAND_LUA && xp->xp_context != EXPAND_OLD_SETTING + && xp->xp_context != EXPAND_STRING_SETTING + && xp->xp_context != EXPAND_SETTING_SUBTRACT && xp->xp_context != EXPAND_OWNSYNTAX && xp->xp_context != EXPAND_PACKADD && xp->xp_context != EXPAND_RUNTIME @@ -2599,7 +2598,7 @@ static int ExpandOther(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches { EXPAND_MENUNAMES, get_menu_names, false, true }, { EXPAND_SYNTAX, get_syntax_name, true, true }, { EXPAND_SYNTIME, get_syntime_arg, true, true }, - { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false }, + { EXPAND_HIGHLIGHT, get_highlight_name, true, false }, { EXPAND_EVENTS, expand_get_event_name, true, false }, { EXPAND_AUGROUP, expand_get_augroup_name, true, false }, { EXPAND_SIGN, get_sign_name, true, true }, @@ -2694,8 +2693,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM return OK; } if (xp->xp_context == EXPAND_OLD_SETTING) { - ExpandOldSetting(numMatches, matches); - return OK; + return ExpandOldSetting(numMatches, matches); } if (xp->xp_context == EXPAND_BUFFERS) { return ExpandBufnames(pat, numMatches, matches, options); @@ -2765,6 +2763,10 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM if (xp->xp_context == EXPAND_SETTINGS || xp->xp_context == EXPAND_BOOL_SETTINGS) { ret = ExpandSettings(xp, ®match, pat, numMatches, matches, fuzzy); + } else if (xp->xp_context == EXPAND_STRING_SETTING) { + ret = ExpandStringSetting(xp, ®match, numMatches, matches); + } else if (xp->xp_context == EXPAND_SETTING_SUBTRACT) { + ret = ExpandSettingSubtract(xp, ®match, numMatches, matches); } else if (xp->xp_context == EXPAND_MAPPINGS) { ret = ExpandMappings(pat, ®match, numMatches, matches); } else if (xp->xp_context == EXPAND_USER_DEFINED) { @@ -2788,9 +2790,8 @@ static int ExpandFromContext(expand_T *xp, char *pat, char ***matches, int *numM /// program. Matching strings are copied into an array, which is returned. /// /// @param func returns a string from the list -static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch, - char ***matches, int *numMatches, CompleteListItemGetter func, - int escaped) +void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regmatch, char ***matches, + int *numMatches, CompleteListItemGetter func, bool escaped) { const bool fuzzy = cmdline_fuzzy_complete(pat); *matches = NULL; @@ -2863,6 +2864,7 @@ static void ExpandGeneric(const char *const pat, expand_T *xp, regmatch_T *regma // in the specified order. const bool sort_matches = !fuzzy && xp->xp_context != EXPAND_MENUNAMES + && xp->xp_context != EXPAND_STRING_SETTING && xp->xp_context != EXPAND_MENUS && xp->xp_context != EXPAND_SCRIPTNAMES; @@ -3076,7 +3078,7 @@ static int ExpandUserDefined(const char *const pat, expand_T *xp, regmatch_T *re *matches = NULL; *numMatches = 0; - char *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp); + char *const retstr = call_user_expand_func(call_func_retstr, xp); if (retstr == NULL) { return FAIL; } @@ -3148,7 +3150,7 @@ static int ExpandUserList(expand_T *xp, char ***matches, int *numMatches) { *matches = NULL; *numMatches = 0; - list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp); + list_T *const retlist = call_user_expand_func(call_func_retlist, xp); if (retlist == NULL) { return FAIL; } @@ -3221,8 +3223,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir char **p; int num_p = 0; - (void)ExpandFromContext(&xpc, buf, &p, &num_p, - WILD_SILENT | expand_options); + (void)ExpandFromContext(&xpc, buf, &p, &num_p, WILD_SILENT | expand_options); if (num_p > 0) { ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options); diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h index 32c23c5d66..81e323c97b 100644 --- a/src/nvim/cmdexpand.h +++ b/src/nvim/cmdexpand.h @@ -1,6 +1,7 @@ #ifndef NVIM_CMDEXPAND_H #define NVIM_CMDEXPAND_H +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_getln.h" #include "nvim/garray.h" diff --git a/src/nvim/cmdexpand_defs.h b/src/nvim/cmdexpand_defs.h new file mode 100644 index 0000000000..a302a32852 --- /dev/null +++ b/src/nvim/cmdexpand_defs.h @@ -0,0 +1,113 @@ +#ifndef NVIM_CMDEXPAND_DEFS_H +#define NVIM_CMDEXPAND_DEFS_H + +#include <stdbool.h> +#include <stddef.h> + +#include "nvim/eval/typval_defs.h" +#include "nvim/types.h" + +typedef enum { + XP_PREFIX_NONE, ///< prefix not used + XP_PREFIX_NO, ///< "no" prefix for bool option + XP_PREFIX_INV, ///< "inv" prefix for bool option +} xp_prefix_T; + +enum { EXPAND_BUF_LEN = 256, }; + +/// used for completion on the command line +typedef struct expand { + char *xp_pattern; ///< start of item to expand + int xp_context; ///< type of expansion + size_t xp_pattern_len; ///< bytes in xp_pattern before cursor + xp_prefix_T xp_prefix; + char *xp_arg; ///< completion function + LuaRef xp_luaref; ///< Ref to Lua completion function + sctx_T xp_script_ctx; ///< SCTX for completion function + int xp_backslash; ///< one of the XP_BS_ values +#ifndef BACKSLASH_IN_FILENAME + bool xp_shell; ///< true for a shell command, more + ///< characters need to be escaped +#endif + int xp_numfiles; ///< number of files found by file name completion + int xp_col; ///< cursor position in line + int xp_selected; ///< selected index in completion + char *xp_orig; ///< originally expanded string + char **xp_files; ///< list of files + char *xp_line; ///< text being completed + char xp_buf[EXPAND_BUF_LEN]; ///< buffer for returned match +} expand_T; + +/// values for xp_backslash +enum { + XP_BS_NONE = 0, ///< nothing special for backslashes + XP_BS_ONE = 1, ///< uses one backslash before a space + XP_BS_THREE = 2, ///< uses three backslashes before a space +}; + +/// values for xp_context when doing command line completion +enum { + EXPAND_UNSUCCESSFUL = -2, + EXPAND_OK = -1, + EXPAND_NOTHING = 0, + EXPAND_COMMANDS, + EXPAND_FILES, + EXPAND_DIRECTORIES, + EXPAND_SETTINGS, + EXPAND_BOOL_SETTINGS, + EXPAND_TAGS, + EXPAND_OLD_SETTING, + EXPAND_HELP, + EXPAND_BUFFERS, + EXPAND_EVENTS, + EXPAND_MENUS, + EXPAND_SYNTAX, + EXPAND_HIGHLIGHT, + EXPAND_AUGROUP, + EXPAND_USER_VARS, + EXPAND_MAPPINGS, + EXPAND_TAGS_LISTFILES, + EXPAND_FUNCTIONS, + EXPAND_USER_FUNC, + EXPAND_EXPRESSION, + EXPAND_MENUNAMES, + EXPAND_USER_COMMANDS, + EXPAND_USER_CMD_FLAGS, + EXPAND_USER_NARGS, + EXPAND_USER_COMPLETE, + EXPAND_ENV_VARS, + EXPAND_LANGUAGE, + EXPAND_COLORS, + EXPAND_COMPILER, + EXPAND_USER_DEFINED, + EXPAND_USER_LIST, + EXPAND_USER_LUA, + EXPAND_SHELLCMD, + EXPAND_SIGN, + EXPAND_PROFILE, + EXPAND_FILETYPE, + EXPAND_FILES_IN_PATH, + EXPAND_OWNSYNTAX, + EXPAND_LOCALES, + EXPAND_HISTORY, + EXPAND_USER, + EXPAND_SYNTIME, + EXPAND_USER_ADDR_TYPE, + EXPAND_PACKADD, + EXPAND_MESSAGES, + EXPAND_MAPCLEAR, + EXPAND_ARGLIST, + EXPAND_DIFF_BUFFERS, + EXPAND_BREAKPOINT, + EXPAND_SCRIPTNAMES, + EXPAND_RUNTIME, + EXPAND_STRING_SETTING, + EXPAND_SETTING_SUBTRACT, + EXPAND_CHECKHEALTH, + EXPAND_LUA, +}; + +/// Type used by ExpandGeneric() +typedef char *(*CompleteListItemGetter)(expand_T *, int); + +#endif // NVIM_CMDEXPAND_DEFS_H diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index 50bdfd892f..b0490670fe 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -12,6 +12,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h index a0f2ab6934..2b7e47e38e 100644 --- a/src/nvim/cmdhist.h +++ b/src/nvim/cmdhist.h @@ -1,6 +1,7 @@ #ifndef NVIM_CMDHIST_H #define NVIM_CMDHIST_H +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/os/time.h" diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index fb2ca9ff8c..ba5f30c20f 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -364,9 +364,9 @@ static void clear_shape_table(void) { for (int idx = 0; idx < SHAPE_IDX_COUNT; idx++) { shape_table[idx].shape = SHAPE_BLOCK; - shape_table[idx].blinkwait = 0L; - shape_table[idx].blinkon = 0L; - shape_table[idx].blinkoff = 0L; + shape_table[idx].blinkwait = 0; + shape_table[idx].blinkon = 0; + shape_table[idx].blinkoff = 0; shape_table[idx].id = 0; shape_table[idx].id_lm = 0; } diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h index 93bddd47c7..33d0344c2d 100644 --- a/src/nvim/cursor_shape.h +++ b/src/nvim/cursor_shape.h @@ -44,9 +44,9 @@ typedef struct cursor_entry { CursorShape shape; ///< cursor shape: one of the SHAPE_ defines int mshape; ///< mouse shape: one of the MSHAPE defines int percentage; ///< percentage of cell for bar - long blinkwait; ///< blinking, wait time before blinking starts - long blinkon; ///< blinking, on time - long blinkoff; ///< blinking, off time + int blinkwait; ///< blinking, wait time before blinking starts + int blinkon; ///< blinking, on time + int blinkoff; ///< blinking, off time int id; ///< highlight group ID int id_lm; ///< highlight group ID for :lmap mode char *name; ///< mode short name diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index 3d5f27e76c..755579a3bc 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -12,6 +12,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 3ab1da76f4..8479675dfa 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -2467,6 +2467,7 @@ int diffopt_changed(void) char *p = p_dip; while (*p != NUL) { + // Note: Keep this in sync with p_dip_values if (strncmp(p, "filler", 6) == 0) { p += 6; diff_flags_new |= DIFF_FILLER; @@ -2513,6 +2514,7 @@ int diffopt_changed(void) p += 8; diff_flags_new |= DIFF_INTERNAL; } else if (strncmp(p, "algorithm:", 10) == 0) { + // Note: Keep this in sync with p_dip_algorithm_values. p += 10; if (strncmp(p, "myers", 5) == 0) { p += 5; @@ -3133,7 +3135,7 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr if (added != 0) { // Adjust marks. This will change the following entries! - mark_adjust(lnum, lnum + count - 1, (long)MAXLNUM, added, kExtmarkNOOP); + mark_adjust(lnum, lnum + count - 1, MAXLNUM, added, kExtmarkNOOP); if (curwin->w_cursor.lnum >= lnum) { // Adjust the cursor position if it's in/after the changed // lines. @@ -3144,7 +3146,7 @@ static void diffgetput(const int addr_count, const int idx_cur, const int idx_fr } } } - extmark_adjust(curbuf, lnum, lnum + count - 1, (long)MAXLNUM, added, kExtmarkUndo); + extmark_adjust(curbuf, lnum, lnum + count - 1, MAXLNUM, added, kExtmarkUndo); changed_lines(curbuf, lnum, 0, lnum + count, added, true); if (did_free) { diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index eef427a7b8..e5d2ba7550 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -3330,5 +3330,6 @@ static void win_put_linebuf(win_T *wp, int row, int coloff, int endcol, int clea } } + grid_adjust(&grid, &row, &coloff); grid_put_linebuf(grid, row, coloff, 0, endcol, clear_width, wp->w_p_rl, bg_attr, wrap, false); } diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index cc22accf2b..e32a556daa 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -1519,16 +1519,12 @@ static void win_update(win_T *wp, DecorProviders *providers) } } - // Force redraw when width of 'number' or 'relativenumber' column - // changes. - int nrwidth = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; - if (wp->w_nrwidth != nrwidth) { + const int nrwidth_before = wp->w_nrwidth; + int nrwidth_new = (wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc) ? number_width(wp) : 0; + // Force redraw when width of 'number' or 'relativenumber' column changes. + if (wp->w_nrwidth != nrwidth_new) { type = UPD_NOT_VALID; - wp->w_nrwidth = nrwidth; - - if (buf->terminal) { - terminal_check_size(buf->terminal); - } + wp->w_nrwidth = nrwidth_new; } else if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0) { @@ -2386,26 +2382,20 @@ static void win_update(win_T *wp, DecorProviders *providers) wp->w_botline = lnum; wp->w_filler_rows = wp->w_grid.rows - srow; } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate" - int scr_row = wp->w_grid.rows - 1; - int symbol = wp->w_p_fcs_chars.lastline; - char fillbuf[12]; // 2 characters of 6 bytes - int charlen = utf_char2bytes(symbol, &fillbuf[0]); - utf_char2bytes(symbol, &fillbuf[charlen]); - // Last line isn't finished: Display "@@@" in the last screen line. - grid_puts(&wp->w_grid, fillbuf, MIN(wp->w_grid.cols, 2) * charlen, scr_row, 0, at_attr); - grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols, symbol, ' ', at_attr); + grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); + grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr); + grid_line_fill(3, wp->w_grid.cols, ' ', at_attr); + grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" - int start_col = wp->w_grid.cols - 3; - int symbol = wp->w_p_fcs_chars.lastline; - // Last line isn't finished: Display "@@@" at the end. // TODO(bfredl): this display ">@@@" when ">" was a left-halve // maybe "@@@@" is preferred when this happens. grid_line_start(&wp->w_grid, wp->w_grid.rows - 1); - grid_line_fill(MAX(start_col, 0), wp->w_grid.cols, symbol, at_attr); + grid_line_fill(MAX(wp->w_grid.cols - 3, 0), wp->w_grid.cols, + wp->w_p_fcs_chars.lastline, at_attr); grid_line_flush(); set_empty_rows(wp, srow); wp->w_botline = lnum; @@ -2498,6 +2488,10 @@ static void win_update(win_T *wp, DecorProviders *providers) } } + if (nrwidth_before != wp->w_nrwidth && buf->terminal) { + terminal_check_size(buf->terminal); + } + // restore got_int, unless CTRL-C was hit while redrawing if (!got_int) { got_int = save_got_int; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 7f29e615ab..4593748c25 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -73,7 +73,7 @@ typedef struct insert_state { int cmdchar; int cmdchar_todo; // cmdchar to handle once in init_prompt int startln; - long count; + int count; int c; int lastc; int i; @@ -1230,7 +1230,7 @@ static void insert_do_cindent(InsertState *s) /// @param count repeat count for the command /// /// @return true if a CTRL-O command caused the return (insert mode pending). -bool edit(int cmdchar, bool startln, long count) +bool edit(int cmdchar, bool startln, int count) { if (curbuf->terminal) { if (ex_normal_busy) { @@ -1412,11 +1412,11 @@ static void ins_ctrl_v(void) // Put a character directly onto the screen. It's not stored in a buffer. // Used while handling CTRL-K, CTRL-V, etc. in Insert mode. static int pc_status; -#define PC_STATUS_UNSET 0 // pc_bytes was not set -#define PC_STATUS_RIGHT 1 // right half of double-wide char -#define PC_STATUS_LEFT 2 // left half of double-wide char -#define PC_STATUS_SET 3 // pc_bytes was filled -static char pc_bytes[MB_MAXBYTES + 1]; // saved bytes +#define PC_STATUS_UNSET 0 // nothing was put on screen +#define PC_STATUS_RIGHT 1 // right half of double-wide char +#define PC_STATUS_LEFT 2 // left half of double-wide char +#define PC_STATUS_SET 3 // pc_schar was filled +static schar_T pc_schar; // saved char static int pc_attr; static int pc_row; static int pc_col; @@ -1433,31 +1433,34 @@ void edit_putchar(int c, bool highlight) attr = 0; } pc_row = curwin->w_wrow; - pc_col = 0; pc_status = PC_STATUS_UNSET; + grid_line_start(&curwin->w_grid, pc_row); if (curwin->w_p_rl) { - pc_col += curwin->w_grid.cols - 1 - curwin->w_wcol; - const int fix_col = grid_fix_col(&curwin->w_grid, pc_col, pc_row); + pc_col = curwin->w_grid.cols - 1 - curwin->w_wcol; - if (fix_col != pc_col) { - grid_putchar(&curwin->w_grid, ' ', pc_row, fix_col, attr); + if (grid_line_getchar(pc_col, NULL) == NUL) { + grid_line_put_schar(pc_col - 1, schar_from_ascii(' '), attr); curwin->w_wcol--; pc_status = PC_STATUS_RIGHT; } } else { - pc_col += curwin->w_wcol; - if (grid_lefthalve(&curwin->w_grid, pc_row, pc_col)) { + pc_col = curwin->w_wcol; + + if (grid_line_getchar(pc_col + 1, NULL) == NUL) { + // pc_col is the left half of a double-width char pc_status = PC_STATUS_LEFT; } } // save the character to be able to put it back if (pc_status == PC_STATUS_UNSET) { - // TODO(bfredl): save the schar_T instead - grid_getbytes(&curwin->w_grid, pc_row, pc_col, pc_bytes, &pc_attr); + pc_schar = grid_line_getchar(pc_col, &pc_attr); pc_status = PC_STATUS_SET; } - grid_putchar(&curwin->w_grid, c, pc_row, pc_col, attr); + + char buf[MB_MAXBYTES + 1]; + grid_line_puts(pc_col, buf, utf_char2bytes(c, buf), attr); + grid_line_flush(); } } @@ -1537,7 +1540,10 @@ void edit_unputchar(void) if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) { redrawWinline(curwin, curwin->w_cursor.lnum); } else { - grid_puts(&curwin->w_grid, pc_bytes, -1, pc_row, pc_col, pc_attr); + // TODO(bfredl): this could be smarter and also handle the dubyawidth case + grid_line_start(&curwin->w_grid, pc_row); + grid_line_put_schar(pc_col, pc_schar, pc_attr); + grid_line_flush(); } } } @@ -3394,7 +3400,7 @@ static void ins_ctrl_hat(void) /// @param nomove when true, don't move the cursor /// /// @return true when leaving insert mode, false when repeating the insert. -static bool ins_esc(long *count, int cmdchar, bool nomove) +static bool ins_esc(int *count, int cmdchar, bool nomove) FUNC_ATTR_NONNULL_ARG(1) { static bool disabled_redraw = false; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0bf26b2451..12226dac91 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21,6 +21,7 @@ #include "nvim/buffer_defs.h" #include "nvim/channel.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" @@ -1225,7 +1226,7 @@ fail: /// /// @return [allocated] NULL when calling function fails, allocated string /// otherwise. -char *call_func_retstr(const char *const func, int argc, typval_T *argv) +void *call_func_retstr(const char *const func, int argc, typval_T *argv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; @@ -1298,7 +1299,7 @@ int eval_foldexpr(win_T *wp, int *cp) // If the result is a string, check if there is a non-digit before // the number. char *s = tv.vval.v_string; - if (!ascii_isdigit(*s) && *s != '-') { + if (*s != NUL && !ascii_isdigit(*s) && *s != '-') { *cp = (uint8_t)(*s++); } retval = atol(s); @@ -8093,13 +8094,6 @@ void ex_execute(exarg_T *eap) } if (ret != FAIL && ga.ga_data != NULL) { - if (eap->cmdidx == CMD_echomsg || eap->cmdidx == CMD_echoerr) { - // Mark the already saved text as finishing the line, so that what - // follows is displayed on a new line when scrolling back at the - // more prompt. - msg_sb_eol(); - } - if (eap->cmdidx == CMD_echomsg) { msg_ext_set_kind("echomsg"); msg(ga.ga_data, echo_attr); @@ -8296,7 +8290,7 @@ void option_last_set_msg(LastSet last_set) msg_puts(p); if (last_set.script_ctx.sc_lnum > 0) { msg_puts(_(line_msg)); - msg_outnum((long)last_set.script_ctx.sc_lnum); + msg_outnum(last_set.script_ctx.sc_lnum); } if (should_free) { xfree(p); diff --git a/src/nvim/eval.h b/src/nvim/eval.h index be69cd5657..38bcf8f50d 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -6,6 +6,7 @@ #include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/event/time.h" #include "nvim/ex_cmds_defs.h" diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 23ad3d3787..5a47286980 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -1542,6 +1542,7 @@ M.funcs = { count = { args = { 2, 4 }, base = 1, + tags = { 'E706' }, desc = [=[ Return the number of times an item with value {expr} appears in |String|, |List| or |Dictionary| {comp}. @@ -7380,7 +7381,6 @@ M.funcs = { precision, the argument(s) to be used must also be specified using a {n$} positional argument specifier. See |printf-$|. - The conversion specifiers and their meanings are: *printf-d* *printf-b* *printf-B* *printf-o* *printf-x* *printf-X* @@ -7570,6 +7570,14 @@ M.funcs = { echo printf("%1$d at width %2$d is: %01$*2$.3$d", 1, 2) < E1505: Invalid format specifier: %1$d at width %2$d is: %01$*2$.3$d + + *E1507* + This internal error indicates that the logic to parse a + positional format argument ran into a problem that couldn't be + otherwise reported. Please file a bug against Vim if you run + into this, copying the exact format string and parameters that + were used. + ]=], name = 'printf', params = { { 'fmt', 'any' }, { 'expr1', 'any' } }, @@ -11121,8 +11129,8 @@ M.funcs = { user user name host host name fname original file name - pid PID of the Vim process that created the swap - file + pid PID of the Nvim process that created the swap + file, or zero if not running. mtime last modification time in seconds inode Optional: INODE number of the file dirty 1 if file was modified, 0 if not diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 5bfce7c272..eb2e2fb1e2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -33,6 +33,7 @@ #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/context.h" #include "nvim/cursor.h" #include "nvim/diff.h" @@ -6884,7 +6885,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) c = -1; } else { char buf[MB_MAXBYTES + 1]; - grid_getbytes(grid, row, col, buf, NULL); + schar_get(buf, grid_getchar(grid, row, col, NULL)); c = utf_ptr2char(buf); } rettv->vval.v_number = c; @@ -6905,7 +6906,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } char buf[MB_MAXBYTES + 1]; - grid_getbytes(grid, row, col, buf, NULL); + schar_get(buf, grid_getchar(grid, row, col, NULL)); int pcc[MAX_MCO]; int c = utfc_ptr2char(buf, pcc); int composing_len = 0; @@ -6950,7 +6951,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr } char buf[MB_MAXBYTES + 1]; - grid_getbytes(grid, row, col, buf, NULL); + schar_get(buf, grid_getchar(grid, row, col, NULL)); rettv->vval.v_string = xstrdup(buf); } @@ -8235,7 +8236,7 @@ static void f_swapfilelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_alloc_ret(rettv); - get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); + swapfile_dict(tv_get_string(argvars), rettv->vval.v_dict); } /// "swapname(expr)" function diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 5dab12787b..5f6132f68c 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -6,6 +6,7 @@ #include "nvim/api/private/dispatch.h" #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/types.h" diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h index d5e8cb0109..c0b5416a05 100644 --- a/src/nvim/eval/typval_defs.h +++ b/src/nvim/eval/typval_defs.h @@ -115,6 +115,19 @@ typedef enum { VAR_BLOB, ///< Blob, .v_blob is used. } VarType; +/// Type values for type(). +enum { + VAR_TYPE_NUMBER = 0, + VAR_TYPE_STRING = 1, + VAR_TYPE_FUNC = 2, + VAR_TYPE_LIST = 3, + VAR_TYPE_DICT = 4, + VAR_TYPE_FLOAT = 5, + VAR_TYPE_BOOL = 6, + VAR_TYPE_SPECIAL = 7, + VAR_TYPE_BLOB = 10, +}; + /// Structure that holds an internal variable value typedef struct { VarType v_type; ///< Variable type. diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 8d85c55e15..ca98aad6bc 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -14,6 +14,7 @@ #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" @@ -1125,7 +1126,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett msg_puts(", "); } if (argvars[i].v_type == VAR_NUMBER) { - msg_outnum((long)argvars[i].vval.v_number); + msg_outnum((int)argvars[i].vval.v_number); } else { // Do not want errors such as E724 here. emsg_off++; @@ -2262,7 +2263,7 @@ void ex_function(exarg_T *eap) } msg_putchar('\n'); if (!eap->forceit) { - msg_outnum((long)j + 1); + msg_outnum(j + 1); if (j < 9) { msg_putchar(' '); } @@ -2490,7 +2491,7 @@ void ex_function(exarg_T *eap) } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) { nextcmd = line_arg; } else if (*p != NUL && *p != '"' && p_verbose > 0) { - give_warning2(_("W22: Text found after :endfunction: %s"), p, true); + swmsg(true, _("W22: Text found after :endfunction: %s"), p); } if (nextcmd != NULL) { // Another command follows. If the line came from "eap" we diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 562c549b4b..c3fe56d30c 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include <stddef.h> +#include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 127397d9fa..c57324d5e9 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -27,6 +27,7 @@ #include "nvim/change.h" #include "nvim/channel.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/decoration.h" @@ -130,7 +131,7 @@ static const char e_non_numeric_argument_to_z[] = N_("E144: Non-numeric argument to :z"); /// ":ascii" and "ga" implementation -void do_ascii(const exarg_T *const eap) +void do_ascii(exarg_T *eap) { char *dig; int cc[MAX_MCO]; @@ -455,7 +456,7 @@ void ex_sort(exarg_T *eap) regmatch_T regmatch; int len; linenr_T lnum; - long maxlen = 0; + int maxlen = 0; size_t count = (size_t)(eap->line2 - eap->line1) + 1; size_t i; char *p; @@ -692,7 +693,7 @@ void ex_sort(exarg_T *eap) // Adjust marks for deleted (or added) lines and prepare for displaying. deleted = (linenr_T)count - (lnum - eap->line2); if (deleted > 0) { - mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, kExtmarkNOOP); + mark_adjust(eap->line2 - deleted, eap->line2, MAXLNUM, -deleted, kExtmarkNOOP); msgmore(-deleted); } else if (deleted < 0) { mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, kExtmarkNOOP); @@ -921,7 +922,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) check_pos(curbuf, &VIsual); } - msgmore((long)count); + msgmore(count); } static char *prevcmd = NULL; // the previous command @@ -1280,7 +1281,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b set_keep_msg(msg_buf, 0); } } else { - msgmore((long)linecount); + msgmore(linecount); } } } else { @@ -3290,7 +3291,7 @@ static int check_regexp_delim(int c) /// @param cmdpreview_ns The namespace to show 'inccommand' preview highlights. /// If <= 0, preview shouldn't be shown. /// @return 0, 1 or 2. See show_cmdpreview() for more information on what the return value means. -static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ns, +static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_ns, const handle_T cmdpreview_bufnr) { #define ADJUST_SUB_FIRSTLNUM() \ @@ -3317,7 +3318,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ } \ } while (0) - long i = 0; + int i = 0; regmmatch_T regmatch; static subflags_T subflags = { .do_all = false, @@ -3345,7 +3346,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 }; static int pre_hl_id = 0; pos_T old_cursor = curwin->w_cursor; - long start_nsubs; + int start_nsubs; bool did_save = false; @@ -3441,7 +3442,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ // check for a trailing count cmd = skipwhite(cmd); if (ascii_isdigit(*cmd)) { - i = getdigits_long(&cmd, true, 0); + i = getdigits_int(&cmd, true, 0); if (i <= 0 && !eap->skip && subflags.do_error) { emsg(_(e_zerocount)); return 0; @@ -3520,8 +3521,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ && (cmdpreview_ns <= 0 || preview_lines.lines_needed <= (linenr_T)p_cwh || lnum <= curwin->w_botline); lnum++) { - long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL, NULL); + int nmatch = (int)vim_regexec_multi(®match, curwin, curbuf, lnum, + (colnr_T)0, NULL, NULL); if (nmatch) { colnr_T copycol; colnr_T matchcol; @@ -3531,7 +3532,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ char *p1; bool did_sub = false; int lastone; - long nmatch_tl = 0; // nr of lines matched below lnum + linenr_T nmatch_tl = 0; // nr of lines matched below lnum int do_again; // do it again after joining lines bool skip_match = false; linenr_T sub_firstlnum; // nr of first sub line @@ -3804,7 +3805,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_ // Same highlight as wait_return(). smsg(HL_ATTR(HLF_R), _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub); msg_no_more = false; - msg_scroll = (int)i; + msg_scroll = i; if (!ui_has(kUIMessages)) { ui_cursor_goto(msg_row, msg_col); } @@ -4086,9 +4087,9 @@ skip: // need to replace the line first (using \zs after \n). if (lastone || nmatch_tl > 0 - || (nmatch = vim_regexec_multi(®match, curwin, - curbuf, sub_firstlnum, - matchcol, NULL, NULL)) == 0 + || (nmatch = (int)vim_regexec_multi(®match, curwin, + curbuf, sub_firstlnum, + matchcol, NULL, NULL)) == 0 || regmatch.startpos[0].lnum > 0) { if (new_start != NULL) { // Copy the rest of the line, that didn't match. @@ -4118,13 +4119,12 @@ skip: for (i = 0; i < nmatch_tl; i++) { ml_delete(lnum, false); } - mark_adjust(lnum, lnum + (linenr_T)nmatch_tl - 1, - (long)MAXLNUM, (linenr_T)(-nmatch_tl), kExtmarkNOOP); + mark_adjust(lnum, lnum + nmatch_tl - 1, MAXLNUM, -nmatch_tl, kExtmarkNOOP); if (subflags.do_ask) { - deleted_lines(lnum, (linenr_T)nmatch_tl); + deleted_lines(lnum, nmatch_tl); } lnum--; - line2 -= (linenr_T)nmatch_tl; // nr of lines decreases + line2 -= nmatch_tl; // nr of lines decreases nmatch_tl = 0; } @@ -4149,8 +4149,8 @@ skip: copycol = 0; } if (nmatch == -1 && !lastone) { - nmatch = vim_regexec_multi(®match, curwin, curbuf, - sub_firstlnum, matchcol, NULL, NULL); + nmatch = (int)vim_regexec_multi(®match, curwin, curbuf, + sub_firstlnum, matchcol, NULL, NULL); } // 5. break if there isn't another match in this line @@ -4581,7 +4581,7 @@ bool prepare_tagpreview(bool undo_sync) /// /// @return 1 if preview window isn't needed, 2 if preview window is needed. static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, int hl_id, - long cmdpreview_ns, handle_T cmdpreview_bufnr) + int cmdpreview_ns, handle_T cmdpreview_bufnr) FUNC_ATTR_NONNULL_ALL { char *save_shm_p = xstrdup(p_shm); @@ -4683,9 +4683,9 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i } linenr_origbuf = match.end.lnum; - bufhl_add_hl_pos_offset(cmdpreview_buf, (int)cmdpreview_ns, hl_id, p_start, p_end, col_width); + bufhl_add_hl_pos_offset(cmdpreview_buf, cmdpreview_ns, hl_id, p_start, p_end, col_width); } - bufhl_add_hl_pos_offset(orig_buf, (int)cmdpreview_ns, hl_id, match.start, match.end, 0); + bufhl_add_hl_pos_offset(orig_buf, cmdpreview_ns, hl_id, match.start, match.end, 0); } xfree(str); @@ -4703,7 +4703,7 @@ void ex_substitute(exarg_T *eap) } /// :substitute command preview callback. -int ex_substitute_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) +int ex_substitute_preview(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr) { // Only preview once the pattern delimiter has been typed if (*eap->arg && !ASCII_ISALNUM(*eap->arg)) { @@ -4772,7 +4772,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags) void ex_oldfiles(exarg_T *eap) { list_T *l = get_vim_var_list(VV_OLDFILES); - long nr = 0; + int nr = 0; if (l == NULL) { msg(_("No old files"), 0); @@ -4806,7 +4806,7 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= tv_list_len(l)) { - const char *const p = tv_list_find_str(l, (int)nr - 1); + const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; } diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index 78f880db56..e15ba673ce 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -5,6 +5,7 @@ #include <stdint.h> #include "nvim/eval/typval_defs.h" +#include "nvim/ex_eval_defs.h" #include "nvim/normal.h" #include "nvim/pos.h" #include "nvim/regexp_defs.h" @@ -69,20 +70,20 @@ #define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file #define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed -// values for cmd_addr_type +/// values for cmd_addr_type typedef enum { - ADDR_LINES, // buffer line numbers - ADDR_WINDOWS, // window number - ADDR_ARGUMENTS, // argument number - ADDR_LOADED_BUFFERS, // buffer number of loaded buffer - ADDR_BUFFERS, // buffer number - ADDR_TABS, // tab page number - ADDR_TABS_RELATIVE, // Tab page that only relative - ADDR_QUICKFIX_VALID, // quickfix list valid entry number - ADDR_QUICKFIX, // quickfix list entry number - ADDR_UNSIGNED, // positive count or zero, defaults to 1 - ADDR_OTHER, // something else, use line number for '$', '%', etc. - ADDR_NONE, // no range used + ADDR_LINES, ///< buffer line numbers + ADDR_WINDOWS, ///< window number + ADDR_ARGUMENTS, ///< argument number + ADDR_LOADED_BUFFERS, ///< buffer number of loaded buffer + ADDR_BUFFERS, ///< buffer number + ADDR_TABS, ///< tab page number + ADDR_TABS_RELATIVE, ///< Tab page that only relative + ADDR_QUICKFIX_VALID, ///< quickfix list valid entry number + ADDR_QUICKFIX, ///< quickfix list entry number + ADDR_UNSIGNED, ///< positive count or zero, defaults to 1 + ADDR_OTHER, ///< something else, use line number for '$', '%', etc. + ADDR_NONE, ///< no range used } cmd_addr_T; typedef struct exarg exarg_T; @@ -93,7 +94,7 @@ typedef struct exarg exarg_T; #define BAD_DROP (-2) // erase it typedef void (*ex_func_T)(exarg_T *eap); -typedef int (*ex_preview_func_T)(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr); +typedef int (*ex_preview_func_T)(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr); // NOTE: These possible could be removed and changed so that // Callback could take a "command" style string, and simply @@ -128,54 +129,13 @@ typedef char *(*LineGetter)(int, void *, int, bool); /// Structure for command definition. typedef struct cmdname { - char *cmd_name; ///< Name of the command. - ex_func_T cmd_func; ///< Function with implementation of this command. - ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command. - uint32_t cmd_argt; ///< Relevant flags from the declared above. - cmd_addr_T cmd_addr_type; ///< Flag for address type. + char *cmd_name; ///< Name of the command. + ex_func_T cmd_func; ///< Function with implementation of this command. + ex_preview_func_T cmd_preview_func; ///< Preview callback function of this command. + uint32_t cmd_argt; ///< Relevant flags from the declared above. + cmd_addr_T cmd_addr_type; ///< Flag for address type. } CommandDefinition; -// A list used for saving values of "emsg_silent". Used by ex_try() to save the -// value of "emsg_silent" if it was non-zero. When this is done, the CSF_SILENT -// flag below is set. -typedef struct eslist_elem eslist_T; -struct eslist_elem { - int saved_emsg_silent; // saved value of "emsg_silent" - eslist_T *next; // next element on the list -}; - -// For conditional commands a stack is kept of nested conditionals. -// When cs_idx < 0, there is no conditional command. -enum { - CSTACK_LEN = 50, -}; - -typedef struct { - int cs_flags[CSTACK_LEN]; // CSF_ flags - char cs_pending[CSTACK_LEN]; // CSTP_: what's pending in ":finally" - union { - void *csp_rv[CSTACK_LEN]; // return typeval for pending return - void *csp_ex[CSTACK_LEN]; // exception for pending throw - } cs_pend; - void *cs_forinfo[CSTACK_LEN]; // info used by ":for" - int cs_line[CSTACK_LEN]; // line nr of ":while"/":for" line - int cs_idx; // current entry, or -1 if none - int cs_looplevel; // nr of nested ":while"s and ":for"s - int cs_trylevel; // nr of nested ":try"s - eslist_T *cs_emsg_silent_list; // saved values of "emsg_silent" - int cs_lflags; // loop flags: CSL_ flags -} cstack_T; -#define cs_rettv cs_pend.csp_rv -#define cs_exception cs_pend.csp_ex - -// Flags for the cs_lflags item in cstack_T. -enum { - CSL_HAD_LOOP = 1, // just found ":while" or ":for" - CSL_HAD_ENDLOOP = 2, // just found ":endwhile" or ":endfor" - CSL_HAD_CONT = 4, // just found ":continue" - CSL_HAD_FINA = 8, // just found ":finally" -}; - /// Arguments used for Ex commands. struct exarg { char *arg; ///< argument of the command @@ -222,41 +182,6 @@ struct exarg { #define EXFLAG_NR 0x02 // '#': number #define EXFLAG_PRINT 0x04 // 'p': print -typedef enum { - XP_PREFIX_NONE, ///< prefix not used - XP_PREFIX_NO, ///< "no" prefix for bool option - XP_PREFIX_INV, ///< "inv" prefix for bool option -} xp_prefix_T; - -// used for completion on the command line -struct expand { - char *xp_pattern; // start of item to expand - int xp_context; // type of expansion - size_t xp_pattern_len; // bytes in xp_pattern before cursor - xp_prefix_T xp_prefix; - char *xp_arg; // completion function - LuaRef xp_luaref; // Ref to Lua completion function - sctx_T xp_script_ctx; // SCTX for completion function - int xp_backslash; // one of the XP_BS_ values -#ifndef BACKSLASH_IN_FILENAME - int xp_shell; // true for a shell command, more - // characters need to be escaped -#endif - int xp_numfiles; // number of files found by file name completion - int xp_col; // cursor position in line - int xp_selected; // selected index in completion - char *xp_orig; // originally expanded string - char **xp_files; // list of files - char *xp_line; // text being completed -#define EXPAND_BUF_LEN 256 - char xp_buf[EXPAND_BUF_LEN]; // buffer for returned match -}; - -// values for xp_backslash -#define XP_BS_NONE 0 // nothing special for backslashes -#define XP_BS_ONE 1 // uses one backslash before a space -#define XP_BS_THREE 2 // uses three backslashes before a space - enum { CMOD_SANDBOX = 0x0001, ///< ":sandbox" CMOD_SILENT = 0x0002, ///< ":silent" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 02783e032b..eb89e0fc9d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -22,6 +22,7 @@ #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/debugger.h" #include "nvim/digraph.h" @@ -1629,7 +1630,7 @@ static int execute_cmd0(int *retv, exarg_T *eap, const char **errormsg, bool pre // Call the function to execute the builtin command or the preview callback. eap->errmsg = NULL; if (preview) { - *retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(), + *retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, (int)cmdpreview_get_ns(), cmdpreview_get_bufnr()); } else { (cmdnames[eap->cmdidx].cmd_func)(eap); @@ -5910,7 +5911,7 @@ static void ex_submagic(exarg_T *eap) } /// ":smagic" and ":snomagic" preview callback. -static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) +static int ex_submagic_preview(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr) { const optmagic_T saved = magic_overruled; @@ -6043,7 +6044,7 @@ static void ex_redo(exarg_T *eap) /// ":earlier" and ":later". static void ex_later(exarg_T *eap) { - long count = 0; + int count = 0; bool sec = false; bool file = false; char *p = eap->arg; @@ -6051,7 +6052,7 @@ static void ex_later(exarg_T *eap) if (*p == NUL) { count = 1; } else if (isdigit((uint8_t)(*p))) { - count = getdigits_long(&p, false, 0); + count = getdigits_int(&p, false, 0); switch (*p) { case 's': p++; sec = true; break; diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index 0fd14c81d3..59f45fce88 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/getchar_defs.h" #include "nvim/globals.h" diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h index 3ad3368900..6713cdb549 100644 --- a/src/nvim/ex_eval_defs.h +++ b/src/nvim/ex_eval_defs.h @@ -5,6 +5,37 @@ #include "nvim/pos.h" +/// A list used for saving values of "emsg_silent". Used by ex_try() to save the +/// value of "emsg_silent" if it was non-zero. When this is done, the CSF_SILENT +/// flag below is set. +typedef struct eslist_elem eslist_T; +struct eslist_elem { + int saved_emsg_silent; ///< saved value of "emsg_silent" + eslist_T *next; ///< next element on the list +}; + +/// For conditional commands a stack is kept of nested conditionals. +/// When cs_idx < 0, there is no conditional command. +enum { CSTACK_LEN = 50, }; + +typedef struct { + int cs_flags[CSTACK_LEN]; ///< CSF_ flags + char cs_pending[CSTACK_LEN]; ///< CSTP_: what's pending in ":finally" + union { + void *csp_rv[CSTACK_LEN]; ///< return typeval for pending return + void *csp_ex[CSTACK_LEN]; ///< exception for pending throw + } cs_pend; + void *cs_forinfo[CSTACK_LEN]; ///< info used by ":for" + int cs_line[CSTACK_LEN]; ///< line nr of ":while"/":for" line + int cs_idx; ///< current entry, or -1 if none + int cs_looplevel; ///< nr of nested ":while"s and ":for"s + int cs_trylevel; ///< nr of nested ":try"s + eslist_T *cs_emsg_silent_list; ///< saved values of "emsg_silent" + int cs_lflags; ///< loop flags: CSL_ flags +} cstack_T; +#define cs_rettv cs_pend.csp_rv +#define cs_exception cs_pend.csp_ex + /// There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if" /// was used. enum { @@ -37,6 +68,14 @@ enum { CSTP_FINISH = 32, ///< ":finish" is pending }; +/// Flags for the cs_lflags item in cstack_T. +enum { + CSL_HAD_LOOP = 1, ///< just found ":while" or ":for" + CSL_HAD_ENDLOOP = 2, ///< just found ":endwhile" or ":endfor" + CSL_HAD_CONT = 4, ///< just found ":continue" + CSL_HAD_FINA = 8, ///< just found ":finally" +}; + /// A list of error messages that can be converted to an exception. "throw_msg" /// is only set in the first element of the list. Usually, it points to the /// original message stored in that element, but sometimes it points to a later diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6fa607d569..2a1dffacb7 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -71,6 +71,7 @@ #include "nvim/search.h" #include "nvim/state.h" #include "nvim/strings.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/usercmd.h" @@ -141,11 +142,11 @@ typedef struct cmdpreview_undo_info { u_header_T *save_b_u_curhead; int save_b_u_numhead; bool save_b_u_synced; - long save_b_u_seq_last; - long save_b_u_save_nr_last; - long save_b_u_seq_cur; + int save_b_u_seq_last; + int save_b_u_save_nr_last; + int save_b_u_seq_cur; time_t save_b_u_time_cur; - long save_b_u_save_nr_cur; + int save_b_u_save_nr_cur; char *save_b_u_line_ptr; linenr_T save_b_u_line_lnum; colnr_T save_b_u_line_colnr; @@ -206,7 +207,7 @@ static int cedit_key = -1; ///< key value of 'cedit' option #endif static handle_T cmdpreview_bufnr = 0; -static long cmdpreview_ns = 0; +static int cmdpreview_ns = 0; static void save_viewstate(win_T *wp, viewstate_T *vs) FUNC_ATTR_NONNULL_ALL @@ -1328,7 +1329,7 @@ static int command_line_execute(VimState *state, int key) if (!cmd_silent) { if (!ui_has(kUICmdline)) { - cmd_cursor_goto(msg_row, 0); + msg_cursor_goto(msg_row, 0); } ui_flush(); } @@ -2766,6 +2767,7 @@ int check_opt_wim(void) } for (char *p = p_wim; *p; p++) { + // Note: Keep this in sync with p_wim_values. for (i = 0; ASCII_ISALPHA(p[i]); i++) {} if (p[i] != NUL && p[i] != ',' && p[i] != ':') { return FAIL; @@ -3882,7 +3884,7 @@ void redrawcmd(void) // when 'incsearch' is set there may be no command line while redrawing if (ccline.cmdbuff == NULL) { - cmd_cursor_goto(cmdline_row, 0); + msg_cursor_goto(cmdline_row, 0); msg_clr_eos(); return; } @@ -3959,14 +3961,7 @@ void cursorcmd(void) } } - cmd_cursor_goto(msg_row, msg_col); -} - -static void cmd_cursor_goto(int row, int col) -{ - ScreenGrid *grid = &msg_grid_adj; - grid_adjust(&grid, &row, &col); - ui_grid_cursor_goto(grid->handle, row, col); + msg_cursor_goto(msg_row, msg_col); } void gotocmdline(bool clr) @@ -3983,7 +3978,7 @@ void gotocmdline(bool clr) if (clr) { // clear the bottom line(s) msg_clr_eos(); // will reset clear_cmdline } - cmd_cursor_goto(cmdline_row, 0); + msg_cursor_goto(cmdline_row, 0); } // Check the word in front of the cursor for an abbreviation. diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index aaafedcb4b..61bbd5b9e1 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "klib/kvec.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/option_defs.h" diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 4e8b7be4aa..05b48966ff 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -28,7 +28,7 @@ #include "nvim/drawscreen.h" #include "nvim/edit.h" #include "nvim/eval.h" -#include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval.h" #include "nvim/fileio.h" #include "nvim/fold.h" @@ -1753,7 +1753,7 @@ failed: c = true; } - msg_add_lines(c, (long)linecnt, filesize); + msg_add_lines(c, linecnt, filesize); XFREE_CLEAR(keep_msg); p = NULL; @@ -2156,7 +2156,7 @@ bool msg_add_fileformat(int eol_type) } /// Append line and character count to IObuff. -void msg_add_lines(int insert_space, long lnum, off_T nchars) +void msg_add_lines(int insert_space, linenr_T lnum, off_T nchars) { char *p = IObuff + strlen(IObuff); @@ -2700,7 +2700,7 @@ int vim_rename(const char *from, const char *to) } // Rename() failed, try copying the file. - long perm = os_getperm(from); + int perm = os_getperm(from); // For systems that support ACL: get the ACL from the original file. vim_acl_T acl = os_get_acl(from); int fd_in = os_open(from, O_RDONLY, 0); @@ -2710,7 +2710,7 @@ int vim_rename(const char *from, const char *to) } // Create the new file with same permissions as the original. - int fd_out = os_open(to, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, (int)perm); + int fd_out = os_open(to, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, perm); if (fd_out < 0) { close(fd_in); os_free_acl(acl); @@ -2905,7 +2905,7 @@ int buf_check_timestamp(buf_T *buf) && (!(file_info_ok = os_fileinfo(buf->b_ffname, &file_info)) || time_differs(&file_info, buf->b_mtime, buf->b_mtime_ns) || (int)file_info.stat.st_mode != buf->b_orig_mode)) { - const long prev_b_mtime = buf->b_mtime; + const int prev_b_mtime = (int)buf->b_mtime; retval = 1; diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index 5eb2689233..92ea866239 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -3,7 +3,10 @@ #include "nvim/buffer_defs.h" #include "nvim/eval/typval_defs.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" +#include "nvim/globals.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" // Values for readfile() flags diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua index b9fae7d0fe..61767583ec 100644 --- a/src/nvim/generators/gen_ex_cmds.lua +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -102,7 +102,7 @@ for _, cmd in ipairs(defs) do end local preview_func if cmd.preview_func then - preview_func = string.format("(ex_preview_func_T)&%s", cmd.preview_func) + preview_func = string.format("&%s", cmd.preview_func) else preview_func = "NULL" end diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index 0932a1357f..05def71caa 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -35,6 +35,8 @@ local redraw_flags={ local list_flags={ comma='P_COMMA', onecomma='P_ONECOMMA', + commacolon='P_COMMA|P_COLON', + onecommacolon='P_ONECOMMA|P_COLON', flags='P_FLAGLIST', flagscomma='P_COMMA|P_FLAGLIST', } @@ -166,6 +168,9 @@ local function dump_option(i, o) if o.cb then w(' .opt_did_set_cb=' .. o.cb) end + if o.expand_cb then + w(' .opt_expand_cb=' .. o.expand_cb) + end if o.enable_if then w('#else') w(' .var=NULL') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b696f8ab37..7c5d39bd70 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -291,10 +291,10 @@ static void delete_buff_tail(buffheader_T *buf, int slen) } /// Add number "n" to buffer "buf". -static void add_num_buff(buffheader_T *buf, long n) +static void add_num_buff(buffheader_T *buf, int n) { char number[32]; - snprintf(number, sizeof(number), "%ld", n); + snprintf(number, sizeof(number), "%d", n); add_buff(buf, number, -1L); } @@ -589,7 +589,7 @@ void AppendCharToRedobuff(int c) } // Append a number to the redo buffer. -void AppendNumberToRedobuff(long n) +void AppendNumberToRedobuff(int n) { if (!block_redo) { add_num_buff(&redobuff, n); @@ -643,7 +643,7 @@ void stuffcharReadbuff(int c) } // Append a number to the stuff buffer. -void stuffnumReadbuff(long n) +void stuffnumReadbuff(int n) { add_num_buff(&readbuf1, n); } @@ -753,7 +753,7 @@ static void copy_redo(bool old_redo) /// CTRL-O <.> in insert mode /// /// @return FAIL for failure, OK otherwise -int start_redo(long count, bool old_redo) +int start_redo(int count, bool old_redo) { // init the pointers; return if nothing to redo if (read_redo(true, old_redo) == FAIL) { diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 1a7a62174d..d462c83710 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -832,7 +832,7 @@ EXTERN char no_lines_msg[] INIT(= N_("--No lines in buffer--")); // When ":global" is used to number of substitutions and changed lines is // accumulated until it's finished. // Also used for ":spellrepall". -EXTERN long sub_nsubs; // total number of substitutions +EXTERN int sub_nsubs; // total number of substitutions EXTERN linenr_T sub_nlines; // total number of lines changed // table to store parsed 'wildmode' diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 712688368b..2eab158bc4 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -201,65 +201,23 @@ static bool grid_invalid_row(ScreenGrid *grid, int row) return grid->attrs[grid->line_offset[row]] < 0; } -/// Return number of display cells for char at grid->chars[off]. -/// We make sure that the offset used is less than "max_off". -static int grid_off2cells(ScreenGrid *grid, size_t off, size_t max_off) -{ - return (off + 1 < max_off && grid->chars[off + 1] == 0) ? 2 : 1; -} - -/// Return true if the character at "row"/"col" on the screen is the left side -/// of a double-width character. -/// -/// Caller must make sure "row" and "col" are not invalid! -bool grid_lefthalve(ScreenGrid *grid, int row, int col) -{ - grid_adjust(&grid, &row, &col); - - return grid_off2cells(grid, grid->line_offset[row] + (size_t)col, - grid->line_offset[row] + (size_t)grid->cols) > 1; -} - -/// Correct a position on the screen, if it's the right half of a double-wide -/// char move it to the left half. Returns the corrected column. -int grid_fix_col(ScreenGrid *grid, int col, int row) -{ - int coloff = 0; - grid_adjust(&grid, &row, &coloff); - - col += coloff; - if (grid->chars != NULL && col > 0 - && grid->chars[grid->line_offset[row] + (size_t)col] == 0) { - return col - 1 - coloff; - } - return col - coloff; -} - -/// output a single character directly to the grid -void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr) -{ - char buf[MB_MAXBYTES + 1]; - - grid_puts(grid, buf, utf_char2bytes(c, buf), row, col, attr); -} - /// Get a single character directly from grid.chars into "bytes", which must /// have a size of "MB_MAXBYTES + 1". /// If "attrp" is not NULL, return the character's attribute in "*attrp". -void grid_getbytes(ScreenGrid *grid, int row, int col, char *bytes, int *attrp) +schar_T grid_getchar(ScreenGrid *grid, int row, int col, int *attrp) { grid_adjust(&grid, &row, &col); // safety check if (grid->chars == NULL || row >= grid->rows || col >= grid->cols) { - return; + return NUL; } size_t off = grid->line_offset[row] + (size_t)col; if (attrp != NULL) { *attrp = grid->attrs[off]; } - schar_get(bytes, grid->chars[off]); + return grid->chars[off]; } static ScreenGrid *grid_line_grid = NULL; @@ -269,38 +227,6 @@ static int grid_line_maxcol = 0; static int grid_line_first = INT_MAX; static int grid_line_last = 0; -/// put string 'text' on the window grid at position 'row' and 'col', with -/// attributes 'attr', and update contents of 'grid' -/// @param textlen length of string or -1 to use strlen(text) -/// Note: only outputs within one row! -int grid_puts(ScreenGrid *grid, const char *text, int textlen, int row, int col, int attr) -{ - grid_line_start(grid, row); - - // Safety check. The check for negative row and column is to fix issue - // vim/vim#4102. TODO(neovim): find out why row/col could be negative. - int off_col = grid_line_coloff + col; - if (grid_line_grid->chars == NULL - || grid_line_row >= grid_line_grid->rows || grid_line_row < 0 - || off_col >= grid_line_grid->cols || off_col < 0) { - if (rdb_flags & RDB_INVALID) { - abort(); - } else { - grid_line_grid = NULL; - return 0; - } - } - - int len = grid_line_puts(col, text, textlen, attr); - if (grid_line_last > grid_line_first) { - // TODO(bfredl): this is bullshit. message.c should manage its own cursor movements - int col_pos = MIN(grid_line_coloff + grid_line_last, grid_line_grid->cols - 1); - ui_grid_cursor_goto(grid_line_grid->handle, grid_line_row, col_pos); - } - grid_line_flush(); - return len; -} - /// Start a group of grid_line_puts calls that builds a single grid line. /// /// Must be matched with a grid_line_flush call before moving to @@ -318,6 +244,25 @@ void grid_line_start(ScreenGrid *grid, int row) grid_line_last = 0; } +/// Get present char from current rendered screen line +/// +/// This indicates what already is on screen, not the pending render buffer. +/// +/// @return char or space if out of bounds +schar_T grid_line_getchar(int col, int *attr) +{ + if (col < grid_line_maxcol) { + size_t off = grid_line_grid->line_offset[grid_line_row] + (size_t)col; + if (attr != NULL) { + *attr = grid_line_grid->attrs[off]; + } + return grid_line_grid->chars[off]; + } else { + // NUL is a very special value (right-half of double width), space is True Neutral™ + return schar_from_ascii(' '); + } +} + void grid_line_put_schar(int col, schar_T schar, int attr) { assert(grid_line_grid); @@ -331,8 +276,13 @@ void grid_line_put_schar(int col, schar_T schar, int attr) linebuf_vcol[col] = -1; } -/// like grid_puts(), but output "text[len]". When "len" is -1 output up to -/// a NUL. +/// Put string "text" at "col" position relative to the grid line from the +/// recent grid_line_start() call. +/// +/// @param textlen length of string or -1 to use strlen(text) +/// Note: only outputs within one row! +/// +/// @return number of grid cells used int grid_line_puts(int col, const char *text, int textlen, int attr) { const char *ptr = text; @@ -435,8 +385,16 @@ void grid_line_fill(int start_col, int end_col, int c, int attr) linebuf_attr[col] = attr; linebuf_vcol[col] = -1; } - grid_line_first = MIN(grid_line_first, start_col); - grid_line_last = MAX(grid_line_last, end_col); + if (start_col < end_col) { + grid_line_first = MIN(grid_line_first, start_col); + grid_line_last = MAX(grid_line_last, end_col); + } +} + +/// move the cursor to a position in a currently rendered line. +void grid_line_cursor_goto(int col) +{ + ui_grid_cursor_goto(grid_line_grid->handle, grid_line_row, col); } /// End a group of grid_line_puts calls and send the screen buffer to the UI layer. @@ -455,6 +413,22 @@ void grid_line_flush(void) false, 0, false, invalid_row); } +/// flush grid line but only if on a valid row +/// +/// This is a stopgap until message.c has been refactored to behave +void grid_line_flush_if_valid_row(void) +{ + if (grid_line_row < 0 || grid_line_row >= grid_line_grid->rows) { + if (rdb_flags & RDB_INVALID) { + abort(); + } else { + grid_line_grid = NULL; + return; + } + } + grid_line_flush(); +} + /// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col" /// to "end_col" (exclusive) with character "c1" in first column followed by /// "c2" in the other columns. Use attributes "attr". @@ -484,27 +458,29 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int } for (int row = start_row; row < end_row; row++) { + int dirty_first = INT_MAX; + int dirty_last = 0; + size_t lineoff = grid->line_offset[row]; + // When drawing over the right half of a double-wide char clear // out the left half. When drawing over the left half of a // double wide-char clear out the right half. Only needed in a // terminal. - if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) { - grid_puts(grid, " ", 1, row, start_col - 1, 0); + if (start_col > 0 && grid->chars[lineoff + (size_t)start_col] == NUL) { + size_t off = lineoff + (size_t)start_col - 1; + grid->chars[off] = schar_from_ascii(' '); + grid->attrs[off] = attr; + dirty_first = start_col - 1; } - if (end_col < grid->cols - && grid_fix_col(grid, end_col, row) != end_col) { - grid_puts(grid, " ", 1, row, end_col, 0); + if (end_col < grid->cols && grid->chars[lineoff + (size_t)end_col] == NUL) { + size_t off = lineoff + (size_t)end_col; + grid->chars[off] = schar_from_ascii(' '); + grid->attrs[off] = attr; + dirty_last = end_col + 1; } - // if grid was resized (in ext_multigrid mode), the UI has no redraw updates - // for the newly resized grid. It is better mark everything as dirty and - // send all the updates. - int dirty_first = INT_MAX; - int dirty_last = 0; - int col = start_col; sc = schar_from_char(c1); - size_t lineoff = grid->line_offset[row]; for (col = start_col; col < end_col; col++) { size_t off = lineoff + (size_t)col; if (grid->chars[off] != sc || grid->attrs[off] != attr || rdb_flags & RDB_NODELTA) { @@ -584,8 +560,6 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol endcol = grid->cols; } - grid_adjust(&grid, &row, &coloff); - // Safety check. Avoids clang warnings down the call stack. if (grid->chars == NULL || row >= grid->rows || coloff >= grid->cols) { DLOG("invalid state, skipped"); @@ -646,12 +620,8 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol // the right half of the old character. // Also required when writing the right half of a double-width // char over the left half of an existing one - if (col + char_cells == endcol - && ((char_cells == 1 - && grid_off2cells(grid, off, max_off_to) > 1) - || (char_cells == 2 - && grid_off2cells(grid, off, max_off_to) == 1 - && grid_off2cells(grid, off + 1, max_off_to) > 1))) { + if (col + char_cells == endcol && off + (size_t)char_cells < max_off_to + && grid->chars[off + (size_t)char_cells] == NUL) { clear_next = true; } diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 080dc79e0e..5cbff6e3dd 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -18,13 +18,13 @@ #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor_shape.h" #include "nvim/decoration_provider.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" -#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/garray.h" #include "nvim/gettext.h" @@ -866,9 +866,17 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) if (strcmp(g->sg_name_u, "NORMAL") == 0) { cterm_normal_fg_color = g->sg_cterm_fg; cterm_normal_bg_color = g->sg_cterm_bg; + bool did_changed = false; + if (normal_bg != g->sg_rgb_bg || normal_fg != g->sg_rgb_fg || normal_sp != g->sg_rgb_sp) { + did_changed = true; + } normal_fg = g->sg_rgb_fg; normal_bg = g->sg_rgb_bg; normal_sp = g->sg_rgb_sp; + + if (did_changed) { + highlight_attr_set_all(); + } ui_default_colors_set(); } else { // a cursor style uses this syn_id, make sure its attribute is updated. @@ -2283,10 +2291,10 @@ static void highlight_list_two(int cnt, int attr) } /// Function given to ExpandGeneric() to obtain the list of group names. -const char *get_highlight_name(expand_T *const xp, int idx) +char *get_highlight_name(expand_T *const xp, int idx) FUNC_ATTR_WARN_UNUSED_RESULT { - return get_highlight_name_ext(xp, idx, true); + return (char *)get_highlight_name_ext(xp, idx, true); } /// Obtain a highlight group name. diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h index 77a3684067..369864552c 100644 --- a/src/nvim/highlight_group.h +++ b/src/nvim/highlight_group.h @@ -3,6 +3,7 @@ #include "nvim/api/keysets.h" #include "nvim/api/private/helpers.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/highlight_defs.h" #include "nvim/types.h" diff --git a/src/nvim/indent.c b/src/nvim/indent.c index b7bc23cda5..d19164b24f 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -55,7 +55,7 @@ /// @return false for an error. bool tabstop_set(char *var, colnr_T **array) { - long valcount = 1; + int valcount = 1; int t; char *cp; @@ -89,7 +89,7 @@ bool tabstop_set(char *var, colnr_T **array) return false; } - *array = (colnr_T *)xmalloc((unsigned)(valcount + 1) * sizeof(long)); + *array = (colnr_T *)xmalloc((unsigned)(valcount + 1) * sizeof(int)); (*array)[0] = (colnr_T)valcount; t = 1; @@ -122,13 +122,13 @@ int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts) OptInt ts = ts_arg == 0 ? 8 : ts_arg; colnr_T tabcol = 0; int t; - long padding = 0; + int padding = 0; if (vts == NULL || vts[0] == 0) { return (int)(ts - (col % ts)); } - const long tabcount = vts[0]; + const int tabcount = vts[0]; for (t = 1; t <= tabcount; t++) { tabcol += vts[t]; @@ -141,7 +141,7 @@ int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts) padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]); } - return (int)padding; + return padding; } /// Find the size of the tab that covers a particular column. @@ -149,13 +149,13 @@ int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts) { colnr_T tabcol = 0; int t; - long tab_size = 0; + int tab_size = 0; if (vts == NULL || vts[0] == 0) { return (int)ts; } - const long tabcount = vts[0]; + const int tabcount = vts[0]; for (t = 1; t <= tabcount; t++) { tabcol += vts[t]; if (tabcol > col) { @@ -167,20 +167,20 @@ int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts) tab_size = vts[tabcount]; } - return (int)tab_size; + return tab_size; } /// Find the column on which a tab starts. -colnr_T tabstop_start(colnr_T col, long ts, colnr_T *vts) +colnr_T tabstop_start(colnr_T col, int ts, colnr_T *vts) { colnr_T tabcol = 0; int t; if (vts == NULL || vts[0] == 0) { - return (int)((col / ts) * ts); + return ((col / ts) * ts); } - const long tabcount = vts[0]; + const int tabcount = vts[0]; for (t = 1; t <= tabcount; t++) { tabcol += vts[t]; if (tabcol > col) { @@ -194,26 +194,26 @@ colnr_T tabstop_start(colnr_T col, long ts, colnr_T *vts) /// Find the number of tabs and spaces necessary to get from one column /// to another. -void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr_T *vts, int *ntabs, +void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, const colnr_T *vts, int *ntabs, int *nspcs) { int spaces = end_col - start_col; colnr_T tabcol = 0; - long padding = 0; + int padding = 0; int t; - long ts = ts_arg == 0 ? (long)curbuf->b_p_ts : ts_arg; + int ts = ts_arg == 0 ? (int)curbuf->b_p_ts : ts_arg; assert(ts != 0); // suppress clang "Division by zero" if (vts == NULL || vts[0] == 0) { int tabs = 0; - const int initspc = (int)(ts - (start_col % ts)); + const int initspc = (ts - (start_col % ts)); if (spaces >= initspc) { spaces -= initspc; tabs++; } - tabs += (int)(spaces / ts); - spaces -= (int)((spaces / ts) * ts); + tabs += (spaces / ts); + spaces -= ((spaces / ts) * ts); *ntabs = tabs; *nspcs = spaces; @@ -221,7 +221,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr } // Find the padding needed to reach the next tabstop. - const long tabcount = vts[0]; + const int tabcount = vts[0]; for (t = 1; t <= tabcount; t++) { tabcol += vts[t]; if (tabcol > start_col) { @@ -241,7 +241,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr } *ntabs = 1; - spaces -= (int)padding; + spaces -= padding; // At least one tab has been used. See if any more will fit. while (spaces != 0 && ++t <= tabcount) { @@ -251,7 +251,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, const colnr return; } *ntabs += 1; - spaces -= (int)padding; + spaces -= padding; } *ntabs += spaces / (int)vts[tabcount]; @@ -283,21 +283,21 @@ bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2) } /// Copy a tabstop array, allocating space for the new array. -int *tabstop_copy(const long *oldts) +int *tabstop_copy(const int *oldts) { - long *newts; + int *newts; int t; if (oldts == 0) { return 0; } - newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long)); + newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(int)); for (t = 0; t <= oldts[0]; t++) { newts[t] = oldts[t]; } - return (int *)newts; + return newts; } /// Return a count of the number of tabstops. @@ -316,25 +316,23 @@ int tabstop_first(colnr_T *ts) /// 'tabstop' value when 'shiftwidth' is zero. int get_sw_value(buf_T *buf) { - long result = get_sw_value_col(buf, 0); - assert(result >= 0 && result <= INT_MAX); - return (int)result; + int result = get_sw_value_col(buf, 0); + return result; } /// Idem, using "pos". -long get_sw_value_pos(buf_T *buf, pos_T *pos) +int get_sw_value_pos(buf_T *buf, pos_T *pos) { pos_T save_cursor = curwin->w_cursor; - long sw_value; curwin->w_cursor = *pos; - sw_value = get_sw_value_col(buf, get_nolist_virtcol()); + int sw_value = get_sw_value_col(buf, get_nolist_virtcol()); curwin->w_cursor = save_cursor; return sw_value; } /// Idem, using the first non-black in the current line. -long get_sw_value_indent(buf_T *buf) +int get_sw_value_indent(buf_T *buf) { pos_T pos = curwin->w_cursor; @@ -343,9 +341,9 @@ long get_sw_value_indent(buf_T *buf) } /// Idem, using virtual column "col". -long get_sw_value_col(buf_T *buf, colnr_T col) +int get_sw_value_col(buf_T *buf, colnr_T col) { - return buf->b_p_sw ? (long)buf->b_p_sw + return buf->b_p_sw ? (int)buf->b_p_sw : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array); } @@ -760,6 +758,7 @@ bool briopt_check(win_T *wp) char *p = wp->w_p_briopt; while (*p != NUL) { + // Note: Keep this in sync with p_briopt_values if (strncmp(p, "shift:", 6) == 0 && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) { p += 6; @@ -939,12 +938,12 @@ void ex_retab(exarg_T *eap) { linenr_T lnum; bool got_tab = false; - long num_spaces = 0; - long num_tabs; - long len; - long start_col = 0; // For start of white-space string - long start_vcol = 0; // For start of white-space string - long old_len; + int num_spaces = 0; + int num_tabs; + int len; + int start_col = 0; // For start of white-space string + int64_t start_vcol = 0; // For start of white-space string + int old_len; char *new_line = (char *)1; // init to non-NULL colnr_T *new_vts_array = NULL; char *new_ts_str; // string value of tab argument @@ -975,8 +974,8 @@ void ex_retab(exarg_T *eap) } for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) { char *ptr = ml_get(lnum); - long col = 0; - long vcol = 0; + int col = 0; + int64_t vcol = 0; bool did_undo = false; // called u_save for current line while (true) { if (ascii_iswhite(ptr[col])) { @@ -995,13 +994,13 @@ void ex_retab(exarg_T *eap) // Retabulate this string of white-space // len is virtual length of white string - len = num_spaces = vcol - start_vcol; + len = num_spaces = (int)(vcol - start_vcol); num_tabs = 0; if (!curbuf->b_p_et) { int t, s; tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol, - (long)curbuf->b_p_ts, new_vts_array, &t, &s); + (int)curbuf->b_p_ts, new_vts_array, &t, &s); num_tabs = t; num_spaces = s; } @@ -1018,7 +1017,7 @@ void ex_retab(exarg_T *eap) // len is actual number of white characters used len = num_spaces + num_tabs; - old_len = (long)strlen(ptr); + old_len = (int)strlen(ptr); const long new_len = old_len - col + start_col + len + 1; if (new_len <= 0 || new_len >= MAXCOL) { emsg_text_too_long(); @@ -1030,7 +1029,7 @@ void ex_retab(exarg_T *eap) memmove(new_line, ptr, (size_t)start_col); } memmove(new_line + start_col + len, - ptr + col, (size_t)(old_len - col + 1)); + ptr + col, (size_t)old_len - (size_t)col + 1); ptr = new_line + start_col; for (col = 0; col < len; col++) { ptr[col] = (col < num_tabs) ? '\t' : ' '; diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 4a6c819015..3ada39c800 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -24,7 +24,6 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" -#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 18da7af416..d6fd14cc10 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -20,6 +20,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/change.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index b132a0d147..f3e2b6d1d0 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -8,6 +8,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/assert.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/func_attr.h" diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 1fb91d85b4..2cfa264754 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -21,6 +21,7 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" diff --git a/src/nvim/mapping.h b/src/nvim/mapping.h index 405234dbd5..860bbad272 100644 --- a/src/nvim/mapping.h +++ b/src/nvim/mapping.h @@ -7,6 +7,7 @@ #include "nvim/api/keysets.h" #include "nvim/api/private/defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/mapping_defs.h" diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 197cc21308..ff82c59783 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -1626,7 +1626,7 @@ static int damage_cmp(const void *s1, const void *s2) { Damage *d1 = (Damage *)s1, *d2 = (Damage *)s2; assert(d1->id != d2->id); - return d1->id > d2->id; + return d1->id > d2->id ? 1 : -1; } bool marktree_splice(MarkTree *b, int32_t start_line, int start_col, int old_extent_line, @@ -1792,6 +1792,7 @@ past_continue_same_node: for (size_t i = 0; i < kv_size(damage); i++) { Damage d = kv_A(damage, i); + assert(i == 0 || d.id > kv_A(damage, i - 1).id); if (!(d.id & MARKTREE_END_FLAG)) { // start if (i + 1 < kv_size(damage) && kv_A(damage, i + 1).id == (d.id | MARKTREE_END_FLAG)) { Damage d2 = kv_A(damage, i + 1); @@ -2267,7 +2268,7 @@ void mt_inspect_dotfile_node(MarkTree *b, garray_T *ga, MTNode *n, MTPos off, ch if (parent != NULL) { snprintf(namebuf, sizeof namebuf, "%s_%c%d", parent, 'a' + n->level, n->p_idx); } else { - snprintf(namebuf, sizeof namebuf, "Node"); + snprintf(namebuf, sizeof namebuf, "MTNode"); } GA_PRINT(" %s[shape=plaintext, label=<\n", namebuf); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7b7c822b3b..a3cd569846 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -43,6 +43,7 @@ #include "nvim/ascii.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/eval/typval.h" @@ -77,8 +78,8 @@ typedef struct { } convertStruct; struct interval { - long first; - long last; + int first; + int last; }; // uncrustify:off @@ -2830,3 +2831,14 @@ void f_charclass(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string); } + +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// encoding options. +char *get_encoding_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + if (idx >= (int)ARRAY_SIZE(enc_canon_table)) { + return NULL; + } + + return (char *)enc_canon_table[idx].name; +} diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 724a16014d..01a7d0d6ae 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -5,6 +5,7 @@ #include <stdint.h> #include <string.h> +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/func_attr.h" #include "nvim/mbyte_defs.h" diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 7bfa6db4ef..716d05e27a 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -168,17 +168,15 @@ enum { B0_MAGIC_CHAR = 0x55, }; -// Block zero holds all info about the swap file. This is the first block in -// the file. +// Block zero holds all info about the swapfile. This is the first block in the file. // -// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing -// swap files unusable! +// NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing swapfiles unusable! // // If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!! // // This block is built up of single bytes, to make it portable across // different machines. b0_magic_* is used to check the byte order and size of -// variables, because the rest of the swap file is not portable. +// variables, because the rest of the swapfile is not portable. typedef struct { char b0_id[2]; ///< ID for block 0: BLOCK0_ID0 and BLOCK0_ID1. char b0_version[10]; // Vim version string @@ -210,8 +208,7 @@ typedef struct { // EOL_MAC + 1. #define B0_FF_MASK 3 -// Swap file is in directory of edited file. Used to find the file from -// different mount points. +// Swapfile is in directory of edited file. Used to find the file from different mount points. #define B0_SAME_DIR 4 // The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it. @@ -289,14 +286,14 @@ int ml_open(buf_T *buf) buf->b_p_swf = false; } - // When 'updatecount' is non-zero swap file may be opened later. + // When 'updatecount' is non-zero swapfile may be opened later. if (!buf->terminal && p_uc && buf->b_p_swf) { buf->b_may_swap = true; } else { buf->b_may_swap = false; } - // Open the memfile. No swap file is created yet. + // Open the memfile. No swapfile is created yet. memfile_T *mfp = mf_open(NULL, 0); if (mfp == NULL) { goto error; @@ -335,7 +332,7 @@ int ml_open(buf_T *buf) } // Always sync block number 0 to disk, so we can check the file name in - // the swap file in findswapname(). Don't do this for a help files or + // the swapfile in findswapname(). Don't do this for a help files or // a spell buffer though. // Only works when there's a swapfile, otherwise it's done when the file // is created. @@ -386,17 +383,17 @@ error: } /// ml_setname() is called when the file name of "buf" has been changed. -/// It may rename the swap file. +/// It may rename the swapfile. void ml_setname(buf_T *buf) { bool success = false; memfile_T *mfp = buf->b_ml.ml_mfp; - if (mfp->mf_fd < 0) { // there is no swap file yet - // When 'updatecount' is 0 and 'noswapfile' there is no swap file. - // For help files we will make a swap file now. + if (mfp->mf_fd < 0) { // there is no swapfile yet + // When 'updatecount' is 0 and 'noswapfile' there is no swapfile. + // For help files we will make a swapfile now. if (p_uc != 0 && (cmdmod.cmod_flags & CMOD_NOSWAPFILE) == 0) { - ml_open_file(buf); // create a swap file + ml_open_file(buf); // create a swapfile } return; } @@ -423,13 +420,13 @@ void ml_setname(buf_T *buf) success = true; break; } - // need to close the swap file before renaming + // need to close the swapfile before renaming if (mfp->mf_fd >= 0) { close(mfp->mf_fd); mfp->mf_fd = -1; } - // try to rename the swap file + // try to rename the swapfile if (vim_rename(mfp->mf_fname, fname) == 0) { success = true; mf_free_fnames(mfp); @@ -440,10 +437,10 @@ void ml_setname(buf_T *buf) xfree(fname); // this fname didn't work, try another } - if (mfp->mf_fd == -1) { // need to (re)open the swap file + if (mfp->mf_fd == -1) { // need to (re)open the swapfile mfp->mf_fd = os_open(mfp->mf_fname, O_RDWR, 0); if (mfp->mf_fd < 0) { - // could not (re)open the swap file, what can we do???? + // could not (re)open the swapfile, what can we do???? emsg(_("E301: Oops, lost the swap file!!!")); return; } @@ -466,7 +463,7 @@ void ml_open_files(void) } } -/// Open a swap file for an existing memfile, if there is no swap file yet. +/// Open a swapfile for an existing memfile, if there is no swapfile yet. /// If we are unable to find a file name, mf_fname will be NULL /// and the memfile will be in memory only (no recovery possible). void ml_open_file(buf_T *buf) @@ -495,7 +492,7 @@ void ml_open_file(buf_T *buf) if (*dirp == NUL) { break; } - // There is a small chance that between choosing the swap file name + // There is a small chance that between choosing the swapfile name // and creating it, another Vim creates the file. In that case the // creation will fail and we will use another directory. char *fname = findswapname(buf, &dirp, NULL, &found_existing_dir); @@ -514,7 +511,7 @@ void ml_open_file(buf_T *buf) if (mf_sync(mfp, MFS_ZERO) == OK) { // Mark all blocks that should be in the swapfile as dirty. // Needed for when the 'swapfile' option was reset, so that - // the swap file was deleted, and then on again. + // the swapfile was deleted, and then on again. mf_set_dirty(mfp); break; } @@ -531,12 +528,12 @@ void ml_open_file(buf_T *buf) no_wait_return--; } - // don't try to open a swap file again + // don't try to open a swapfile again buf->b_may_swap = false; } -/// If still need to create a swap file, and starting to edit a not-readonly -/// file, or reading into an existing buffer, create a swap file now. +/// If still need to create a swapfile, and starting to edit a not-readonly +/// file, or reading into an existing buffer, create a swapfile now. /// /// @param newfile reading file into new buffer void check_need_swap(bool newfile) @@ -553,7 +550,7 @@ void check_need_swap(bool newfile) /// Close memline for buffer 'buf'. /// -/// @param del_file if true, delete the swap file +/// @param del_file if true, delete the swapfile void ml_close(buf_T *buf, int del_file) { if (buf->b_ml.ml_mfp == NULL) { // not open @@ -643,7 +640,7 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what) mf_put(mfp, hp, true, false); } -/// Write file name and timestamp into block 0 of a swap file. +/// Write file name and timestamp into block 0 of a swapfile. /// Also set buf->b_mtime. /// Don't use NameBuff[]!!! static void set_b0_fname(ZeroBlock *b0p, buf_T *buf) @@ -695,7 +692,7 @@ static void set_b0_fname(ZeroBlock *b0p, buf_T *buf) add_b0_fenc(b0p, curbuf); } -/// Update the B0_SAME_DIR flag of the swap file. It's set if the file and the +/// Update the B0_SAME_DIR flag of the swapfile. It's set if the file and the /// swapfile for "buf" are in the same directory. /// This is fail safe: if we are not sure the directories are equal the flag is /// not set. @@ -724,27 +721,30 @@ static void add_b0_fenc(ZeroBlock *b0p, buf_T *buf) } } -/// Return true if the process with number "b0p->b0_pid" is still running. -/// "swap_fname" is the name of the swap file, if it's from before a reboot then -/// the result is false; -static bool swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname) +/// Returns the PID of the process that owns the swapfile, if it is running. +/// +/// @param b0p swapfile data +/// @param swap_fname Name of the swapfile. If it's from before a reboot, the result is 0. +/// +/// @return PID, or 0 if process is not running or the swapfile is from before a reboot. +static int swapfile_process_running(const ZeroBlock *b0p, const char *swap_fname) { FileInfo st; double uptime; - // If the system rebooted after when the swap file was written then the + // If the system rebooted after when the swapfile was written then the // process can't be running now. if (os_fileinfo(swap_fname, &st) && uv_uptime(&uptime) == 0 && (Timestamp)st.stat.st_mtim.tv_sec < os_time() - (Timestamp)uptime) { - return false; + return 0; } - return os_proc_running((int)char_to_long(b0p->b0_pid)); + int pid = (int)char_to_long(b0p->b0_pid); + return os_proc_running(pid) ? pid : 0; } /// Try to recover curbuf from the .swp file. /// -/// @param checkext if true, check the extension and detect whether it is a -/// swap file. +/// @param checkext if true, check the extension and detect whether it is a swapfile. void ml_recover(bool checkext) { buf_T *buf = NULL; @@ -766,8 +766,8 @@ void ml_recover(bool checkext) int called_from_main = (curbuf->b_ml.ml_mfp == NULL); int attr = HL_ATTR(HLF_E); - // If the file name ends in ".s[a-w][a-z]" we assume this is the swap file. - // Otherwise a search is done to find the swap file(s). + // If the file name ends in ".s[a-w][a-z]" we assume this is the swapfile. + // Otherwise a search is done to find the swapfile(s). char *fname = curbuf->b_fname; if (fname == NULL) { // When there is no file name fname = ""; @@ -782,17 +782,17 @@ void ml_recover(bool checkext) } else { directly = false; - // count the number of matching swap files + // count the number of matching swapfiles len = recover_names(fname, false, NULL, 0, NULL); - if (len == 0) { // no swap files found + if (len == 0) { // no swapfiles found semsg(_("E305: No swap file found for %s"), fname); goto theend; } int i; - if (len == 1) { // one swap file found, use it + if (len == 1) { // one swapfile found, use it i = 1; - } else { // several swap files found, choose - // list the names of the swap files + } else { // several swapfiles found, choose + // list the names of the swapfiles (void)recover_names(fname, true, NULL, 0, NULL); msg_putchar('\n'); msg_puts(_("Enter number of swap file to use (0 to quit): ")); @@ -801,7 +801,7 @@ void ml_recover(bool checkext) goto theend; } } - // get the swap file name that will be used + // get the swapfile name that will be used (void)recover_names(fname, false, NULL, i, &fname_used); } if (fname_used == NULL) { @@ -812,7 +812,7 @@ void ml_recover(bool checkext) getout(1); } - // Allocate a buffer structure for the swap file that is used for recovery. + // Allocate a buffer structure for the swapfile that is used for recovery. // Only the memline in it is really used. buf = xmalloc(sizeof(buf_T)); @@ -825,7 +825,7 @@ void ml_recover(bool checkext) buf->b_ml.ml_locked = NULL; // no locked block buf->b_ml.ml_flags = 0; - // open the memfile from the old swap file + // open the memfile from the old swapfile p = xstrdup(fname_used); // save "fname_used" for the message: // mf_open() will consume "fname_used"! mfp = mf_open(fname_used, O_RDONLY); @@ -837,7 +837,7 @@ void ml_recover(bool checkext) buf->b_ml.ml_mfp = mfp; // The page size set in mf_open() might be different from the page size - // used in the swap file, we must get it from block 0. But to read block + // used in the swapfile, we must get it from block 0. But to read block // 0 we need a page size. Use the minimal size for block 0 here, it will // be set to the real value below. mfp->mf_page_size = MIN_SWAP_PAGE_SIZE; @@ -910,7 +910,7 @@ void ml_recover(bool checkext) b0p = hp->bh_data; } - // If .swp file name given directly, use name from swap file for buffer. + // If .swp file name given directly, use name from swapfile for buffer. if (directly) { expand_env(b0p->b0_fname, NameBuff, MAXPATHL); if (setfname(curbuf, NameBuff, NULL, true) == FAIL) { @@ -929,7 +929,7 @@ void ml_recover(bool checkext) smsg(0, _("Original file \"%s\""), NameBuff); msg_putchar('\n'); - // check date of swap file and original file + // check date of swapfile and original file FileInfo org_file_info; FileInfo swp_file_info; long mtime = char_to_long(b0p->b0_mtime); @@ -968,7 +968,7 @@ void ml_recover(bool checkext) 0, MAXLNUM, NULL, READ_NEW, false); } - // Use the 'fileformat' and 'fileencoding' as stored in the swap file. + // Use the 'fileformat' and 'fileencoding' as stored in the swapfile. if (b0_ff != 0) { set_fileformat(b0_ff - 1, OPT_LOCAL); } @@ -1215,7 +1215,7 @@ void ml_recover(bool checkext) // Warn there could be an active Vim on the same file, the user may // want to kill it. msg_puts(_("\nNote: process STILL RUNNING: ")); - msg_outnum(char_to_long(b0p->b0_pid)); + msg_outnum((int)char_to_long(b0p->b0_pid)); } msg_puts("\n\n"); cmdline_row = msg_row; @@ -1231,7 +1231,7 @@ theend: } mf_close(mfp, false); // will also xfree(mfp->mf_fname) } - if (buf != NULL) { // may be NULL if swap file not found. + if (buf != NULL) { // may be NULL if swapfile not found. xfree(buf->b_ml.ml_stack); xfree(buf); } @@ -1243,20 +1243,20 @@ theend: } } -/// Find the names of swap files in current directory and the directory given +/// Find the names of swapfiles in current directory and the directory given /// with the 'directory' option. /// /// Used to: -/// - list the swap files for "vim -r" -/// - count the number of swap files when recovering -/// - list the swap files when recovering -/// - list the swap files for swapfilelist() -/// - find the name of the n'th swap file when recovering +/// - list the swapfiles for "vim -r" +/// - count the number of swapfiles when recovering +/// - list the swapfiles when recovering +/// - list the swapfiles for swapfilelist() +/// - find the name of the n'th swapfile when recovering /// -/// @param fname base for swap file name -/// @param do_list when true, list the swap file names +/// @param fname base for swapfile name +/// @param do_list when true, list the swapfile names /// @param ret_list when not NULL add file names to it -/// @param nr when non-zero, return nr'th swap file name +/// @param nr when non-zero, return nr'th swapfile name /// @param fname_out result when "nr" > 0 int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fname_out) { @@ -1273,7 +1273,7 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn if (fname != NULL) { #ifdef HAVE_READLINK - // Expand symlink in the file name, because the swap file is created + // Expand symlink in the file name, because the swapfile is created // with the actual file instead of with the symlink. if (resolve_symlink(fname, fname_buf) == OK) { fname_res = fname_buf; @@ -1342,9 +1342,9 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn num_files = 0; } - // When no swap file found, wildcard expansion might have failed (e.g. + // When no swapfile found, wildcard expansion might have failed (e.g. // not able to execute the shell). - // Try finding a swap file by simply adding ".swp" to the file name. + // Try finding a swapfile by simply adding ".swp" to the file name. if (*dirp == NUL && file_count + num_files == 0 && fname != NULL) { char *swapname = modname(fname_res, ".swp", true); if (swapname != NULL) { @@ -1402,8 +1402,8 @@ int recover_names(char *fname, bool do_list, list_T *ret_list, int nr, char **fn if (num_files) { for (int i = 0; i < num_files; i++) { - // print the swap file name - msg_outnum((long)++file_count); + // print the swapfile name + msg_outnum(++file_count); msg_puts(". "); msg_puts(path_tail(files[i])); msg_putchar('\n'); @@ -1456,12 +1456,13 @@ char *make_percent_swname(const char *dir, const char *name) return d; } -static bool process_still_running; +// PID of swapfile owner, or zero if not running. +static int process_running; -/// This is used by the swapinfo() function. +/// For Vimscript "swapinfo()". /// /// @return information found in swapfile "fname" in dictionary "d". -void get_b0_dict(const char *fname, dict_T *d) +void swapfile_dict(const char *fname, dict_T *d) { int fd; ZeroBlock b0; @@ -1482,7 +1483,7 @@ void get_b0_dict(const char *fname, dict_T *d) tv_dict_add_str_len(d, S_LEN("fname"), b0.b0_fname, B0_FNAME_SIZE_ORG); - tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid)); + tv_dict_add_nr(d, S_LEN("pid"), swapfile_process_running(&b0, fname)); tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime)); tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0); tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino)); @@ -1496,7 +1497,7 @@ void get_b0_dict(const char *fname, dict_T *d) } } -/// Give information about an existing swap file. +/// Loads info from swapfile `fname`, and displays it to the user. /// /// @return timestamp (0 when unknown). static time_t swapfile_info(char *fname) @@ -1509,7 +1510,7 @@ static time_t swapfile_info(char *fname) char uname[B0_UNAME_SIZE]; #endif - // print the swap file date + // print the swapfile date FileInfo file_info; if (os_fileinfo(fname, &file_info)) { #ifdef UNIX @@ -1566,10 +1567,9 @@ static time_t swapfile_info(char *fname) if (char_to_long(b0.b0_pid) != 0L) { msg_puts(_("\n process ID: ")); - msg_outnum(char_to_long(b0.b0_pid)); - if (swapfile_process_running(&b0, fname)) { + msg_outnum((int)char_to_long(b0.b0_pid)); + if ((process_running = swapfile_process_running(&b0, fname))) { msg_puts(_(" (STILL RUNNING)")); - process_still_running = true; } } @@ -1589,13 +1589,12 @@ static time_t swapfile_info(char *fname) return x; } -/// @return true if the swap file looks OK and there are no changes, thus it -/// can be safely deleted. +/// @return true if the swapfile looks OK and there are no changes, thus it can be safely deleted. static bool swapfile_unchanged(char *fname) { ZeroBlock b0; - // Swap file must exist. + // Swapfile must exist. if (!os_path_exists(fname)) { return false; } @@ -1653,7 +1652,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot) { int num_names = 0; - // May also add the file name with a dot prepended, for swap file in same + // May also add the file name with a dot prepended, for swapfile in same // dir as original file. if (prepend_dot) { names[num_names] = modname(path, ".sw?", true); @@ -1663,7 +1662,7 @@ static int recov_file_names(char **names, char *path, int prepend_dot) num_names++; } - // Form the normal swap file name pattern by appending ".sw?". + // Form the normal swapfile name pattern by appending ".sw?". names[num_names] = concat_fnames(path, ".sw?", false); if (num_names >= 1) { // check if we have the same name twice char *p = names[num_names - 1]; @@ -1724,7 +1723,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) /// sync one buffer, including negative blocks /// -/// after this all the blocks are in the swap file +/// after this all the blocks are in the swapfile /// /// Used for the :preserve command and when the original file has been /// changed or deleted. @@ -3132,7 +3131,7 @@ int resolve_symlink(const char *fname, char *buf) } #endif -/// Make swap file name out of the file name and a directory name. +/// Make swapfile name out of the file name and a directory name. /// /// @return pointer to allocated memory or NULL. char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) @@ -3141,7 +3140,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) #ifdef HAVE_READLINK char fname_buf[MAXPATHL]; - // Expand symlink in the file name, so that we put the swap file with the + // Expand symlink in the file name, so that we put the swapfile with the // actual file instead of with the symlink. if (resolve_symlink(fname, fname_buf) == OK) { fname_res = fname_buf; @@ -3162,7 +3161,7 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) return r; } - // Prepend a '.' to the swap file name for the current directory. + // Prepend a '.' to the swapfile name for the current directory. char *r = modname(fname_res, ".swp", dir_name[0] == '.' && dir_name[1] == NUL); if (r == NULL) { // out of memory @@ -3174,14 +3173,11 @@ char *makeswapname(char *fname, char *ffname, buf_T *buf, char *dir_name) return s; } -/// Get file name to use for swap file or backup file. -/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' -/// option "dname". -/// - If "dname" is ".", return "fname" (swap file in dir of file). -/// - If "dname" starts with "./", insert "dname" in "fname" (swap file -/// relative to dir of file). -/// - Otherwise, prepend "dname" to the tail of "fname" (swap file in specific -/// dir). +/// Get file name to use for swapfile or backup file. +/// Use the name of the edited file "fname" and an entry in the 'dir' or 'bdir' option "dname". +/// - If "dname" is ".", return "fname" (swapfile in dir of file). +/// - If "dname" starts with "./", insert "dname" in "fname" (swapfile relative to dir of file). +/// - Otherwise, prepend "dname" to the tail of "fname" (swapfile in specific dir). /// /// The return value is an allocated string and can be NULL. /// @@ -3212,10 +3208,10 @@ char *get_file_in_dir(char *fname, char *dname) return retval; } -/// Print the ATTENTION message: info about an existing swap file. +/// Print the ATTENTION message: info about an existing swapfile. /// /// @param buf buffer being edited -/// @param fname swap file name +/// @param fname swapfile name static void attention_message(buf_T *buf, char *fname) { assert(buf->b_fname != NULL); @@ -3299,7 +3295,7 @@ static int do_swapexists(buf_T *buf, char *fname) return 0; } -/// Find out what name to use for the swap file for buffer 'buf'. +/// Find out what name to use for the swapfile for buffer 'buf'. /// /// Several names are tried to find one that does not exist. Last directory in /// option is automatically created. @@ -3308,20 +3304,20 @@ static int do_swapexists(buf_T *buf, char *fname) /// not being able to open the swap or undo file. /// @note May trigger SwapExists autocmd, pointers may change! /// -/// @param[in] buf Buffer for which swap file names needs to be found. +/// @param[in] buf Buffer for which swapfile names needs to be found. /// @param[in,out] dirp Pointer to a list of directories. When out of memory, /// is set to NULL. Is advanced to the next directory in /// the list otherwise. -/// @param[in] old_fname Allowed existing swap file name. Except for this +/// @param[in] old_fname Allowed existing swapfile name. Except for this /// case, name of the non-existing file is used. /// @param[in,out] found_existing_dir If points to true, then new directory -/// for swap file is not created. At first +/// for swapfile is not created. At first /// findswapname() call this argument must /// point to false. This parameter may only /// be set to true by this function, it is /// never set to false. /// -/// @return [allocated] Name of the swap file. +/// @return [allocated] Name of the swapfile. static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_existing_dir) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4) { @@ -3333,7 +3329,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ char *dir_name = xmalloc(dir_len); (void)copy_option_part(dirp, dir_name, dir_len, ","); - // we try different names until we find one that does not exist yet + // We try different swapfile names until we find one that does not exist yet. char *fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); while (true) { @@ -3346,7 +3342,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ break; } // check if the swapfile already exists - // Extra security check: When a swap file is a symbolic link, this + // Extra security check: When a swapfile is a symbolic link, this // is most likely a symlink attack. FileInfo file_info; bool file_or_link_found = os_fileinfo_link(fname, &file_info); @@ -3365,17 +3361,17 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ // Give an error message, unless recovering, no file name, we are // viewing a help file or when the path of the file is different // (happens when all .swp files are in one directory). - if (!recoverymode && buf_fname != NULL - && !buf->b_help && !(buf->b_flags & BF_DUMMY)) { + if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) { int fd; ZeroBlock b0; int differ = false; - // Try to read block 0 from the swap file to get the original - // file name (and inode number). + // Try to read block 0 from the swapfile to get the original file name (and inode number). fd = os_open(fname, O_RDONLY, 0); if (fd >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { + process_running = swapfile_process_running(&b0, fname); + // If the swapfile has the same directory as the // buffer don't compare the directory names, they can // have a different mountpoint. @@ -3393,8 +3389,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ } } } else { - // The name in the swap file may be - // "~user/path/file". Expand it first. + // The name in the swapfile may be "~user/path/file". Expand it first. expand_env(b0.b0_fname, NameBuff, MAXPATHL); if (fnamecmp_ino(buf->b_ffname, NameBuff, char_to_long(b0.b0_ino))) { @@ -3405,16 +3400,16 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ close(fd); } - // give the ATTENTION message when there is an old swap file - // for the current file, and the buffer was not recovered. + // Show the ATTENTION message when: + // - there is an old swapfile for the current file + // - the buffer was not recovered if (differ == false && !(curbuf->b_flags & BF_RECOVERED) && vim_strchr(p_shm, SHM_ATTENTION) == NULL) { int choice = 0; - process_still_running = false; - // It's safe to delete the swap file if all these are true: + // It's safe to delete the swapfile if all these are true: // - the edited file exists - // - the swap file has no changes and looks OK + // - the swapfile has no changes and looks OK if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) { choice = 4; if (p_verbose > 0) { @@ -3430,8 +3425,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ choice = do_swapexists(buf, fname); } + process_running = 0; // Set by attention_message..swapfile_info. if (choice == 0) { - // Show info about the existing swap file. + // Show info about the existing swapfile. attention_message(buf, fname); // We don't want a 'q' typed at the more-prompt @@ -3459,15 +3455,15 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ xstrlcat(name, sw_msg_2, name_len); choice = do_dialog(VIM_WARNING, _("VIM - ATTENTION"), name, - process_still_running + process_running ? _("&Open Read-Only\n&Edit anyway\n&Recover" "\n&Quit\n&Abort") : _("&Open Read-Only\n&Edit anyway\n&Recover" "\n&Delete it\n&Quit\n&Abort"), 1, NULL, false); - if (process_still_running && choice >= 4) { - choice++; // Skip missing "Delete it" button. + if (process_running && choice >= 4) { + choice++; // Skip missing "Delete it" button. } xfree(name); @@ -3477,27 +3473,27 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ if (choice > 0) { switch (choice) { - case 1: + case 1: // "Open Read-Only" buf->b_p_ro = true; break; - case 2: + case 2: // "Edit anyway" break; - case 3: + case 3: // "Recover" swap_exists_action = SEA_RECOVER; break; - case 4: + case 4: // "Delete it" os_remove(fname); break; - case 5: + case 5: // "Quit" swap_exists_action = SEA_QUIT; break; - case 6: + case 6: // "Abort" swap_exists_action = SEA_QUIT; got_int = true; break; } - // If the file was deleted this fname can be used. + // If the swapfile was deleted this `fname` can be used. if (!os_path_exists(fname)) { break; } @@ -3512,10 +3508,10 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ } } - // Change the ".swp" extension to find another file that can be used. + // Permute the ".swp" extension to find a unique swapfile name. // First decrement the last char: ".swo", ".swn", etc. // If that still isn't enough decrement the last but one char: ".svz" - // Can happen when editing many "No Name" buffers. + // Can happen when many Nvim instances are editing the same file (including "No Name" buffers). if (fname[n - 1] == 'a') { // ".s?a" if (fname[n - 2] == 'a') { // ".saa": tried enough, give up emsg(_("E326: Too many swap files found")); @@ -3553,7 +3549,7 @@ static int b0_magic_wrong(ZeroBlock *b0p) || b0p->b0_magic_char != B0_MAGIC_CHAR; } -/// Compare current file name with file name from swap file. +/// Compare current file name with file name from swapfile. /// Try to use inode numbers when possible. /// Return non-zero when files are different. /// @@ -3563,7 +3559,7 @@ static int b0_magic_wrong(ZeroBlock *b0p) /// because the device number cannot be used over a network. /// - When a file does not exist yet (editing a new file) there is no inode /// number. -/// - The file name in a swap file may not be valid on the current host. The +/// - The file name in a swapfile may not be valid on the current host. The /// "~user" form is used whenever possible to avoid this. /// /// This is getting complicated, let's make a table: @@ -3577,7 +3573,7 @@ static int b0_magic_wrong(ZeroBlock *b0p) /// == 0 X OK OK fname_c != fname_s /// X == 0 OK OK fname_c != fname_s /// -/// current file doesn't exist, file for swap file exist, file name(s) not +/// current file doesn't exist, file for swapfile exist, file name(s) not /// available -> probably different /// == 0 != 0 FAIL X true /// == 0 != 0 X FAIL true @@ -3600,11 +3596,11 @@ static int b0_magic_wrong(ZeroBlock *b0p) /// without making the block 0 incompatible with 32 bit versions. /// /// @param fname_c current file name -/// @param fname_s file name from swap file +/// @param fname_s file name from swapfile static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) { uint64_t ino_c = 0; // ino of current file - uint64_t ino_s; // ino of file from swap file + uint64_t ino_s; // ino of file from swapfile char buf_c[MAXPATHL]; // full path of fname_c char buf_s[MAXPATHL]; // full path of fname_s int retval_c; // flag: buf_c valid @@ -3616,7 +3612,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) } // First we try to get the inode from the file name, because the inode in - // the swap file may be outdated. If that fails (e.g. this path is not + // the swapfile may be outdated. If that fails (e.g. this path is not // valid on this machine), use the inode from block 0. if (os_fileinfo(fname_s, &file_info)) { ino_s = os_fileinfo_inode(&file_info); @@ -3638,7 +3634,7 @@ static bool fnamecmp_ino(char *fname_c, char *fname_s, long ino_block0) // Can't compare inodes or file names, guess that the files are different, // unless both appear not to exist at all, then compare with the file name - // in the swap file. + // in the swapfile. if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL) { return strcmp(fname_c, fname_s) != 0; } @@ -3675,7 +3671,7 @@ static long char_to_long(const char *s_in) return retval; } -/// Set the flags in the first block of the swap file: +/// Set the flags in the first block of the swapfile: /// - file is modified or not: buf->b_changed /// - 'fileformat' /// - 'fileencoding' diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 2204a0a291..4a72464527 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -12,6 +12,7 @@ #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" @@ -365,7 +366,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const menu->en_name = NULL; menu->en_dname = NULL; } - menu->priority = pri_tab[pri_idx]; + menu->priority = (int)pri_tab[pri_idx]; menu->parent = parent; // Add after menu that has lower priority. @@ -658,7 +659,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) dict_T *dict = tv_dict_alloc(); tv_dict_add_str(dict, S_LEN("name"), menu->dname); - tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority); + tv_dict_add_nr(dict, S_LEN("priority"), menu->priority); tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname)); if (menu->mnemonic) { @@ -1846,7 +1847,7 @@ static void menuitem_getinfo(const char *menu_name, const vimmenu_T *menu, int m if (menu->actext != NULL) { tv_dict_add_str(dict, S_LEN("accel"), menu->actext); } - tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority); + tv_dict_add_nr(dict, S_LEN("priority"), menu->priority); tv_dict_add_str(dict, S_LEN("modes"), get_menu_mode_str(menu->modes)); char buf[NUMBUFLEN]; diff --git a/src/nvim/menu.h b/src/nvim/menu.h index 32959cf35f..8d7a98d270 100644 --- a/src/nvim/menu.h +++ b/src/nvim/menu.h @@ -3,6 +3,7 @@ #include <stdbool.h> +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/menu_defs.h" #include "nvim/types.h" diff --git a/src/nvim/menu_defs.h b/src/nvim/menu_defs.h index 79b267ae49..1e010c07ba 100644 --- a/src/nvim/menu_defs.h +++ b/src/nvim/menu_defs.h @@ -52,7 +52,7 @@ struct VimMenu { char *en_dname; ///< NULL when "dname" untranslated int mnemonic; ///< mnemonic key (after '&') char *actext; ///< accelerator text (after TAB) - long priority; ///< Menu order priority + int priority; ///< Menu order priority char *strings[MENU_MODES]; ///< Mapped string for each mode int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode bool silent[MENU_MODES]; ///< A silent flag for each mode diff --git a/src/nvim/message.c b/src/nvim/message.c index 68ea49d53b..67266b325c 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -475,7 +475,14 @@ void trunc_string(const char *s, char *buf, int room_in, int buflen) } } -// Note: Caller of smsg() must check the resulting string is shorter than IOSIZE!!! +/// Shows a printf-style message with attributes. +/// +/// Note: Caller must check the resulting string is shorter than IOSIZE!!! +/// +/// @see semsg +/// @see swmsg +/// +/// @param s printf-style format message int smsg(int attr, const char *s, ...) FUNC_ATTR_PRINTF(2, 3) { @@ -757,6 +764,8 @@ void emsg_invreg(int name) } /// Print an error message with unknown number of arguments +/// +/// @return whether the message was displayed bool semsg(const char *const fmt, ...) FUNC_ATTR_PRINTF(1, 2) { @@ -1025,10 +1034,9 @@ int delete_first_msg(void) } /// :messages command implementation -void ex_messages(void *const eap_p) +void ex_messages(exarg_T *eap) FUNC_ATTR_NONNULL_ALL { - const exarg_T *const eap = (const exarg_T *)eap_p; struct msg_hist *p; if (strcmp(eap->arg, "clear") == 0) { @@ -1347,7 +1355,7 @@ bool messaging(void) return !(p_lz && char_avail() && !KeyTyped) && (p_ch > 0 || ui_has(kUIMessages)); } -void msgmore(long n) +void msgmore(int n) { long pn; @@ -1481,11 +1489,11 @@ void msg_putchar_attr(int c, int attr) msg_puts_attr(buf, attr); } -void msg_outnum(long n) +void msg_outnum(int n) { char buf[20]; - snprintf(buf, sizeof(buf), "%ld", n); + snprintf(buf, sizeof(buf), "%d", n); msg_puts(buf); } @@ -1962,40 +1970,6 @@ void msg_prt_line(const char *s, int list) msg_clr_eos(); } -/// Use grid_puts() to output one multi-byte character. -/// -/// @return the pointer "s" advanced to the next character. -static const char *screen_puts_mbyte(const char *s, int l, int attr) -{ - int cw; - attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); - - msg_didout = true; // remember that line is not empty - cw = utf_ptr2cells(s); - if (cw > 1 - && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) { - // Doesn't fit, print a highlighted '>' to fill it up. - msg_screen_putchar('>', HL_ATTR(HLF_AT)); - return s; - } - - grid_puts(&msg_grid_adj, s, l, msg_row, msg_col, attr); - if (cmdmsg_rl) { - msg_col -= cw; - if (msg_col == 0) { - msg_col = Columns; - msg_row++; - } - } else { - msg_col += cw; - if (msg_col >= Columns) { - msg_col = 0; - msg_row++; - } - } - return s + l; -} - /// Output a string to the screen at position msg_row, msg_col. /// Update msg_row and msg_col for the next message. void msg_puts(const char *s) @@ -2133,14 +2107,8 @@ static void msg_ext_emit_chunk(void) static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) { const char *s = str; - const char *t_s = str; // String from "t_s" to "s" is still todo. - int t_col = 0; // Screen cells todo, 0 when "t_s" not used. - int l; - int cw; const char *sb_str = str; int sb_col = msg_col; - int wrap; - int did_last_char; did_wait_return = false; @@ -2156,175 +2124,143 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) return; } + int print_attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); msg_grid_validate(); cmdline_was_last_drawn = redrawing_cmdline; - while ((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL) { - // We are at the end of the screen line when: - // - When outputting a newline. - // - When outputting a character in the last column. - if (!recurse && msg_row >= Rows - 1 - && (*s == '\n' || (cmdmsg_rl - ? (msg_col <= 1 - || (*s == TAB && msg_col <= 7) - || (utf_ptr2cells(s) > 1 - && msg_col <= 2)) - : ((*s != '\r' && msg_col + t_col >= Columns - 1) - || (*s == TAB - && msg_col + t_col >= ((Columns - 1) & ~7)) - || (utf_ptr2cells(s) > 1 - && msg_col + t_col >= Columns - 2))))) { - // The screen is scrolled up when at the last row (some terminals - // scroll automatically, some don't. To avoid problems we scroll - // ourselves). - if (t_col > 0) { - // output postponed text - t_puts(&t_col, t_s, s, attr); - } + int msg_row_pending = -1; - // When no more prompt and no more room, truncate here + while (true) { + if (cmdmsg_rl ? msg_col <= 0 : msg_col >= Columns) { + if (p_more && !recurse) { + // Store text for scrolling back. + store_sb_text(&sb_str, s, attr, &sb_col, true); + } if (msg_no_more && lines_left == 0) { break; } - // Scroll the screen up one line. - bool has_last_char = ((uint8_t)(*s) >= ' ' && !cmdmsg_rl); - msg_scroll_up(!has_last_char, false); + msg_col = cmdmsg_rl ? Columns - 1 : 0; + msg_row++; + msg_didout = false; + } - msg_row = Rows - 2; - if (msg_col >= Columns) { // can happen after screen resize - msg_col = Columns - 1; - } + if (msg_row >= Rows) { + msg_row = Rows - 1; - // Display char in last column before showing more-prompt. - if (has_last_char) { - if (maxlen >= 0) { - // Avoid including composing chars after the end. - l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); - } else { - l = utfc_ptr2len(s); - } - s = screen_puts_mbyte(s, l, attr); - did_last_char = true; - } else { - did_last_char = false; + // When no more prompt and no more room, truncate here + if (msg_no_more && lines_left == 0) { + break; } - // Tricky: if last cell will be written, delay the throttle until - // after the first scroll. Otherwise we would need to keep track of it. - if (has_last_char && msg_do_throttle()) { - if (!msg_grid.throttled) { - msg_grid_scroll_discount++; + if (!recurse) { + if (msg_row_pending >= 0) { + grid_line_flush_if_valid_row(); + msg_row_pending = -1; } - msg_grid.throttled = true; - } - if (p_more) { - // Store text for scrolling back. - store_sb_text(&sb_str, s, attr, &sb_col, true); - } + // Scroll the screen up one line. + msg_scroll_up(true, false); - inc_msg_scrolled(); - need_wait_return = true; // may need wait_return() in main() - redraw_cmdline = true; - if (cmdline_row > 0 && !exmode_active) { - cmdline_row--; - } - - // If screen is completely filled and 'more' is set then wait - // for a character. - if (lines_left > 0) { - lines_left--; - } - if (p_more && lines_left == 0 && State != MODE_HITRETURN - && !msg_no_more && !exmode_active) { - if (do_more_prompt(NUL)) { - s = confirm_msg_tail; + inc_msg_scrolled(); + need_wait_return = true; // may need wait_return() in main() + redraw_cmdline = true; + if (cmdline_row > 0 && !exmode_active) { + cmdline_row--; } - if (quit_more) { - return; + + // If screen is completely filled and 'more' is set then wait + // for a character. + if (lines_left > 0) { + lines_left--; } - } - // When we displayed a char in last column need to check if there - // is still more. - if (did_last_char) { - continue; + if (p_more && lines_left == 0 && State != MODE_HITRETURN + && !msg_no_more && !exmode_active) { + if (do_more_prompt(NUL)) { + s = confirm_msg_tail; + } + if (quit_more) { + return; + } + } } } - wrap = *s == '\n' - || msg_col + t_col >= Columns - || (utf_ptr2cells(s) > 1 - && msg_col + t_col >= Columns - 1) - ; - if (t_col > 0 && (wrap || *s == '\r' || *s == '\b' - || *s == '\t' || *s == BELL)) { - // Output any postponed text. - t_puts(&t_col, t_s, s, attr); + if (!((maxlen < 0 || (int)(s - str) < maxlen) && *s != NUL)) { + break; } - if (wrap && p_more && !recurse) { - // Store text for scrolling back. - store_sb_text(&sb_str, s, attr, &sb_col, true); + if (msg_row != msg_row_pending && ((uint8_t)(*s) >= 0x20 || *s == TAB)) { + // TODO(bfredl): this logic is messier that it has to be. What + // messages really want is its own private linebuf_char buffer. + if (msg_row_pending >= 0) { + grid_line_flush_if_valid_row(); + } + grid_line_start(&msg_grid_adj, msg_row); + msg_row_pending = msg_row; } - if (*s == '\n') { // go to next line - msg_didout = false; // remember that line is empty - if (cmdmsg_rl) { - msg_col = Columns - 1; + if ((uint8_t)(*s) >= 0x20) { // printable char + int cw = utf_ptr2cells(s); + // avoid including composing chars after the end + int l = (maxlen >= 0) ? utfc_ptr2len_len(s, (int)((str + maxlen) - s)) : utfc_ptr2len(s); + + if (cw > 1 && (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) { + // Doesn't fit, print a highlighted '>' to fill it up. + grid_line_puts(msg_col, ">", 1, HL_ATTR(HLF_AT)); + cw = 1; } else { - msg_col = 0; + grid_line_puts(msg_col, s, l, print_attr); + s += l; } - if (++msg_row >= Rows) { // safety check - msg_row = Rows - 1; - } - } else if (*s == '\r') { // go to column 0 - msg_col = 0; - } else if (*s == '\b') { // go to previous char - if (msg_col) { - msg_col--; - } - } else if (*s == TAB) { // translate Tab into spaces - do { - msg_screen_putchar(' ', attr); - } while (msg_col & 7); - } else if (*s == BELL) { // beep (from ":sh") - vim_beep(BO_SH); - } else if ((uint8_t)(*s) >= 0x20) { // printable char - cw = utf_ptr2cells(s); - if (maxlen >= 0) { - // avoid including composing chars after the end - l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); + msg_didout = true; // remember that line is not empty + if (cmdmsg_rl) { + msg_col -= cw; } else { - l = utfc_ptr2len(s); + msg_col += cw; } - // When drawing from right to left or when a double-wide character - // doesn't fit, draw a single character here. Otherwise collect - // characters and draw them all at once later. - if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) { - if (l > 1) { - s = screen_puts_mbyte(s, l, attr) - 1; + } else { + char c = *s++; + if (c == '\n') { // go to next line + msg_didout = false; // remember that line is empty + if (cmdmsg_rl) { + msg_col = Columns - 1; } else { - msg_screen_putchar(*s, attr); + msg_col = 0; } - } else { - // postpone this character until later - if (t_col == 0) { - t_s = s; + msg_row++; + if (p_more && !recurse) { + // Store text for scrolling back. + store_sb_text(&sb_str, s, attr, &sb_col, true); + } + } else if (c == '\r') { // go to column 0 + msg_col = 0; + } else if (c == '\b') { // go to previous char + if (msg_col) { + msg_col--; } - t_col += cw; - s += l - 1; + } else if (c == TAB) { // translate Tab into spaces + do { + grid_line_puts(msg_col, " ", 1, print_attr); + msg_col += cmdmsg_rl ? -1 : 1; + + if (msg_col == (cmdmsg_rl ? 0 : Columns)) { + break; + } + } while (msg_col & 7); + } else if (c == BELL) { // beep (from ":sh") + vim_beep(BO_SH); } } - s++; } - // Output any postponed text. - if (t_col > 0) { - t_puts(&t_col, t_s, s, attr); + if (msg_row_pending >= 0) { + grid_line_flush_if_valid_row(); } + msg_cursor_goto(msg_row, msg_col); + if (p_more && !recurse && !(s == sb_str + 1 && *sb_str == '\n')) { store_sb_text(&sb_str, s, attr, &sb_col, false); } @@ -2332,6 +2268,13 @@ static void msg_puts_display(const char *str, int maxlen, int attr, int recurse) msg_check(); } +void msg_cursor_goto(int row, int col) +{ + ScreenGrid *grid = &msg_grid_adj; + grid_adjust(&grid, &row, &col); + ui_grid_cursor_goto(grid->handle, row, col); +} + /// @return true when ":filter pattern" was used and "msg" does not match /// "pattern". bool message_filtered(const char *msg) @@ -2508,6 +2451,9 @@ static void store_sb_text(const char **sb_str, const char *s, int attr, int *sb_ || do_clear_sb_text == SB_CLEAR_CMDLINE_DONE) { clear_sb_text(do_clear_sb_text == SB_CLEAR_ALL); msg_sb_eol(); // prevent messages from overlapping + if (do_clear_sb_text == SB_CLEAR_CMDLINE_DONE && s > *sb_str && **sb_str == '\n') { + (*sb_str)++; + } do_clear_sb_text = SB_CLEAR_NONE; } @@ -2655,9 +2601,6 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) msg_row = row; msg_col = mp->sb_msg_col; char *p = mp->sb_text; - if (*p == '\n') { // don't display the line break - p++; - } msg_puts_display(p, -1, mp->sb_attr, true); if (mp->sb_eol || mp->sb_next == NULL) { break; @@ -2668,26 +2611,6 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) return mp->sb_next; } -/// Output any postponed text for msg_puts_len(). -static void t_puts(int *t_col, const char *t_s, const char *s, int attr) -{ - attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); - // Output postponed text. - msg_didout = true; // Remember that line is not empty. - grid_puts(&msg_grid_adj, t_s, (int)(s - t_s), msg_row, msg_col, attr); - msg_col += *t_col; - *t_col = 0; - // If the string starts with a composing character don't increment the - // column position for it. - if (utf_iscomposing(utf_ptr2char(t_s))) { - msg_col--; - } - if (msg_col >= Columns) { - msg_col = 0; - msg_row++; - } -} - /// @return true when messages should be printed to stdout/stderr: /// - "batch mode" ("silent mode", -es/-Es) /// - no UI and not embedded @@ -3034,37 +2957,17 @@ void os_msg(const char *str) } #endif // MSWIN -/// Put a character on the screen at the current message position and advance -/// to the next position. Only for printable ASCII! -static void msg_screen_putchar(int c, int attr) -{ - attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); - msg_didout = true; // remember that line is not empty - grid_putchar(&msg_grid_adj, c, msg_row, msg_col, attr); - if (cmdmsg_rl) { - if (--msg_col == 0) { - msg_col = Columns; - msg_row++; - } - } else { - if (++msg_col >= Columns) { - msg_col = 0; - msg_row++; - } - } -} - void msg_moremsg(int full) { - int attr; - char *s = _("-- More --"); - - attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M)); - grid_puts(&msg_grid_adj, s, -1, Rows - 1, 0, attr); + int attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M)); + grid_line_start(&msg_grid_adj, Rows - 1); + int len = grid_line_puts(0, _("-- More --"), -1, attr); if (full) { - grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), -1, - Rows - 1, vim_strsize(s), attr); + len += grid_line_puts(len, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "), + -1, attr); } + grid_line_cursor_goto(len); + grid_line_flush(); } /// Repeat the message for the current mode: MODE_ASKMORE, MODE_EXTERNCMD, @@ -3443,9 +3346,22 @@ void give_warning(const char *message, bool hl) no_wait_return--; } -void give_warning2(const char *const message, const char *const a1, bool hl) +/// Shows a warning, with optional highlighting. +/// +/// @param hl enable highlighting +/// @param fmt printf-style format message +/// +/// @see smsg +/// @see semsg +void swmsg(bool hl, const char *const fmt, ...) + FUNC_ATTR_PRINTF(2, 3) { - vim_snprintf(IObuff, IOSIZE, message, a1); + va_list args; + + va_start(args, fmt); + vim_vsnprintf(IObuff, IOSIZE, fmt, args); + va_end(args); + give_warning(IObuff, hl); } diff --git a/src/nvim/message.h b/src/nvim/message.h index 78cd5b7d0e..6fc6674cc2 100644 --- a/src/nvim/message.h +++ b/src/nvim/message.h @@ -7,6 +7,7 @@ #include "klib/kvec.h" #include "nvim/api/private/defs.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/grid_defs.h" #include "nvim/macros.h" #include "nvim/types.h" diff --git a/src/nvim/move.c b/src/nvim/move.c index 8efb038a55..8eaf40d135 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -32,6 +32,7 @@ #include "nvim/grid.h" #include "nvim/highlight.h" #include "nvim/macros.h" +#include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/message.h" @@ -1312,7 +1313,6 @@ bool scrolldown(long line_count, int byfold) } if (col > width2 && width2 > 0) { row += (int)col / width2; - col = col % width2; } if (row >= curwin->w_height_inner) { curwin->w_curswant = curwin->w_virtcol - (row - curwin->w_height_inner + 1) * width2; @@ -1517,7 +1517,6 @@ void adjust_skipcol(void) } if (col > width2) { row += (int)col / width2; - col = col % width2; } if (row >= curwin->w_height_inner) { if (curwin->w_skipcol == 0) { @@ -2455,11 +2454,11 @@ int onepage(Direction dir, long count) if (curwin->w_topfill == loff.fill) { curwin->w_topline--; curwin->w_topfill = 0; + curwin->w_valid &= ~(VALID_WROW|VALID_CROW); } comp_botline(curwin); curwin->w_cursor.lnum = curwin->w_botline - 1; - curwin->w_valid &= - ~(VALID_WCOL | VALID_CHEIGHT | VALID_WROW | VALID_CROW); + curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|VALID_CROW); } else { curwin->w_topline = loff.lnum; curwin->w_topfill = loff.fill; @@ -2684,6 +2683,15 @@ void halfpage(bool flag, linenr_T Prenum) void do_check_cursorbind(void) { + static win_T *prev_curwin = NULL; + static pos_T prev_cursor = { 0, 0, 0 }; + + if (curwin == prev_curwin && equalpos(curwin->w_cursor, prev_cursor)) { + return; + } + prev_curwin = curwin; + prev_cursor = curwin->w_cursor; + linenr_T line = curwin->w_cursor.lnum; colnr_T col = curwin->w_cursor.col; colnr_T coladd = curwin->w_cursor.coladd; @@ -2699,7 +2707,7 @@ void do_check_cursorbind(void) FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { curwin = wp; curbuf = curwin->w_buffer; - // skip original window and windows with 'noscrollbind' + // skip original window and windows with 'nocursorbind' if (curwin != old_curwin && curwin->w_p_crb) { if (curwin->w_p_diff) { curwin->w_cursor.lnum = diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 715b98377a..1a212661c5 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1735,13 +1735,13 @@ static void prep_redo_cmd(cmdarg_T *cap) /// Prepare for redo of any command. /// Note that only the last argument can be a multi-byte char. -void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) +void prep_redo(int regname, int num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { prep_redo_num2(regname, num, cmd1, cmd2, 0L, cmd3, cmd4, cmd5); } /// Prepare for redo of any command with extra count after "cmd2". -void prep_redo_num2(int regname, long num1, int cmd1, int cmd2, long num2, int cmd3, int cmd4, +void prep_redo_num2(int regname, int num1, int cmd1, int cmd2, int num2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); diff --git a/src/nvim/normal.h b/src/nvim/normal.h index b9fdd21652..3d7782c05c 100644 --- a/src/nvim/normal.h +++ b/src/nvim/normal.h @@ -39,7 +39,7 @@ typedef struct oparg_S { pos_T end; // end of the operator pos_T cursor_start; // cursor position before motion for "gw" - long line_count; // number of lines from op_start to op_end + linenr_T line_count; // number of lines from op_start to op_end // (inclusive) bool empty; // op_start and op_end the same (only used by // op_change()) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5d3e285e3b..348a86a0f6 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -217,7 +217,7 @@ int get_extra_op_char(int optype) /// handle a shift operation void op_shift(oparg_T *oap, int curs_top, int amount) { - long i; + int i; int block_col = 0; if (u_save((linenr_T)(oap->start.lnum - 1), @@ -292,7 +292,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount) /// @param call_changed_bytes call changed_bytes() void shift_line(int left, int round, int amount, int call_changed_bytes) { - const int sw_val = (int)get_sw_value_indent(curbuf); + const int sw_val = get_sw_value_indent(curbuf); int count = get_indent(); // get current indent @@ -338,7 +338,7 @@ static void shift_block(oparg_T *oap, int amount) const int oldstate = State; char *newp; const int oldcol = curwin->w_cursor.col; - const int sw_val = (int)get_sw_value_indent(curbuf); + const int sw_val = get_sw_value_indent(curbuf); const int ts_val = (int)curbuf->b_p_ts; struct block_def bd; int incr; @@ -634,7 +634,7 @@ static void block_insert(oparg_T *oap, char *s, int b_insert, struct block_def * /// Handle reindenting a block of lines. void op_reindent(oparg_T *oap, Indenter how) { - long i = 0; + int i = 0; linenr_T first_changed = 0; linenr_T last_changed = 0; linenr_T start_lnum = curwin->w_cursor.lnum; @@ -647,8 +647,8 @@ void op_reindent(oparg_T *oap, Indenter how) // Save for undo. Do this once for all lines, much faster than doing this // for each line separately, especially when undoing. - if (u_savecommon(curbuf, start_lnum - 1, start_lnum + (linenr_T)oap->line_count, - start_lnum + (linenr_T)oap->line_count, false) == OK) { + if (u_savecommon(curbuf, start_lnum - 1, start_lnum + oap->line_count, + start_lnum + oap->line_count, false) == OK) { char *l; int amount; for (i = oap->line_count - 1; i >= 0 && !got_int; i--) { @@ -693,7 +693,7 @@ void op_reindent(oparg_T *oap, Indenter how) // there is no change still need to remove the Visual highlighting. if (last_changed != 0) { changed_lines(curbuf, first_changed, 0, - oap->is_VIsual ? start_lnum + (linenr_T)oap->line_count : + oap->is_VIsual ? start_lnum + oap->line_count : last_changed + 1, 0L, true); } else if (oap->is_VIsual) { redraw_curbuf_later(UPD_INVERTED); @@ -1717,8 +1717,8 @@ int op_delete(oparg_T *oap) pos_T curpos; // save deleted and changed lines for undo - if (u_save((linenr_T)(curwin->w_cursor.lnum - 1), - (linenr_T)(curwin->w_cursor.lnum + oap->line_count)) == FAIL) { + if (u_save(curwin->w_cursor.lnum - 1, + curwin->w_cursor.lnum + oap->line_count) == FAIL) { return FAIL; } @@ -2204,7 +2204,7 @@ bool swapchar(int op_type, pos_T *pos) /// Insert and append operators for Visual mode. void op_insert(oparg_T *oap, long count1) { - long pre_textlen = 0; + int pre_textlen = 0; char *firstline; colnr_T ind_pre_col = 0; int ind_pre_vcol = 0; @@ -2251,7 +2251,7 @@ void op_insert(oparg_T *oap, long count1) if (oap->op_type == OP_APPEND) { firstline += bd.textlen; } - pre_textlen = (long)strlen(firstline); + pre_textlen = (int)strlen(firstline); } if (oap->op_type == OP_APPEND) { @@ -2400,7 +2400,7 @@ void op_insert(oparg_T *oap, long count1) } else { firstline += add; } - long ins_len = (long)strlen(firstline) - pre_textlen - offset; + 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); // block handled here @@ -2421,8 +2421,8 @@ void op_insert(oparg_T *oap, long count1) int op_change(oparg_T *oap) { int retval; - long pre_textlen = 0; - long pre_indent = 0; + int pre_textlen = 0; + int pre_indent = 0; char *firstline; struct block_def bd; @@ -2456,8 +2456,8 @@ int op_change(oparg_T *oap) coladvance_force(getviscol()); } firstline = ml_get(oap->start.lnum); - pre_textlen = (long)strlen(firstline); - pre_indent = (long)getwhitecols(firstline); + pre_textlen = (int)strlen(firstline); + pre_indent = (int)getwhitecols(firstline); bd.textcol = curwin->w_cursor.col; } @@ -2478,25 +2478,25 @@ int op_change(oparg_T *oap) // Don't repeat the insert when Insert mode ended with CTRL-C. if (oap->motion_type == kMTBlockWise && oap->start.lnum != oap->end.lnum && !got_int) { - long ins_len; + int ins_len; // Auto-indenting may have changed the indent. If the cursor was past // the indent, exclude that indent change from the inserted text. firstline = ml_get(oap->start.lnum); if (bd.textcol > (colnr_T)pre_indent) { - long new_indent = (long)getwhitecols(firstline); + int new_indent = (int)getwhitecols(firstline); pre_textlen += new_indent - pre_indent; bd.textcol += (colnr_T)(new_indent - pre_indent); } - ins_len = (long)strlen(firstline) - pre_textlen; + ins_len = (int)strlen(firstline) - pre_textlen; if (ins_len > 0) { long offset; char *newp; char *oldp; // Subsequent calls to ml_get() flush the firstline data - take a // copy of the inserted text. - char *ins_text = xmalloc((size_t)(ins_len + 1)); + char *ins_text = xmalloc((size_t)ins_len + 1); xstrlcpy(ins_text, firstline + bd.textcol, (size_t)ins_len + 1); for (linenr_T linenr = oap->start.lnum + 1; linenr <= oap->end.lnum; linenr++) { @@ -2526,7 +2526,7 @@ int op_change(oparg_T *oap) STRMOVE(newp + offset, oldp); ml_replace(linenr, newp, false); extmark_splice_cols(curbuf, (int)linenr - 1, bd.textcol, - 0, vpos.coladd + (int)ins_len, kExtmarkUndo); + 0, vpos.coladd + ins_len, kExtmarkUndo); } } check_cursor(); @@ -2903,7 +2903,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) /// PUT_LINE force linewise put (":put") /// PUT_BLOCK_INNER in block mode, do not add trailing spaces /// @param dir BACKWARD for 'P', FORWARD for 'p' -void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) +void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) { char *ptr; char *newp; @@ -3320,7 +3320,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) break; } - totlen = (size_t)(count * (yanklen + spaces) + bd.startspaces + bd.endspaces); + totlen = (size_t)count * (size_t)(yanklen + spaces) + (size_t)bd.startspaces + + (size_t)bd.endspaces; newp = xmalloc(totlen + oldlen + 1); // copy part up to cursor to new line @@ -3446,7 +3447,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // multiplication overflow emsg(_(e_resulting_text_too_long)); } else { - totlen = (size_t)(count * yanklen); + totlen = (size_t)count * (size_t)yanklen; do { oldp = ml_get(lnum); oldlen = strlen(oldp); @@ -4166,7 +4167,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions // have moved up (last line deleted), so the current lnum is kept in t. t = curwin->w_cursor.lnum; curwin->w_cursor.lnum++; - del_lines((long)count - 1, false); + del_lines((int)count - 1, false); curwin->w_cursor.lnum = t; curbuf_splice_pending--; curbuf->deleted_bytes2 = 0; @@ -5569,7 +5570,7 @@ static void op_colon(oparg_T *oap) if (oap->start.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); } else { - stuffnumReadbuff((long)oap->start.lnum); + stuffnumReadbuff(oap->start.lnum); } // When using !! on a closed fold the range ".!" works best to operate @@ -5590,7 +5591,7 @@ static void op_colon(oparg_T *oap) stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); } else { - stuffnumReadbuff((long)oap->end.lnum); + stuffnumReadbuff(oap->end.lnum); } } } @@ -5996,7 +5997,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) resel_VIsual_vcol = oap->end_vcol; } } - resel_VIsual_line_count = (linenr_T)oap->line_count; + resel_VIsual_line_count = oap->line_count; } // can't redo yank (unless 'y' is in 'cpoptions') and ":" diff --git a/src/nvim/option.c b/src/nvim/option.c index eef5e66aeb..c0353e52be 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -40,6 +40,7 @@ #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor_shape.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" @@ -136,12 +137,12 @@ static char *p_vsts_nopaste; #define OPTION_COUNT ARRAY_SIZE(options) +/// :set boolean option prefix typedef enum { - OP_NONE = 0, - OP_ADDING, ///< "opt+=arg" - OP_PREPENDING, ///< "opt^=arg" - OP_REMOVING, ///< "opt-=arg" -} set_op_T; + PREFIX_NO = 0, ///< "no" prefix + PREFIX_NONE, ///< no prefix + PREFIX_INV, ///< "inv" prefix +} set_prefix_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" @@ -557,7 +558,7 @@ static char *find_dup_item(char *origval, const char *newval, uint32_t flags) /// Set the Vi-default value of a number option. /// Used for 'lines' and 'columns'. -void set_number_default(char *name, long val) +void set_number_default(char *name, OptInt val) { int opt_idx = findoption(name); if (opt_idx >= 0) { @@ -752,8 +753,8 @@ void ex_set(exarg_T *eap) (void)do_set(eap->arg, flags); } -static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, const void *varp, - const char **errmsg) +static void do_set_bool(int opt_idx, int opt_flags, set_prefix_T prefix, int nextchar, + const void *varp, const char **errmsg) { varnumber_T value; @@ -772,10 +773,12 @@ static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, co value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); } } else { - if (prefix == 2) { - value = *(int *)varp ^ 1; // ":set invopt": invert + // ":set invopt": invert + // ":set opt" or ":set noopt": set or reset + if (prefix == PREFIX_INV) { + value = *(int *)varp ^ 1; } else { - value = prefix; // ":set opt" or ":set noopt": set or reset + value = prefix == PREFIX_NO ? 0 : 1; } } @@ -797,7 +800,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co // other error arg++; if (nextchar == '&') { - value = (long)(intptr_t)options[opt_idx].def_val; + value = (varnumber_T)options[opt_idx].def_val; } else if (nextchar == '<') { if ((OptInt *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) { // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global value @@ -843,7 +846,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co if (op == OP_REMOVING) { value = *(OptInt *)varp - value; } - *errmsg = set_num_option(opt_idx, (void *)varp, (long)value, + *errmsg = set_num_option(opt_idx, (void *)varp, value, errbuf, errbuflen, opt_flags); } @@ -888,7 +891,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op, // For MS-Windows backslashes before normal file name characters // are not removed, and keep backslash at start, for "\\machine\path", // but do remove it for "\\\\machine\\path". - // The reverse is found in ExpandOldSetting(). + // The reverse is found in escape_option_str_cmdline(). while (*arg != NUL && !ascii_iswhite(*arg)) { if (*arg == '\\' && arg[1] != NUL #ifdef BACKSLASH_IN_FILENAME @@ -1168,7 +1171,7 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne // be triggered that can cause havoc. *errmsg = did_set_string_option(curbuf, curwin, opt_idx, (char **)varp, oldval, errbuf, errbuflen, - opt_flags, value_checked); + opt_flags, op, value_checked); secure = secure_saved; @@ -1204,17 +1207,17 @@ static set_op_T get_op(const char *arg) return op; } -static int get_option_prefix(char **argp) +static set_prefix_T get_option_prefix(char **argp) { if (strncmp(*argp, "no", 2) == 0) { *argp += 2; - return 0; + return PREFIX_NO; } else if (strncmp(*argp, "inv", 3) == 0) { *argp += 3; - return 2; + return PREFIX_INV; } - return 1; + return PREFIX_NONE; } /// @param[in] arg Pointer to start option name @@ -1273,11 +1276,11 @@ static int parse_option_name(char *arg, int *keyp, int *lenp, int *opt_idxp) return OK; } -static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags, int prefix, - const char **errmsg) +static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t flags, + set_prefix_T prefix, const char **errmsg) { // Only bools can have a prefix of 'inv' or 'no' - if (!(flags & P_BOOL) && prefix != 1) { + if (!(flags & P_BOOL) && prefix != PREFIX_NONE) { *errmsg = e_invarg; return FAIL; } @@ -1325,8 +1328,8 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla return OK; } -static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int prefix, int nextchar, - set_op_T op, uint32_t flags, void *varp, char *errbuf, +static void do_set_option_value(int opt_idx, int opt_flags, char **argp, set_prefix_T prefix, + int nextchar, set_op_T op, uint32_t flags, void *varp, char *errbuf, size_t errbuflen, const char **errmsg) { bool value_checked = false; @@ -1355,7 +1358,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb size_t errbuflen, const char **errmsg) { // 1: nothing, 0: "no", 2: "inv" in front of name - int prefix = get_option_prefix(argp); + set_prefix_T prefix = get_option_prefix(argp); char *arg = *argp; @@ -1434,7 +1437,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb // '=' character per "set" command line. grrr. (jw) // if (nextchar == '?' - || (prefix == 1 + || (prefix == PREFIX_NONE && vim_strchr("=:&<", nextchar) == NULL && !(flags & P_BOOL))) { // print value @@ -1976,8 +1979,8 @@ void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx) } /// Apply the OptionSet autocommand. -static void apply_optionset_autocmd(int opt_idx, long opt_flags, OptInt oldval, OptInt oldval_g, - long newval, const char *errmsg) +static void apply_optionset_autocmd(int opt_idx, int opt_flags, OptInt oldval, OptInt oldval_g, + OptInt newval, const char *errmsg) { // Don't do this while starting up, failure or recursively. if (starting || errmsg != NULL || *get_vim_var_str(VV_OPTION_TYPE) != NUL) { @@ -1988,7 +1991,7 @@ static void apply_optionset_autocmd(int opt_idx, long opt_flags, OptInt oldval, vim_snprintf(buf_old, sizeof(buf_old), "%" PRId64, oldval); vim_snprintf(buf_old_global, sizeof(buf_old_global), "%" PRId64, oldval_g); - vim_snprintf(buf_new, sizeof(buf_new), "%ld", newval); + vim_snprintf(buf_new, sizeof(buf_new), "%" PRId64, newval); vim_snprintf(buf_type, sizeof(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); @@ -2899,9 +2902,9 @@ static const char *set_bool_option(const int opt_idx, char *const varp, const in options[opt_idx].flags |= P_WAS_SET; apply_optionset_autocmd(opt_idx, opt_flags, - (long)(old_value ? true : false), - (long)(old_global_value ? true : false), - (long)(value ? true : false), NULL); + (old_value ? true : false), + (old_global_value ? true : false), + (value ? true : false), NULL); if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -2921,8 +2924,8 @@ static const char *set_bool_option(const int opt_idx, char *const varp, const in } /// Check the bounds of numeric options. -static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long old_Rows, - char *errbuf, size_t errbuflen, const char *errmsg) +static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, int old_Rows, char *errbuf, + size_t errbuflen, const char *errmsg) { // Check the (new) bounds for Rows and Columns here. if (p_lines < min_rows() && full_screen) { @@ -2997,9 +3000,9 @@ static const char *check_num_option_bounds(OptInt *pp, OptInt old_value, long ol } /// Options that need some validation. -static const char *validate_num_option(const OptInt *pp, long *valuep) +static const char *validate_num_option(const OptInt *pp, OptInt *valuep) { - long value = *valuep; + OptInt value = *valuep; // Many number options assume their value is in the signed int range. if (value < INT_MIN || value > INT_MAX) { @@ -3157,12 +3160,12 @@ static const char *validate_num_option(const OptInt *pp, long *valuep) /// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE. /// /// @return NULL on success, error message on error. -static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf, +static const char *set_num_option(int opt_idx, void *varp, OptInt value, char *errbuf, size_t errbuflen, int opt_flags) { OptInt old_value = *(OptInt *)varp; OptInt old_global_value = 0; // only used when setting a local and global option - long old_Rows = Rows; // remember old Rows + int old_Rows = Rows; // remember old Rows OptInt *pp = (OptInt *)varp; // Disallow changing some options from secure mode. @@ -3184,7 +3187,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err return errmsg; } - *pp = (OptInt)value; + *pp = value; // Remember where the option was set. set_option_sctx_idx(opt_idx, opt_flags, current_sctx); @@ -3195,7 +3198,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err .os_varp = varp, .os_flags = opt_flags, .os_oldval.number = old_value, - .os_newval.number = (OptInt)value, + .os_newval.number = value, .os_errbuf = NULL, .os_errbuflen = 0, .os_buf = curbuf, @@ -3215,7 +3218,7 @@ static const char *set_num_option(int opt_idx, void *varp, long value, char *err options[opt_idx].flags |= P_WAS_SET; apply_optionset_autocmd(opt_idx, opt_flags, old_value, old_global_value, - value, errmsg); + (int)value, errmsg); if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -3786,7 +3789,7 @@ static const char *set_option(int opt_idx, void *varp, OptVal *v, int opt_flags, if (v->type == kOptValTypeBoolean) { errmsg = set_bool_option(opt_idx, varp, (int)v->data.boolean, opt_flags); } else if (v->type == kOptValTypeNumber) { - errmsg = set_num_option(opt_idx, varp, (long)v->data.number, errbuf, errbuflen, opt_flags); + errmsg = set_num_option(opt_idx, varp, v->data.number, errbuf, errbuflen, opt_flags); } else if (v->type == kOptValTypeString) { errmsg = set_string_option(opt_idx, varp, v->data.string.data, opt_flags, &value_checked, errbuf, errbuflen); @@ -5305,8 +5308,10 @@ void set_imsearch_global(buf_T *buf) } static int expand_option_idx = -1; +static int expand_option_start_col = 0; static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL }; static int expand_option_flags = 0; +static bool expand_option_append = false; /// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) @@ -5405,7 +5410,15 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) } } // handle "-=" and "+=" + expand_option_append = false; + bool expand_option_subtract = false; if ((nextchar == '-' || nextchar == '+' || nextchar == '^') && p[1] == '=') { + if (nextchar == '-') { + expand_option_subtract = true; + } + if (nextchar == '+' || nextchar == '^') { + expand_option_append = true; + } p++; nextchar = '='; } @@ -5414,28 +5427,51 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) xp->xp_context = EXPAND_UNSUCCESSFUL; return; } - if (p[1] == NUL) { - xp->xp_context = EXPAND_OLD_SETTING; - if (is_term_option) { - expand_option_idx = -1; - } else { - expand_option_idx = opt_idx; - } - xp->xp_pattern = p + 1; - return; - } - xp->xp_context = EXPAND_NOTHING; - if (is_term_option || (flags & P_NUM)) { - return; + + // Below are for handling expanding a specific option's value after the '=' or ':' + + if (is_term_option) { + expand_option_idx = -1; + } else { + expand_option_idx = opt_idx; } xp->xp_pattern = p + 1; + expand_option_start_col = (int)(p + 1 - xp->xp_line); + // Certain options currently have special case handling to reuse the + // expansion logic with other commands. if (options[opt_idx].var == &p_syn) { xp->xp_context = EXPAND_OWNSYNTAX; return; } + if (options[opt_idx].var == &p_ft) { + xp->xp_context = EXPAND_FILETYPE; + return; + } + // Now pick. If the option has a custom expander, use that. Otherwise, just + // fill with the existing option value. + if (expand_option_subtract) { + xp->xp_context = EXPAND_SETTING_SUBTRACT; + return; + } else if (expand_option_idx >= 0 + && options[expand_option_idx].opt_expand_cb != NULL) { + xp->xp_context = EXPAND_STRING_SETTING; + } else if (*xp->xp_pattern == NUL) { + xp->xp_context = EXPAND_OLD_SETTING; + return; + } else { + xp->xp_context = EXPAND_NOTHING; + } + + if (is_term_option || (flags & P_NUM)) { + return; + } + + // Only string options below + + // Options that have P_EXPAND are considered to all use file/dir expansion. if (flags & P_EXPAND) { p = options[opt_idx].var; if (p == (char *)&p_bdir @@ -5451,8 +5487,6 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) } else { xp->xp_backslash = XP_BS_ONE; } - } else if (p == (char *)&p_ft) { - xp->xp_context = EXPAND_FILETYPE; } else { xp->xp_context = EXPAND_FILES; // for 'tags' need three backslashes for a space @@ -5464,27 +5498,45 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) } } - // For an option that is a list of file names, find the start of the - // last file name. - for (p = arg + strlen(arg) - 1; p > xp->xp_pattern; p--) { - // count number of backslashes before ' ' or ',' - if (*p == ' ' || *p == ',') { - char *s = p; - while (s > xp->xp_pattern && *(s - 1) == '\\') { - s--; - } - if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3)) - || (*p == ',' && (flags & P_COMMA) && ((p - s) & 1) == 0)) { - xp->xp_pattern = p + 1; - break; + // For an option that is a list of file names, or comma/colon-separated + // values, split it by the delimiter and find the start of the current + // pattern, while accounting for backslash-escaped space/commas/colons. + // Triple-backslashed escaped file names (e.g. 'path') can also be + // delimited by space. + if ((flags & P_EXPAND) || (flags & P_COMMA) || (flags & P_COLON)) { + for (p = arg + strlen(arg) - 1; p > xp->xp_pattern; p--) { + // count number of backslashes before ' ' or ',' + if (*p == ' ' || *p == ',' || (*p == ':' && (flags & P_COLON))) { + char *s = p; + while (s > xp->xp_pattern && *(s - 1) == '\\') { + s--; + } + if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3)) + || (*p == ',' && (flags & P_COMMA) && ((p - s) % 1) == 0) + || (*p == ':' && (flags & P_COLON))) { + xp->xp_pattern = p + 1; + break; + } } } + } - // for 'spellsuggest' start at "file:" - if (options[opt_idx].var == &p_sps - && strncmp(p, "file:", 5) == 0) { - xp->xp_pattern = p + 5; - break; + // An option that is a list of single-character flags should always start + // at the end as we don't complete words. + if (flags & P_FLAGLIST) { + xp->xp_pattern = arg + strlen(arg); + } + + // Some options can either be using file/dir expansions, or custom value + // expansion depending on what the user typed. Unfortunately we have to + // manually handle it here to make sure we have the correct xp_context set. + // for 'spellsuggest' start at "file:" + if (options[opt_idx].var == &p_sps) { + if (strncmp(xp->xp_pattern, "file:", 5) == 0) { + xp->xp_pattern += 5; + return; + } else if (options[expand_option_idx].opt_expand_cb != NULL) { + xp->xp_context = EXPAND_STRING_SETTING; } } } @@ -5609,7 +5661,33 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, char *fuzzystr, int *numM return OK; } -void ExpandOldSetting(int *numMatches, char ***matches) +/// Escape an option value that can be used on the command-line with :set. +/// Caller needs to free the returned string, unless NULL is returned. +static char *escape_option_str_cmdline(char *var) +{ + // A backslash is required before some characters. This is the reverse of + // what happens in do_set(). + char *buf = vim_strsave_escaped(var, escape_chars); + +#ifdef BACKSLASH_IN_FILENAME + // For MS-Windows et al. we don't double backslashes at the start and + // before a file name character. + // The reverse is found at stropt_copy_value(). + for (var = buf; *var != NUL; MB_PTR_ADV(var)) { + if (var[0] == '\\' && var[1] == '\\' + && expand_option_idx >= 0 + && (options[expand_option_idx].flags & P_EXPAND) + && vim_isfilec((uint8_t)var[2]) + && (var[2] != '\\' || (var == buf && var[4] != '\\'))) { + STRMOVE(var, var + 1); + } + } +#endif + return buf; +} + +/// Expansion handler for :set= when we just want to fill in with the existing value. +int ExpandOldSetting(int *numMatches, char ***matches) { char *var = NULL; @@ -5629,26 +5707,149 @@ void ExpandOldSetting(int *numMatches, char ***matches) var = ""; } - // A backslash is required before some characters. This is the reverse of - // what happens in do_set(). - char *buf = vim_strsave_escaped(var, escape_chars); + char *buf = escape_option_str_cmdline(var); -#ifdef BACKSLASH_IN_FILENAME - // For MS-Windows et al. we don't double backslashes at the start and - // before a file name character. - for (var = buf; *var != NUL; MB_PTR_ADV(var)) { - if (var[0] == '\\' && var[1] == '\\' - && expand_option_idx >= 0 - && (options[expand_option_idx].flags & P_EXPAND) - && vim_isfilec((uint8_t)var[2]) - && (var[2] != '\\' || (var == buf && var[4] != '\\'))) { - STRMOVE(var, var + 1); + (*matches)[0] = buf; + *numMatches = 1; + return OK; +} + +/// Expansion handler for :set=/:set+= when the option has a custom expansion handler. +int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches) +{ + if (expand_option_idx < 0 + || options[expand_option_idx].opt_expand_cb == NULL) { + // Not supposed to reach this. This function is only for options with + // custom expansion callbacks. + return FAIL; + } + + optexpand_T args = { + .oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags), + .oe_append = expand_option_append, + .oe_regmatch = regmatch, + .oe_xp = xp, + .oe_set_arg = xp->xp_line + expand_option_start_col, + }; + args.oe_include_orig_val = !expand_option_append && (*args.oe_set_arg == NUL); + + // Retrieve the existing value, but escape it as a reverse of setting it. + // We technically only need to do this when oe_append or + // oe_include_orig_val is true. + option_value2string(&options[expand_option_idx], expand_option_flags); + char *var = NameBuff; + char *buf = escape_option_str_cmdline(var); + args.oe_opt_value = buf; + + int num_ret = options[expand_option_idx].opt_expand_cb(&args, numMatches, matches); + + xfree(buf); + return num_ret; +} + +/// Expansion handler for :set-= +int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, char ***matches) +{ + if (expand_option_idx < 0) { + // term option + return ExpandOldSetting(numMatches, matches); + } + + char *option_val = *(char **)get_option_varp_scope_from(expand_option_idx, + expand_option_flags, + curbuf, curwin); + + uint32_t option_flags = options[expand_option_idx].flags; + + if (option_flags & P_NUM) { + return ExpandOldSetting(numMatches, matches); + } else if (option_flags & P_COMMA) { + // Split the option by comma, then present each option to the user if + // it matches the pattern. + // This condition needs to go first, because 'whichwrap' has both + // P_COMMA and P_FLAGLIST. + + if (*option_val == NUL) { + return FAIL; } + + // Make a copy as we need to inject null characters destructively. + char *option_copy = xstrdup(option_val); + char *next_val = option_copy; + + garray_T ga; + ga_init(&ga, sizeof(char *), 10); + + do { + char *item = next_val; + char *comma = vim_strchr(next_val, ','); + while (comma != NULL && comma != next_val && *(comma - 1) == '\\') { + // "\," is interpreted as a literal comma rather than option + // separator when reading options in copy_option_part(). Skip + // it. + comma = vim_strchr(comma + 1, ','); + } + if (comma != NULL) { + *comma = NUL; // null-terminate this value, required by later functions + next_val = comma + 1; + } else { + next_val = NULL; + } + + if (*item == NUL) { + // empty value, don't add to list + continue; + } + + if (!vim_regexec(regmatch, item, (colnr_T)0)) { + continue; + } + + char *buf = escape_option_str_cmdline(item); + GA_APPEND(char *, &ga, buf); + } while (next_val != NULL); + + xfree(option_copy); + + *matches = ga.ga_data; + *numMatches = ga.ga_len; + return OK; + } else if (option_flags & P_FLAGLIST) { + // Only present the flags that are set on the option as the other flags + // are not meaningful to do set-= on. + + if (*xp->xp_pattern != NUL) { + // Don't suggest anything if cmdline is non-empty. Vim's set-= + // behavior requires consecutive strings and it's usually + // unintuitive to users if ther try to subtract multiple flags at + // once. + return FAIL; + } + + size_t num_flags = strlen(option_val); + if (num_flags == 0) { + return FAIL; + } + + *matches = xmalloc(sizeof(char *) * (num_flags + 1)); + + int count = 0; + + (*matches)[count++] = xstrdup(option_val); + + if (num_flags > 1) { + // 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); + } + } + + *numMatches = count; + return OK; } -#endif - *matches[0] = buf; - *numMatches = 1; + return ExpandOldSetting(numMatches, matches); } /// Get the value for the numeric or string option///opp in a nice format into @@ -5772,6 +5973,7 @@ int fill_culopt_flags(char *val, win_T *wp) p = val; } while (*p != NUL) { + // Note: Keep this in sync with p_culopt_values. if (strncmp(p, "line", 4) == 0) { p += 4; culopt_flags_new |= CULOPT_LINE; diff --git a/src/nvim/option.h b/src/nvim/option.h index 9e3bf25bc3..593917407a 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -4,9 +4,11 @@ #include <stdint.h> #include "nvim/api/private/helpers.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/option_defs.h" +#include "nvim/search.h" /// The options that are local to a window or buffer have "indir" set to one of /// these values. Special values: @@ -45,7 +47,15 @@ typedef struct vimoption { ///< local option: indirect option index ///< callback function to invoke after an option is modified to validate and ///< apply the new value. + + /// callback function to invoke after an option is modified to validate and + /// apply the new value. opt_did_set_cb_T opt_did_set_cb; + + /// callback function to invoke when expanding possible values on the + /// cmdline. Only useful for string options. + opt_expand_cb_T opt_expand_cb; + void *def_val; ///< default values for variable (neovim!!) LastSet last_set; ///< script in which the option was last set } vimoption_T; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index f078f6073c..a11ed9188c 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -1,8 +1,12 @@ #ifndef NVIM_OPTION_DEFS_H #define NVIM_OPTION_DEFS_H +#include <stdbool.h> +#include <stddef.h> + #include "nvim/api/private/defs.h" -#include "nvim/eval/typval_defs.h" +#include "nvim/cmdexpand_defs.h" +#include "nvim/regexp_defs.h" #include "nvim/types.h" /// Option value type @@ -25,6 +29,14 @@ typedef struct { } data; } OptVal; +/// :set operator types +typedef enum { + OP_NONE = 0, + OP_ADDING, ///< "opt+=arg" + OP_PREPENDING, ///< "opt^=arg" + OP_REMOVING, ///< "opt-=arg" +} set_op_T; + /// Argument for the callback function (opt_did_set_cb_T) invoked after an /// option value is modified. typedef struct { @@ -33,6 +45,7 @@ typedef struct { void *os_varp; int os_idx; int os_flags; + set_op_T os_op; /// old value of the option (can be a string, number or a boolean) union { @@ -80,4 +93,40 @@ typedef struct { /// Otherwise returns an error message. typedef const char *(*opt_did_set_cb_T)(optset_T *args); +/// Argument for the callback function (opt_expand_cb_T) invoked after a string +/// option value is expanded for cmdline completion. +typedef struct { + /// Pointer to the option variable. It's always a string. + char *oe_varp; + /// The original option value, escaped. + char *oe_opt_value; + + /// true if using set+= instead of set= + bool oe_append; + /// true if we would like to add the original option value as the first choice. + bool oe_include_orig_val; + + /// Regex from the cmdline, for matching potential options against. + regmatch_T *oe_regmatch; + /// The expansion context. + expand_T *oe_xp; + + /// The full argument passed to :set. For example, if the user inputs + /// ":set dip=icase,algorithm:my<Tab>", oe_xp->xp_pattern will only have + /// "my", but oe_set_arg will contain the whole "icase,algorithm:my". + char *oe_set_arg; +} optexpand_T; + +/// Type for the callback function that is invoked when expanding possible +/// string option values during cmdline completion. +/// +/// Strings in returned matches will be managed and freed by caller. +/// +/// Returns OK if the expansion succeeded (numMatches and matches have to be +/// set). Otherwise returns FAIL. +/// +/// Note: If returned FAIL or *numMatches is 0, *matches will NOT be freed by +/// caller. +typedef int (*opt_expand_cb_T)(optexpand_T *args, int *numMatches, char ***matches); + #endif // NVIM_OPTION_DEFS_H diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index d8bbce21b3..86e7f4cee8 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -16,6 +16,7 @@ ///< the same. #define P_EXPAND 0x10U ///< environment expansion. NOTE: P_EXPAND can ///< never be used for local or hidden options +#define P_NO_DEF_EXP 0x20U ///< do not expand default value #define P_NODEFAULT 0x40U ///< don't set to default value #define P_DEF_ALLOCED 0x80U ///< default value is in allocated memory, must ///< use free() when assigning new value @@ -51,8 +52,10 @@ #define P_RWINONLY 0x10000000U ///< only redraw current window #define P_MLE 0x20000000U ///< under control of 'modelineexpr' #define P_FUNC 0x40000000U ///< accept a function reference or a lambda - -#define P_NO_DEF_EXP 0x80000000U ///< Do not expand default value. +#define P_COLON 0x80000000U ///< values use colons to create sublists +// Warning: Currently we have used all 32 bits for option flags, and adding more +// flags will overflow it. Adding another flag will need to change how +// it's stored first. #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ @@ -183,7 +186,7 @@ #define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_" // characters for p_ww option: -#define WW_ALL "bshl<>[],~" +#define WW_ALL "bshl<>[]~" // characters for p_mouse option: #define MOUSE_NORMAL 'n' // use mouse in Normal mode @@ -757,9 +760,9 @@ extern char *p_vfile; ///< 'verbosefile' EXTERN int p_warn; ///< 'warn' EXTERN char *p_wop; ///< 'wildoptions' EXTERN unsigned wop_flags; -#define WOP_TAGFILE 0x01 -#define WOP_PUM 0x02 -#define WOP_FUZZY 0x04 +#define WOP_FUZZY 0x01 +#define WOP_TAGFILE 0x02 +#define WOP_PUM 0x04 EXTERN OptInt p_window; ///< 'window' EXTERN char *p_wak; ///< 'winaltkeys' EXTERN char *p_wig; ///< 'wildignore' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index cd1d760836..e70fe8614f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -6,7 +6,7 @@ --- @field varname? string --- @field pv_name? string --- @field type 'bool'|'number'|'string' ---- @field list? 'comma'|'onecomma'|'flags'|'flagscomma' +--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma' --- @field scope vim.option_scope[] --- @field deny_duplicates? boolean --- @field enable_if? string|false @@ -25,6 +25,7 @@ --- @field alloced? true --- @field redraw? vim.option_redraw[] --- @field cb? string +--- @field expand_cb? string --- @field tags? string[] --- @class vim.option_defaults @@ -192,6 +193,7 @@ return { set to one of CJK locales. See Unicode Standard Annex #11 (https://www.unicode.org/reports/tr11). ]=], + expand_cb = 'expand_set_ambiwidth', full_name = 'ambiwidth', redraw = { 'all_windows', 'ui_option' }, scope = { 'global' }, @@ -331,6 +333,7 @@ return { option, you must load syntax.vim again to see the result. This can be done with ":syntax on". ]=], + expand_cb = 'expand_set_background', full_name = 'background', scope = { 'global' }, short_desc = N_('"dark" or "light", used for highlight colors'), @@ -357,6 +360,7 @@ return { When the value is empty, Vi compatible backspacing is used, none of the ways mentioned for the items above are possible. ]=], + expand_cb = 'expand_set_backspace', full_name = 'backspace', list = 'onecomma', scope = { 'global' }, @@ -453,6 +457,7 @@ return { the system may refuse to do this. In that case the "auto" value will again not rename the file. ]=], + expand_cb = 'expand_set_backupcopy', full_name = 'backupcopy', list = 'onecomma', scope = { 'global', 'buffer' }, @@ -621,6 +626,7 @@ return { indicate that an error occurred. It can be silenced by adding the "error" keyword. ]=], + expand_cb = 'expand_set_belloff', full_name = 'belloff', list = 'comma', scope = { 'global' }, @@ -763,6 +769,7 @@ return { added for the 'showbreak' setting. (default: off) ]=], + expand_cb = 'expand_set_breakindentopt', full_name = 'breakindentopt', list = 'onecomma', redraw = { 'current_buffer' }, @@ -816,6 +823,7 @@ return { This option is used together with 'buftype' and 'swapfile' to specify special kinds of buffers. See |special-buffers|. ]=], + expand_cb = 'expand_set_bufhidden', full_name = 'bufhidden', noglob = true, scope = { 'buffer' }, @@ -893,6 +901,7 @@ return { without saving. For writing there must be matching |BufWriteCmd|, |FileWriteCmd| or |FileAppendCmd| autocommands. ]=], + expand_cb = 'expand_set_buftype', full_name = 'buftype', noglob = true, scope = { 'buffer' }, @@ -917,6 +926,7 @@ return { case mapping, the current locale is not effective. This probably only matters for Turkish. ]=], + expand_cb = 'expand_set_casemap', full_name = 'casemap', list = 'onecomma', scope = { 'global' }, @@ -1183,6 +1193,7 @@ return { "*". See |clipboard|. ]=], deny_duplicates = true, + expand_cb = 'expand_set_clipboard', full_name = 'clipboard', list = 'onecomma', scope = { 'global' }, @@ -1369,6 +1380,7 @@ return { based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns |i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions). ]=], + expand_cb = 'expand_set_complete', full_name = 'complete', list = 'onecomma', scope = { 'buffer' }, @@ -1399,7 +1411,9 @@ return { Keep in mind that the cursor position is not always where it's displayed. E.g., when moving vertically it may change column. ]=], + expand_cb = 'expand_set_concealcursor', full_name = 'concealcursor', + list = 'flags', redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('whether concealable text is hidden in cursor line'), @@ -1492,6 +1506,7 @@ return { select one from the menu. Only works in combination with "menu" or "menuone". ]=], + expand_cb = 'expand_set_completeopt', full_name = 'completeopt', list = 'onecomma', scope = { 'global' }, @@ -1517,6 +1532,7 @@ return { command line completion the global value is used. ]=], enable_if = 'BACKSLASH_IN_FILENAME', + expand_cb = 'expand_set_completeslash', full_name = 'completeslash', scope = { 'buffer' }, type = 'string', @@ -1797,6 +1813,7 @@ return { _ When using |cw| on a word, do not include the whitespace following the word in the motion. ]=], + expand_cb = 'expand_set_cpoptions', full_name = 'cpoptions', list = 'flags', redraw = { 'all_windows' }, @@ -1878,6 +1895,7 @@ return { "line" and "screenline" cannot be used together. ]=], + expand_cb = 'expand_set_cursorlineopt', full_name = 'cursorlineopt', list = 'onecomma', redraw = { 'current_window_only' }, @@ -1900,6 +1918,7 @@ return { "msg" and "throw" are useful for debugging 'foldexpr', 'formatexpr' or 'indentexpr'. ]=], + expand_cb = 'expand_set_debug', full_name = 'debug', scope = { 'global' }, short_desc = N_('to "msg" to see all error messages'), @@ -2048,7 +2067,8 @@ return { When omitted a context of six lines is used. When using zero the context is actually one, since folds require a line in between, also - for a deleted line. + for a deleted line. Set it to a very large + value (999999) to disable folding completely. See |fold-diff|. iblank Ignore changes where lines are all blank. Adds @@ -2140,8 +2160,9 @@ return { :set diffopt-=internal " do NOT use the internal diff parser < ]=], + expand_cb = 'expand_set_diffopt', full_name = 'diffopt', - list = 'onecomma', + list = 'onecommacolon', redraw = { 'current_window' }, scope = { 'global' }, short_desc = N_('options for using diff mode'), @@ -2242,6 +2263,7 @@ return { The "@" character can be changed by setting the "lastline" item in 'fillchars'. The character is highlighted with |hl-NonText|. ]=], + expand_cb = 'expand_set_display', full_name = 'display', list = 'onecomma', redraw = { 'all_windows' }, @@ -2260,6 +2282,7 @@ return { hor horizontally, height of windows is not affected both width and height of windows is affected ]=], + expand_cb = 'expand_set_eadirection', full_name = 'eadirection', scope = { 'global' }, short_desc = N_("in which direction 'equalalways' works"), @@ -2470,6 +2493,7 @@ return { :set ei=WinEnter,WinLeave < ]=], + expand_cb = 'expand_set_eventignore', full_name = 'eventignore', list = 'onecomma', scope = { 'global' }, @@ -2558,6 +2582,7 @@ return { This option cannot be changed when 'modifiable' is off. ]=], + expand_cb = 'expand_set_encoding', full_name = 'fileencoding', no_mkrc = true, redraw = { 'statuslines', 'current_buffer' }, @@ -2619,6 +2644,7 @@ return { Setting this option does not have an effect until the next time a file is read. ]=], + expand_cb = 'expand_set_encoding', full_name = 'fileencodings', list = 'onecomma', scope = { 'global' }, @@ -2651,6 +2677,7 @@ return { option is set, because the file would be different when written. This option cannot be changed when 'modifiable' is off. ]=], + expand_cb = 'expand_set_fileformat', full_name = 'fileformat', no_mkrc = true, redraw = { 'curswant', 'statuslines' }, @@ -2714,6 +2741,7 @@ return { used. Also see |file-formats|. ]=], + expand_cb = 'expand_set_fileformat', full_name = 'fileformats', list = 'onecomma', scope = { 'global' }, @@ -2846,6 +2874,7 @@ return { eob EndOfBuffer |hl-EndOfBuffer| lastline NonText |hl-NonText| ]=], + expand_cb = 'expand_set_chars_option', full_name = 'fillchars', list = 'onecomma', redraw = { 'current_window' }, @@ -2884,6 +2913,7 @@ return { its level is higher than 'foldlevel'. Useful if you want folds to automatically close when moving out of them. ]=], + expand_cb = 'expand_set_foldclose', full_name = 'foldclose', list = 'onecomma', redraw = { 'current_window' }, @@ -2906,6 +2936,7 @@ return { "[1-9]": to display a fixed number of columns See |folding|. ]=], + expand_cb = 'expand_set_foldcolumn', full_name = 'foldcolumn', redraw = { 'current_window' }, scope = { 'window' }, @@ -3045,6 +3076,7 @@ return { |fold-syntax| syntax Syntax highlighting items specify folds. |fold-diff| diff Fold text that is not changed. ]=], + expand_cb = 'expand_set_foldmethod', full_name = 'foldmethod', redraw = { 'current_window' }, scope = { 'window' }, @@ -3122,6 +3154,7 @@ return { To close folds you can re-apply 'foldlevel' with the |zx| command or set the 'foldclose' option to "all". ]=], + expand_cb = 'expand_set_foldopen', full_name = 'foldopen', list = 'onecomma', redraw = { 'curswant' }, @@ -3218,6 +3251,7 @@ return { To avoid problems with flags that are added in the future, use the "+=" and "-=" feature of ":set" |add-option-flags|. ]=], + expand_cb = 'expand_set_formatoptions', full_name = 'formatoptions', list = 'flags', scope = { 'buffer' }, @@ -4050,6 +4084,7 @@ return { 'redrawtime') then 'inccommand' is automatically disabled until |Command-line-mode| is done. ]=], + expand_cb = 'expand_set_inccommand', full_name = 'inccommand', scope = { 'global' }, short_desc = N_('Live preview of substitution'), @@ -4263,9 +4298,9 @@ return { defaults = { condition = 'BACKSLASH_IN_FILENAME', if_false = '@,48-57,/,.,-,_,+,,,#,$,%,~,=', - if_true = '@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=', + if_true = '@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=', doc = [[for Windows: - "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=" + "@,48-57,/,\,.,-,_,+,,,#,$,%,{,},[,],@-@,!,~,=" otherwise: "@,48-57,/,.,-,_,+,,,#,$,%,~,="]], }, deny_duplicates = true, @@ -4451,6 +4486,7 @@ return { |alternate-file| or using |mark-motions| try to restore the |mark-view| in which the action occurred. ]=], + expand_cb = 'expand_set_jumpoptions', full_name = 'jumpoptions', list = 'onecomma', scope = { 'global' }, @@ -4495,6 +4531,7 @@ return { Special keys in this context are the cursor keys, <End>, <Home>, <PageUp> and <PageDown>. ]=], + expand_cb = 'expand_set_keymodel', full_name = 'keymodel', list = 'onecomma', scope = { 'global' }, @@ -4779,6 +4816,7 @@ return { Note that when using 'indentexpr' the `=` operator indents all the lines, otherwise the first line is not indented (Vi-compatible). ]=], + expand_cb = 'expand_set_lispoptions', full_name = 'lispoptions', list = 'onecomma', pv_name = 'p_lop', @@ -4933,6 +4971,7 @@ return { "precedes". |hl-Whitespace| for "nbsp", "space", "tab", "multispace", "lead" and "trail". ]=], + expand_cb = 'expand_set_chars_option', full_name = 'listchars', list = 'onecomma', redraw = { 'current_window' }, @@ -5015,6 +5054,7 @@ return { :set makeencoding=char " system locale is used < ]=], + expand_cb = 'expand_set_encoding', full_name = 'makeencoding', scope = { 'global', 'buffer' }, short_desc = N_('Converts the output of external commands'), @@ -5377,6 +5417,7 @@ return { 'mousehide' hide mouse pointer while typing text 'selectmode' whether to start Select mode or Visual mode ]=], + expand_cb = 'expand_set_mouse', full_name = 'mouse', list = 'flags', scope = { 'global' }, @@ -5468,6 +5509,7 @@ return { "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click) "g<RightMouse>" is "<C-RightMouse> ("CTRL-T") ]=], + expand_cb = 'expand_set_mousemodel', full_name = 'mousemodel', scope = { 'global' }, short_desc = N_('changes meaning of mouse buttons'), @@ -5513,6 +5555,7 @@ return { < Will make Nvim scroll 5 lines at a time when scrolling vertically, and scroll 2 columns at a time when scrolling horizontally. ]=], + expand_cb = 'expand_set_mousescroll', full_name = 'mousescroll', list = 'comma', scope = { 'global' }, @@ -5645,6 +5688,7 @@ return { considered decimal. This also happens for numbers that are not recognized as octal or hex. ]=], + expand_cb = 'expand_set_nrformats', full_name = 'nrformats', list = 'onecomma', scope = { 'buffer' }, @@ -6307,6 +6351,7 @@ return { This is useful for languages such as Hebrew, Arabic and Farsi. The 'rightleft' option must be set for 'rightleftcmd' to take effect. ]=], + expand_cb = 'expand_set_rightleftcmd', full_name = 'rightleftcmd', redraw = { 'current_window' }, scope = { 'window' }, @@ -6634,6 +6679,7 @@ return { When 'diff' mode is active there always is vertical scroll binding, even when "ver" isn't there. ]=], + expand_cb = 'expand_set_scrollopt', full_name = 'scrollopt', list = 'onecomma', scope = { 'global' }, @@ -6687,6 +6733,7 @@ return { backwards, you cannot include the last character of a line, when starting in Normal mode and 'virtualedit' empty. ]=], + expand_cb = 'expand_set_selection', full_name = 'selection', scope = { 'global' }, short_desc = N_('what type of selection to use'), @@ -6707,6 +6754,7 @@ return { cmd when using "v", "V" or CTRL-V See |Select-mode|. ]=], + expand_cb = 'expand_set_selectmode', full_name = 'selectmode', list = 'onecomma', scope = { 'global' }, @@ -6758,6 +6806,7 @@ return { If you leave out "options" many things won't work well after restoring the session. ]=], + expand_cb = 'expand_set_sessionoptions', full_name = 'sessionoptions', list = 'onecomma', scope = { 'global' }, @@ -7292,6 +7341,7 @@ return { shm=a Abbreviation, but no loss of information. shm=at Abbreviation, and truncate message when necessary. ]=], + expand_cb = 'expand_set_shortmess', full_name = 'shortmess', list = 'flags', scope = { 'global' }, @@ -7368,6 +7418,7 @@ return { place the text. Without a custom 'statusline' or 'tabline' it will be displayed in a convenient location. ]=], + expand_cb = 'expand_set_showcmdloc', full_name = 'showcmdloc', scope = { 'global' }, short_desc = N_('change location of partial command'), @@ -7530,6 +7581,7 @@ return { This is done in order for the signcolumn appearance not appear weird during line deletion. ]=], + expand_cb = 'expand_set_signcolumn', full_name = 'signcolumn', redraw = { 'current_window' }, scope = { 'window' }, @@ -7828,6 +7880,7 @@ return { security reasons. ]=], expand = true, + expand_cb = 'expand_set_spellsuggest', full_name = 'spellsuggest', list = 'onecomma', scope = { 'global' }, @@ -7852,7 +7905,7 @@ return { designated regions of the buffer are spellchecked in this case. ]=], - expand = true, + expand_cb = 'expand_set_spelloptions', full_name = 'spelloptions', list = 'onecomma', redraw = { 'current_buffer' }, @@ -7892,6 +7945,7 @@ return { with the previous cursor position. For "screen", the text cannot always be kept on the same screen line when 'wrap' is enabled. ]=], + expand_cb = 'expand_set_splitkeep', full_name = 'splitkeep', scope = { 'global' }, short_desc = N_('determines scroll behavior for split windows'), @@ -8326,6 +8380,7 @@ return { uselast If included, jump to the previously used window when jumping to errors with |quickfix| commands. ]=], + expand_cb = 'expand_set_switchbuf', full_name = 'switchbuf', list = 'onecomma', scope = { 'global' }, @@ -8580,6 +8635,7 @@ return { match Match case smart Ignore case unless an upper case letter is used ]=], + expand_cb = 'expand_set_tagcase', full_name = 'tagcase', scope = { 'global', 'buffer' }, short_desc = N_('how to handle case when searching in tags files'), @@ -8728,6 +8784,7 @@ return { C1 Control characters 0x80...0x9F ]=], + expand_cb = 'expand_set_termpastefilter', full_name = 'termpastefilter', list = 'onecomma', scope = { 'global' }, @@ -9275,6 +9332,7 @@ return { slash |deprecated| Always enabled. Uses "/" in filenames. unix |deprecated| Always enabled. Uses "\n" line endings. ]=], + expand_cb = 'expand_set_sessionoptions', full_name = 'viewoptions', list = 'onecomma', scope = { 'global' }, @@ -9331,6 +9389,7 @@ return { not get a warning for it. When combined with other words, "none" is ignored. ]=], + expand_cb = 'expand_set_virtualedit', full_name = 'virtualedit', list = 'onecomma', redraw = { 'curswant' }, @@ -9395,6 +9454,7 @@ return { line (not an empty line) then it will not move to the next line. This makes "dl", "cl", "yl" etc. work normally. ]=], + expand_cb = 'expand_set_whichwrap', full_name = 'whichwrap', list = 'flagscomma', scope = { 'global' }, @@ -9578,8 +9638,9 @@ return { < Complete longest common string, then list alternatives. More info here: |cmdline-completion|. ]=], + expand_cb = 'expand_set_wildmode', full_name = 'wildmode', - list = 'onecomma', + list = 'onecommacolon', scope = { 'global' }, short_desc = N_("mode for 'wildchar' command-line expansion"), type = 'string', @@ -9609,6 +9670,7 @@ return { d #define f function ]=], + expand_cb = 'expand_set_wildoptions', full_name = 'wildoptions', list = 'onecomma', scope = { 'global' }, @@ -9637,6 +9699,7 @@ return { key is never used for the menu. This option is not used for <F10>; on Win32. ]=], + expand_cb = 'expand_set_winaltkeys', full_name = 'winaltkeys', scope = { 'global' }, short_desc = N_('when the windows system handles ALT keys'), @@ -9691,7 +9754,7 @@ return { { abbreviation = 'winhl', alloced = true, - cb = 'did_set_winhl', + cb = 'did_set_winhighlight', defaults = { if_true = '' }, deny_duplicates = true, desc = [=[ @@ -9713,8 +9776,9 @@ return { set winhighlight=Normal:MyNormal,NormalNC:MyNormalNC < ]=], + expand_cb = 'expand_set_winhighlight', full_name = 'winhighlight', - list = 'onecomma', + list = 'onecommacolon', redraw = { 'current_window' }, scope = { 'window' }, short_desc = N_('Setup window-local highlights'), diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 750941da07..6f41bba99b 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -11,6 +11,8 @@ #include "nvim/autocmd.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" #include "nvim/diff.h" @@ -41,6 +43,7 @@ #include "nvim/optionstr.h" #include "nvim/os/os.h" #include "nvim/pos.h" +#include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/spell.h" #include "nvim/spellfile.h" @@ -72,12 +75,25 @@ static char *(p_bkc_values[]) = { "yes", "auto", "no", "breaksymlink", "breakhar static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete", "copy", "ctrlg", "error", "esc", "ex", "hangul", "lang", "mess", "showmatch", "operator", "register", "shell", "spell", "wildmode", NULL }; +// Note: Keep this in sync with briopt_check() +static char *(p_briopt_values[]) = { "shift:", "min:", "sbr", "list:", "column:", NULL }; +// Note: Keep this in sync with diffopt_changed() +static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase", + "iwhite", "iwhiteall", "iwhiteeol", "horizontal", "vertical", + "closeoff", "hiddenoff", "foldcolumn:", "followwrap", "internal", + "indent-heuristic", "linematch:", "algorithm:", NULL }; +static char *(p_dip_algorithm_values[]) = { "myers", "minimal", "patience", "histogram", NULL }; static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", NULL }; static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL }; +static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL }; static char *(p_cmp_values[]) = { "internal", "keepascii", NULL }; +// Note: Keep this in sync with fill_culopt_flags() +static char *(p_culopt_values[]) = { "line", "screenline", "number", "both", NULL }; static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep", NULL }; static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent", "quickfix", "search", "tag", "insert", "undo", "jump", NULL }; +// Note: Keep this in sync with spell_check_sps() +static char *(p_sps_values[]) = { "best", "fast", "double", "expr:", "file:", "timeout:", NULL }; /// Also used for 'viewoptions'! Keep in sync with SSOP_ flags. static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "localoptions", "options", "help", "blank", "globals", "slash", "unix", "sesdir", @@ -89,7 +105,9 @@ static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vspli static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL }; static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL }; static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL }; -static char *(p_wop_values[]) = { "tagfile", "pum", "fuzzy", NULL }; +// Note: Keep this in sync with check_opt_wim() +static char *(p_wim_values[]) = { "full", "longest", "list", "lastused", NULL }; +static char *(p_wop_values[]) = { "fuzzy", "tagfile", "pum", NULL }; static char *(p_wak_values[]) = { "yes", "menu", "no", NULL }; static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL }; static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL }; @@ -118,7 +136,6 @@ static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL }; -static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL }; static char *(p_spo_values[]) = { "camel", "noplainbuffer", NULL }; static char *(p_icm_values[]) = { "nosplit", "split", NULL }; static char *(p_jop_values[]) = { "stack", "view", NULL }; @@ -487,8 +504,9 @@ const char *set_string_option(const int opt_idx, void *varp_arg, const char *val secure = 1; } - const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval, errbuf, - errbuflen, opt_flags, value_checked); + const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval, + errbuf, errbuflen, + opt_flags, OP_NONE, value_checked); secure = secure_saved; @@ -659,6 +677,116 @@ static const char *did_set_option_listflag(char *val, char *flags, char *errbuf, return NULL; } +/// Expand an option that accepts a list of string values. +static int expand_set_opt_string(optexpand_T *args, char **values, size_t numValues, + int *numMatches, char ***matches) +{ + regmatch_T *regmatch = args->oe_regmatch; + bool include_orig_val = args->oe_include_orig_val; + char *option_val = args->oe_opt_value; + + // Assume numValues is small since they are fixed enums, so just allocate + // upfront instead of needing two passes to calculate output size. + *matches = xmalloc(sizeof(char *) * (numValues + 1)); + + int count = 0; + + if (include_orig_val && *option_val != NUL) { + (*matches)[count++] = xstrdup(option_val); + } + + for (char **val = values; *val != NULL; val++) { + if (include_orig_val && *option_val != NUL) { + if (strcmp(*val, option_val) == 0) { + continue; + } + } + if (vim_regexec(regmatch, *val, (colnr_T)0)) { + (*matches)[count++] = xstrdup(*val); + } + } + if (count == 0) { + XFREE_CLEAR(*matches); + return FAIL; + } + *numMatches = count; + return OK; +} + +static char *set_opt_callback_orig_option = NULL; +static char *((*set_opt_callback_func)(expand_T *, int)); + +/// Callback used by expand_set_opt_generic to also include the original value. +static char *expand_set_opt_callback(expand_T *xp, int idx) +{ + if (idx == 0) { + if (set_opt_callback_orig_option != NULL) { + return set_opt_callback_orig_option; + } else { + return ""; // empty strings are ignored + } + } + return set_opt_callback_func(xp, idx - 1); +} + +/// Expand an option with a callback that iterates through a list of possible names. +static int expand_set_opt_generic(optexpand_T *args, CompleteListItemGetter func, int *numMatches, + char ***matches) +{ + set_opt_callback_orig_option = args->oe_include_orig_val ? args->oe_opt_value : NULL; + set_opt_callback_func = func; + + // not using fuzzy as currently EXPAND_STRING_SETTING doesn't use it + ExpandGeneric("", args->oe_xp, args->oe_regmatch, matches, numMatches, + expand_set_opt_callback, false); + + set_opt_callback_orig_option = NULL; + set_opt_callback_func = NULL; + return OK; +} + +/// Expand an option which is a list of flags. +static int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatches, char ***matches) +{ + char *option_val = args->oe_opt_value; + char *cmdline_val = args->oe_set_arg; + bool append = args->oe_append; + bool include_orig_val = args->oe_include_orig_val && (*option_val != NUL); + + size_t num_flags = strlen(flags); + + // Assume we only have small number of flags, so just allocate max size. + *matches = xmalloc(sizeof(char *) * (num_flags + 1)); + + int count = 0; + + if (include_orig_val) { + (*matches)[count++] = xstrdup(option_val); + } + + for (char *flag = flags; *flag != NUL; flag++) { + if (append && vim_strchr(option_val, *flag) != NULL) { + continue; + } + + if (vim_strchr(cmdline_val, *flag) == NULL) { + if (include_orig_val && option_val[1] == NUL && *flag == option_val[0]) { + // This value is already used as the first choice as it's the + // existing flag. Just skip it to avoid duplicate. + continue; + } + (*matches)[count++] = xstrnsave(flag, 1); + } + } + + if (count == 0) { + XFREE_CLEAR(*matches); + return FAIL; + } + *numMatches = count; + return OK; +} + /// The 'ambiwidth' option is changed. const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED) { @@ -668,6 +796,15 @@ const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED) return check_chars_options(); } +int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ambw_values, + ARRAY_SIZE(p_ambw_values) - 1, + numMatches, + matches); +} + /// The 'background' option is changed. const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) { @@ -692,6 +829,15 @@ const char *did_set_background(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_background(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bg_values, + ARRAY_SIZE(p_bg_values) - 1, + numMatches, + matches); +} + /// The 'backspace' option is changed. const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) { @@ -705,6 +851,15 @@ const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_backspace(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bs_values, + ARRAY_SIZE(p_bs_values) - 1, + numMatches, + matches); +} + /// The 'backupcopy' option is changed. const char *did_set_backupcopy(optset_T *args) { @@ -739,6 +894,15 @@ const char *did_set_backupcopy(optset_T *args) return NULL; } +int expand_set_backupcopy(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bkc_values, + ARRAY_SIZE(p_bkc_values) - 1, + numMatches, + matches); +} + /// The 'backupext' or the 'patchmode' option is changed. const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED) { @@ -756,6 +920,15 @@ const char *did_set_belloff(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true); } +int expand_set_belloff(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bo_values, + ARRAY_SIZE(p_bo_values) - 1, + numMatches, + matches); +} + /// The 'breakindentopt' option is changed. const char *did_set_breakindentopt(optset_T *args) { @@ -771,6 +944,15 @@ const char *did_set_breakindentopt(optset_T *args) return NULL; } +int expand_set_breakindentopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_briopt_values, + ARRAY_SIZE(p_briopt_values) - 1, + numMatches, + matches); +} + /// The 'bufhidden' option is changed. const char *did_set_bufhidden(optset_T *args) { @@ -778,6 +960,15 @@ const char *did_set_bufhidden(optset_T *args) return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false); } +int expand_set_bufhidden(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_bufhidden_values, + ARRAY_SIZE(p_bufhidden_values) - 1, + numMatches, + matches); +} + /// The 'buftype' option is changed. const char *did_set_buftype(optset_T *args) { @@ -798,12 +989,30 @@ const char *did_set_buftype(optset_T *args) return NULL; } +int expand_set_buftype(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_buftype_values, + ARRAY_SIZE(p_buftype_values) - 1, + numMatches, + matches); +} + /// The 'casemap' option is changed. const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true); } +int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_cmp_values, + ARRAY_SIZE(p_cmp_values) - 1, + numMatches, + matches); +} + /// The global 'listchars' or 'fillchars' option is changed. static const char *did_set_global_listfillchars(win_T *win, char *val, bool opt_lcs, int opt_flags) { @@ -867,6 +1076,17 @@ const char *did_set_chars_option(optset_T *args) return errmsg; } +/// Expand 'fillchars' or 'listchars' option value. +int expand_set_chars_option(optexpand_T *args, int *numMatches, char ***matches) +{ + char **varp = (char **)args->oe_varp; + bool is_lcs = (varp == &p_lcs || varp == &curwin->w_p_lcs); + return expand_set_opt_generic(args, + is_lcs ? get_listchars_name : get_fillchars_name, + numMatches, + matches); +} + /// The 'cinoptions' option is changed. const char *did_set_cinoptions(optset_T *args FUNC_ATTR_UNUSED) { @@ -882,6 +1102,15 @@ const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true); } +int expand_set_clipboard(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_cb_values, + ARRAY_SIZE(p_cb_values) - 1, + numMatches, + matches); +} + /// The 'colorcolumn' option is changed. const char *did_set_colorcolumn(optset_T *args) { @@ -972,6 +1201,18 @@ const char *did_set_complete(optset_T *args) return NULL; } +int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_cpt_values[]) = { + ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", NULL + }; + return expand_set_opt_string(args, + p_cpt_values, + ARRAY_SIZE(p_cpt_values) - 1, + numMatches, + matches); +} + /// The 'completeopt' option is changed. const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) { @@ -982,6 +1223,15 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_completeopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_cot_values, + ARRAY_SIZE(p_cot_values) - 1, + numMatches, + matches); +} + #ifdef BACKSLASH_IN_FILENAME /// The 'completeslash' option is changed. const char *did_set_completeslash(optset_T *args) @@ -993,6 +1243,15 @@ const char *did_set_completeslash(optset_T *args) } return NULL; } + +int expand_set_completeslash(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_csl_values, + ARRAY_SIZE(p_csl_values) - 1, + numMatches, + matches); +} #endif /// The 'concealcursor' option is changed. @@ -1003,6 +1262,11 @@ const char *did_set_concealcursor(optset_T *args) return did_set_option_listflag(*varp, COCU_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_concealcursor(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, COCU_ALL, numMatches, matches); +} + /// The 'cpoptions' option is changed. const char *did_set_cpoptions(optset_T *args) { @@ -1011,12 +1275,18 @@ const char *did_set_cpoptions(optset_T *args) return did_set_option_listflag(*varp, CPO_VI, args->os_errbuf, args->os_errbuflen); } +int expand_set_cpoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, CPO_VI, numMatches, matches); +} + /// The 'cursorlineopt' option is changed. const char *did_set_cursorlineopt(optset_T *args) { win_T *win = (win_T *)args->os_win; char **varp = (char **)args->os_varp; + // This could be changed to use opt_strings_flags() instead. if (**varp == NUL || fill_culopt_flags(*varp, win) != OK) { return e_invarg; } @@ -1024,12 +1294,30 @@ const char *did_set_cursorlineopt(optset_T *args) return NULL; } +int expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_culopt_values, + ARRAY_SIZE(p_culopt_values) - 1, + numMatches, + matches); +} + /// The 'debug' option is changed. const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_debug, p_debug_values, false); } +int expand_set_debug(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_debug_values, + ARRAY_SIZE(p_debug_values) - 1, + numMatches, + matches); +} + /// The 'diffopt' option is changed. const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) { @@ -1039,6 +1327,31 @@ const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches) +{ + expand_T *xp = args->oe_xp; + + if (xp->xp_pattern > args->oe_set_arg && *(xp->xp_pattern - 1) == ':') { + // Within "algorithm:", we have a subgroup of possible options. + const size_t algo_len = strlen("algorithm:"); + if (xp->xp_pattern - args->oe_set_arg >= (int)algo_len + && strncmp(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0) { + return expand_set_opt_string(args, + p_dip_algorithm_values, + ARRAY_SIZE(p_dip_algorithm_values) - 1, + numMatches, + matches); + } + return FAIL; + } + + return expand_set_opt_string(args, + p_dip_values, + ARRAY_SIZE(p_dip_values) - 1, + numMatches, + matches); +} + /// The 'display' option is changed. const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) { @@ -1050,12 +1363,30 @@ const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_display(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_dy_values, + ARRAY_SIZE(p_dy_values) - 1, + numMatches, + matches); +} + /// The 'eadirection' option is changed. const char *did_set_eadirection(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_ead, p_ead_values, false); } +int expand_set_eadirection(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ead_values, + ARRAY_SIZE(p_ead_values) - 1, + numMatches, + matches); +} + /// One of the 'encoding', 'fileencoding' or 'makeencoding' /// options is changed. const char *did_set_encoding(optset_T *args) @@ -1098,6 +1429,11 @@ const char *did_set_encoding(optset_T *args) return NULL; } +int expand_set_encoding(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_generic(args, get_encoding_name, numMatches, matches); +} + /// The 'eventignore' option is changed. const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) { @@ -1107,6 +1443,21 @@ const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +static char *get_eventignore_name(expand_T *xp, int idx) +{ + // 'eventignore' allows special keyword "all" in addition to + // all event names. + if (idx == 0) { + return "all"; + } + return get_event_name_no_group(xp, idx - 1); +} + +int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_generic(args, get_eventignore_name, numMatches, matches); +} + /// The 'fileformat' option is changed. const char *did_set_fileformat(optset_T *args) { @@ -1130,6 +1481,15 @@ const char *did_set_fileformat(optset_T *args) return NULL; } +int expand_set_fileformat(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ff_values, + ARRAY_SIZE(p_ff_values) - 1, + numMatches, + matches); +} + /// The 'fileformats' option is changed. const char *did_set_fileformats(optset_T *args) { @@ -1160,6 +1520,15 @@ const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_fcl, p_fcl_values, true); } +int expand_set_foldclose(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fcl_values, + ARRAY_SIZE(p_fcl_values) - 1, + numMatches, + matches); +} + /// The 'foldcolumn' option is changed. const char *did_set_foldcolumn(optset_T *args) { @@ -1170,6 +1539,15 @@ const char *did_set_foldcolumn(optset_T *args) return NULL; } +int expand_set_foldcolumn(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fdc_values, + ARRAY_SIZE(p_fdc_values) - 1, + numMatches, + matches); +} + /// The 'foldexpr' option is changed. const char *did_set_foldexpr(optset_T *args) { @@ -1229,12 +1607,30 @@ const char *did_set_foldmethod(optset_T *args) return NULL; } +int expand_set_foldmethod(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fdm_values, + ARRAY_SIZE(p_fdm_values) - 1, + numMatches, + matches); +} + /// The 'foldopen' option is changed. const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true); } +int expand_set_foldopen(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_fdo_values, + ARRAY_SIZE(p_fdo_values) - 1, + numMatches, + matches); +} + /// The 'formatoptions' option is changed. const char *did_set_formatoptions(optset_T *args) { @@ -1243,6 +1639,11 @@ const char *did_set_formatoptions(optset_T *args) return did_set_option_listflag(*varp, FO_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_formatoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, FO_ALL, numMatches, matches); +} + /// The 'guicursor' option is changed. const char *did_set_guicursor(optset_T *args FUNC_ATTR_UNUSED) { @@ -1300,6 +1701,15 @@ const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_icm, p_icm_values, false); } +int expand_set_inccommand(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_icm_values, + ARRAY_SIZE(p_icm_values) - 1, + numMatches, + matches); +} + /// The 'isident' or the 'iskeyword' or the 'isprint' or the 'isfname' option is /// changed. const char *did_set_isopt(optset_T *args) @@ -1321,6 +1731,15 @@ const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true); } +int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_jop_values, + ARRAY_SIZE(p_jop_values) - 1, + numMatches, + matches); +} + /// The 'keymap' option has changed. const char *did_set_keymap(optset_T *args) { @@ -1384,6 +1803,15 @@ const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_keymodel(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_km_values, + ARRAY_SIZE(p_km_values) - 1, + numMatches, + matches); +} + /// The 'lispoptions' option is changed. const char *did_set_lispoptions(optset_T *args) { @@ -1395,6 +1823,16 @@ const char *did_set_lispoptions(optset_T *args) return NULL; } +int expand_set_lispoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_lop_values[]) = { "expr:0", "expr:1", NULL }; + return expand_set_opt_string(args, + p_lop_values, + ARRAY_SIZE(p_lop_values) - 1, + numMatches, + matches); +} + /// The 'matchpairs' option is changed. const char *did_set_matchpairs(optset_T *args) { @@ -1439,12 +1877,26 @@ const char *did_set_mouse(optset_T *args) return did_set_option_listflag(*varp, MOUSE_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_mouse(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, MOUSE_ALL, numMatches, matches); +} + /// The 'mousemodel' option is changed. const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_mousem, p_mousem_values, false); } +int expand_set_mousemodel(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_mousem_values, + ARRAY_SIZE(p_mousem_values) - 1, + numMatches, + matches); +} + /// Handle setting 'mousescroll'. /// @return error message, NULL if it's OK. const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) @@ -1510,6 +1962,16 @@ const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_mousescroll(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_mousescroll_values[]) = { "hor:", "ver:", NULL }; + return expand_set_opt_string(args, + p_mousescroll_values, + ARRAY_SIZE(p_mousescroll_values) - 1, + numMatches, + matches); +} + /// The 'nrformats' option is changed. const char *did_set_nrformats(optset_T *args) { @@ -1518,6 +1980,15 @@ const char *did_set_nrformats(optset_T *args) return did_set_opt_strings(*varp, p_nf_values, true); } +int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_nf_values, + ARRAY_SIZE(p_nf_values) - 1, + numMatches, + matches); +} + /// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext', /// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'. const char *did_set_optexpr(optset_T *args) @@ -1553,6 +2024,16 @@ const char *did_set_rightleftcmd(optset_T *args) return NULL; } +int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char ***matches) +{ + static char *(p_rlc_values[]) = { "search", NULL }; + return expand_set_opt_string(args, + p_rlc_values, + ARRAY_SIZE(p_rlc_values) - 1, + numMatches, + matches); +} + /// The 'rulerformat' option is changed. const char *did_set_rulerformat(optset_T *args) { @@ -1565,6 +2046,15 @@ const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_sbo, p_scbopt_values, true); } +int expand_set_scrollopt(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_scbopt_values, + ARRAY_SIZE(p_scbopt_values) - 1, + numMatches, + matches); +} + /// The 'selection' option is changed. const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) { @@ -1574,12 +2064,30 @@ const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_selection(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_sel_values, + ARRAY_SIZE(p_sel_values) - 1, + numMatches, + matches); +} + /// The 'selectmode' option is changed. const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_slm, p_slm_values, true); } +int expand_set_selectmode(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_slm_values, + ARRAY_SIZE(p_slm_values) - 1, + numMatches, + matches); +} + /// The 'sessionoptions' option is changed. const char *did_set_sessionoptions(optset_T *args) { @@ -1595,6 +2103,15 @@ const char *did_set_sessionoptions(optset_T *args) return NULL; } +int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ssop_values, + ARRAY_SIZE(p_ssop_values) - 1, + numMatches, + matches); +} + static const char *did_set_shada(vimoption_T **opt, int *opt_idx, bool *free_oldval, char *errbuf, size_t errbuflen) { @@ -1661,6 +2178,11 @@ const char *did_set_shortmess(optset_T *args) return did_set_option_listflag(*varp, SHM_ALL, args->os_errbuf, args->os_errbuflen); } +int expand_set_shortmess(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, SHM_ALL, numMatches, matches); +} + /// The 'showbreak' option is changed. const char *did_set_showbreak(optset_T *args) { @@ -1681,6 +2203,15 @@ const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_strings(p_sloc, p_sloc_values, true); } +int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_sloc_values, + ARRAY_SIZE(p_sloc_values) - 1, + numMatches, + matches); +} + /// The 'signcolumn' option is changed. const char *did_set_signcolumn(optset_T *args) { @@ -1700,6 +2231,15 @@ const char *did_set_signcolumn(optset_T *args) return NULL; } +int expand_set_signcolumn(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_scl_values, + ARRAY_SIZE(p_scl_values) - 1, + numMatches, + matches); +} + /// The 'spellcapcheck' option is changed. const char *did_set_spellcapcheck(optset_T *args) { @@ -1745,6 +2285,15 @@ const char *did_set_spelloptions(optset_T *args) return NULL; } +int expand_set_spelloptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_spo_values, + ARRAY_SIZE(p_spo_values) - 1, + numMatches, + matches); +} + /// The 'spellsuggest' option is changed. const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) { @@ -1754,12 +2303,30 @@ const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_sps_values, + ARRAY_SIZE(p_sps_values) - 1, + numMatches, + matches); +} + /// The 'splitkeep' option is changed. const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_strings(p_spk, p_spk_values, false); } +int expand_set_splitkeep(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_spk_values, + ARRAY_SIZE(p_spk_values) - 1, + numMatches, + matches); +} + /// The 'statuscolumn' option is changed. const char *did_set_statuscolumn(optset_T *args) { @@ -1817,6 +2384,15 @@ const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED) return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true); } +int expand_set_switchbuf(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_swb_values, + ARRAY_SIZE(p_swb_values) - 1, + numMatches, + matches); +} + /// The 'tabline' option is changed. const char *did_set_tabline(optset_T *args) { @@ -1850,12 +2426,30 @@ const char *did_set_tagcase(optset_T *args) return NULL; } +int expand_set_tagcase(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_tc_values, + ARRAY_SIZE(p_tc_values) - 1, + numMatches, + matches); +} + /// The 'termpastefilter' option is changed. const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true); } +int expand_set_termpastefilter(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_tpf_values, + ARRAY_SIZE(p_tpf_values) - 1, + numMatches, + matches); +} + /// The 'titlestring' or the 'iconstring' option is changed. static const char *did_set_titleiconstring(optset_T *args, int flagval) { @@ -1988,12 +2582,28 @@ const char *did_set_virtualedit(optset_T *args) return NULL; } +int expand_set_virtualedit(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_ve_values, + ARRAY_SIZE(p_ve_values) - 1, + numMatches, + matches); +} + /// The 'whichwrap' option is changed. const char *did_set_whichwrap(optset_T *args) { char **varp = (char **)args->os_varp; - return did_set_option_listflag(*varp, WW_ALL, args->os_errbuf, args->os_errbuflen); + // Add ',' to the list flags because 'whichwrap' is a flag + // list that is comma-separated. + return did_set_option_listflag(*varp, WW_ALL ",", args->os_errbuf, args->os_errbuflen); +} + +int expand_set_whichwrap(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_listflag(args, WW_ALL, numMatches, matches); } /// The 'wildmode' option is changed. @@ -2005,12 +2615,30 @@ const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_wildmode(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_wim_values, + ARRAY_SIZE(p_wim_values) - 1, + numMatches, + matches); +} + /// The 'wildoptions' option is changed. const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED) { return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true); } +int expand_set_wildoptions(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_wop_values, + ARRAY_SIZE(p_wop_values) - 1, + numMatches, + matches); +} + /// The 'winaltkeys' option is changed. const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED) { @@ -2020,13 +2648,23 @@ const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED) return NULL; } +int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_string(args, + p_wak_values, + ARRAY_SIZE(p_wak_values) - 1, + numMatches, + matches); +} + /// The 'winbar' option is changed. const char *did_set_winbar(optset_T *args) { return did_set_statustabline_rulerformat(args, false, false); } -const char *did_set_winhl(optset_T *args) +/// The 'winhighlight' option is changed. +const char *did_set_winhighlight(optset_T *args) { win_T *win = (win_T *)args->os_win; if (!parse_winhl_opt(win)) { @@ -2035,6 +2673,11 @@ const char *did_set_winhl(optset_T *args) return NULL; } +int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches) +{ + return expand_set_opt_generic(args, get_highlight_name, numMatches, matches); +} + // When 'syntax' is set, load the syntax of that name static void do_syntax_autocmd(buf_T *buf, bool value_changed) { @@ -2084,11 +2727,12 @@ static void do_spelllang_source(win_T *win) /// @param errbuf buffer for errors, or NULL /// @param errbuflen length of errors buffer /// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL +/// @param op OP_ADDING/OP_PREPENDING/OP_REMOVING /// @param value_checked value was checked to be safe, no need to set P_INSECURE /// /// @return NULL for success, or an untranslated error message for an error const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **varp, char *oldval, - char *errbuf, size_t errbuflen, int opt_flags, + char *errbuf, size_t errbuflen, int opt_flags, set_op_T op, bool *value_checked) { const char *errmsg = NULL; @@ -2102,6 +2746,7 @@ const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **va .os_varp = varp, .os_idx = opt_idx, .os_flags = opt_flags, + .os_op = op, .os_oldval.string = oldval, .os_newval.string = *varp, .os_value_checked = false, @@ -2324,15 +2969,17 @@ static const struct chars_tab fcs_tab[] = { static lcs_chars_T lcs_chars; static const struct chars_tab lcs_tab[] = { - { &lcs_chars.eol, "eol", NUL, NUL }, - { &lcs_chars.ext, "extends", NUL, NUL }, - { &lcs_chars.nbsp, "nbsp", NUL, NUL }, - { &lcs_chars.prec, "precedes", NUL, NUL }, - { &lcs_chars.space, "space", NUL, NUL }, - { &lcs_chars.tab2, "tab", NUL, NUL }, - { &lcs_chars.lead, "lead", NUL, NUL }, - { &lcs_chars.trail, "trail", NUL, NUL }, - { &lcs_chars.conceal, "conceal", NUL, NUL }, + { &lcs_chars.eol, "eol", NUL, NUL }, + { &lcs_chars.ext, "extends", NUL, NUL }, + { &lcs_chars.nbsp, "nbsp", NUL, NUL }, + { &lcs_chars.prec, "precedes", NUL, NUL }, + { &lcs_chars.space, "space", NUL, NUL }, + { &lcs_chars.tab2, "tab", NUL, NUL }, + { &lcs_chars.lead, "lead", NUL, NUL }, + { &lcs_chars.trail, "trail", NUL, NUL }, + { &lcs_chars.conceal, "conceal", NUL, NUL }, + { NULL, "multispace", NUL, NUL }, + { NULL, "leadmultispace", NUL, NUL }, }; /// Handle setting 'listchars' or 'fillchars'. @@ -2403,54 +3050,13 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ int i; for (i = 0; i < entries; i++) { const size_t len = strlen(tab[i].name); - if (strncmp(p, tab[i].name, len) == 0 - && p[len] == ':' - && p[len + 1] != NUL) { - const char *s = p + len + 1; - int c1 = get_encoded_char_adv(&s); - if (c1 == 0 || char2cells(c1) > 1) { - return e_invarg; - } - int c2 = 0, c3 = 0; - if (tab[i].cp == &lcs_chars.tab2) { - if (*s == NUL) { - return e_invarg; - } - c2 = get_encoded_char_adv(&s); - if (c2 == 0 || char2cells(c2) > 1) { - return e_invarg; - } - if (!(*s == ',' || *s == NUL)) { - c3 = get_encoded_char_adv(&s); - if (c3 == 0 || char2cells(c3) > 1) { - return e_invarg; - } - } - } - - if (*s == ',' || *s == NUL) { - if (round > 0) { - if (tab[i].cp == &lcs_chars.tab2) { - lcs_chars.tab1 = c1; - lcs_chars.tab2 = c2; - lcs_chars.tab3 = c3; - } else if (tab[i].cp != NULL) { - *(tab[i].cp) = c1; - } - } - p = s; - break; - } + if (!(strncmp(p, tab[i].name, len) == 0 + && p[len] == ':' + && p[len + 1] != NUL)) { + continue; } - } - if (i == entries) { - const size_t len = strlen("multispace"); - const size_t len2 = strlen("leadmultispace"); - if (is_listchars - && strncmp(p, "multispace", len) == 0 - && p[len] == ':' - && p[len + 1] != NUL) { + if (is_listchars && strcmp(tab[i].name, "multispace") == 0) { const char *s = p + len + 1; if (round == 0) { // Get length of lcs-multispace string in the first round @@ -2478,11 +3084,11 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ } p = s; } - } else if (is_listchars - && strncmp(p, "leadmultispace", len2) == 0 - && p[len2] == ':' - && p[len2 + 1] != NUL) { - const char *s = p + len2 + 1; + break; + } + + if (is_listchars && strcmp(tab[i].name, "leadmultispace") == 0) { + const char *s = p + len + 1; if (round == 0) { // get length of lcs-leadmultispace string in first round last_lmultispace = p; @@ -2509,9 +3115,48 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ } p = s; } - } else { + break; + } + + const char *s = p + len + 1; + int c1 = get_encoded_char_adv(&s); + if (c1 == 0 || char2cells(c1) > 1) { return e_invarg; } + int c2 = 0, c3 = 0; + if (tab[i].cp == &lcs_chars.tab2) { + if (*s == NUL) { + return e_invarg; + } + c2 = get_encoded_char_adv(&s); + if (c2 == 0 || char2cells(c2) > 1) { + return e_invarg; + } + if (!(*s == ',' || *s == NUL)) { + c3 = get_encoded_char_adv(&s); + if (c3 == 0 || char2cells(c3) > 1) { + return e_invarg; + } + } + } + + if (*s == ',' || *s == NUL) { + if (round > 0) { + if (tab[i].cp == &lcs_chars.tab2) { + lcs_chars.tab1 = c1; + lcs_chars.tab2 = c2; + lcs_chars.tab3 = c3; + } else if (tab[i].cp != NULL) { + *(tab[i].cp) = c1; + } + } + p = s; + break; + } + } + + if (i == entries) { + return e_invarg; } if (*p == ',') { @@ -2536,6 +3181,40 @@ static const char *set_chars_option(win_T *wp, const char *value, const bool is_ return NULL; // no error } +/// Handle the new value of 'fillchars'. +const char *set_fillchars_option(win_T *wp, char *val, bool apply) +{ + return set_chars_option(wp, val, false, apply); +} + +/// Handle the new value of 'listchars'. +const char *set_listchars_option(win_T *wp, char *val, bool apply) +{ + return set_chars_option(wp, val, true, apply); +} + +/// Function given to ExpandGeneric() to obtain possible arguments of the +/// 'fillchars' option. +char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + if (idx >= (int)ARRAY_SIZE(fcs_tab)) { + return NULL; + } + + return (char *)fcs_tab[idx].name; +} + +/// Function given to ExpandGeneric() to obtain possible arguments of the +/// 'listchars' option. +char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + if (idx >= (int)ARRAY_SIZE(lcs_tab)) { + return NULL; + } + + return (char *)lcs_tab[idx].name; +} + /// Check all global and local values of 'listchars' and 'fillchars'. /// May set different defaults in case character widths change. /// @@ -2558,15 +3237,3 @@ const char *check_chars_options(void) } return NULL; } - -/// Handle the new value of 'fillchars'. -const char *set_fillchars_option(win_T *wp, char *val, bool apply) -{ - return set_chars_option(wp, val, false, apply); -} - -/// Handle the new value of 'listchars'. -const char *set_listchars_option(win_T *wp, char *val, bool apply) -{ - return set_chars_option(wp, val, true, apply); -} diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h index 3520cc2061..a481ed1d07 100644 --- a/src/nvim/optionstr.h +++ b/src/nvim/optionstr.h @@ -2,6 +2,7 @@ #define NVIM_OPTIONSTR_H #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/option_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index b03509a313..7de7168d62 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -17,7 +17,6 @@ #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/eval.h" -#include "nvim/ex_cmds_defs.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/log.h" @@ -29,7 +28,6 @@ #include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/strings.h" -#include "nvim/types.h" #include "nvim/version.h" #include "nvim/vim.h" diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 846219f720..119a42f074 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -282,9 +282,10 @@ static char writebuf[kRWBufferSize]; /// /// @param[in,out] rv RBuffer instance used. /// @param[in,out] fp File to work with. -static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp) +static void file_rb_write_full_cb(RBuffer *const rv, void *const fp_in) FUNC_ATTR_NONNULL_ALL { + FileDescriptor *const fp = fp_in; assert(fp->wr); assert(rv->data == (void *)fp); if (rbuffer_size(rv) == 0) { diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 476ede2046..2712b874bb 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -63,8 +63,6 @@ #ifdef HAVE_XATTR static const char e_xattr_erange[] = N_("E1506: Buffer too small to copy xattr value or key"); -static const char e_xattr_enotsup[] - = N_("E1507: Extended attributes are not supported by the filesystem"); static const char e_xattr_e2big[] = N_("E1508: Size of the extended attribute value is larger than the maximum size allowed"); static const char e_xattr_other[] @@ -800,9 +798,9 @@ void os_copy_xattr(const char *from_file, const char *to_file) case E2BIG: errmsg = e_xattr_e2big; goto error_exit; - case ENOTSUP: - errmsg = e_xattr_enotsup; - goto error_exit; + case EACCES: + case EPERM: + break; case ERANGE: errmsg = e_xattr_erange; goto error_exit; diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 652b851903..c3958cb3f2 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -5,6 +5,7 @@ # define Boolean CFBoolean // Avoid conflict with API's Boolean # define FileInfo CSFileInfo // Avoid conflict with API's Fileinfo # include <CoreServices/CoreServices.h> + # undef Boolean # undef FileInfo #endif @@ -17,6 +18,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" @@ -30,7 +32,6 @@ #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/profile.h" -#include "nvim/types.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/os/lang.h b/src/nvim/os/lang.h index bb1ebfb721..ad64b38916 100644 --- a/src/nvim/os/lang.h +++ b/src/nvim/os/lang.h @@ -1,6 +1,7 @@ #ifndef NVIM_OS_LANG_H #define NVIM_OS_LANG_H +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h index a7496130cc..006dfbfc04 100644 --- a/src/nvim/os/os.h +++ b/src/nvim/os/os.h @@ -4,9 +4,12 @@ #include <stdbool.h> #include <uv.h> +#include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" +#include "nvim/garray.h" #include "nvim/os/fs_defs.h" #include "nvim/os/stdpaths_defs.h" -#include "nvim/vim.h" +#include "nvim/types.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/env.h.generated.h" diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 411ba91fa7..b23d2b7b13 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -10,10 +10,10 @@ #include "auto/config.h" #include "nvim/ascii.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/garray.h" #include "nvim/memory.h" #include "nvim/os/os.h" -#include "nvim/types.h" #include "nvim/vim.h" #ifdef HAVE_PWD_FUNCS # include <pwd.h> diff --git a/src/nvim/path.c b/src/nvim/path.c index 21f0543756..5d991ce719 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -648,11 +648,13 @@ static size_t do_path_expand(garray_T *gap, const char *path, size_t wildoff, in } s = p + 1; } else if (path_end >= path + wildoff +#ifdef MSWIN + && vim_strchr("*?[~", (uint8_t)(*path_end)) != NULL +#else && (vim_strchr("*?[{~$", (uint8_t)(*path_end)) != NULL -#ifndef MSWIN - || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end))) + || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end)))) #endif - )) { // NOLINT(whitespace/parens) + ) { // NOLINT(whitespace/parens) e = p; } len = (size_t)(utfc_ptr2len(path_end)); diff --git a/src/nvim/profile.c b/src/nvim/profile.c index 3162a446c0..73ad534de7 100644 --- a/src/nvim/profile.c +++ b/src/nvim/profile.c @@ -11,6 +11,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" @@ -30,7 +31,6 @@ #include "nvim/profile.h" #include "nvim/runtime.h" #include "nvim/types.h" -#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "profile.c.generated.h" diff --git a/src/nvim/profile.h b/src/nvim/profile.h index 547d11185f..7450a0dfdc 100644 --- a/src/nvim/profile.h +++ b/src/nvim/profile.h @@ -4,6 +4,7 @@ #include <stdint.h> #include <time.h> +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/runtime.h" diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 12cf08636f..360088758d 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -20,6 +20,7 @@ #include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index 7308ef5f75..8286a11f89 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -5,6 +5,7 @@ #include "klib/kvec.h" #include "nvim/autocmd.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_eval_defs.h" diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 14d35d9b59..cd1d016c7b 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1564,15 +1564,14 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr break; } case kSDItemVariable: { - if (entry.data.global_var.value.v_type == VAR_TYPE_BLOB) { + if (entry.data.global_var.value.v_type == VAR_BLOB) { // Strings and Blobs both pack as msgpack BINs; differentiate them by // storing an additional VAR_TYPE_BLOB element alongside Blobs list_T *const list = tv_list_alloc(1); tv_list_append_number(list, VAR_TYPE_BLOB); entry.data.global_var.additional_elements = list; } - const size_t arr_size = 2 + (size_t)( - tv_list_len(entry.data.global_var.additional_elements)); + const size_t arr_size = 2 + (size_t)(tv_list_len(entry.data.global_var.additional_elements)); msgpack_pack_array(spacker, arr_size); const String varname = cstr_as_string(entry.data.global_var.name); PACK_BIN(varname); diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 96af14bfc6..5d18ed393a 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -16,6 +16,7 @@ #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/drawscreen.h" #include "nvim/edit.h" diff --git a/src/nvim/sign.h b/src/nvim/sign.h index ba84cd71a4..89d765bf38 100644 --- a/src/nvim/sign.h +++ b/src/nvim/sign.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/sign_defs.h" diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index f1df2e56f3..25e08abb0e 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -452,24 +452,24 @@ struct wordnode_S { // Info used while reading the spell files. typedef struct spellinfo_S { wordnode_T *si_foldroot; // tree with case-folded words - long si_foldwcount; // nr of words in si_foldroot + int si_foldwcount; // nr of words in si_foldroot wordnode_T *si_keeproot; // tree with keep-case words - long si_keepwcount; // nr of words in si_keeproot + int si_keepwcount; // nr of words in si_keeproot wordnode_T *si_prefroot; // tree with postponed prefixes - long si_sugtree; // creating the soundfolding trie + int si_sugtree; // creating the soundfolding trie sblock_T *si_blocks; // memory blocks used - long si_blocks_cnt; // memory blocks allocated + int si_blocks_cnt; // memory blocks allocated int si_did_emsg; // true when ran out of memory - long si_compress_cnt; // words to add before lowering - // compression limit + int si_compress_cnt; // words to add before lowering + // compression limit wordnode_T *si_first_free; // List of nodes that have been freed during // compression, linked by "wn_child" field. - long si_free_count; // number of nodes in si_first_free + int si_free_count; // number of nodes in si_first_free #ifdef SPELL_PRINTTREE int si_wordnode_nr; // sequence nr for nodes #endif @@ -1874,24 +1874,24 @@ static void spell_reload_one(char *fname, bool added_word) // Tunable parameters for when the tree is compressed. Filled from the // 'mkspellmem' option. -static long compress_start = 30000; // memory / SBLOCKSIZE -static long compress_inc = 100; // memory / SBLOCKSIZE -static long compress_added = 500000; // word count +static int compress_start = 30000; // memory / SBLOCKSIZE +static int compress_inc = 100; // memory / SBLOCKSIZE +static int compress_added = 500000; // word count // Check the 'mkspellmem' option. Return FAIL if it's wrong. // Sets "sps_flags". int spell_check_msm(void) { char *p = p_msm; - long start = 0; - long incr = 0; - long added = 0; + int start = 0; + int incr = 0; + int added = 0; if (!ascii_isdigit(*p)) { return FAIL; } // block count = (value * 1024) / SBLOCKSIZE (but avoid overflow) - start = (getdigits_long(&p, true, 0) * 10) / (SBLOCKSIZE / 102); + start = (getdigits_int(&p, true, 0) * 10) / (SBLOCKSIZE / 102); if (*p != ',') { return FAIL; } @@ -1899,7 +1899,7 @@ int spell_check_msm(void) if (!ascii_isdigit(*p)) { return FAIL; } - incr = (getdigits_long(&p, true, 0) * 102) / (SBLOCKSIZE / 10); + incr = (getdigits_int(&p, true, 0) * 102) / (SBLOCKSIZE / 10); if (*p != ',') { return FAIL; } @@ -1907,7 +1907,7 @@ int spell_check_msm(void) if (!ascii_isdigit(*p)) { return FAIL; } - added = getdigits_long(&p, true, 0) * 1024; + added = getdigits_int(&p, true, 0) * 1024; if (*p != NUL) { return FAIL; } @@ -3177,7 +3177,7 @@ static int spell_read_dic(spellinfo_T *spin, char *fname, afffile_T *affile) if (os_time() > last_msg_time) { last_msg_time = os_time(); vim_snprintf(message, sizeof(message), - _("line %6d, word %6ld - %s"), + _("line %6d, word %6d - %s"), lnum, spin->si_foldwcount + spin->si_keepwcount, w); msg_start(); msg_outtrans_long(message, 0); @@ -3635,7 +3635,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_ static int spell_read_wordfile(spellinfo_T *spin, char *fname) { FILE *fd; - long lnum = 0; + linenr_T lnum = 0; char rline[MAXLINELEN]; char *line; char *pc = NULL; @@ -3682,7 +3682,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) if (spin->si_conv.vc_type != CONV_NONE) { pc = string_convert(&spin->si_conv, rline, NULL); if (pc == NULL) { - smsg(0, _("Conversion failure for word in %s line %ld: %s"), + smsg(0, _("Conversion failure for word in %s line %" PRIdLINENR ": %s"), fname, lnum, rline); continue; } @@ -3696,10 +3696,10 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) line++; if (strncmp(line, "encoding=", 9) == 0) { if (spin->si_conv.vc_type != CONV_NONE) { - smsg(0, _("Duplicate /encoding= line ignored in %s line %ld: %s"), + smsg(0, _("Duplicate /encoding= line ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line - 1); } else if (did_word) { - smsg(0, _("/encoding= line after word ignored in %s line %ld: %s"), + smsg(0, _("/encoding= line after word ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line - 1); } else { char *enc; @@ -3720,12 +3720,12 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) if (strncmp(line, "regions=", 8) == 0) { if (spin->si_region_count > 1) { - smsg(0, _("Duplicate /regions= line ignored in %s line %ld: %s"), + smsg(0, _("Duplicate /regions= line ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line); } else { line += 8; if (strlen(line) > MAXREGIONS * 2) { - smsg(0, _("Too many regions in %s line %ld: %s"), + smsg(0, _("Too many regions in %s line %" PRIdLINENR ": %s"), fname, lnum, line); } else { spin->si_region_count = (int)strlen(line) / 2; @@ -3738,7 +3738,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) continue; } - smsg(0, _("/ line ignored in %s line %ld: %s"), + smsg(0, _("/ line ignored in %s line %" PRIdLINENR ": %s"), fname, lnum, line - 1); continue; } @@ -3765,13 +3765,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char *fname) l = (uint8_t)(*p) - '0'; if (l == 0 || l > spin->si_region_count) { - smsg(0, _("Invalid region nr in %s line %ld: %s"), + smsg(0, _("Invalid region nr in %s line %" PRIdLINENR ": %s"), fname, lnum, p); break; } regionmask |= 1 << (l - 1); } else { - smsg(0, _("Unrecognized flags in %s line %ld: %s"), + smsg(0, _("Unrecognized flags in %s line %" PRIdLINENR ": %s"), fname, lnum, p); break; } @@ -4168,7 +4168,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n FUNC_ATTR_NONNULL_ALL { hashtab_T ht; - long tot = 0; + int tot = 0; long perc; // Skip the root itself, it's not actually used. The first sibling is the @@ -4178,7 +4178,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n } hash_init(&ht); - const long n = node_compress(spin, root->wn_sibling, &ht, &tot); + const int n = node_compress(spin, root->wn_sibling, &ht, &tot); #ifndef SPELL_PRINTTREE if (spin->si_verbose || p_verbose > 2) @@ -4192,7 +4192,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n perc = (tot - n) * 100 / tot; } vim_snprintf(IObuff, IOSIZE, - _("Compressed %s of %ld nodes; %ld (%ld%%) remaining"), + _("Compressed %s of %d nodes; %d (%ld%%) remaining"), name, tot, tot - n, perc); spell_message(spin, IObuff); } @@ -4206,7 +4206,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n /// Returns the number of compressed nodes. /// /// @param tot total count of nodes before compressing, incremented while going through the tree -static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, long *tot) +static int node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, int *tot) FUNC_ATTR_NONNULL_ALL { wordnode_T *np; @@ -4216,7 +4216,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo hashitem_T *hi; long len = 0; unsigned nr, n; - long compressed = 0; + int compressed = 0; // Go through the list of siblings. Compress each child and then try // finding an identical child to replace it. @@ -4262,7 +4262,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo } } } - *tot += len + 1; // add one for the node that stores the length + *tot += (int)len + 1; // add one for the node that stores the length // Make a hash key for the node and its siblings, so that we can quickly // find a lookalike node. This must be done after compressing the sibling @@ -5559,15 +5559,15 @@ void spell_add_word(char *word, int len, SpellAddType what, int idx, bool undo) } if (what == SPELL_ADD_BAD || undo) { - long fpos_next = 0; - long fpos = 0; + int fpos_next = 0; + int fpos = 0; // When the word appears as good word we need to remove that one, // since its flags sort before the one with WF_BANNED. fd = os_fopen(fname, "r"); if (fd != NULL) { while (!vim_fgets(line, MAXWLEN * 2, fd)) { fpos = fpos_next; - fpos_next = ftell(fd); + fpos_next = (int)ftell(fd); if (fpos_next < 0) { break; // should never happen } diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 2ee93b4934..7b92e69821 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -403,6 +403,7 @@ int spell_check_sps(void) if (*s != NUL && !ascii_isdigit(*s)) { f = -1; } + // Note: Keep this in sync with p_sps_values. } else if (strcmp(buf, "best") == 0) { f = SPS_BEST; } else if (strcmp(buf, "fast") == 0) { diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index 1529b63812..fb467093ad 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -182,7 +182,9 @@ void win_redr_status(win_T *wp) attr = win_hl_attr(wp, HLF_C); fillchar = wp->w_p_fcs_chars.vert; } - grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr); + grid_line_start(&default_grid, W_ENDROW(wp)); + grid_line_put_schar(W_ENDCOL(wp), schar_from_char(fillchar), attr); + grid_line_flush(); } busy = false; } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 0d307a760f..cc66f917f8 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -46,7 +46,7 @@ static const char e_positional_arg_num_type_inconsistent_str_str[] static const char e_invalid_format_specifier_str[] = N_("E1505: Invalid format specifier: %s"); static const char e_aptypes_is_null_nr_str[] - = "E1520: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; + = "E1507: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"; static const char typename_unknown[] = N_("unknown"); static const char typename_int[] = N_("int"); @@ -1079,7 +1079,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * any_arg = 1; CHECK_POS_ARG; } - } else if (ascii_isdigit((int)(*(arg = p)))) { + } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do unsigned uj = (unsigned)(*p++ - '0'); @@ -1126,7 +1126,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * any_arg = 1; CHECK_POS_ARG; } - } else if (ascii_isdigit((int)(*(arg = p)))) { + } else if (ascii_isdigit((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do unsigned uj = (unsigned)(*p++ - '0'); @@ -1155,7 +1155,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char * p++; if (length_modifier == 'l' && *p == 'l') { // double l = long long - length_modifier = 'L'; + // length_modifier = 'L'; p++; } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 9b238e6ff9..6bb841b1f8 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -15,6 +15,7 @@ #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" #include "nvim/eval/typval_defs.h" @@ -3554,7 +3555,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const msg_putchar(','); // Separate with commas. } msg_puts(spo_name_tab[i]); - const long n = spp->sp_offsets[i]; + const int n = spp->sp_offsets[i]; if (i != SPO_LC_OFF) { if (spp->sp_off_flags & mask) { msg_putchar('s'); diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index 0a63392a04..c56624bc37 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "nvim/buffer_defs.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" #include "nvim/macros.h" diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 636d16d4e6..674d22ba44 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -56,7 +56,6 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" #include "nvim/window.h" diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c index 0de62440f9..13e51b9a9e 100644 --- a/src/nvim/textformat.c +++ b/src/nvim/textformat.c @@ -822,7 +822,7 @@ void op_format(oparg_T *oap, bool keep_cursor) saved_cursor = oap->cursor_start; } - format_lines((linenr_T)oap->line_count, keep_cursor); + format_lines(oap->line_count, keep_cursor); // Leave the cursor at the first non-blank of the last formatted line. // If the cursor was moved one line back (e.g. with "Q}") go to the next diff --git a/src/nvim/types.h b/src/nvim/types.h index c2c815a0bc..7b23fe419b 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -31,8 +31,6 @@ typedef union { typedef handle_T NS; -typedef struct expand expand_T; - typedef uint64_t proftime_T; typedef enum { diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 0f26e269ae..8c16380f1f 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -532,7 +532,7 @@ void ui_comp_raw_line(Integer grid, Integer row, Integer startcol, Integer endco compose_debug(row, row + 1, startcol, clearcol, dbghl_composed, true); compose_line(row, startcol, clearcol, flags); } else { - compose_debug(row, row + 1, startcol, endcol, dbghl_normal, false); + compose_debug(row, row + 1, startcol, endcol, dbghl_normal, endcol >= clearcol); compose_debug(row, row + 1, endcol, clearcol, dbghl_clear, true); #ifndef NDEBUG for (int i = 0; i < endcol - startcol; i++) { diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 5396bfcda2..758ee036b4 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -260,10 +260,10 @@ int u_save_buf(buf_T *buf, linenr_T top, linenr_T bot) } if (top + 2 == bot) { - u_saveline(buf, (linenr_T)(top + 1)); + u_saveline(buf, top + 1); } - return u_savecommon(buf, top, bot, (linenr_T)0, false); + return u_savecommon(buf, top, bot, 0, false); } /// Save the line "lnum" (used by ":s" and "~" command). @@ -289,9 +289,9 @@ int u_inssub(linenr_T lnum) /// becomes empty. /// Careful: may trigger autocommands that reload the buffer. /// Returns FAIL when lines could not be saved, OK otherwise. -int u_savedel(linenr_T lnum, long nlines) +int u_savedel(linenr_T lnum, linenr_T nlines) { - return u_savecommon(curbuf, lnum - 1, lnum + (linenr_T)nlines, + return u_savecommon(curbuf, lnum - 1, lnum + nlines, nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false); } @@ -378,7 +378,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re u_entry_T *uep; u_entry_T *prev_uep; - long size = bot - top - 1; + linenr_T size = bot - top - 1; // If curbuf->b_u_synced == true make a new header. if (buf->b_u_synced) { @@ -1488,7 +1488,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT time_t seq_time = undo_read_time(&bi); // Optional header fields. - long last_save_nr = 0; + int last_save_nr = 0; while (true) { int len = undo_read_byte(&bi); @@ -1519,7 +1519,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT } } - long num_read_uhps = 0; + int num_read_uhps = 0; int c; while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC) { @@ -1655,7 +1655,7 @@ void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATT error: xfree(line_ptr); if (uhp_table != NULL) { - for (long i = 0; i < num_read_uhps; i++) { + for (int i = 0; i < num_read_uhps; i++) { if (uhp_table[i] != NULL) { u_free_uhp(uhp_table[i]); } @@ -1920,7 +1920,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) // When "file" is true use "step" as a number of file writes. // When "absolute" is true use "step" as the sequence number to jump to. // "sec" must be false then. -void undo_time(long step, bool sec, bool file, bool absolute) +void undo_time(int step, bool sec, bool file, bool absolute) { if (text_locked()) { text_locked_msg(); @@ -1938,8 +1938,8 @@ void undo_time(long step, bool sec, bool file, bool absolute) u_oldcount = -1; } - long target; - long closest; + int target; + int closest; u_header_T *uhp = NULL; bool dosec = sec; bool dofile = file; @@ -1953,7 +1953,7 @@ void undo_time(long step, bool sec, bool file, bool absolute) closest = -1; } else { if (dosec) { - target = (long)(curbuf->b_u_time_cur) + step; + target = (int)curbuf->b_u_time_cur + step; } else if (dofile) { if (step < 0) { // Going back to a previous write. If there were changes after @@ -1998,7 +1998,7 @@ void undo_time(long step, bool sec, bool file, bool absolute) closest = -1; } else { if (dosec) { - closest = (long)(os_time() + 1); + closest = (int)(os_time() + 1); } else if (dofile) { closest = curbuf->b_u_save_nr_last + 2; } else { @@ -2010,7 +2010,7 @@ void undo_time(long step, bool sec, bool file, bool absolute) } } long closest_start = closest; - long closest_seq = curbuf->b_u_seq_cur; + int closest_seq = curbuf->b_u_seq_cur; int mark; int nomark = 0; // shut up compiler @@ -2042,8 +2042,8 @@ void undo_time(long step, bool sec, bool file, bool absolute) while (uhp != NULL) { uhp->uh_walk = mark; - long val = dosec ? (long)(uhp->uh_time) : - dofile ? uhp->uh_save_nr + int val = dosec ? (int)(uhp->uh_time) : + dofile ? uhp->uh_save_nr : uhp->uh_seq; if (round == 1 && !(dofile && val == 0)) { @@ -2298,7 +2298,7 @@ static void u_undoredo(int undo, bool do_buf_event) } linenr_T oldsize = bot - top - 1; // number of lines before undo - linenr_T newsize = (linenr_T)uep->ue_size; // number of lines after undo + linenr_T newsize = uep->ue_size; // number of lines after undo if (top < newlnum) { // If the saved cursor is somewhere in this undo block, move it to @@ -2676,13 +2676,13 @@ void ex_undolist(exarg_T *eap) while (uhp != NULL) { if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark && uhp->uh_walk != mark) { - vim_snprintf(IObuff, IOSIZE, "%6ld %7d ", uhp->uh_seq, changes); + vim_snprintf(IObuff, IOSIZE, "%6d %7d ", uhp->uh_seq, changes); undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { while (strlen(IObuff) < 33) { xstrlcat(IObuff, " ", IOSIZE); } - vim_snprintf_add(IObuff, IOSIZE, " %3ld", uhp->uh_save_nr); + vim_snprintf_add(IObuff, IOSIZE, " %3d", uhp->uh_save_nr); } GA_APPEND(char *, &ga, xstrdup(IObuff)); } @@ -2850,7 +2850,7 @@ static void u_getbot(buf_T *buf) // inserted (0 - deleted) since calling u_save. This is equal to the // old line count subtracted from the current line count. linenr_T extra = buf->b_ml.ml_line_count - uep->ue_lcount; - uep->ue_bot = uep->ue_top + (linenr_T)uep->ue_size + 1 + extra; + uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) { iemsg(_(e_undo_line_missing)); uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index 9cc2e4a52b..aa7d6e3355 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -26,7 +26,7 @@ struct u_entry { linenr_T ue_bot; // number of line below undo block linenr_T ue_lcount; // linecount when u_save called char **ue_array; // array of lines in undo block - long ue_size; // number of lines in ue_array + linenr_T ue_size; // number of lines in ue_array #ifdef U_DEBUG int ue_magic; // magic number to check allocation #endif @@ -51,19 +51,19 @@ struct u_header { u_header_T *ptr; // pointer to previous header for alt. redo long seq; } uh_alt_prev; - long uh_seq; // sequence number, higher == newer undo + int uh_seq; // sequence number, higher == newer undo int uh_walk; // used by undo_time() - u_entry_T *uh_entry; // pointer to first entry + u_entry_T *uh_entry; // pointer to first entry u_entry_T *uh_getbot_entry; // pointer to where ue_bot must be set pos_T uh_cursor; // cursor position before saving colnr_T uh_cursor_vcol; - int uh_flags; // see below - fmark_T uh_namedm[NMARKS]; // marks before undo/after redo + int uh_flags; // see below + fmark_T uh_namedm[NMARKS]; // marks before undo/after redo extmark_undo_vec_t uh_extmark; // info to move extmarks - visualinfo_T uh_visual; // Visual areas before undo/after redo - time_t uh_time; // timestamp when the change was made - long uh_save_nr; // set when the file was saved after the - // changes in this block + visualinfo_T uh_visual; // Visual areas before undo/after redo + time_t uh_time; // timestamp when the change was made + int uh_save_nr; // set when the file was saved after the + // changes in this block #ifdef U_DEBUG int uh_magic; // magic number to check allocation #endif diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 131b1ce3ef..4bab3f52cd 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -16,6 +16,7 @@ #include "nvim/ascii.h" #include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/eval.h" #include "nvim/ex_docmd.h" #include "nvim/garray.h" diff --git a/src/nvim/usercmd.h b/src/nvim/usercmd.h index 0d9838abf2..34f1439b10 100644 --- a/src/nvim/usercmd.h +++ b/src/nvim/usercmd.h @@ -3,6 +3,7 @@ #include <stdint.h> +#include "nvim/cmdexpand_defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/garray.h" diff --git a/src/nvim/version.c b/src/nvim/version.c index 20bbcb2f8a..c3bfad4706 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2827,7 +2827,7 @@ void intro_message(int colon) } if (*mesg != NUL) { - do_intro_line(row, mesg, 0); + do_intro_line((int)row, mesg, 0); } row++; @@ -2838,14 +2838,13 @@ void intro_message(int colon) } } -static void do_intro_line(long row, char *mesg, int attr) +static void do_intro_line(int row, char *mesg, int attr) { char *p; int l; - int clen; // Center the message horizontally. - long col = vim_strsize(mesg); + int col = vim_strsize(mesg); col = (Columns - col) / 2; @@ -2853,21 +2852,18 @@ static void do_intro_line(long row, char *mesg, int attr) col = 0; } + grid_line_start(&default_grid, row); // Split up in parts to highlight <> items differently. for (p = mesg; *p != NUL; p += l) { - clen = 0; - for (l = 0; p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>')); l++) { - clen += ptr2cells(p + l); l += utfc_ptr2len(p + l) - 1; } assert(row <= INT_MAX && col <= INT_MAX); - grid_puts(&default_grid, p, l, (int)row, (int)col, - *p == '<' ? HL_ATTR(HLF_8) : attr); - col += clen; + col += grid_line_puts(col, p, l, *p == '<' ? HL_ATTR(HLF_8) : attr); } + grid_line_flush(); } /// ":intro": clear screen, display intro screen and wait for return. diff --git a/src/nvim/vim.h b/src/nvim/vim.h index fc1f15b285..22db3751cd 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -96,83 +96,10 @@ typedef enum { #define FAIL 0 #define NOTDONE 2 // not OK or FAIL but skipped -// Type values for type(). -#define VAR_TYPE_NUMBER 0 -#define VAR_TYPE_STRING 1 -#define VAR_TYPE_FUNC 2 -#define VAR_TYPE_LIST 3 -#define VAR_TYPE_DICT 4 -#define VAR_TYPE_FLOAT 5 -#define VAR_TYPE_BOOL 6 -#define VAR_TYPE_SPECIAL 7 -#define VAR_TYPE_BLOB 10 - -// values for xp_context when doing command line completion - -enum { - EXPAND_UNSUCCESSFUL = -2, - EXPAND_OK = -1, - EXPAND_NOTHING = 0, - EXPAND_COMMANDS, - EXPAND_FILES, - EXPAND_DIRECTORIES, - EXPAND_SETTINGS, - EXPAND_BOOL_SETTINGS, - EXPAND_TAGS, - EXPAND_OLD_SETTING, - EXPAND_HELP, - EXPAND_BUFFERS, - EXPAND_EVENTS, - EXPAND_MENUS, - EXPAND_SYNTAX, - EXPAND_HIGHLIGHT, - EXPAND_AUGROUP, - EXPAND_USER_VARS, - EXPAND_MAPPINGS, - EXPAND_TAGS_LISTFILES, - EXPAND_FUNCTIONS, - EXPAND_USER_FUNC, - EXPAND_EXPRESSION, - EXPAND_MENUNAMES, - EXPAND_USER_COMMANDS, - EXPAND_USER_CMD_FLAGS, - EXPAND_USER_NARGS, - EXPAND_USER_COMPLETE, - EXPAND_ENV_VARS, - EXPAND_LANGUAGE, - EXPAND_COLORS, - EXPAND_COMPILER, - EXPAND_USER_DEFINED, - EXPAND_USER_LIST, - EXPAND_USER_LUA, - EXPAND_SHELLCMD, - EXPAND_SIGN, - EXPAND_PROFILE, - EXPAND_FILETYPE, - EXPAND_FILES_IN_PATH, - EXPAND_OWNSYNTAX, - EXPAND_LOCALES, - EXPAND_HISTORY, - EXPAND_USER, - EXPAND_SYNTIME, - EXPAND_USER_ADDR_TYPE, - EXPAND_PACKADD, - EXPAND_MESSAGES, - EXPAND_MAPCLEAR, - EXPAND_ARGLIST, - EXPAND_DIFF_BUFFERS, - EXPAND_BREAKPOINT, - EXPAND_SCRIPTNAMES, - EXPAND_RUNTIME, - EXPAND_CHECKHEALTH, - EXPAND_LUA, -}; - // Minimal size for block 0 of a swap file. // NOTE: This depends on size of struct block0! It's not done with a sizeof(), // because struct block0 is defined in memline.c (Sorry). // The maximal block size is arbitrary. - #define MIN_SWAP_PAGE_SIZE 1048 #define MAX_SWAP_PAGE_SIZE 50000 diff --git a/src/nvim/window.c b/src/nvim/window.c index be1bb8edaf..04b5afe624 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6947,11 +6947,13 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f bool is_url = false; // Search backward for first char of the file name. - // Go one char back to ":" before "//" even when ':' is not in 'isfname'. + // Go one char back to ":" before "//", or to the drive letter before ":\" (even if ":" + // is not in 'isfname'). while (ptr > line) { if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) { ptr -= len + 1; } else if (vim_isfilec((uint8_t)ptr[-1]) + || (len >= 2 && path_has_drive_letter(ptr - 2)) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) { ptr--; } else { @@ -6961,14 +6963,13 @@ char *file_name_in_line(char *line, int col, int options, int count, char *rel_f // Search forward for the last char of the file name. // Also allow ":/" when ':' is not in 'isfname'. - len = 0; + len = path_has_drive_letter(ptr) ? 2 : 0; while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') || ((options & FNAME_HYP) && path_is_url(ptr + len)) || (is_url && vim_strchr(":?&=", (uint8_t)ptr[len]) != NULL)) { // After type:// we also include :, ?, & and = as valid characters, so that // http://google.com:8080?q=this&that=ok works. - if ((ptr[len] >= 'A' && ptr[len] <= 'Z') - || (ptr[len] >= 'a' && ptr[len] <= 'z')) { + if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) { if (in_type && path_is_url(ptr + len + 1)) { is_url = true; } diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 09495fbaac..f28d6ea869 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -2869,6 +2869,18 @@ describe('API', function() eq(false, eval('g:fired')) end) + it('TextChanged and TextChangedI do not trigger without changes', function() + local buf = meths.create_buf(true, false) + command([[let g:changed = '']]) + meths.create_autocmd({'TextChanged', 'TextChangedI'}, { + buffer = buf, + command = 'let g:changed ..= mode()', + }) + meths.set_current_buf(buf) + feed('i') + eq('', meths.get_var('changed')) + end) + it('scratch-buffer', function() eq({id=2}, meths.create_buf(false, true)) eq({id=3}, meths.create_buf(true, true)) diff --git a/test/functional/core/fileio_spec.lua b/test/functional/core/fileio_spec.lua index f2b360f0d8..a23dad226a 100644 --- a/test/functional/core/fileio_spec.lua +++ b/test/functional/core/fileio_spec.lua @@ -40,6 +40,7 @@ describe('fileio', function() os.remove('Xtest_startup_file1') os.remove('Xtest_startup_file1~') os.remove('Xtest_startup_file2') + os.remove('Xtest_startup_file2~') os.remove('Xtest_тест.md') os.remove('Xtest-u8-int-max') os.remove('Xtest-overwrite-forced') @@ -136,7 +137,7 @@ describe('fileio', function() it('backup with full path with spaces', function() skip(is_ci('cirrus')) clear() - mkdir('Xtest_backup with spaces') + mkdir('Xtest_backupdir with spaces') command('set backup') command('set backupdir=Xtest_backupdir\\ with\\ spaces//') command('write Xtest_startup_file1') @@ -258,8 +259,8 @@ describe('fileio', function() screen:expect([[ {2:WARNING: The file has been changed since}| {2: reading it!!!} | - {3:Do you really want to write to it (y/n)^?}| - | + {3:Do you really want to write to it (y/n)?}| + ^ | ]]) feed("n") diff --git a/test/functional/core/path_spec.lua b/test/functional/core/path_spec.lua index a786887bbd..06fa13dca5 100644 --- a/test/functional/core/path_spec.lua +++ b/test/functional/core/path_spec.lua @@ -6,16 +6,19 @@ local command = helpers.command local insert = helpers.insert local feed = helpers.feed local is_os = helpers.is_os +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir +local write_file = helpers.write_file + +local function join_path(...) + local pathsep = (is_os('win') and '\\' or '/') + return table.concat({...}, pathsep) +end describe('path collapse', function() local targetdir local expected_path - local function join_path(...) - local pathsep = (is_os('win') and '\\' or '/') - return table.concat({...}, pathsep) - end - before_each(function() targetdir = join_path('test', 'functional', 'fixtures') clear() @@ -57,7 +60,28 @@ describe('path collapse', function() end) end) -describe('file search', function() +describe('expand wildcard', function() + before_each(clear) + + it('with special characters #24421', function() + local folders = is_os('win') and { + '{folder}', + 'folder$name' + } or { + 'folder-name', + 'folder#name' + } + for _, folder in ipairs(folders) do + mkdir(folder) + local file = join_path(folder, 'file.txt') + write_file(file, '') + eq(file, eval('expand("'..folder..'/*")')) + rmdir(folder) + end + end) +end) + +describe('file search (gf, <cfile>)', function() before_each(clear) it('find multibyte file name in line #20517', function() @@ -67,4 +91,42 @@ describe('file search', function() feed('gf') eq('filename_with_unicode_ααα', eval('expand("%:t")')) end) + + it('matches Windows drive-letter filepaths (without ":" in &isfname)', function() + local iswin = is_os('win') + local function test_cfile(input, expected, expected_win) + expected = (iswin and expected_win or expected) or input + command('%delete') + insert(input) + command('norm! 0') + eq(expected, eval('expand("<cfile>")')) + end + + test_cfile([[c:/d:/foo/bar.txt]]) -- TODO(justinmk): should return "d:/foo/bar.txt" ? + test_cfile([[//share/c:/foo/bar/]]) + test_cfile([[file://c:/foo/bar]]) + test_cfile([[file://c:/foo/bar:42]]) + test_cfile([[file://c:/foo/bar:42:666]]) + test_cfile([[https://c:/foo/bar]]) + test_cfile([[\foo\bar]], [[foo]], [[\foo\bar]]) + test_cfile([[/foo/bar]], [[/foo/bar]]) + test_cfile([[c:\foo\bar]], [[c:]], [[c:\foo\bar]]) + test_cfile([[c:\foo\bar:42:666]], [[c:]], [[c:\foo\bar]]) + test_cfile([[c:/foo/bar]]) + test_cfile([[c:/foo/bar:42]], [[c:/foo/bar]]) + test_cfile([[c:/foo/bar:42:666]], [[c:/foo/bar]]) + test_cfile([[c:foo\bar]], [[c]]) + test_cfile([[c:foo/bar]], [[c]]) + test_cfile([[c:foo]], [[c]]) + -- Examples from: https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#example-ways-to-refer-to-the-same-file + test_cfile([[c:\temp\test-file.txt]], [[c:]], [[c:\temp\test-file.txt]]) + test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) + test_cfile([[\\LOCALHOST\c$\temp\test-file.txt]], [[LOCALHOST]], [[\\LOCALHOST\c$\temp\test-file.txt]]) + -- not supported yet + test_cfile([[\\.\c:\temp\test-file.txt]], [[.]], [[\\.\c]]) + -- not supported yet + test_cfile([[\\?\c:\temp\test-file.txt]], [[c:]], [[\\]]) + test_cfile([[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]], [[.]], [[\\.\UNC\LOCALHOST\c$\temp\test-file.txt]]) + test_cfile([[\\127.0.0.1\c$\temp\test-file.txt]], [[127.0.0.1]], [[\\127.0.0.1\c$\temp\test-file.txt]]) + end) end) diff --git a/test/functional/editor/completion_spec.lua b/test/functional/editor/completion_spec.lua index 8c299636cc..ea3397d50d 100644 --- a/test/functional/editor/completion_spec.lua +++ b/test/functional/editor/completion_spec.lua @@ -941,6 +941,15 @@ describe('completion', function() end) end) + it('cmdline completion supports various string options', function() + eq('auto', funcs.getcompletion('set foldcolumn=', 'cmdline')[2]) + eq({'nosplit', 'split'}, funcs.getcompletion('set inccommand=', 'cmdline')) + eq({'ver:3,hor:6', 'hor:', 'ver:'}, funcs.getcompletion('set mousescroll=', 'cmdline')) + eq('BS', funcs.getcompletion('set termpastefilter=', 'cmdline')[2]) + eq('SpecialKey', funcs.getcompletion('set winhighlight=', 'cmdline')[1]) + eq('SpecialKey', funcs.getcompletion('set winhighlight=NonText:', 'cmdline')[1]) + end) + describe('from the commandline window', function() it('is cleared after CTRL-C', function () feed('q:') diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index 3e0f6d1fcc..436873b464 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -171,6 +171,7 @@ describe('swapfile detection', function() local screen2 = Screen.new(256, 40) screen2:attach() exec(init) + command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog). -- With shortmess+=F command('set shortmess+=F') @@ -219,11 +220,29 @@ describe('swapfile detection', function() nvim2:close() end) + it('default SwapExists handler selects "(E)dit" and skips prompt', function() + exec(init) + command('edit Xfile1') + command("put ='some text...'") + command('preserve') -- Make sure the swap file exists. + local nvimpid = funcs.getpid() + + local nvim1 = spawn(new_argv(), true, nil, true) + set_session(nvim1) + local screen = Screen.new(75, 18) + screen:attach() + exec(init) + feed(':edit Xfile1\n') + + screen:expect({ any = ('W325: Ignoring swapfile from Nvim process %d'):format(nvimpid) }) + nvim1:close() + end) + -- oldtest: Test_swap_prompt_splitwin() it('selecting "q" in the attention prompt', function() exec(init) command('edit Xfile1') - command('preserve') -- should help to make sure the swap file exists + command('preserve') -- Make sure the swap file exists. local screen = Screen.new(75, 18) screen:set_default_attr_ids({ @@ -235,7 +254,9 @@ describe('swapfile detection', function() set_session(nvim1) screen:attach() exec(init) + command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog). feed(':split Xfile1\n') + -- The default SwapExists handler does _not_ skip this prompt. screen:expect({ any = pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^') }) @@ -267,6 +288,7 @@ describe('swapfile detection', function() set_session(nvim2) screen:attach() exec(init) + command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog). command('set more') command('au bufadd * let foo_w = wincol()') feed(':e Xfile1<CR>') @@ -300,8 +322,9 @@ describe('swapfile detection', function() nvim2:close() end) - -- oldtest: Test_nocatch_process_still_running() - it('allows deleting swapfile created before boot vim-patch:8.2.2586', function() + --- @param swapexists boolean Enable the default SwapExists handler. + --- @param on_swapfile_running fun(screen: any) Called after swapfile ("STILL RUNNING") prompt. + local function test_swapfile_after_reboot(swapexists, on_swapfile_running) local screen = Screen.new(75, 30) screen:set_default_attr_ids({ [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText @@ -311,6 +334,9 @@ describe('swapfile detection', function() screen:attach() exec(init) + if not swapexists then + command('autocmd! nvim_swapfile') -- Delete the default handler (which skips the dialog). + end command('set nohidden') exec([=[ @@ -347,12 +373,7 @@ describe('swapfile detection', function() os.rename('Xswap', swname) feed(':edit Xswaptest<CR>') - screen:expect({any = table.concat({ - pesc('{2:E325: ATTENTION}'), - 'file name: .*Xswaptest', - 'process ID: %d* %(STILL RUNNING%)', - pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'), - }, '.*')}) + on_swapfile_running(screen) feed('e') @@ -374,7 +395,26 @@ describe('swapfile detection', function() }, '.*')}) feed('e') + end + + -- oldtest: Test_nocatch_process_still_running() + it('swapfile created before boot vim-patch:8.2.2586', function() + test_swapfile_after_reboot(false, function(screen) + screen:expect({any = table.concat({ + pesc('{2:E325: ATTENTION}'), + 'file name: .*Xswaptest', + 'process ID: %d* %(STILL RUNNING%)', + pesc('{1:[O]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: }^'), + }, '.*')}) + end) + end) + + it('swapfile created before boot + default SwapExists handler', function() + test_swapfile_after_reboot(true, function(screen) + screen:expect({ any = 'W325: Ignoring swapfile from Nvim process' }) + end) end) + end) describe('quitting swapfile dialog on startup stops TUI properly', function() diff --git a/test/functional/legacy/digraph_spec.lua b/test/functional/legacy/digraph_spec.lua index 0cb0bb84be..7eeb83eb5f 100644 --- a/test/functional/legacy/digraph_spec.lua +++ b/test/functional/legacy/digraph_spec.lua @@ -22,7 +22,7 @@ describe('digraph', function() {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) feed('1') screen:expect([[ @@ -31,7 +31,7 @@ describe('digraph', function() {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) feed('2') screen:expect([[ @@ -40,7 +40,7 @@ describe('digraph', function() {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) end) end) diff --git a/test/functional/legacy/edit_spec.lua b/test/functional/legacy/edit_spec.lua index 186bf395cc..939999e21b 100644 --- a/test/functional/legacy/edit_spec.lua +++ b/test/functional/legacy/edit_spec.lua @@ -43,7 +43,7 @@ describe('edit', function() {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) feed('=') screen:expect([[ diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index bd3d8f5247..c69990d84b 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -2401,6 +2401,14 @@ describe('lua stdlib', function() insert([[αα]]) eq({0,5}, exec_lua[[ return vim.region(0,{0,0},{0,4},'3',true)[0] ]]) end) + it('linewise', function() + insert(dedent( [[ + text tααt tααt text + text tαxt txtα tex + text tαxt tαxt + ]])) + eq({0,-1}, exec_lua[[ return vim.region(0,{1,5},{1,14},'V',true)[1] ]]) + end) it('getpos() input', function() insert('getpos') eq({0,6}, exec_lua[[ return vim.region(0,{0,0},'.','v',true)[0] ]]) diff --git a/test/functional/plugin/lsp/snippet_spec.lua b/test/functional/plugin/lsp/snippet_spec.lua index 7903885420..13df861b91 100644 --- a/test/functional/plugin/lsp/snippet_spec.lua +++ b/test/functional/plugin/lsp/snippet_spec.lua @@ -1,130 +1,70 @@ local helpers = require('test.functional.helpers')(after_each) -local snippet = require('vim.lsp._snippet') +local snippet = require('vim.lsp._snippet_grammar') local eq = helpers.eq local exec_lua = helpers.exec_lua -describe('vim.lsp._snippet', function() +describe('vim.lsp._snippet_grammar', function() before_each(helpers.clear) after_each(helpers.clear) local parse = function(...) - return exec_lua('return require("vim.lsp._snippet").parse(...)', ...) + local res = exec_lua('return require("vim.lsp._snippet_grammar").parse(...)', ...) + return res.data.children end - it('should parse only text', function() + it('parses only text', function() eq({ - type = snippet.NodeType.SNIPPET, - children = { - { - type = snippet.NodeType.TEXT, - raw = 'TE\\$\\}XT', - esc = 'TE$}XT', - }, - }, + { type = snippet.NodeType.Text, data = { text = 'TE$}XT' } }, }, parse('TE\\$\\}XT')) end) - it('should parse tabstop', function() + it('parses tabstops', function() eq({ - type = snippet.NodeType.SNIPPET, - children = { - { - type = snippet.NodeType.TABSTOP, - tabstop = 1, - }, - { - type = snippet.NodeType.TABSTOP, - tabstop = 2, - }, - }, + { type = snippet.NodeType.Tabstop, data = { tabstop = 1 } }, + { type = snippet.NodeType.Tabstop, data = { tabstop = 2 } }, }, parse('$1${2}')) end) - it('should parse placeholders', function() + it('parses nested placeholders', function() eq({ - type = snippet.NodeType.SNIPPET, - children = { - { - type = snippet.NodeType.PLACEHOLDER, + { + type = snippet.NodeType.Placeholder, + data = { tabstop = 1, - children = { - { - type = snippet.NodeType.PLACEHOLDER, + value = { + type = snippet.NodeType.Placeholder, + data = { tabstop = 2, - children = { - { - type = snippet.NodeType.TEXT, - raw = 'TE\\$\\}XT', - esc = 'TE$}XT', - }, - { - type = snippet.NodeType.TABSTOP, - tabstop = 3, - }, - { - type = snippet.NodeType.TABSTOP, - tabstop = 1, - transform = { - type = snippet.NodeType.TRANSFORM, - pattern = 'regex', - option = 'i', - format = { - { - type = snippet.NodeType.FORMAT, - capture_index = 1, - modifier = 'upcase', - }, - }, - }, - }, - { - type = snippet.NodeType.TEXT, - raw = 'TE\\$\\}XT', - esc = 'TE$}XT', - }, - }, + value = { type = snippet.NodeType.Tabstop, data = { tabstop = 3 } }, }, }, }, }, - }, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}')) + }, parse('${1:${2:${3}}}')) end) - it('should parse variables', function() + it('parses variables', function() eq({ - type = snippet.NodeType.SNIPPET, - children = { - { - type = snippet.NodeType.VARIABLE, - name = 'VAR', - }, - { - type = snippet.NodeType.VARIABLE, + { type = snippet.NodeType.Variable, data = { name = 'VAR' } }, + { type = snippet.NodeType.Variable, data = { name = 'VAR' } }, + { + type = snippet.NodeType.Variable, + data = { name = 'VAR', + default = { type = snippet.NodeType.Tabstop, data = { tabstop = 1 } }, }, - { - type = snippet.NodeType.VARIABLE, + }, + { + type = snippet.NodeType.Variable, + data = { name = 'VAR', - children = { + regex = 'regex', + options = '', + format = { { - type = snippet.NodeType.TABSTOP, - tabstop = 1, - }, - }, - }, - { - type = snippet.NodeType.VARIABLE, - name = 'VAR', - transform = { - type = snippet.NodeType.TRANSFORM, - pattern = 'regex', - format = { - { - type = snippet.NodeType.FORMAT, - capture_index = 1, - modifier = 'upcase', - }, + type = snippet.NodeType.Format, + data = { capture = 1, modifier = 'upcase' }, }, }, }, @@ -132,105 +72,82 @@ describe('vim.lsp._snippet', function() }, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}')) end) - it('should parse choice', function() + it('parses choice', function() eq({ - type = snippet.NodeType.SNIPPET, - children = { - { - type = snippet.NodeType.CHOICE, - tabstop = 1, - items = { - ',', - '|', - }, - }, + { + type = snippet.NodeType.Choice, + data = { tabstop = 1, values = { ',', '|' } }, }, }, parse('${1|\\,,\\||}')) end) - it('should parse format', function() - eq({ - type = snippet.NodeType.SNIPPET, - children = { + it('parses format', function() + eq( + { { - type = snippet.NodeType.VARIABLE, - name = 'VAR', - transform = { - type = snippet.NodeType.TRANSFORM, - pattern = 'regex', + type = snippet.NodeType.Variable, + data = { + name = 'VAR', + regex = 'regex', + options = '', format = { { - type = snippet.NodeType.FORMAT, - capture_index = 1, - modifier = 'upcase', + type = snippet.NodeType.Format, + data = { capture = 1, modifier = 'upcase' }, }, { - type = snippet.NodeType.FORMAT, - capture_index = 1, - if_text = 'if_text', - else_text = '', + type = snippet.NodeType.Format, + data = { capture = 1, if_text = 'if_text' }, }, { - type = snippet.NodeType.FORMAT, - capture_index = 1, - if_text = '', - else_text = 'else_text', + type = snippet.NodeType.Format, + data = { capture = 1, else_text = 'else_text' }, }, { - type = snippet.NodeType.FORMAT, - capture_index = 1, - else_text = 'else_text', - if_text = 'if_text', + type = snippet.NodeType.Format, + data = { capture = 1, if_text = 'if_text', else_text = 'else_text' }, }, { - type = snippet.NodeType.FORMAT, - capture_index = 1, - if_text = '', - else_text = 'else_text', + type = snippet.NodeType.Format, + data = { capture = 1, else_text = 'else_text' }, }, }, }, }, }, - }, parse('${VAR/regex/${1:/upcase}${1:+if_text}${1:-else_text}${1:?if_text:else_text}${1:else_text}/}')) + parse( + '${VAR/regex/${1:/upcase}${1:+if_text}${1:-else_text}${1:?if_text:else_text}${1:else_text}/}' + ) + ) end) - it('should parse empty strings', function() + it('parses empty strings', function() eq({ - children = { - { - children = { { - esc = '', - raw = '', - type = 7, - } }, + { + type = snippet.NodeType.Placeholder, + data = { tabstop = 1, - type = 2, - }, - { - esc = ' ', - raw = ' ', - type = 7, + value = { type = snippet.NodeType.Text, data = { text = '' } }, }, - { + }, + { + type = snippet.NodeType.Text, + data = { text = ' ' }, + }, + { + type = snippet.NodeType.Variable, + data = { name = 'VAR', - transform = { - format = { - { - capture_index = 1, - else_text = '', - if_text = '', - type = 6, - }, + regex = 'erg', + format = { + { + type = snippet.NodeType.Format, + data = { capture = 1, if_text = '' }, }, - option = 'g', - pattern = 'erg', - type = 5, }, - type = 3, + options = 'g', }, }, - type = 0, - }, parse('${1:} ${VAR/erg/${1:?:}/g}')) + }, parse('${1:} ${VAR/erg/${1:+}/g}')) end) end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 155c9ad96c..73e05d8d11 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -2301,7 +2301,7 @@ describe('LSP', function() { label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} }, { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} }, -- nested snippet tokens - { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} }, + { label='foocar', sortText="i", insertText='foodar(${1:${2|typ1,typ2|}}) {$0\\}', insertTextFormat=2, textEdit={} }, -- braced tabstop { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, textEdit={} }, -- plain text @@ -2317,7 +2317,7 @@ describe('LSP', function() { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="f", textEdit={newText='foobar'} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} } } } } }, - { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(var1 typ2 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } }, + { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(typ1) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:${2|typ1,typ2|}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar()', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar()${0}', insertTextFormat=2, textEdit={} } } } } }, { abbr = 'foocar', dup = 1, empty = 1, icase = 1, kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="k", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } }, } @@ -3483,6 +3483,50 @@ describe('LSP', function() end } end) + it("Fallback to command execution on resolve error", function() + clear() + exec_lua(create_server_definition) + local result = exec_lua([[ + local server = _create_server({ + capabilities = { + executeCommandProvider = { + commands = {"command:1"}, + }, + codeActionProvider = { + resolveProvider = true + } + }, + handlers = { + ["textDocument/codeAction"] = function() + return { + { + title = "Code Action 1", + command = { + title = "Command 1", + command = "command:1", + } + } + } + end, + ["codeAction/resolve"] = function() + return nil, "resolve failed" + end, + } + }) + + local client_id = vim.lsp.start({ + name = "dummy", + cmd = server.cmd, + }) + + vim.lsp.buf.code_action({ apply = true }) + vim.lsp.stop_client(client_id) + return server.messages + ]]) + eq("codeAction/resolve", result[4].method) + eq("workspace/executeCommand", result[5].method) + eq("command:1", result[5].params.command) + end) end) describe('vim.lsp.commands', function() it('Accepts only string keys', function() diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index f90e4f7e9d..39fc2c2562 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -44,7 +44,7 @@ describe(':terminal window', function() {7:6 } | {3:-- TERMINAL --} | ]]) - feed_data({'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'}) + feed_data('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') screen:expect([[ {7:1 }tty ready | {7:2 }rows: 6, cols: 48 | @@ -55,8 +55,6 @@ describe(':terminal window', function() {3:-- TERMINAL --} | ]]) - skip(is_os('win'), 'win: :terminal resize is unreliable #7007') - -- numberwidth=9 feed([[<C-\><C-N>]]) feed([[:set numberwidth=9 number<CR>i]]) @@ -69,7 +67,7 @@ describe(':terminal window', function() {7: 6 } | {3:-- TERMINAL --} | ]]) - feed_data({' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'}) + feed_data(' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') screen:expect([[ {7: 1 }tty ready | {7: 2 }rows: 6, cols: 48 | @@ -82,6 +80,41 @@ describe(':terminal window', function() end) end) + describe("with 'statuscolumn'", function() + it('wraps text', function() + command([[set number statuscolumn=++%l\ \ ]]) + screen:expect([[ + {7:++1 }tty ready | + {7:++2 }rows: 6, cols: 45 | + {7:++3 }{1: } | + {7:++4 } | + {7:++5 } | + {7:++6 } | + {3:-- TERMINAL --} | + ]]) + feed_data('\n\n\n\n\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + screen:expect([[ + {7:++4 } | + {7:++5 } | + {7:++6 } | + {7:++7 } | + {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRS| + {7:++9 }TUVWXYZ{1: } | + {3:-- TERMINAL --} | + ]]) + feed_data('\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + screen:expect([[ + {7:++7 } | + {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| + {7:++9 }STUVWXYZ | + {7:++10 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| + {7:++11 }STUVWXYZrows: 6, cols: 44 | + {7:++12 }{1: } | + {3:-- TERMINAL --} | + ]]) + end) + end) + describe("with 'colorcolumn'", function() before_each(function() feed([[<C-\><C-N>]]) diff --git a/test/functional/treesitter/fold_spec.lua b/test/functional/treesitter/fold_spec.lua index 9ed86e87f1..a8abbc002b 100644 --- a/test/functional/treesitter/fold_spec.lua +++ b/test/functional/treesitter/fold_spec.lua @@ -359,3 +359,116 @@ void ui_refresh(void) end) end) + +describe('treesitter foldtext', function() + local test_text = [[ +void qsort(void *base, size_t nel, size_t width, int (*compar)(const void *, const void *)) +{ + int width = INT_MAX, height = INT_MAX; + bool ext_widgets[kUIExtCount]; + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { + ext_widgets[i] = true; + } + + bool inclusive = ui_override(); + for (size_t i = 0; i < ui_count; i++) { + UI *ui = uis[i]; + width = MIN(ui->width, width); + height = MIN(ui->height, height); + foo = BAR(ui->bazaar, bazaar); + for (UIExtension j = 0; (int)j < kUIExtCount; j++) { + ext_widgets[j] &= (ui->ui_ext[j] || inclusive); + } + } +}]] + local screen + + before_each(function() + screen = Screen.new(60, 5) + screen:set_default_attr_ids({ + [0] = {foreground = Screen.colors.Blue, bold = true}, + [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray}; + [2] = {bold = true, background = Screen.colors.LightGray, foreground = Screen.colors.SeaGreen}; + [3] = {foreground = Screen.colors.DarkCyan, background = Screen.colors.LightGray}; + [4] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightGray}; + [5] = {bold = true, background = Screen.colors.LightGray, foreground = Screen.colors.Brown}; + [6] = {background = Screen.colors.Red1}; + [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Red}; + [8] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red}; + [9] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.Red}; + [10] = {bold = true}; + }) + screen:attach() + end) + + it('displays highlighted content', function() + command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]]) + insert(test_text) + exec_lua([[vim.treesitter.get_parser(0, "c")]]) + + feed('ggVGzf') + screen:expect{grid=[[ + {2:^void}{1: }{3:qsort}{4:(}{2:void}{1: }{5:*}{3:base}{4:,}{1: }{2:size_t}{1: }{3:nel}{4:,}{1: }{2:size_t}{1: }{3:width}{4:,}{1: }{2:int}{1: }{4:(}{5:*}{3:compa}| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end) + + it('handles deep nested captures', function() + command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext() updatetime=50]]) + insert([[ +function FoldInfo.new() + return setmetatable({ + start_counts = {}, + stop_counts = {}, + levels0 = {}, + levels = {}, + }, FoldInfo) +end]]) + exec_lua([[vim.treesitter.get_parser(0, "lua")]]) + + feed('ggjVGkzfgg') + screen:expect{grid=[[ + ^function FoldInfo.new() | + {1: }{5:return}{1: }{4:setmetatable({}{1:·····································}| + end | + {0:~ }| + | + ]]} + + command('hi! Visual guibg=Red') + feed('GVgg') + screen:expect{grid=[[ + ^f{6:unction FoldInfo.new()} | + {7: }{8:return}{7: }{9:setmetatable({}{7:·····································}| + {6:end} | + {0:~ }| + {10:-- VISUAL LINE --} | + ]]} + + feed('10l<C-V>') + screen:expect{grid=[[ + {6:function F}^oldInfo.new() | + {7: }{8:return}{7: }{9:se}{4:tmetatable({}{1:·····································}| + {6:end} | + {0:~ }| + {10:-- VISUAL BLOCK --} | + ]]} + end) + + it('falls back to default', function() + command([[set foldmethod=manual foldtext=v:lua.vim.treesitter.foldtext()]]) + insert(test_text) + + feed('ggVGzf') + screen:expect{grid=[[ + {1:^+-- 19 lines: void qsort(void *base, size_t nel, size_t widt}| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end) +end) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 17e6855ee4..e4766103c2 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -577,10 +577,10 @@ describe('Command-line coloring', function() | {EOB:~ }| {EOB:~ }| + {EOB:~ }| {MSEP: }| :+ | {ERR:E5404: Chunk 1 end 3 not in range (1, 2]}| - | :++^ | ]]) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 931e1f9985..7776e024b0 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -2523,6 +2523,8 @@ describe('highlight namespaces', function() [6] = {bold = true, reverse = true}; [7] = {reverse = true}; [8] = {foreground = Screen.colors.Gray20}; + [9] = {foreground = Screen.colors.Blue}; + [10] = {bold = true, foreground = Screen.colors.SeaGreen}; } ns1 = meths.create_namespace 'grungy' @@ -2655,4 +2657,21 @@ describe('highlight namespaces', function() "Normal:Visual", },res) end) + + it('Normal in set_hl #25474', function() + meths.set_hl(0, 'Normal', {bg='#333333'}) + command('highlight Ignore') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {6: }| + | + Ignore {8:xxx} {9:ctermf}| + {9:g=}15 {9:guifg=}| + bg | + {10:Press ENTER or type comma}| + {10:nd to continue}^ | + ]]} + end) end) diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 46a42e5beb..8a13796c04 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1366,7 +1366,7 @@ describe('ui/ext_messages', function() feed(":intro<cr>") screen:expect{grid=[[ - | + ^ | | | | diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index ea65cf35bd..38649a2be3 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -2439,14 +2439,14 @@ describe('builtin popupmenu', function() prefix | bef{n: word } | tex{n: }^ | - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) -- can't draw the pum, but check we don't crash screen:try_resize(12,2) screen:expect([[ {1:<<<}t^ | - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) -- but state is preserved, pum reappears diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index bc9517aa60..7cc1accd3f 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -795,7 +795,7 @@ local function screen_tests(linegrid) screen:try_resize(1, 1) screen:expect([[ resize^ | - {2:-- INSERT -} | + {2:-- INSERT --}| ]]) feed('<esc>:ls') diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim index 4e8859a33e..b0c2a15a3f 100644 --- a/test/old/testdir/setup.vim +++ b/test/old/testdir/setup.vim @@ -26,6 +26,9 @@ if exists('s:did_load') set sessionoptions+=options set viewoptions+=options set switchbuf= + if has('win32') + set isfname+=: + endif if g:testname !~ 'test_mapping.vim$' " Make "Q" switch to Ex mode. " This does not work for all tests. @@ -50,6 +53,9 @@ tlunmenu * " roughly equivalent to test_setmouse() in Vim func Ntest_setmouse(row, col) call nvim_input_mouse('move', '', '', 0, a:row - 1, a:col - 1) + if state('m') == '' + call getchar(0) + endif endfunc " Prevent Nvim log from writing to stderr. diff --git a/test/old/testdir/test_diffmode.vim b/test/old/testdir/test_diffmode.vim index d6d9f351fc..a06411883c 100644 --- a/test/old/testdir/test_diffmode.vim +++ b/test/old/testdir/test_diffmode.vim @@ -1616,6 +1616,42 @@ func Test_diff_scroll_wrap_on() call assert_equal(1, winsaveview().topline) normal! j call assert_equal(2, winsaveview().topline) + + bwipe! + bwipe! +endfunc + +func Test_diff_scroll_many_filler() + 20new + vnew + call setline(1, ['^^^', '^^^', '$$$', '$$$']) + diffthis + setlocal scrolloff=0 + wincmd p + call setline(1, ['^^^', '^^^'] + repeat(['###'], 41) + ['$$$', '$$$']) + diffthis + setlocal scrolloff=0 + wincmd p + redraw + + " Note: need a redraw after each scroll, otherwise the test always passes. + normal! G + redraw + call assert_equal(3, winsaveview().topline) + call assert_equal(18, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(3, winsaveview().topline) + call assert_equal(19, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(2, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + exe "normal! \<C-B>" + redraw + call assert_equal(1, winsaveview().topline) + call assert_equal(0, winsaveview().topfill) + bwipe! bwipe! endfunc diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index e5d7085d71..c3002b6747 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -340,6 +340,7 @@ func s:GetFilenameChecks() abort \ 'jsonnet': ['file.jsonnet', 'file.libsonnet'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], + \ 'just': ['justfile', 'Justfile', '.justfile', 'config.just'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'], \ 'kdl': ['file.kdl'], \ 'kivy': ['file.kv'], diff --git a/test/old/testdir/test_fold.vim b/test/old/testdir/test_fold.vim index ccd1bfecf8..e529a94174 100644 --- a/test/old/testdir/test_fold.vim +++ b/test/old/testdir/test_fold.vim @@ -1569,4 +1569,13 @@ func Test_foldcolumn_linebreak_control_char() bwipe! endfunc +" This used to cause invalid memory access +func Test_foldexpr_return_empty_string() + new + setlocal foldexpr='' foldmethod=expr + redraw + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim index 7b20f47f7a..b27b0be802 100644 --- a/test/old/testdir/test_functions.vim +++ b/test/old/testdir/test_functions.vim @@ -3303,4 +3303,22 @@ func Test_glob_extended_bash() let &shell=_shell endfunc +" Test for glob() with extended patterns (MS-Windows) +" Vim doesn't use 'shell' to expand wildcards on MS-Windows. +" Unlike bash, it doesn't support {,} expansion. +func Test_glob_extended_mswin() + CheckMSWindows + + call mkdir('Xtestglob/foo/bar/src', 'p') + call writefile([], 'Xtestglob/foo/bar/src/foo.sh') + call writefile([], 'Xtestglob/foo/bar/src/foo.h') + call writefile([], 'Xtestglob/foo/bar/src/foo.cpp') + + " Sort output of glob() otherwise we end up with different + " ordering depending on whether file system is case-sensitive. + let expected = ['Xtestglob/foo/bar/src/foo.cpp', 'Xtestglob/foo/bar/src/foo.h', 'Xtestglob/foo/bar/src/foo.sh'] + call assert_equal(expected, sort(glob('Xtestglob/**/foo.*', 0, 1))) + call delete('Xtestglob', 'rf') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_history.vim b/test/old/testdir/test_history.vim index f1c31dee04..bb6d671725 100644 --- a/test/old/testdir/test_history.vim +++ b/test/old/testdir/test_history.vim @@ -244,8 +244,13 @@ endfunc " Test for making sure the key value is not stored in history func Test_history_crypt_key() CheckFeature cryptv + call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt') call assert_equal('set bs=2 key= ts=8', histget(':')) + + call assert_fails("call feedkeys(':set bs=2 key-=abc ts=8\<CR>', 'xt')") + call assert_equal('set bs=2 key-= ts=8', histget(':')) + set key& bs& ts& endfunc diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index c56efe8786..76b7cc920b 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -299,11 +299,11 @@ func Test_set_completion() call assert_equal('"set tabstop thesaurus thesaurusfunc', @:) " Expand current value - call feedkeys(":set fileencodings=\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"set fileencodings=ucs-bom,utf-8,default,latin1', @:) + call feedkeys(":set suffixes=\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set suffixes=.bak,~,.o,.h,.info,.swp,.obj', @:) - call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:) + call feedkeys(":set suffixes:\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set suffixes:.bak,~,.o,.h,.info,.swp,.obj', @:) " Expand key codes. " call feedkeys(":set <H\<C-A>\<C-B>\"\<CR>", 'tx') @@ -364,14 +364,20 @@ func Test_set_completion() call assert_equal("\"set invtabstop=", @:) " Expand options for 'spellsuggest' - call feedkeys(":set spellsuggest=best,file:xyz\<Tab>\<C-B>\"\<CR>", 'xt') - call assert_equal("\"set spellsuggest=best,file:xyz", @:) - - " Expand value for 'key' - " set key=abcd - " call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt') - " call assert_equal('"set key=*****', @:) - " set key= + call feedkeys(":set spellsuggest=file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"set spellsuggest=file:test_options.vim", @:) + call feedkeys(":set spellsuggest=best,file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"set spellsuggest=best,file:test_options.vim", @:) + + " Expanding value for 'key' is disallowed + if exists('+key') + set key=abcd + call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set key=', @:) + call feedkeys(":set key-=\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set key-=', @:) + set key= + endif " Expand values for 'filetype' call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt') @@ -386,6 +392,286 @@ func Test_set_completion() call assert_equal('"set syntax=' .. getcompletion('a*', 'syntax')->join(), @:) endfunc +" Test handling of expanding individual string option values +func Test_set_completion_string_values() + " + " Test basic enum string options that have well-defined enum names + " + + " call assert_equal(['lastline', 'truncate', 'uhex'], getcompletion('set display=', 'cmdline')) + call assert_equal(['lastline', 'truncate', 'uhex', 'msgsep'], getcompletion('set display=', 'cmdline')) + call assert_equal(['truncate'], getcompletion('set display=t', 'cmdline')) + call assert_equal(['uhex'], getcompletion('set display=*ex*', 'cmdline')) + + " Test that if a value is set, it will populate the results, but only if + " typed value is empty. + set display=uhex,lastline + " call assert_equal(['uhex,lastline', 'lastline', 'truncate', 'uhex'], getcompletion('set display=', 'cmdline')) + call assert_equal(['uhex,lastline', 'lastline', 'truncate', 'uhex', 'msgsep'], getcompletion('set display=', 'cmdline')) + call assert_equal(['uhex'], getcompletion('set display=u', 'cmdline')) + " If the set value is part of the enum list, it will show as the first + " result with no duplicate. + set display=uhex + " call assert_equal(['uhex', 'lastline', 'truncate'], getcompletion('set display=', 'cmdline')) + call assert_equal(['uhex', 'lastline', 'truncate', 'msgsep'], getcompletion('set display=', 'cmdline')) + " If empty value, will just show the normal list without an empty item + set display= + " call assert_equal(['lastline', 'truncate', 'uhex'], getcompletion('set display=', 'cmdline')) + call assert_equal(['lastline', 'truncate', 'uhex', 'msgsep'], getcompletion('set display=', 'cmdline')) + " Test escaping of the values + " call assert_equal('vert:\|,fold:-,eob:~,lastline:@', getcompletion('set fillchars=', 'cmdline')[0]) + call assert_equal('vert:\|,foldsep:\|,fold:-', getcompletion('set fillchars=', 'cmdline')[0]) + + " Test comma-separated lists will expand after a comma. + call assert_equal(['uhex'], getcompletion('set display=truncate,*ex*', 'cmdline')) + " Also test the positioning of the expansion is correct + call feedkeys(":set display=truncate,l\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set display=truncate,lastline', @:) + set display& + + " Test single-value options will not expand after a comma + call assert_equal([], getcompletion('set ambw=single,', 'cmdline')) + + " Test the other simple options to make sure they have basic auto-complete, + " but don't exhaustively validate their results. + call assert_equal('single', getcompletion('set ambw=', 'cmdline')[0]) + call assert_match('light\|dark', getcompletion('set bg=', 'cmdline')[1]) + call assert_equal('indent', getcompletion('set backspace=', 'cmdline')[0]) + call assert_equal('yes', getcompletion('set backupcopy=', 'cmdline')[1]) + call assert_equal('backspace', getcompletion('set belloff=', 'cmdline')[1]) + call assert_equal('min:', getcompletion('set briopt=', 'cmdline')[1]) + if exists('+browsedir') + call assert_equal('current', getcompletion('set browsedir=', 'cmdline')[1]) + endif + call assert_equal('unload', getcompletion('set bufhidden=', 'cmdline')[1]) + call assert_equal('nowrite', getcompletion('set buftype=', 'cmdline')[1]) + call assert_equal('internal', getcompletion('set casemap=', 'cmdline')[1]) + if exists('+clipboard') + " call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1]) + call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[0]) + endif + call assert_equal('.', getcompletion('set complete=', 'cmdline')[1]) + call assert_equal('menu', getcompletion('set completeopt=', 'cmdline')[1]) + if exists('+completeslash') + call assert_equal('backslash', getcompletion('set completeslash=', 'cmdline')[1]) + endif + if exists('+cryptmethod') + call assert_equal('zip', getcompletion('set cryptmethod=', 'cmdline')[1]) + endif + if exists('+cursorlineopt') + call assert_equal('line', getcompletion('set cursorlineopt=', 'cmdline')[1]) + endif + call assert_equal('throw', getcompletion('set debug=', 'cmdline')[1]) + call assert_equal('ver', getcompletion('set eadirection=', 'cmdline')[1]) + call assert_equal('mac', getcompletion('set fileformat=', 'cmdline')[2]) + if exists('+foldclose') + call assert_equal('all', getcompletion('set foldclose=', 'cmdline')[0]) + endif + if exists('+foldmethod') + call assert_equal('expr', getcompletion('set foldmethod=', 'cmdline')[1]) + endif + if exists('+foldopen') + call assert_equal('all', getcompletion('set foldopen=', 'cmdline')[1]) + endif + call assert_equal('stack', getcompletion('set jumpoptions=', 'cmdline')[0]) + call assert_equal('stopsel', getcompletion('set keymodel=', 'cmdline')[1]) + call assert_equal('expr:1', getcompletion('set lispoptions=', 'cmdline')[1]) + call assert_match('popup', getcompletion('set mousemodel=', 'cmdline')[2]) + call assert_equal('bin', getcompletion('set nrformats=', 'cmdline')[1]) + if exists('+rightleftcmd') + call assert_equal('search', getcompletion('set rightleftcmd=', 'cmdline')[0]) + endif + call assert_equal('ver', getcompletion('set scrollopt=', 'cmdline')[1]) + call assert_equal('exclusive', getcompletion('set selection=', 'cmdline')[1]) + call assert_equal('key', getcompletion('set selectmode=', 'cmdline')[1]) + if exists('+ssop') + call assert_equal('buffers', getcompletion('set ssop=', 'cmdline')[1]) + endif + call assert_equal('statusline', getcompletion('set showcmdloc=', 'cmdline')[1]) + if exists('+signcolumn') + call assert_equal('yes', getcompletion('set signcolumn=', 'cmdline')[1]) + endif + if exists('+spelloptions') + call assert_equal('camel', getcompletion('set spelloptions=', 'cmdline')[0]) + endif + if exists('+spellsuggest') + call assert_equal('best', getcompletion('set spellsuggest+=', 'cmdline')[0]) + endif + call assert_equal('screen', getcompletion('set splitkeep=', 'cmdline')[1]) + " call assert_equal('sync', getcompletion('set swapsync=', 'cmdline')[1]) + call assert_equal('usetab', getcompletion('set switchbuf=', 'cmdline')[1]) + call assert_equal('ignore', getcompletion('set tagcase=', 'cmdline')[1]) + if exists('+termwintype') + call assert_equal('conpty', getcompletion('set termwintype=', 'cmdline')[1]) + endif + if exists('+toolbar') + call assert_equal('text', getcompletion('set toolbar=', 'cmdline')[1]) + endif + if exists('+tbis') + call assert_equal('medium', getcompletion('set tbis=', 'cmdline')[2]) + endif + if exists('+ttymouse') + set ttymouse= + call assert_equal('xterm2', getcompletion('set ttymouse=', 'cmdline')[1]) + set ttymouse& + endif + call assert_equal('insert', getcompletion('set virtualedit=', 'cmdline')[1]) + call assert_equal('longest', getcompletion('set wildmode=', 'cmdline')[1]) + call assert_equal('full', getcompletion('set wildmode=list,longest:', 'cmdline')[0]) + call assert_equal('tagfile', getcompletion('set wildoptions=', 'cmdline')[1]) + if exists('+winaltkeys') + call assert_equal('yes', getcompletion('set winaltkeys=', 'cmdline')[1]) + endif + + " Other string options that queries the system rather than fixed enum names + call assert_equal(['all', 'BufAdd'], getcompletion('set eventignore=', 'cmdline')[0:1]) + call assert_equal('latin1', getcompletion('set fileencodings=', 'cmdline')[1]) + " call assert_equal('top', getcompletion('set printoptions=', 'cmdline')[0]) + " call assert_equal('SpecialKey', getcompletion('set wincolor=', 'cmdline')[0]) + + call assert_equal('eol', getcompletion('set listchars+=', 'cmdline')[0]) + call assert_equal(['multispace', 'leadmultispace'], getcompletion('set listchars+=', 'cmdline')[-2:]) + call assert_equal('eol', getcompletion('setl listchars+=', 'cmdline')[0]) + call assert_equal(['multispace', 'leadmultispace'], getcompletion('setl listchars+=', 'cmdline')[-2:]) + call assert_equal('stl', getcompletion('set fillchars+=', 'cmdline')[0]) + call assert_equal('stl', getcompletion('setl fillchars+=', 'cmdline')[0]) + + " + " Unique string options below + " + + " keyprotocol: only auto-complete when after ':' with known protocol types + " call assert_equal([&keyprotocol], getcompletion('set keyprotocol=', 'cmdline')) + " call feedkeys(":set keyprotocol+=someterm:m\<Tab>\<C-B>\"\<CR>", 'xt') + " call assert_equal('"set keyprotocol+=someterm:mok2', @:) + " set keyprotocol& + + " previewpopup / completepopup + " call assert_equal('height:', getcompletion('set previewpopup=', 'cmdline')[0]) + " call assert_equal('EndOfBuffer', getcompletion('set previewpopup=highlight:End*Buffer', 'cmdline')[0]) + " call feedkeys(":set previewpopup+=border:\<Tab>\<C-B>\"\<CR>", 'xt') + " call assert_equal('"set previewpopup+=border:on', @:) + " call feedkeys(":set completepopup=height:10,align:\<Tab>\<C-B>\"\<CR>", 'xt') + " call assert_equal('"set completepopup=height:10,align:item', @:) + " call assert_equal([], getcompletion('set completepopup=bogusname:', 'cmdline')) + " set previewpopup& completepopup& + + " diffopt: special handling of algorithm:<alg_list> + call assert_equal('filler', getcompletion('set diffopt+=', 'cmdline')[0]) + call assert_equal([], getcompletion('set diffopt+=iblank,foldcolumn:', 'cmdline')) + call assert_equal('patience', getcompletion('set diffopt+=iblank,algorithm:pat*', 'cmdline')[0]) + + " highlight: special parsing, including auto-completing highlight groups + " after ':' + " call assert_equal([&hl, '8'], getcompletion('set hl=', 'cmdline')[0:1]) + " call assert_equal('8', getcompletion('set hl+=', 'cmdline')[0]) + " call assert_equal(['8:', '8b', '8i'], getcompletion('set hl+=8', 'cmdline')[0:2]) + " call assert_equal('8bi', getcompletion('set hl+=8b', 'cmdline')[0]) + " call assert_equal('NonText', getcompletion('set hl+=8:No*ext', 'cmdline')[0]) + " If all the display modes are used up we should be suggesting nothing. Make + " a hl typed option with all the modes which will look like '8bi-nrsuc2d=t', + " and make sure nothing is suggested from that. + " let hl_display_modes = join( + " \ filter(map(getcompletion('set hl+=8', 'cmdline'), + " \ {idx, val -> val[1]}), + " \ {idx, val -> val != ':'}), + " \ '') + " call assert_equal([], getcompletion('set hl+=8'..hl_display_modes, 'cmdline')) + + " + " Test flag lists + " + + " Test set=. Show the original value if nothing is typed after '='. + " Otherwise, the list should avoid showing what's already typed. + set mouse=v + call assert_equal(['v','a','n','i','c','h','r'], getcompletion('set mouse=', 'cmdline')) + set mouse=nvi + call assert_equal(['nvi','a','n','v','i','c','h','r'], getcompletion('set mouse=', 'cmdline')) + call assert_equal(['a','v','i','c','r'], getcompletion('set mouse=hn', 'cmdline')) + + " Test set+=. Never show original value, and it also tries to avoid listing + " flags that's already in the option value. + call assert_equal(['a','c','h','r'], getcompletion('set mouse+=', 'cmdline')) + call assert_equal(['a','c','r'], getcompletion('set mouse+=hn', 'cmdline')) + call assert_equal([], getcompletion('set mouse+=acrhn', 'cmdline')) + + " Test that the position of the expansion is correct (even if there are + " additional values after the current cursor) + call feedkeys(":set mouse=hn\<Left>\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set mouse=han', @:) + set mouse& + + " Test that other flag list options have auto-complete, but don't + " exhaustively validate their results. + if exists('+concealcursor') + call assert_equal('n', getcompletion('set cocu=', 'cmdline')[0]) + endif + call assert_equal('a', getcompletion('set cpo=', 'cmdline')[1]) + call assert_equal('t', getcompletion('set fo=', 'cmdline')[1]) + if exists('+guioptions') + call assert_equal('!', getcompletion('set go=', 'cmdline')[1]) + endif + call assert_equal('r', getcompletion('set shortmess=', 'cmdline')[1]) + call assert_equal('b', getcompletion('set whichwrap=', 'cmdline')[1]) + + " + "Test set-= + " + + " Normal single-value option just shows the existing value + set ambiwidth=double + call assert_equal(['double'], getcompletion('set ambw-=', 'cmdline')) + set ambiwidth& + + " Works on numbers and term options as well + call assert_equal([string(&laststatus)], getcompletion('set laststatus-=', 'cmdline')) + set t_Ce=testCe + " call assert_equal(['testCe'], getcompletion('set t_Ce-=', 'cmdline')) + set t_Ce& + + " Comma-separated lists should present each option + set diffopt=context:123,,,,,iblank,iwhiteall + call assert_equal(['context:123', 'iblank', 'iwhiteall'], getcompletion('set diffopt-=', 'cmdline')) + call assert_equal(['context:123', 'iblank'], getcompletion('set diffopt-=*n*', 'cmdline')) + call assert_equal(['iblank', 'iwhiteall'], getcompletion('set diffopt-=i', 'cmdline')) + " Don't present more than one option as it doesn't make sense in set-= + call assert_equal([], getcompletion('set diffopt-=iblank,', 'cmdline')) + " Test empty option + set diffopt= + call assert_equal([], getcompletion('set diffopt-=', 'cmdline')) + set diffopt& + + " Test escaping output + call assert_equal('vert:\|', getcompletion('set fillchars-=', 'cmdline')[0]) + + " Test files with commas in name are being parsed and escaped properly + set path=has\\\ space,file\\,with\\,comma,normal_file + if exists('+completeslash') + call assert_equal(['has\\\ space', 'file\,with\,comma', 'normal_file'], getcompletion('set path-=', 'cmdline')) + else + call assert_equal(['has\\\ space', 'file\\,with\\,comma', 'normal_file'], getcompletion('set path-=', 'cmdline')) + endif + set path& + + " Flag list should present orig value, then individual flags + set mouse=v + call assert_equal(['v'], getcompletion('set mouse-=', 'cmdline')) + set mouse=avn + call assert_equal(['avn','a','v','n'], getcompletion('set mouse-=', 'cmdline')) + " Don't auto-complete when we have at least one flags already + call assert_equal([], getcompletion('set mouse-=n', 'cmdline')) + " Test empty option + set mouse= + call assert_equal([], getcompletion('set mouse-=', 'cmdline')) + set mouse& + + " 'whichwrap' is an odd case where it's both flag list and comma-separated + set ww=b,h + call assert_equal(['b','h'], getcompletion('set ww-=', 'cmdline')) + set ww& +endfunc + func Test_set_option_errors() call assert_fails('set scroll=-1', 'E49:') call assert_fails('set backupcopy=', 'E474:') @@ -1433,7 +1719,7 @@ func Test_opt_cdhome() set cdhome& endfunc -func Test_set_completion_2() +func Test_set_completion_fuzzy() CheckOption termguicolors " Test default option completion diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index f60c0ddb59..80f73ba5d9 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -1,4 +1,4 @@ -" Test for reset 'scroll' and 'smoothscroll' +" Test for 'scroll', 'scrolloff', 'smoothscroll', etc. source check.vim source screendump.vim @@ -39,20 +39,74 @@ func Test_reset_scroll() endfunc func Test_scolloff_even_line_count() - new - resize 6 - setlocal scrolloff=3 - call setline(1, range(20)) - normal 2j - call assert_equal(1, getwininfo(win_getid())[0].topline) - normal j - call assert_equal(1, getwininfo(win_getid())[0].topline) - normal j - call assert_equal(2, getwininfo(win_getid())[0].topline) - normal j - call assert_equal(3, getwininfo(win_getid())[0].topline) - - bwipe! + new + resize 6 + setlocal scrolloff=3 + call setline(1, range(20)) + normal 2j + call assert_equal(1, getwininfo(win_getid())[0].topline) + normal j + call assert_equal(1, getwininfo(win_getid())[0].topline) + normal j + call assert_equal(2, getwininfo(win_getid())[0].topline) + normal j + call assert_equal(3, getwininfo(win_getid())[0].topline) + + bwipe! +endfunc + +func Test_mouse_scroll_inactive_with_cursorbind() + for scb in [0, 1] + for so in [0, 1, 2] + let msg = $'scb={scb} so={so}' + + new | only + let w1 = win_getid() + setlocal cursorbind + let &l:scb = scb + let &l:so = so + call setline(1, range(101, 109)) + rightbelow vnew + let w2 = win_getid() + setlocal cursorbind + let &l:scb = scb + let &l:so = so + call setline(1, range(101, 109)) + + normal! $ + call assert_equal(3, col('.', w1), msg) + call assert_equal(3, col('.', w2), msg) + call Ntest_setmouse(1, 1) + call feedkeys("\<ScrollWheelDown>", 'xt') + call assert_equal(4, line('w0', w1), msg) + call assert_equal(4 + so, line('.', w1), msg) + call assert_equal(1, line('w0', w2), msg) + call assert_equal(1, line('.', w2), msg) + call feedkeys("\<ScrollWheelDown>", 'xt') + call assert_equal(7, line('w0', w1), msg) + call assert_equal(7 + so, line('.', w1), msg) + call assert_equal(1, line('w0', w2), msg) + call assert_equal(1, line('.', w2), msg) + call feedkeys("\<ScrollWheelUp>", 'xt') + call assert_equal(4, line('w0', w1), msg) + call assert_equal(7 + so, line('.', w1), msg) + call assert_equal(1, line('w0', w2), msg) + call assert_equal(1, line('.', w2), msg) + call feedkeys("\<ScrollWheelUp>", 'xt') + call assert_equal(1, line('w0', w1), msg) + call assert_equal(7 + so, line('.', w1), msg) + call assert_equal(1, line('w0', w2), msg) + call assert_equal(1, line('.', w2), msg) + normal! 0 + call assert_equal(1, line('.', w1), msg) + call assert_equal(1, col('.', w1), msg) + call assert_equal(1, line('.', w2), msg) + call assert_equal(1, col('.', w2), msg) + + bwipe! + bwipe! + endfor + endfor endfunc func Test_CtrlE_CtrlY_stop_at_end() |