diff options
144 files changed, 2526 insertions, 1530 deletions
diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index a1929636e5..7da97c9690 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -14,6 +14,7 @@ " 2024 May 10 by Vim Project: recursively delete directories by default " 2024 May 13 by Vim Project: prefer scp over pscp " 2024 Jun 04 by Vim Project: set bufhidden if buffer changed, nohidden is set and buffer shall be switched (#14915) +" 2024 Jun 13 by Vim Project: glob() on Windows fails when a directory name contains [] (#14952) " Former Maintainer: Charles E Campbell " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 @@ -5756,16 +5757,20 @@ fun! s:NetrwGlob(direntry,expr,pare) let filelist= w:netrw_treedict[a:direntry] endif let w:netrw_liststyle= keep_liststyle - elseif v:version > 704 || (v:version == 704 && has("patch656")) - let filelist= glob(s:ComposePath(fnameescape(a:direntry),a:expr),0,1,1) - if a:pare - let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') - endif else - let filelist= glob(s:ComposePath(fnameescape(a:direntry),a:expr),0,1) - if a:pare - let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') - endif + let path= s:ComposePath(fnameescape(a:direntry),a:expr) + if has("win32") + " escape [ so it is not detected as wildcard character, see :h wildcard + let path= substitute(path, '[', '[[]', 'g') + endif + if v:version > 704 || (v:version == 704 && has("patch656")) + let filelist= glob(path,0,1,1) + else + let filelist= glob(path,0,1) + endif + if a:pare + let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') + endif endif " call Dret("s:NetrwGlob ".string(filelist)) return filelist diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim index c0034f8a7a..d0e706e83a 100644 --- a/runtime/autoload/zip.vim +++ b/runtime/autoload/zip.vim @@ -4,17 +4,18 @@ " Version: 33 " Maintainer: This runtime file is looking for a new maintainer. " Former Maintainer: Charles E Campbell +" Last Change: +" 2024 Jun 16 by Vim Project: handle whitespace on Windows properly (#14998) " License: Vim License (see vim's :help license) -" Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1 -" Permission is hereby granted to use and distribute this code, -" with or without modifications, provided that this copyright -" notice is copied with it. Like anything else that's free, -" zip.vim and zipPlugin.vim are provided *as is* and comes with -" no warranty of any kind, either expressed or implied. By using -" this plugin, you agree that in no event will the copyright -" holder be liable for any damages resulting from the use -" of this software. -"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore() +" Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1 +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" zip.vim and zipPlugin.vim are provided *as is* and comes with +" no warranty of any kind, either expressed or implied. By using +" this plugin, you agree that in no event will the copyright +" holder be liable for any damages resulting from the use +" of this software. " --------------------------------------------------------------------- " Load Once: {{{1 @@ -214,7 +215,6 @@ endfun " --------------------------------------------------------------------- " zip#Read: {{{2 fun! zip#Read(fname,mode) -" call Dfunc("zip#Read(fname<".a:fname.">,mode=".a:mode.")") let repkeep= &report set report=10 @@ -226,15 +226,12 @@ fun! zip#Read(fname,mode) let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') let fname = substitute(fname, '[', '[[]', 'g') endif -" call Decho("zipfile<".zipfile.">") -" call Decho("fname <".fname.">") " sanity check if !executable(substitute(g:zip_unzipcmd,'\s\+.*$','','')) redraw! echohl Error | echo "***error*** (zip#Read) sorry, your system doesn't appear to have the ".g:zip_unzipcmd." program" | echohl None " call inputsave()|call input("Press <cr> to continue")|call inputrestore() let &report= repkeep -" call Dret("zip#Write") return endif @@ -242,10 +239,8 @@ fun! zip#Read(fname,mode) " exe "keepj sil! r! ".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1) " but allows zipfile://... entries in quickfix lists let temp = tempname() -" call Decho("using temp file<".temp.">") let fn = expand('%:p') - exe "sil! !".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1).' > '.temp -" call Decho("exe sil! !".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fnameescape(fname),1).' > '.temp) + exe "sil! !".g:zip_unzipcmd." -p -- ".s:Escape(zipfile,1)." ".s:Escape(fname,1).' > '.temp sil exe 'keepalt file '.temp sil keepj e! sil exe 'keepalt file '.fnameescape(fn) @@ -254,11 +249,9 @@ fun! zip#Read(fname,mode) filetype detect " cleanup - " keepj 0d " used to be needed for the ...r! ... method set nomod let &report= repkeep -" call Dret("zip#Read") endfun " --------------------------------------------------------------------- @@ -422,7 +415,6 @@ endfun " --------------------------------------------------------------------- " s:Escape: {{{2 fun! s:Escape(fname,isfilt) -" call Dfunc("QuoteFileDir(fname<".a:fname."> isfilt=".a:isfilt.")") if exists("*shellescape") if a:isfilt let qnameq= shellescape(a:fname,1) @@ -432,7 +424,10 @@ fun! s:Escape(fname,isfilt) else let qnameq= g:zip_shq.escape(a:fname,g:zip_shq).g:zip_shq endif -" call Dret("QuoteFileDir <".qnameq.">") + if exists("+shellslash") && &shellslash && &shell =~ "cmd.exe" + " renormalize directory separator on Windows + let qnameq=substitute(qnameq, '/', '\\', 'g') + endif return qnameq endfun diff --git a/runtime/colors/vim.lua b/runtime/colors/vim.lua index 7231418f5f..5b9309ab38 100644 --- a/runtime/colors/vim.lua +++ b/runtime/colors/vim.lua @@ -56,6 +56,8 @@ hi('CursorLineFold', { link = 'FoldColumn' }) hi('CurSearch', { link = 'Search' }) hi('PmenuKind', { link = 'Pmenu' }) hi('PmenuKindSel', { link = 'PmenuSel' }) +hi('PmenuMatch', { link = 'Pmenu' }) +hi('PmenuMatchSel', { link = 'PmenuSel' }) hi('PmenuExtra', { link = 'Pmenu' }) hi('PmenuExtraSel', { link = 'PmenuSel' }) hi('Substitute', { link = 'Search' }) @@ -209,81 +211,85 @@ hi('@lsp.type.variable', { link = 'Identifier' }) if vim.o.background == 'light' then -- Default colors only used with a light background. - hi('ColorColumn', { bg = 'LightRed', ctermbg = 'LightRed' }) - hi('CursorColumn', { bg = 'Grey90', ctermbg = 'LightGrey' }) - hi('CursorLine', { bg = 'Grey90', cterm = { underline = true } }) - hi('CursorLineNr', { fg = 'Brown', bold = true, ctermfg = 'Brown', cterm = { underline = true } }) - hi('DiffAdd', { bg = 'LightBlue', ctermbg = 'LightBlue' }) - hi('DiffChange', { bg = 'LightMagenta', ctermbg = 'LightMagenta' }) - hi('DiffDelete', { fg = 'Blue', bg = 'LightCyan', bold = true, ctermfg = 'Blue', ctermbg = 'LightCyan' }) - hi('Directory', { fg = 'Blue', ctermfg = 'DarkBlue' }) - hi('FoldColumn', { fg = 'DarkBlue', bg = 'Grey', ctermfg = 'DarkBlue', ctermbg = 'Grey' }) - hi('Folded', { fg = 'DarkBlue', bg = 'LightGrey', ctermfg = 'DarkBlue', ctermbg = 'Grey' }) - hi('LineNr', { fg = 'Brown', ctermfg = 'Brown' }) - hi('MatchParen', { bg = 'Cyan', ctermbg = 'Cyan' }) - hi('MoreMsg', { fg = 'SeaGreen', bold = true, ctermfg = 'DarkGreen' }) - hi('Pmenu', { bg = 'LightMagenta', ctermfg = 'Black', ctermbg = 'LightMagenta' }) - hi('PmenuSel', { bg = 'Grey', ctermfg = 'Black', ctermbg = 'LightGrey' }) - hi('PmenuThumb', { bg = 'Black', ctermbg = 'Black' }) - hi('Question', { fg = 'SeaGreen', bold = true, ctermfg = 'DarkGreen' }) - hi('Search', { bg = 'Yellow', ctermbg = 'Yellow' }) - hi('SignColumn', { fg = 'DarkBlue', bg = 'Grey', ctermfg = 'DarkBlue', ctermbg = 'Grey' }) - hi('SpecialKey', { fg = 'Blue', ctermfg = 'DarkBlue' }) - hi('SpellBad', { sp = 'Red', undercurl = true, ctermbg = 'LightRed' }) - hi('SpellCap', { sp = 'Blue', undercurl = true, ctermbg = 'LightBlue' }) - hi('SpellLocal', { sp = 'DarkCyan', undercurl = true, ctermbg = 'Cyan' }) - hi('SpellRare', { sp = 'Magenta', undercurl = true, ctermbg = 'LightMagenta' }) - hi('TabLine', { bg = 'LightGrey', underline = true, ctermfg = 'Black', ctermbg = 'LightGrey', cterm = { underline = true } }) - hi('Title', { fg = 'Magenta', bold = true, ctermfg = 'DarkMagenta' }) - hi('Visual', { fg = 'Black', bg = 'LightGrey', ctermfg = 'Black', ctermbg = 'Grey' }) - hi('WarningMsg', { fg = 'Red', ctermfg = 'DarkRed' }) - hi('Comment', { fg = 'Blue', ctermfg = 'DarkBlue' }) - hi('Constant', { fg = 'Magenta', ctermfg = 'DarkRed' }) - hi('Special', { fg = '#6a5acd', ctermfg = 'DarkMagenta' }) - hi('Identifier', { fg = 'DarkCyan', ctermfg = 'DarkCyan' }) - hi('Statement', { fg = 'Brown', bold = true, ctermfg = 'Brown' }) - hi('PreProc', { fg = '#6a0dad', ctermfg = 'DarkMagenta' }) - hi('Type', { fg = 'SeaGreen', bold = true, ctermfg = 'DarkGreen' }) - hi('Underlined', { fg = 'SlateBlue', underline = true, ctermfg = 'DarkMagenta', cterm = { underline = true } }) - hi('Ignore', { ctermfg = 'White' }) + hi('ColorColumn', { bg = 'LightRed', ctermbg = 'LightRed' }) + hi('CursorColumn', { bg = 'Grey90', ctermbg = 'LightGrey' }) + hi('CursorLine', { bg = 'Grey90', cterm = { underline = true } }) + hi('CursorLineNr', { fg = 'Brown', bold = true, ctermfg = 'Brown', cterm = { underline = true } }) + hi('DiffAdd', { bg = 'LightBlue', ctermbg = 'LightBlue' }) + hi('DiffChange', { bg = 'LightMagenta', ctermbg = 'LightMagenta' }) + hi('DiffDelete', { fg = 'Blue', bg = 'LightCyan', bold = true, ctermfg = 'Blue', ctermbg = 'LightCyan' }) + hi('Directory', { fg = 'Blue', ctermfg = 'DarkBlue' }) + hi('FoldColumn', { fg = 'DarkBlue', bg = 'Grey', ctermfg = 'DarkBlue', ctermbg = 'Grey' }) + hi('Folded', { fg = 'DarkBlue', bg = 'LightGrey', ctermfg = 'DarkBlue', ctermbg = 'Grey' }) + hi('LineNr', { fg = 'Brown', ctermfg = 'Brown' }) + hi('MatchParen', { bg = 'Cyan', ctermbg = 'Cyan' }) + hi('MoreMsg', { fg = 'SeaGreen', bold = true, ctermfg = 'DarkGreen' }) + hi('Pmenu', { bg = 'LightMagenta', ctermfg = 'Black', ctermbg = 'LightMagenta' }) + hi('PmenuSel', { bg = 'Grey', ctermfg = 'Black', ctermbg = 'LightGrey' }) + hi('PmenuThumb', { bg = 'Black', ctermbg = 'Black' }) + hi('Question', { fg = 'SeaGreen', bold = true, ctermfg = 'DarkGreen' }) + hi('Search', { bg = 'Yellow', ctermbg = 'Yellow' }) + hi('SignColumn', { fg = 'DarkBlue', bg = 'Grey', ctermfg = 'DarkBlue', ctermbg = 'Grey' }) + hi('SpecialKey', { fg = 'Blue', ctermfg = 'DarkBlue' }) + hi('SpellBad', { sp = 'Red', undercurl = true, ctermbg = 'LightRed' }) + hi('SpellCap', { sp = 'Blue', undercurl = true, ctermbg = 'LightBlue' }) + hi('SpellLocal', { sp = 'DarkCyan', undercurl = true, ctermbg = 'Cyan' }) + hi('SpellRare', { sp = 'Magenta', undercurl = true, ctermbg = 'LightMagenta' }) + hi('StatusLineTerm', { fg = 'White', bg = 'DarkGreen', bold = true, ctermfg = 'White', ctermbg = 'DarkGreen', cterm = { bold = true } }) + hi('StatusLineTermNC', { fg = 'White', bg = 'DarkGreen', ctermfg = 'White', ctermbg = 'DarkGreen' }) + hi('TabLine', { bg = 'LightGrey', underline = true, ctermfg = 'Black', ctermbg = 'LightGrey', cterm = { underline = true } }) + hi('Title', { fg = 'Magenta', bold = true, ctermfg = 'DarkMagenta' }) + hi('Visual', { fg = 'Black', bg = 'LightGrey', ctermfg = 'Black', ctermbg = 'Grey' }) + hi('WarningMsg', { fg = 'Red', ctermfg = 'DarkRed' }) + hi('Comment', { fg = 'Blue', ctermfg = 'DarkBlue' }) + hi('Constant', { fg = 'Magenta', ctermfg = 'DarkRed' }) + hi('Special', { fg = '#6a5acd', ctermfg = 'DarkMagenta' }) + hi('Identifier', { fg = 'DarkCyan', ctermfg = 'DarkCyan' }) + hi('Statement', { fg = 'Brown', bold = true, ctermfg = 'Brown' }) + hi('PreProc', { fg = '#6a0dad', ctermfg = 'DarkMagenta' }) + hi('Type', { fg = 'SeaGreen', bold = true, ctermfg = 'DarkGreen' }) + hi('Underlined', { fg = 'SlateBlue', underline = true, ctermfg = 'DarkMagenta', cterm = { underline = true } }) + hi('Ignore', { ctermfg = 'White' }) else -- Default colors only used with a dark background. - hi('ColorColumn', { bg = 'DarkRed', ctermbg = 'DarkRed' }) - hi('CursorColumn', { bg = 'Grey40', ctermbg = 'DarkGrey' }) - hi('CursorLine', { bg = 'Grey40', cterm = { underline = true } }) - hi('CursorLineNr', { fg = 'Yellow', bold = true, ctermfg = 'Yellow', cterm = { underline = true } }) - hi('DiffAdd', { bg = 'DarkBlue', ctermbg = 'DarkBlue' }) - hi('DiffChange', { bg = 'DarkMagenta', ctermbg = 'DarkMagenta' }) - hi('DiffDelete', { fg = 'Blue', bg = 'DarkCyan', bold = true, ctermfg = 'Blue', ctermbg = 'DarkCyan' }) - hi('Directory', { fg = 'Cyan', ctermfg = 'LightCyan' }) - hi('FoldColumn', { fg = 'Cyan', bg = 'Grey', ctermfg = 'Cyan', ctermbg = 'DarkGrey' }) - hi('Folded', { fg = 'Cyan', bg = 'DarkGrey', ctermfg = 'Cyan', ctermbg = 'DarkGrey' }) - hi('LineNr', { fg = 'Yellow', ctermfg = 'Yellow' }) - hi('MatchParen', { bg = 'DarkCyan', ctermbg = 'DarkCyan' }) - hi('MoreMsg', { fg = 'SeaGreen', bold = true, ctermfg = 'LightGreen' }) - hi('Pmenu', { bg = 'Magenta', ctermfg = 'Black', ctermbg = 'Magenta' }) - hi('PmenuSel', { bg = 'DarkGrey', ctermfg = 'DarkGrey', ctermbg = 'Black' }) - hi('PmenuThumb', { bg = 'White', ctermbg = 'White' }) - hi('Question', { fg = 'Green', bold = true, ctermfg = 'LightGreen' }) - hi('Search', { fg = 'Black', bg = 'Yellow', ctermfg = 'Black', ctermbg = 'Yellow' }) - hi('SignColumn', { fg = 'Cyan', bg = 'Grey', ctermfg = 'Cyan', ctermbg = 'DarkGrey' }) - hi('SpecialKey', { fg = 'Cyan', ctermfg = 'LightBlue' }) - hi('SpellBad', { sp = 'Red', undercurl = true, ctermbg = 'Red' }) - hi('SpellCap', { sp = 'Blue', undercurl = true, ctermbg = 'Blue' }) - hi('SpellLocal', { sp = 'Cyan', undercurl = true, ctermbg = 'Cyan' }) - hi('SpellRare', { sp = 'Magenta', undercurl = true, ctermbg = 'Magenta' }) - hi('TabLine', { bg = 'DarkGrey', underline = true, ctermfg = 'White', ctermbg = 'DarkGrey', cterm = { underline = true } }) - hi('Title', { fg = 'Magenta', bold = true, ctermfg = 'LightMagenta' }) - hi('Visual', { fg = 'LightGrey', bg = '#575757', ctermfg = 'Black', ctermbg = 'Grey' }) - hi('WarningMsg', { fg = 'Red', ctermfg = 'LightRed' }) - hi('Comment', { fg = '#80a0ff', ctermfg = 'Cyan' }) - hi('Constant', { fg = '#ffa0a0', ctermfg = 'Magenta' }) - hi('Special', { fg = 'Orange', ctermfg = 'LightRed' }) - hi('Identifier', { fg = '#40ffff', ctermfg = 'Cyan', cterm = { bold = true } }) - hi('Statement', { fg = '#ffff60', bold = true, ctermfg = 'Yellow' }) - hi('PreProc', { fg = '#ff80ff', ctermfg = 'LightBlue' }) - hi('Type', { fg = '#60ff60', bold = true, ctermfg = 'LightGreen' }) - hi('Underlined', { fg = '#80a0ff', underline = true, ctermfg = 'LightBlue', cterm = { underline = true } }) - hi('Ignore', { ctermfg = 'Black' }) + hi('ColorColumn', { bg = 'DarkRed', ctermbg = 'DarkRed' }) + hi('CursorColumn', { bg = 'Grey40', ctermbg = 'DarkGrey' }) + hi('CursorLine', { bg = 'Grey40', cterm = { underline = true } }) + hi('CursorLineNr', { fg = 'Yellow', bold = true, ctermfg = 'Yellow', cterm = { underline = true } }) + hi('DiffAdd', { bg = 'DarkBlue', ctermbg = 'DarkBlue' }) + hi('DiffChange', { bg = 'DarkMagenta', ctermbg = 'DarkMagenta' }) + hi('DiffDelete', { fg = 'Blue', bg = 'DarkCyan', bold = true, ctermfg = 'Blue', ctermbg = 'DarkCyan' }) + hi('Directory', { fg = 'Cyan', ctermfg = 'LightCyan' }) + hi('FoldColumn', { fg = 'Cyan', bg = 'Grey', ctermfg = 'Cyan', ctermbg = 'DarkGrey' }) + hi('Folded', { fg = 'Cyan', bg = 'DarkGrey', ctermfg = 'Cyan', ctermbg = 'DarkGrey' }) + hi('LineNr', { fg = 'Yellow', ctermfg = 'Yellow' }) + hi('MatchParen', { bg = 'DarkCyan', ctermbg = 'DarkCyan' }) + hi('MoreMsg', { fg = 'SeaGreen', bold = true, ctermfg = 'LightGreen' }) + hi('Pmenu', { bg = 'Magenta', ctermfg = 'Black', ctermbg = 'Magenta' }) + hi('PmenuSel', { bg = 'DarkGrey', ctermfg = 'DarkGrey', ctermbg = 'Black' }) + hi('PmenuThumb', { bg = 'White', ctermbg = 'White' }) + hi('Question', { fg = 'Green', bold = true, ctermfg = 'LightGreen' }) + hi('Search', { fg = 'Black', bg = 'Yellow', ctermfg = 'Black', ctermbg = 'Yellow' }) + hi('SignColumn', { fg = 'Cyan', bg = 'Grey', ctermfg = 'Cyan', ctermbg = 'DarkGrey' }) + hi('SpecialKey', { fg = 'Cyan', ctermfg = 'LightBlue' }) + hi('SpellBad', { sp = 'Red', undercurl = true, ctermbg = 'Red' }) + hi('SpellCap', { sp = 'Blue', undercurl = true, ctermbg = 'Blue' }) + hi('SpellLocal', { sp = 'Cyan', undercurl = true, ctermbg = 'Cyan' }) + hi('SpellRare', { sp = 'Magenta', undercurl = true, ctermbg = 'Magenta' }) + hi('StatusLineTerm', { fg = 'Black', bg = 'LightGreen', bold = true, ctermfg = 'Black', ctermbg = 'LightGreen', cterm = { bold = true } }) + hi('StatusLineTermNC', { fg = 'Black', bg = 'LightGreen', ctermfg = 'Black', ctermbg = 'LightGreen' }) + hi('TabLine', { bg = 'DarkGrey', underline = true, ctermfg = 'White', ctermbg = 'DarkGrey', cterm = { underline = true } }) + hi('Title', { fg = 'Magenta', bold = true, ctermfg = 'LightMagenta' }) + hi('Visual', { fg = 'LightGrey', bg = '#575757', ctermfg = 'Black', ctermbg = 'Grey' }) + hi('WarningMsg', { fg = 'Red', ctermfg = 'LightRed' }) + hi('Comment', { fg = '#80a0ff', ctermfg = 'Cyan' }) + hi('Constant', { fg = '#ffa0a0', ctermfg = 'Magenta' }) + hi('Special', { fg = 'Orange', ctermfg = 'LightRed' }) + hi('Identifier', { fg = '#40ffff', ctermfg = 'Cyan', cterm = { bold = true } }) + hi('Statement', { fg = '#ffff60', bold = true, ctermfg = 'Yellow' }) + hi('PreProc', { fg = '#ff80ff', ctermfg = 'LightBlue' }) + hi('Type', { fg = '#60ff60', bold = true, ctermfg = 'LightGreen' }) + hi('Underlined', { fg = '#80a0ff', underline = true, ctermfg = 'LightBlue', cterm = { underline = true } }) + hi('Ignore', { ctermfg = 'Black' }) end --stylua: ignore end diff --git a/runtime/compiler/javac.vim b/runtime/compiler/javac.vim index f5fe84124f..9bd4cdf270 100644 --- a/runtime/compiler/javac.vim +++ b/runtime/compiler/javac.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Java Development Kit Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2024 Apr 03 +" Last Change: 2024 Jun 14 if exists("current_compiler") finish @@ -11,7 +11,12 @@ let current_compiler = "javac" let s:cpo_save = &cpo set cpo&vim -CompilerSet makeprg=javac +if exists("g:javac_makeprg_params") + execute $'CompilerSet makeprg=javac\ {escape(g:javac_makeprg_params, ' \|"')}' +else + CompilerSet makeprg=javac +endif + CompilerSet errorformat=%E%f:%l:\ error:\ %m, \%W%f:%l:\ warning:\ %m, \%-Z%p^, diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index f92a69af4e..a7b59f20bc 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2800,7 +2800,7 @@ nvim_set_decoration_provider({ns_id}, {opts}) Note: this function should not be called often. Rather, the callbacks themselves can be used to throttle unneeded callbacks. the `on_start` callback can return `false` to disable the provider until the next redraw. - Similarly, return `false` in `on_win` will skip the `on_lines` calls for + Similarly, return `false` in `on_win` will skip the `on_line` calls for that window (but any extmarks set in `on_win` will still be used). A plugin managing multiple sources of decoration should ideally only set one provider, and merge the sources internally. You can use multiple `ns_id` @@ -2809,10 +2809,10 @@ nvim_set_decoration_provider({ns_id}, {opts}) Note: doing anything other than setting extmarks is considered experimental. Doing things like changing options are not explicitly forbidden, but is likely to have unexpected consequences (such as 100% CPU - consumption). doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is + consumption). Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious for the moment. - Note: It is not allowed to remove or update extmarks in 'on_line' + Note: It is not allowed to remove or update extmarks in `on_line` callbacks. Attributes: ~ diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 1a762c6ec0..1e0df1918b 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -701,33 +701,33 @@ clearmatches([{win}]) *clearmatches()* col({expr} [, {winid}]) *col()* The result is a Number, which is the byte index of the column - position given with {expr}. The accepted positions are: - . the cursor position - $ the end of the cursor line (the result is the - number of bytes in the cursor line plus one) - 'x position of mark x (if the mark is not set, 0 is - returned) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. + position given with {expr}. + For accepted positions see |getpos()|. + When {expr} is "$", it means the end of the cursor line, so + the result is the number of bytes in the cursor line plus one. Additionally {expr} can be [lnum, col]: a |List| with the line and column number. Most useful when the column is "$", to get the last column of a specific line. When "lnum" or "col" is out of range then col() returns zero. + With the optional {winid} argument the values are obtained for that window instead of the current window. + To get the line number use |line()|. To get both use |getpos()|. + For the screen column position use |virtcol()|. For the character position use |charcol()|. + Note that only marks in the current file can be used. + Examples: >vim echo col(".") " column of cursor echo col("$") " length of cursor line plus one echo col("'t") " column of mark t echo col("'" .. markname) " column of mark markname -< The first column is 1. Returns 0 if {expr} is invalid or when +< + The first column is 1. Returns 0 if {expr} is invalid or when the window with ID {winid} is not found. For an uppercase mark the column may actually be in another buffer. @@ -2739,9 +2739,34 @@ getpid() *getpid()* This is a unique number, until Vim exits. getpos({expr}) *getpos()* - Get the position for String {expr}. For possible values of - {expr} see |line()|. For getting the cursor position see - |getcurpos()|. + Get the position for String {expr}. + The accepted values for {expr} are: + . The cursor position. + $ The last line in the current buffer. + 'x Position of mark x (if the mark is not set, 0 is + returned for all values). + w0 First line visible in current window (one if the + display isn't updated, e.g. in silent Ex mode). + w$ Last line visible in current window (this is one + less than "w0" if no lines are visible). + v When not in Visual mode, returns the cursor + position. In Visual mode, returns the other end + of the Visual area. A good way to think about + this is that in Visual mode "v" and "." complement + each other. While "." refers to the cursor + position, "v" refers to where |v_o| would move the + cursor. As a result, you can use "v" and "." + together to work on all of a selection in + characterwise Visual mode. If the cursor is at + the end of a characterwise Visual area, "v" refers + to the start of the same Visual area. And if the + cursor is at the start of a characterwise Visual + area, "v" refers to the end of the same Visual + area. "v" differs from |'<| and |'>| in that it's + updated right away. + Note that a mark in another file can be used. The line number + then applies to another buffer. + The result is a |List| with four numbers: [bufnum, lnum, col, off] "bufnum" is zero, unless a mark like '0 or 'A is used, then it @@ -2752,20 +2777,25 @@ getpos({expr}) *getpos()* it is the offset in screen columns from the start of the character. E.g., a position within a <Tab> or after the last character. - Note that for '< and '> Visual mode matters: when it is "V" - (visual line mode) the column of '< is zero and the column of - '> is a large number equal to |v:maxcol|. + + For getting the cursor position see |getcurpos()|. The column number in the returned List is the byte position within the line. To get the character position in the line, use |getcharpos()|. + + Note that for '< and '> Visual mode matters: when it is "V" + (visual line mode) the column of '< is zero and the column of + '> is a large number equal to |v:maxcol|. A very large column number equal to |v:maxcol| can be returned, in which case it means "after the end of the line". If {expr} is invalid, returns a list with all zeros. + This can be used to save and restore the position of a mark: >vim let save_a_mark = getpos("'a") " ... call setpos("'a", save_a_mark) -< Also see |getcharpos()|, |getcurpos()| and |setpos()|. +< + Also see |getcharpos()|, |getcurpos()| and |setpos()|. getqflist([{what}]) *getqflist()* Returns a |List| with all the current quickfix errors. Each @@ -2963,8 +2993,8 @@ getregion({pos1}, {pos2} [, {opts}]) *getregion()* difference if the buffer is displayed in a window with different 'virtualedit' or 'list' values. - Examples: > - :xnoremap <CR> + Examples: >vim + xnoremap <CR> \ <Cmd>echom getregion( \ getpos('v'), getpos('.'), #{ type: mode() })<CR> < @@ -4091,28 +4121,16 @@ libcallnr({libname}, {funcname}, {argument}) *libcallnr()* < line({expr} [, {winid}]) *line()* - The result is a Number, which is the line number of the file - position given with {expr}. The {expr} argument is a string. - The accepted positions are: - . the cursor position - $ the last line in the current buffer - 'x position of mark x (if the mark is not set, 0 is - returned) - w0 first line visible in current window (one if the - display isn't updated, e.g. in silent Ex mode) - w$ last line visible in current window (this is one - less than "w0" if no lines are visible) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. - Note that a mark in another file can be used. The line number - then applies to another buffer. + See |getpos()| for accepted positions. + To get the column number use |col()|. To get both use |getpos()|. + With the optional {winid} argument the values are obtained for that window instead of the current window. + Returns 0 for invalid values of {expr} and {winid}. + Examples: >vim echo line(".") " line number of the cursor echo line(".", winid) " idem, in window "winid" @@ -4629,19 +4647,19 @@ matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}]) *matchbufline()* Examples: >vim " Assuming line 3 in buffer 5 contains "a" - :echo matchbufline(5, '\<\k\+\>', 3, 3) - [{'lnum': 3, 'byteidx': 0, 'text': 'a'}] + echo matchbufline(5, '\<\k\+\>', 3, 3) +< `[{'lnum': 3, 'byteidx': 0, 'text': 'a'}]` >vim " Assuming line 4 in buffer 10 contains "tik tok" - :echo matchbufline(10, '\<\k\+\>', 1, 4) - [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}] -< + echo matchbufline(10, '\<\k\+\>', 1, 4) +< `[{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]` + If {submatch} is present and is v:true, then submatches like "\1", "\2", etc. are also returned. Example: >vim " Assuming line 2 in buffer 2 contains "acd" - :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 + echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 \ {'submatches': v:true}) - [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] -< The "submatches" List always contains 9 items. If a submatch +< `[{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]` + The "submatches" List always contains 9 items. If a submatch is not found, then an empty string is returned for that submatch. @@ -4801,17 +4819,17 @@ matchstrlist({list}, {pat} [, {dict}]) *matchstrlist()* option settings on the pattern. Example: >vim - :echo matchstrlist(['tik tok'], '\<\k\+\>') - [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}] - :echo matchstrlist(['a', 'b'], '\<\k\+\>') - [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}] -< + echo matchstrlist(['tik tok'], '\<\k\+\>') +< `[{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]` >vim + echo matchstrlist(['a', 'b'], '\<\k\+\>') +< `[{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]` + If "submatches" is present and is v:true, then submatches like "\1", "\2", etc. are also returned. Example: >vim - :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', + echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', \ #{submatches: v:true}) - [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] -< The "submatches" List always contains 9 items. If a submatch +< `[{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]` + The "submatches" List always contains 9 items. If a submatch is not found, then an empty string is returned for that submatch. @@ -4976,17 +4994,14 @@ mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E73 When {flags} is present it must be a string. An empty string has no effect. - If {flags} contains "p" then intermediate directories are - created as necessary. + {flags} can contain these character flags: + "p" intermediate directories will be created as necessary + "D" {name} will be deleted at the end of the current + function, but not recursively |:defer| + "R" {name} will be deleted recursively at the end of the + current function |:defer| - If {flags} contains "D" then {name} is deleted at the end of - the current function, as with: >vim - defer delete({name}, 'd') -< - If {flags} contains "R" then {name} is deleted recursively at - the end of the current function, as with: >vim - defer delete({name}, 'rf') -< Note that when {name} has more than one part and "p" is used + Note that when {name} has more than one part and "p" is used some directories may already exist. Only the first one that is created and what it contains is scheduled to be deleted. E.g. when using: >vim @@ -6039,7 +6054,7 @@ screencol() *screencol()* the following mappings: >vim nnoremap <expr> GG ":echom " .. screencol() .. "\n" nnoremap <silent> GG :echom screencol()<CR> - noremap GG <Cmd>echom screencol()<Cr> + noremap GG <Cmd>echom screencol()<CR> < screenpos({winid}, {lnum}, {col}) *screenpos()* @@ -8735,7 +8750,9 @@ virtcol({expr} [, {list} [, {winid}]]) *virtcol()* set to 8, it returns 8. |conceal| is ignored. For the byte position use |col()|. - For the use of {expr} see |col()|. + For the use of {expr} see |getpos()| and |col()|. + When {expr} is "$", it means the end of the cursor line, so + the result is the number of cells in the cursor line plus one. When 'virtualedit' is used {expr} can be [lnum, col, off], where "off" is the offset in screen columns from the start of @@ -8745,18 +8762,6 @@ virtcol({expr} [, {list} [, {winid}]]) *virtcol()* beyond the end of the line can be returned. Also see |'virtualedit'| - The accepted positions are: - . the cursor position - $ the end of the cursor line (the result is the - number of displayed characters in the cursor line - plus one) - 'x position of mark x (if the mark is not set, 0 is - returned) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. - If {list} is present and non-zero then virtcol() returns a List with the first and last screen position occupied by the character. @@ -8775,7 +8780,9 @@ virtcol({expr} [, {list} [, {winid}]]) *virtcol()* " With text " there", with 't at 'h': echo virtcol("'t") " returns 6 -< The first column is 1. 0 or [0, 0] is returned for an error. +< + The first column is 1. 0 or [0, 0] is returned for an error. + A more advanced example that echoes the maximum length of all lines: >vim echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 68dc9d6549..05af2eddc3 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -363,7 +363,6 @@ Lua module: vim.diagnostic *diagnostic-api* • {message} (`string`) The diagnostic text • {source}? (`string`) The source of the diagnostic • {code}? (`string|integer`) The diagnostic code - • {_tags}? (`{ deprecated: boolean, unnecessary: boolean}`) • {user_data}? (`any`) arbitrary data plugins can add • {namespace}? (`integer`) diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index ca9dfd0350..7b5eee0a14 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1679,7 +1679,7 @@ is_enabled({filter}) *vim.lsp.inlay_hint.is_enabled()* Query whether inlay hint is enabled in the {filter}ed scope Parameters: ~ - • {filter} (`table`) Optional filters |kwargs|, or `nil` for all. + • {filter} (`table?`) Optional filters |kwargs|, or `nil` for all. • {bufnr} (`integer?`) Buffer number, or 0 for current buffer, or nil for all. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index e2fcac1bda..3bfd1d1885 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -981,6 +981,9 @@ vim.str_byteindex({str}, {index}, {use_utf16}) *vim.str_byteindex()* • {index} (`integer`) • {use_utf16} (`boolean?`) + Return: ~ + (`integer`) + vim.str_utf_end({str}, {index}) *vim.str_utf_end()* Gets the distance (in bytes) from the last byte of the codepoint (character) that {index} points to. @@ -4079,10 +4082,10 @@ Iter:map({f}) *Iter:map()* < Parameters: ~ - • {f} (`fun(...):any`) Mapping function. Takes all values returned from - the previous stage in the pipeline as arguments and returns one - or more new values, which are used in the next pipeline stage. - Nil return values are filtered from the output. + • {f} (`fun(...):...:any`) Mapping function. Takes all values returned + from the previous stage in the pipeline as arguments and returns + one or more new values, which are used in the next pipeline + stage. Nil return values are filtered from the output. Return: ~ (`Iter`) @@ -4385,8 +4388,9 @@ vim.text.hexdecode({enc}) *vim.text.hexdecode()* Parameters: ~ • {enc} (`string`) String to decode - Return: ~ - (`string`) Decoded string + Return (multiple): ~ + (`string?`) Decoded string + (`string?`) Error message, if any vim.text.hexencode({str}) *vim.text.hexencode()* Hex encode a string. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 2ff6b0302c..848a642774 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -62,7 +62,10 @@ LUA OPTIONS -• TODO +• The 'statuscolumn' `%l` item can now be used as a number column segment that + changes according to related options. It takes care of alignment, 'number', + 'relativenumber' and 'signcolumn' set to "number". The now redundant `%r` item + is no longer treated specially for 'statuscolumn'. PLUGINS @@ -138,7 +141,10 @@ STARTUP TERMINAL -• TODO +• The |terminal| now understands the OSC 52 escape sequence to write to the + system clipboard (copy). Querying with OSC 52 (paste) is not supported. +• |hl-StatusLineTerm| and |hl-StatusLineTermNC| define highlights for the + status line in |terminal| windows. TREESITTER diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index a6ebc7e958..81bcd89146 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -164,7 +164,22 @@ directory indicated in the request. >lua To try it out, select the above code and source it with `:'<,'>lua`, then run this command in a :terminal buffer: > - printf "\033]7;file://./foo/bar\033\\" + printf "\033]7;file://./foo/bar\033\\" + +OSC 52: write to system clipboard *terminal-osc52* + +Applications in the :terminal buffer can write to the system clipboard by +emitting an OSC 52 sequence. Example: > + + printf '\033]52;;%s\033\\' "$(echo -n 'Hello world' | base64)" + +Nvim uses the configured |clipboard| provider to write to the system +clipboard. Reading from the system clipboard with OSC 52 is not supported, as +this would allow any arbitrary program in the :terminal to read the user's +clipboard. + +OSC 52 sequences sent from the :terminal buffer do not emit a |TermRequest| +event. The event is handled directly by Nvim and is not forwarded to plugins. ============================================================================== Status Variables *terminal-status* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index f02d3c9741..1b71050620 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3842,6 +3842,9 @@ A jump table for the options with a short description can be found at |Q_op|. between tabs and spaces and for trailing blanks. Further changed by the 'listchars' option. + When 'listchars' does not contain "tab" field, tabs are shown as "^I" + or "<09>", like how unprintable characters are displayed. + The cursor is displayed at the start of the space a Tab character occupies, not at the end as usual in Normal mode. To get this cursor position while displaying Tabs with spaces, use: >vim @@ -5951,8 +5954,7 @@ A jump table for the options with a short description can be found at |Q_op|. Some of the items from the 'statusline' format are different for 'statuscolumn': - %l line number of currently drawn line - %r relative line number of currently drawn line + %l line number column for currently drawn line %s sign column for currently drawn line %C fold column for currently drawn line @@ -5979,11 +5981,8 @@ A jump table for the options with a short description can be found at |Q_op|. handler should be written with this in mind. Examples: >vim - " Relative number with bar separator and click handlers: - set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T - - " Right aligned relative cursor line number: - let &stc='%=%{v:relnum?v:relnum:v:lnum} ' + " Line number with bar separator and click handlers: + set statuscolumn=%@SignCb@%s%=%T%@NumCb@%l│%T " Line numbers in hexadecimal for non wrapped part of lines: let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 897e503fc4..9ac7492b9b 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1288,6 +1288,13 @@ g:compiler_gcc_ignore_unmatched_lines commands run from make are generating false positives. +JAVAC *compiler-javac* + +Commonly used compiler options can be added to 'makeprg' by setting the +g:javac_makeprg_params variable. For example: > + let g:javac_makeprg_params = "-Xlint:all -encoding utf-8" +< + PANDOC *quickfix-pandoc* *compiler-pandoc* The Pandoc compiler plugin expects that an output file type extension is diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 61028c791d..4a1588a910 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -468,10 +468,10 @@ Configuration The following variables control certain syntax highlighting features. You can add them to your .vimrc. -To enables TypeScript and TSX for ".astro" files (default "disable"): > +To enable TypeScript and TSX for ".astro" files (default "disable"): > let g:astro_typescript = "enable" < -To enables Stylus for ".astro" files (default "disable"): > +To enable Stylus for ".astro" files (default "disable"): > let g:astro_stylus = "enable" < NOTE: You need to install an external plugin to support stylus in astro files. @@ -3327,28 +3327,31 @@ The g:vimsyn_embed option allows users to select what, if any, types of embedded script highlighting they wish to have. > g:vimsyn_embed == 0 : disable (don't embed any scripts) - g:vimsyn_embed == 'lpPr' : support embedded lua, perl, python and ruby + g:vimsyn_embed == 'lpPr' : support embedded Lua, Perl, Python and Ruby < This option is disabled by default. *g:vimsyn_folding* - Some folding is now supported with when 'foldmethod' is set to "syntax": > g:vimsyn_folding == 0 or doesn't exist: no syntax-based folding g:vimsyn_folding =~ 'a' : augroups g:vimsyn_folding =~ 'f' : fold functions g:vimsyn_folding =~ 'h' : fold heredocs - g:vimsyn_folding =~ 'l' : fold lua script - g:vimsyn_folding =~ 'p' : fold perl script - g:vimsyn_folding =~ 'P' : fold python script - g:vimsyn_folding =~ 'r' : fold ruby script + g:vimsyn_folding =~ 'l' : fold Lua script + g:vimsyn_folding =~ 'p' : fold Perl script + g:vimsyn_folding =~ 'P' : fold Python script + g:vimsyn_folding =~ 'r' : fold Ruby script < By default, g:vimsyn_folding is unset. Concatenate the indicated characters -to support folding of multiple syntax constructs; i.e. -g:vimsyn_folding = "fh" will enable folding of both functions and heredocs. +to support folding of multiple syntax constructs (e.g., +g:vimsyn_folding = "fh" will enable folding of both functions and heredocs). + + *g:vimsyn_comment_strings* +By default, strings are highlighted inside comments. This may be disabled by +setting g:vimsyn_comment_strings to false. - *g:vimsyn_noerror* + *g:vimsyn_noerror* Not all error highlighting that syntax/vim.vim does may be correct; Vim script is a difficult language to highlight correctly. A way to suppress error highlighting is to put the following line in your |vimrc|: > @@ -5011,7 +5014,6 @@ IncSearch 'incsearch' highlighting; also used for the text replaced with ":s///c". *hl-Substitute* Substitute |:substitute| replacement text highlighting. - *hl-LineNr* LineNr Line number for ":number" and ":#" commands, and when 'number' or 'relativenumber' option is set. @@ -5031,7 +5033,6 @@ CursorLineSign Like SignColumn when 'cursorline' is set for the cursor line. *hl-MatchParen* MatchParen Character under the cursor or just before it, if it is a paired bracket, and its match. |pi_paren.txt| - *hl-ModeMsg* ModeMsg 'showmode' message (e.g., "-- INSERT --"). *hl-MsgArea* @@ -5073,11 +5074,15 @@ PmenuExtraSel Popup menu: Selected item "extra text". PmenuSbar Popup menu: Scrollbar. *hl-PmenuThumb* PmenuThumb Popup menu: Thumb of the scrollbar. + *hl-PmenuMatch* +PmenuMatch Popup menu: Matched text in normal item + *hl-PmenuMatchSel* +PmenuMatchSel Popup menu: Matched text in selected item *hl-Question* Question |hit-enter| prompt and yes/no questions. *hl-QuickFixLine* QuickFixLine Current |quickfix| item in the quickfix window. Combined with - |hl-CursorLine| when the cursor is there. + |hl-CursorLine| when the cursor is there. *hl-Search* Search Last search pattern highlighting (see 'hlsearch'). Also used for similar items that need to stand out. @@ -5104,6 +5109,11 @@ SpellRare Word that is recognized by the spellchecker as one that is StatusLine Status line of current window. *hl-StatusLineNC* StatusLineNC Status lines of not-current windows. + *hl-StatusLineTerm* +StatusLineTerm Status line of |terminal| window. + *hl-StatusLineTermNC* +StatusLineTermNC + Status line of non-current |terminal| windows. *hl-TabLine* TabLine Tab pages line, not active tab page label. *hl-TabLineFill* diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 25b070b310..ed514cb722 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -22,9 +22,11 @@ search for in the `parser` runtime directory. Nvim includes these parsers: +- Bash - C - Lua - Markdown +- Python - Vimscript - Vimdoc - Treesitter query files |ft-query-plugin| diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 0287271d4c..63fca2c1aa 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -140,7 +140,7 @@ gx Opens the current filepath or URL (decided by :[range]# [count] [flags] synonym for :number. - *:#!* + *:#!* *vim-shebang* :#!{anything} Ignored, so that you can start a Vim script with: > #!vim -S echo "this is a Vim script" diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index ac20948f14..a6f08402f6 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -166,6 +166,14 @@ nvim_terminal: when 'background' is "light". While this may not reflect the actual foreground/background color, it permits 'background' to be retained for a nested Nvim instance running in the terminal emulator. +- TermOpen: Sets default options for |terminal| buffers: + - 'nomodifiable' + - 'undolevels' set to -1 + - 'textwidth' set to 0 + - 'nowrap' + - 'nolist' + - 'winhighlight' uses |hl-StatusLineTerm| and |hl-StatusLineTermNC| in + place of |hl-StatusLine| and |hl-StatusLineNC| nvim_cmdwin: - CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|. @@ -538,6 +546,8 @@ Highlight groups: - Highlight groups names are allowed to contain `@` characters. - It is an error to define a highlight group with a name that doesn't match the regexp `[a-zA-Z0-9_.@-]*` (see |group-name|). +- |hl-StatusLineTerm| |hl-StatusLineTermNC| are implemented as 'winhighlight' + window-local highlights which are set by the default |TermOpen| handler. Macro (|recording|) behavior: - Replay of a macro recorded during :lmap produces the same actions as when it @@ -665,17 +675,6 @@ Events: - *SafeStateAgain* - *SigUSR1* Use |Signal| to detect `SIGUSR1` signal instead. -Highlight groups: -- *hl-StatusLineTerm* *hl-StatusLineTermNC* are unnecessary because Nvim - supports 'winhighlight' window-local highlights. For example, to mimic Vim's - StatusLineTerm: >vim - hi StatusLineTerm ctermfg=black ctermbg=green - hi StatusLineTermNC ctermfg=green - autocmd TermOpen,WinEnter * if &buftype=='terminal' - \|setlocal winhighlight=StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC - \|else|setlocal winhighlight=|endif -< - Options: - *'aleph'* *'al'* - antialias diff --git a/runtime/ftplugin/kdl.vim b/runtime/ftplugin/kdl.vim new file mode 100644 index 0000000000..c9a1d8b185 --- /dev/null +++ b/runtime/ftplugin/kdl.vim @@ -0,0 +1,17 @@ +" Vim filetype plugin +" Language: KDL +" Author: Aram Drevekenin <aram@poor.dev> +" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com> +" Last Change: 2024-06-10 + +if exists("b:did_ftplugin") + finish +endif + +let b:did_ftplugin = 1 + +setlocal comments=:// +setlocal commentstring=//\ %s +setlocal formatoptions-=t + +let b:undo_ftplugin = 'setlocal comments< commentstring< formatoptions<' diff --git a/runtime/ftplugin/snakemake.vim b/runtime/ftplugin/snakemake.vim new file mode 100644 index 0000000000..ab90ca10b5 --- /dev/null +++ b/runtime/ftplugin/snakemake.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: snakemake +" Maintainer: Riley Bruins <ribru17@gmail.com> +" Last Change: 2024 Jun 13 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=:# commentstring=#\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/svelte.vim b/runtime/ftplugin/svelte.vim new file mode 100644 index 0000000000..e0ec4e0ae3 --- /dev/null +++ b/runtime/ftplugin/svelte.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin +" Language: svelte +" Maintainer: Igor Lacerda <igorlafarsi@gmail.com> +" Last Change: 2024 Jun 09 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl commentstring=<!--\ %s\ --> + +let b:undo_ftplugin = 'setl cms<' diff --git a/runtime/indent/kdl.vim b/runtime/indent/kdl.vim new file mode 100644 index 0000000000..b0a6bd90d9 --- /dev/null +++ b/runtime/indent/kdl.vim @@ -0,0 +1,30 @@ +" Vim indent file +" Language: KDL +" Author: Aram Drevekenin <aram@poor.dev> +" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com> +" Last Change: 2024-06-16 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=KdlIndent() +let b:undo_indent = "setlocal indentexpr<" + +function! KdlIndent(...) + let line = substitute(getline(v:lnum), '//.*$', '', '') + let previousNum = prevnonblank(v:lnum - 1) + let previous = substitute(getline(previousNum), '//.*$', '', '') + + let l:indent = indent(previousNum) + if previous =~ "{" && previous !~ "}" + let l:indent += shiftwidth() + endif + if line =~ "}" && line !~ "{" + let l:indent -= shiftwidth() + endif + return l:indent +endfunction +" vim: sw=2 sts=2 et diff --git a/runtime/keymap/georgian-qwerty.vim b/runtime/keymap/georgian-qwerty.vim new file mode 100644 index 0000000000..df18729f06 --- /dev/null +++ b/runtime/keymap/georgian-qwerty.vim @@ -0,0 +1,51 @@ +" Vim keymap file for Georgian (Mkhedruli) layout +" Maintainer: Mishiko Okropiridze <misho.okropiridze@gmail.com> +" Last changed: 2024-06-14 + + +let b:keymap_name = "ge" + +loadkeymap +a ა +A ა +b ბ +c ც +C ჩ +d დ +e ე +E ე +f ფ +F ფ +g გ +G გ +h ჰ +H ჵ +i ი +I ი +j ჯ +J ჟ +k კ +l ლ +m მ +n ნ +o ო +p პ +q ქ +Q ქ +r რ +R ღ +s ს +S შ +t ტ +T თ +u უ +v ვ +V ვ +w წ +W ჭ +x ხ +X ხ +y ყ +Y ყ +z ზ +Z ძ diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 01dcd4bf74..630f2219c7 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -85,13 +85,13 @@ do vim.keymap.set( 'x', 'Q', - "mode() == 'V' ? ':normal! @<C-R>=reg_recorded()<CR><CR>' : 'Q'", + "mode() ==# 'V' ? ':normal! @<C-R>=reg_recorded()<CR><CR>' : 'Q'", { silent = true, expr = true, desc = ':help v_Q-default' } ) vim.keymap.set( 'x', '@', - "mode() == 'V' ? ':normal! @'.getcharstr().'<CR>' : '@'", + "mode() ==# 'V' ? ':normal! @'.getcharstr().'<CR>' : '@'", { silent = true, expr = true, desc = ':help v_@-default' } ) @@ -282,6 +282,26 @@ do end, }) + vim.api.nvim_create_autocmd('TermOpen', { + group = nvim_terminal_augroup, + desc = 'Default settings for :terminal buffers', + callback = function() + vim.bo.modifiable = false + vim.bo.undolevels = -1 + vim.bo.scrollback = vim.o.scrollback < 0 and 10000 or math.max(1, vim.o.scrollback) + vim.bo.textwidth = 0 + vim.wo[0][0].wrap = false + vim.wo[0][0].list = false + + -- This is gross. Proper list options support when? + local winhl = vim.o.winhighlight + if winhl ~= '' then + winhl = winhl .. ',' + end + vim.wo[0][0].winhighlight = winhl .. 'StatusLine:StatusLineTerm,StatusLineNC:StatusLineTermNC' + end, + }) + vim.api.nvim_create_autocmd('CmdwinEnter', { pattern = '[:>]', desc = 'Limit syntax sync to maxlines=1 in the command window', diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index d91b2d08bf..1b70cc275f 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -1910,7 +1910,7 @@ function vim.api.nvim_set_current_win(window) end --- Note: this function should not be called often. Rather, the callbacks --- themselves can be used to throttle unneeded callbacks. the `on_start` --- callback can return `false` to disable the provider until the next redraw. ---- Similarly, return `false` in `on_win` will skip the `on_lines` calls for +--- Similarly, return `false` in `on_win` will skip the `on_line` calls for --- that window (but any extmarks set in `on_win` will still be used). A --- plugin managing multiple sources of decoration should ideally only set one --- provider, and merge the sources internally. You can use multiple `ns_id` @@ -1919,10 +1919,10 @@ function vim.api.nvim_set_current_win(window) end --- Note: doing anything other than setting extmarks is considered --- experimental. Doing things like changing options are not explicitly --- forbidden, but is likely to have unexpected consequences (such as 100% CPU ---- consumption). doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is +--- consumption). Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is --- quite dubious for the moment. --- ---- Note: It is not allowed to remove or update extmarks in 'on_line' +--- Note: It is not allowed to remove or update extmarks in `on_line` --- callbacks. --- --- @param ns_id integer Namespace id from `nvim_create_namespace()` diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index 75737bd040..3aca3cdfa5 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -121,6 +121,7 @@ function vim.stricmp(a, b) end --- @param str string --- @param index integer --- @param use_utf16? boolean +--- @return integer function vim.str_byteindex(str, index, use_utf16) end --- Gets a list of the starting byte positions of each UTF-8 codepoint in the given string. diff --git a/runtime/lua/vim/_meta/builtin_types.lua b/runtime/lua/vim/_meta/builtin_types.lua index 53dd8d95e9..9afb8c84f4 100644 --- a/runtime/lua/vim/_meta/builtin_types.lua +++ b/runtime/lua/vim/_meta/builtin_types.lua @@ -34,6 +34,11 @@ --- @field filename? string --- @field lnum integer +--- @class vim.fn.getmarklist.ret.item +--- @field mark string +--- @field pos [integer, integer, integer, integer] +--- @field file string + --- @class vim.fn.getmousepos.ret --- @field screenrow integer --- @field screencol integer diff --git a/runtime/lua/vim/_meta/lpeg.lua b/runtime/lua/vim/_meta/lpeg.lua index 73b3375c82..39a894aaeb 100644 --- a/runtime/lua/vim/_meta/lpeg.lua +++ b/runtime/lua/vim/_meta/lpeg.lua @@ -2,7 +2,7 @@ error('Cannot require a meta file') -- These types were taken from https://github.com/LuaCATS/lpeg --- (based on revision e6789e28e5b91a4a277a2a03081d708c403a3e34) +-- (based on revision 82c6a8fc676bbc20722026afd952668f3919b11d) -- with types being renamed to include the vim namespace and with some descriptions made less verbose. --- @brief <pre>help @@ -32,7 +32,7 @@ vim.lpeg = {} --- @operator div(table): vim.lpeg.Capture --- @operator div(function): vim.lpeg.Capture --- @operator pow(number): vim.lpeg.Pattern ---- @operator mod(function): nil +--- @operator mod(function): vim.lpeg.Capture local Pattern = {} --- @alias vim.lpeg.Capture vim.lpeg.Pattern diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 155c93726b..902df8f7d6 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -3830,6 +3830,9 @@ vim.go.lw = vim.go.lispwords --- between tabs and spaces and for trailing blanks. Further changed by --- the 'listchars' option. --- +--- When 'listchars' does not contain "tab" field, tabs are shown as "^I" +--- or "<09>", like how unprintable characters are displayed. +--- --- The cursor is displayed at the start of the space a Tab character --- occupies, not at the end as usual in Normal mode. To get this cursor --- position while displaying Tabs with spaces, use: @@ -6373,8 +6376,7 @@ vim.go.sol = vim.go.startofline --- Some of the items from the 'statusline' format are different for --- 'statuscolumn': --- ---- %l line number of currently drawn line ---- %r relative line number of currently drawn line +--- %l line number column for currently drawn line --- %s sign column for currently drawn line --- %C fold column for currently drawn line --- @@ -6403,11 +6405,8 @@ vim.go.sol = vim.go.startofline --- Examples: --- --- ```vim ---- " Relative number with bar separator and click handlers: ---- set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T ---- ---- " Right aligned relative cursor line number: ---- let &stc='%=%{v:relnum?v:relnum:v:lnum} ' +--- " Line number with bar separator and click handlers: +--- set statuscolumn=%@SignCb@%s%=%T%@NumCb@%l│%T --- --- " Line numbers in hexadecimal for non wrapped part of lines: --- let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index a1600e1cb5..11dcbc010b 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -911,33 +911,33 @@ function vim.fn.cindent(lnum) end function vim.fn.clearmatches(win) end --- The result is a Number, which is the byte index of the column ---- position given with {expr}. The accepted positions are: ---- . the cursor position ---- $ the end of the cursor line (the result is the ---- number of bytes in the cursor line plus one) ---- 'x position of mark x (if the mark is not set, 0 is ---- returned) ---- v In Visual mode: the start of the Visual area (the ---- cursor is the end). When not in Visual mode ---- returns the cursor position. Differs from |'<| in ---- that it's updated right away. +--- position given with {expr}. +--- For accepted positions see |getpos()|. +--- When {expr} is "$", it means the end of the cursor line, so +--- the result is the number of bytes in the cursor line plus one. --- Additionally {expr} can be [lnum, col]: a |List| with the line --- and column number. Most useful when the column is "$", to get --- the last column of a specific line. When "lnum" or "col" is --- out of range then col() returns zero. +--- --- With the optional {winid} argument the values are obtained for --- that window instead of the current window. +--- --- To get the line number use |line()|. To get both use --- |getpos()|. +--- --- For the screen column position use |virtcol()|. For the --- character position use |charcol()|. +--- --- Note that only marks in the current file can be used. +--- --- Examples: >vim --- echo col(".") " column of cursor --- echo col("$") " length of cursor line plus one --- echo col("'t") " column of mark t --- echo col("'" .. markname) " column of mark markname ---- <The first column is 1. Returns 0 if {expr} is invalid or when +--- < +--- The first column is 1. Returns 0 if {expr} is invalid or when --- the window with ID {winid} is not found. --- For an uppercase mark the column may actually be in another --- buffer. @@ -2766,8 +2766,9 @@ function vim.fn.getchangelist(buf) end --- endfunction --- < --- +--- @param expr? 0|1 --- @return integer -function vim.fn.getchar() end +function vim.fn.getchar(expr) end --- The result is a Number which is the state of the modifiers for --- the last obtained character with getchar() or in another way. @@ -2837,8 +2838,9 @@ function vim.fn.getcharsearch() end --- Otherwise this works like |getchar()|, except that a number --- result is converted to a string. --- +--- @param expr? 0|1 --- @return string -function vim.fn.getcharstr() end +function vim.fn.getcharstr(expr) end --- Return the type of the current command-line completion. --- Only works when the command line is being edited, thus @@ -3249,8 +3251,8 @@ function vim.fn.getloclist(nr, what) end --- Refer to |getpos()| for getting information about a specific --- mark. --- ---- @param buf? any ---- @return any +--- @param buf? integer? +--- @return vim.fn.getmarklist.ret.item[] function vim.fn.getmarklist(buf) end --- Returns a |List| with all matches previously defined for the @@ -3327,9 +3329,34 @@ function vim.fn.getmousepos() end --- @return integer function vim.fn.getpid() end ---- Get the position for String {expr}. For possible values of ---- {expr} see |line()|. For getting the cursor position see ---- |getcurpos()|. +--- Get the position for String {expr}. +--- The accepted values for {expr} are: +--- . The cursor position. +--- $ The last line in the current buffer. +--- 'x Position of mark x (if the mark is not set, 0 is +--- returned for all values). +--- w0 First line visible in current window (one if the +--- display isn't updated, e.g. in silent Ex mode). +--- w$ Last line visible in current window (this is one +--- less than "w0" if no lines are visible). +--- v When not in Visual mode, returns the cursor +--- position. In Visual mode, returns the other end +--- of the Visual area. A good way to think about +--- this is that in Visual mode "v" and "." complement +--- each other. While "." refers to the cursor +--- position, "v" refers to where |v_o| would move the +--- cursor. As a result, you can use "v" and "." +--- together to work on all of a selection in +--- characterwise Visual mode. If the cursor is at +--- the end of a characterwise Visual area, "v" refers +--- to the start of the same Visual area. And if the +--- cursor is at the start of a characterwise Visual +--- area, "v" refers to the end of the same Visual +--- area. "v" differs from |'<| and |'>| in that it's +--- updated right away. +--- Note that a mark in another file can be used. The line number +--- then applies to another buffer. +--- --- The result is a |List| with four numbers: --- [bufnum, lnum, col, off] --- "bufnum" is zero, unless a mark like '0 or 'A is used, then it @@ -3340,20 +3367,25 @@ function vim.fn.getpid() end --- it is the offset in screen columns from the start of the --- character. E.g., a position within a <Tab> or after the last --- character. ---- Note that for '< and '> Visual mode matters: when it is "V" ---- (visual line mode) the column of '< is zero and the column of ---- '> is a large number equal to |v:maxcol|. +--- +--- For getting the cursor position see |getcurpos()|. --- The column number in the returned List is the byte position --- within the line. To get the character position in the line, --- use |getcharpos()|. +--- +--- Note that for '< and '> Visual mode matters: when it is "V" +--- (visual line mode) the column of '< is zero and the column of +--- '> is a large number equal to |v:maxcol|. --- A very large column number equal to |v:maxcol| can be returned, --- in which case it means "after the end of the line". --- If {expr} is invalid, returns a list with all zeros. +--- --- This can be used to save and restore the position of a mark: >vim --- let save_a_mark = getpos("'a") --- " ... --- call setpos("'a", save_a_mark) ---- <Also see |getcharpos()|, |getcurpos()| and |setpos()|. +--- < +--- Also see |getcharpos()|, |getcurpos()| and |setpos()|. --- --- @param expr string --- @return integer[] @@ -3569,8 +3601,8 @@ function vim.fn.getreginfo(regname) end --- difference if the buffer is displayed in a window with --- different 'virtualedit' or 'list' values. --- ---- Examples: > ---- :xnoremap <CR> +--- Examples: >vim +--- xnoremap <CR> --- \ <Cmd>echom getregion( --- \ getpos('v'), getpos('.'), #{ type: mode() })<CR> --- < @@ -4958,28 +4990,16 @@ function vim.fn.libcall(libname, funcname, argument) end --- @return any function vim.fn.libcallnr(libname, funcname, argument) end ---- The result is a Number, which is the line number of the file ---- position given with {expr}. The {expr} argument is a string. ---- The accepted positions are: ---- . the cursor position ---- $ the last line in the current buffer ---- 'x position of mark x (if the mark is not set, 0 is ---- returned) ---- w0 first line visible in current window (one if the ---- display isn't updated, e.g. in silent Ex mode) ---- w$ last line visible in current window (this is one ---- less than "w0" if no lines are visible) ---- v In Visual mode: the start of the Visual area (the ---- cursor is the end). When not in Visual mode ---- returns the cursor position. Differs from |'<| in ---- that it's updated right away. ---- Note that a mark in another file can be used. The line number ---- then applies to another buffer. +--- See |getpos()| for accepted positions. +--- --- To get the column number use |col()|. To get both use --- |getpos()|. +--- --- With the optional {winid} argument the values are obtained for --- that window instead of the current window. +--- --- Returns 0 for invalid values of {expr} and {winid}. +--- --- Examples: >vim --- echo line(".") " line number of the cursor --- echo line(".", winid) " idem, in window "winid" @@ -5297,8 +5317,9 @@ function vim.fn.mapcheck(name, mode, abbr) end --- ounmap xyzzy --- echo printf("Operator-pending mode bit: 0x%x", op_bit) --- ---- @return any -function vim.fn.maplist() end +--- @param abbr? 0|1 +--- @return table[] +function vim.fn.maplist(abbr) end --- Like |map()| but instead of replacing items in {expr1} a new --- List or Dictionary is created and returned. {expr1} remains @@ -5575,19 +5596,19 @@ function vim.fn.matcharg(nr) end --- --- Examples: >vim --- " Assuming line 3 in buffer 5 contains "a" ---- :echo matchbufline(5, '\<\k\+\>', 3, 3) ---- [{'lnum': 3, 'byteidx': 0, 'text': 'a'}] +--- echo matchbufline(5, '\<\k\+\>', 3, 3) +--- < `[{'lnum': 3, 'byteidx': 0, 'text': 'a'}]` >vim --- " Assuming line 4 in buffer 10 contains "tik tok" ---- :echo matchbufline(10, '\<\k\+\>', 1, 4) ---- [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}] ---- < +--- echo matchbufline(10, '\<\k\+\>', 1, 4) +--- < `[{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]` +--- --- If {submatch} is present and is v:true, then submatches like --- "\1", "\2", etc. are also returned. Example: >vim --- " Assuming line 2 in buffer 2 contains "acd" ---- :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 +--- echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 --- \ {'submatches': v:true}) ---- [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] ---- <The "submatches" List always contains 9 items. If a submatch +--- < `[{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]` +--- The "submatches" List always contains 9 items. If a submatch --- is not found, then an empty string is returned for that --- submatch. --- @@ -5786,17 +5807,17 @@ function vim.fn.matchstr(expr, pat, start, count) end --- option settings on the pattern. --- --- Example: >vim ---- :echo matchstrlist(['tik tok'], '\<\k\+\>') ---- [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}] ---- :echo matchstrlist(['a', 'b'], '\<\k\+\>') ---- [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}] ---- < +--- echo matchstrlist(['tik tok'], '\<\k\+\>') +--- < `[{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]` >vim +--- echo matchstrlist(['a', 'b'], '\<\k\+\>') +--- < `[{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]` +--- --- If "submatches" is present and is v:true, then submatches like --- "\1", "\2", etc. are also returned. Example: >vim ---- :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', +--- echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', --- \ #{submatches: v:true}) ---- [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] ---- <The "submatches" List always contains 9 items. If a submatch +--- < `[{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]` +--- The "submatches" List always contains 9 items. If a submatch --- is not found, then an empty string is returned for that --- submatch. --- @@ -5986,17 +6007,14 @@ function vim.fn.min(expr) end --- When {flags} is present it must be a string. An empty string --- has no effect. --- ---- If {flags} contains "p" then intermediate directories are ---- created as necessary. +--- {flags} can contain these character flags: +--- "p" intermediate directories will be created as necessary +--- "D" {name} will be deleted at the end of the current +--- function, but not recursively |:defer| +--- "R" {name} will be deleted recursively at the end of the +--- current function |:defer| --- ---- If {flags} contains "D" then {name} is deleted at the end of ---- the current function, as with: >vim ---- defer delete({name}, 'd') ---- < ---- If {flags} contains "R" then {name} is deleted recursively at ---- the end of the current function, as with: >vim ---- defer delete({name}, 'rf') ---- <Note that when {name} has more than one part and "p" is used +--- Note that when {name} has more than one part and "p" is used --- some directories may already exist. Only the first one that --- is created and what it contains is scheduled to be deleted. --- E.g. when using: >vim @@ -7236,7 +7254,7 @@ function vim.fn.screenchars(row, col) end --- the following mappings: >vim --- nnoremap <expr> GG ":echom " .. screencol() .. "\n" --- nnoremap <silent> GG :echom screencol()<CR> ---- noremap GG <Cmd>echom screencol()<Cr> +--- noremap GG <Cmd>echom screencol()<CR> --- < --- --- @return any @@ -7634,8 +7652,15 @@ function vim.fn.searchdecl(name, global, thisblock) end --- \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string"') --- < --- ---- @return any -function vim.fn.searchpair() end +--- @param start any +--- @param middle any +--- @param end_ any +--- @param flags? string +--- @param skip? any +--- @param stopline? any +--- @param timeout? integer +--- @return integer +function vim.fn.searchpair(start, middle, end_, flags, skip, stopline, timeout) end --- Same as |searchpair()|, but returns a |List| with the line and --- column position of the match. The first element of the |List| @@ -7647,8 +7672,15 @@ function vim.fn.searchpair() end --- < --- See |match-parens| for a bigger and more useful example. --- ---- @return any -function vim.fn.searchpairpos() end +--- @param start any +--- @param middle any +--- @param end_ any +--- @param flags? string +--- @param skip? any +--- @param stopline? any +--- @param timeout? integer +--- @return [integer, integer] +function vim.fn.searchpairpos(start, middle, end_, flags, skip, stopline, timeout) end --- Same as |search()|, but returns a |List| with the line and --- column position of the match. The first element of the |List| @@ -10382,7 +10414,9 @@ function vim.fn.values(dict) end --- set to 8, it returns 8. |conceal| is ignored. --- For the byte position use |col()|. --- ---- For the use of {expr} see |col()|. +--- For the use of {expr} see |getpos()| and |col()|. +--- When {expr} is "$", it means the end of the cursor line, so +--- the result is the number of cells in the cursor line plus one. --- --- When 'virtualedit' is used {expr} can be [lnum, col, off], --- where "off" is the offset in screen columns from the start of @@ -10392,18 +10426,6 @@ function vim.fn.values(dict) end --- beyond the end of the line can be returned. Also see --- |'virtualedit'| --- ---- The accepted positions are: ---- . the cursor position ---- $ the end of the cursor line (the result is the ---- number of displayed characters in the cursor line ---- plus one) ---- 'x position of mark x (if the mark is not set, 0 is ---- returned) ---- v In Visual mode: the start of the Visual area (the ---- cursor is the end). When not in Visual mode ---- returns the cursor position. Differs from |'<| in ---- that it's updated right away. ---- --- If {list} is present and non-zero then virtcol() returns a --- List with the first and last screen position occupied by the --- character. @@ -10422,7 +10444,9 @@ function vim.fn.values(dict) end --- " With text " there", with 't at 'h': --- --- echo virtcol("'t") " returns 6 ---- <The first column is 1. 0 or [0, 0] is returned for an error. +--- < +--- The first column is 1. 0 or [0, 0] is returned for an error. +--- --- A more advanced example that echoes the maximum length of --- all lines: >vim --- echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index 4a360c18e4..a61fa61256 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -174,6 +174,11 @@ local function new_buf_opt_accessor(bufnr) end local function new_win_opt_accessor(winid, bufnr) + -- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value + if bufnr ~= nil and bufnr ~= 0 then + error('only bufnr=0 is supported') + end + return setmetatable({}, { __index = function(_, k) if bufnr == nil and type(k) == 'number' then @@ -184,11 +189,6 @@ local function new_win_opt_accessor(winid, bufnr) end end - if bufnr ~= nil and bufnr ~= 0 then - error('only bufnr=0 is supported') - end - - -- TODO(lewis6991): allow passing both buf and win to nvim_get_option_value return api.nvim_get_option_value(k, { scope = bufnr and 'local' or nil, win = winid or 0, @@ -196,7 +196,6 @@ local function new_win_opt_accessor(winid, bufnr) end, __newindex = function(_, k, v) - -- TODO(lewis6991): allow passing both buf and win to nvim_set_option_value return api.nvim_set_option_value(k, v, { scope = bufnr and 'local' or nil, win = winid or 0, diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index cc2fbe0cec..2bb50d8c0b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -985,6 +985,7 @@ local extension = { smt = 'smith', smithy = 'smithy', sml = 'sml', + smk = 'snakemake', spt = 'snobol4', sno = 'snobol4', sln = 'solution', @@ -1620,6 +1621,7 @@ local filename = { ['/etc/slp.spi'] = 'slpspi', ['.slrnrc'] = 'slrnrc', ['sendmail.cf'] = 'sm', + Snakefile = 'snakemake', ['.sqlite_history'] = 'sql', ['squid.conf'] = 'squid', ['ssh_config'] = 'sshconfig', diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index c56ece6289..fa90c83b81 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1783,6 +1783,7 @@ local patterns_hashbang = { ['^janet\\>'] = { 'janet', { vim_regex = true } }, ['^dart\\>'] = { 'dart', { vim_regex = true } }, ['^execlineb\\>'] = { 'execline', { vim_regex = true } }, + ['^vim\\>'] = { 'vim', { vim_regex = true } }, } ---@private diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua index 1093759efe..6bddf0bc5e 100644 --- a/runtime/lua/vim/iter.lua +++ b/runtime/lua/vim/iter.lua @@ -276,7 +276,7 @@ end --- -- { 6, 12 } --- ``` --- ----@param f fun(...):any Mapping function. Takes all values returned from +---@param f fun(...):...:any Mapping function. Takes all values returned from --- the previous stage in the pipeline as arguments --- and returns one or more new values, which are used --- in the next pipeline stage. Nil return values diff --git a/runtime/lua/vim/lsp/_dynamic.lua b/runtime/lua/vim/lsp/_dynamic.lua index 819b03a63a..27113c0e74 100644 --- a/runtime/lua/vim/lsp/_dynamic.lua +++ b/runtime/lua/vim/lsp/_dynamic.lua @@ -24,7 +24,6 @@ function M:supports_registration(method) end --- @param registrations lsp.Registration[] ---- @package function M:register(registrations) -- remove duplicates self:unregister(registrations) @@ -38,7 +37,6 @@ function M:register(registrations) end --- @param unregisterations lsp.Unregistration[] ---- @package function M:unregister(unregisterations) for _, unreg in ipairs(unregisterations) do local method = unreg.method @@ -58,7 +56,6 @@ end --- @param method string --- @param opts? {bufnr: integer?} --- @return lsp.Registration? (table|nil) the registration if found ---- @package function M:get(method, opts) opts = opts or {} opts.bufnr = opts.bufnr or vim.api.nvim_get_current_buf() @@ -78,7 +75,6 @@ end --- @param method string --- @param opts? {bufnr: integer?} ---- @package function M:supports(method, opts) return self:get(method, opts) ~= nil end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 327cd19125..8cdfdd4b90 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -436,7 +436,7 @@ local function ensure_list(x) return { x } end ---- @package +--- @nodoc --- @param config vim.lsp.ClientConfig --- @return vim.lsp.Client? function Client.create(config) @@ -535,7 +535,7 @@ function Client:_run_callbacks(cbs, error_id, ...) end end ---- @package +--- @nodoc function Client:initialize() local config = self.config @@ -656,7 +656,7 @@ end --- @param method string LSP method name. --- @param params? table LSP request params. --- @param handler? lsp.Handler Response |lsp-handler| for this method. ---- @param bufnr? integer Buffer handle (0 for current). +--- @param bufnr integer Buffer handle (0 for current). --- @return boolean status, integer? request_id {status} is a bool indicating --- whether the request was successful. If it is `false`, then it will --- always be `false` (the client has shutdown). If it was @@ -861,7 +861,6 @@ function Client:_is_stopped() return self.rpc.is_closing() end ---- @package --- Execute a lsp command, either via client command function (if available) --- or via workspace/executeCommand (if supported by the server) --- @@ -906,7 +905,6 @@ function Client:_exec_cmd(command, context, handler, on_unsupported) self.request(ms.workspace_executeCommand, params, handler, context.bufnr) end ---- @package --- Default handler for the 'textDocument/didOpen' LSP notification. --- --- @param bufnr integer Number of the buffer, or 0 for current @@ -918,18 +916,16 @@ function Client:_text_document_did_open_handler(bufnr) if not api.nvim_buf_is_loaded(bufnr) then return end - local filetype = vim.bo[bufnr].filetype - local params = { + local filetype = vim.bo[bufnr].filetype + self.notify(ms.textDocument_didOpen, { textDocument = { - version = 0, + version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), languageId = self.get_language_id(bufnr, filetype), text = lsp._buf_get_full_text(bufnr), }, - } - self.notify(ms.textDocument_didOpen, params) - lsp.util.buf_versions[bufnr] = params.textDocument.version + }) -- Next chance we get, we should re-do the diagnostics vim.schedule(function() @@ -942,7 +938,6 @@ function Client:_text_document_did_open_handler(bufnr) end) end ---- @package --- Runs the on_attach function from the client's config if it was defined. --- @param bufnr integer Buffer number function Client:_on_attach(bufnr) @@ -1065,7 +1060,6 @@ function Client:_on_exit(code, signal) ) end ---- @package --- Add a directory to the workspace folders. --- @param dir string? function Client:_add_workspace_folder(dir) @@ -1088,7 +1082,6 @@ function Client:_add_workspace_folder(dir) vim.list_extend(self.workspace_folders, wf) end ---- @package --- Remove a directory to the workspace folders. --- @param dir string? function Client:_remove_workspace_folder(dir) diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 4b7deabf41..2e6d82b367 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -145,8 +145,8 @@ local function get_completion_word(item) -- label: insert -- -- Typing `i` would remove the candidate because newText starts with `t`. - local text = item.insertText or item.textEdit.newText - return #text < #item.label and text or item.label + local text = parse_snippet(item.insertText or item.textEdit.newText) + return #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label elseif item.insertText and item.insertText ~= '' then return parse_snippet(item.insertText) else diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 38c43893eb..44548fec92 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -646,6 +646,7 @@ M[ms.window_showMessage] = function(_, result, ctx, _) if message_type == protocol.MessageType.Error then err_message('LSP[', client_name, '] ', message) else + --- @type string local message_type_name = protocol.MessageType[message_type] api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) end diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index 2d784816cb..aa84294cc4 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -370,7 +370,7 @@ api.nvim_set_decoration_provider(namespace, { }) --- Query whether inlay hint is enabled in the {filter}ed scope ---- @param filter vim.lsp.inlay_hint.enable.Filter +--- @param filter? vim.lsp.inlay_hint.enable.Filter --- @return boolean --- @since 12 function M.is_enabled(filter) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 9f2bd71158..0438ca84af 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -9,7 +9,7 @@ local log_levels = vim.log.levels --- Can be used to lookup the number from the name or the name from the number. --- Levels by name: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" --- Level numbers begin with "TRACE" at 0 ---- @type table<string|integer, string|integer> +--- @type table<string,integer> | table<integer, string> --- @nodoc log.levels = vim.deepcopy(log_levels) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 8ac4cc794b..eb18043843 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -12,9 +12,6 @@ end local sysname = vim.uv.os_uname().sysname --- Protocol for the Microsoft Language Server Protocol (mslsp) -local protocol = {} - local constants = { --- @enum lsp.DiagnosticSeverity DiagnosticSeverity = { @@ -46,6 +43,8 @@ local constants = { Info = 3, -- A log message. Log = 4, + -- A debug message. + Debug = 5, }, -- The file event type. @@ -308,326 +307,18 @@ local constants = { }, } -for k1, v1 in pairs(constants) do - local tbl = vim.deepcopy(v1, true) - for _, k2 in ipairs(vim.tbl_keys(tbl)) do - local v2 = tbl[k2] - tbl[v2] = k2 +-- Protocol for the Microsoft Language Server Protocol (mslsp) +local protocol = {} + +--- @diagnostic disable:no-unknown +for k1, v1 in pairs(vim.deepcopy(constants, true)) do + for _, k2 in ipairs(vim.tbl_keys(v1)) do + local v2 = v1[k2] + v1[v2] = k2 end - protocol[k1] = tbl + protocol[k1] = v1 end - ---[=[ ---Text document specific client capabilities. -export interface TextDocumentClientCapabilities { - synchronization?: { - --Whether text document synchronization supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports sending will save notifications. - willSave?: boolean; - --The client supports sending a will save request and - --waits for a response providing text edits which will - --be applied to the document before it is saved. - willSaveWaitUntil?: boolean; - --The client supports did save notifications. - didSave?: boolean; - } - --Capabilities specific to the `textDocument/completion` - completion?: { - --Whether completion supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports the following `CompletionItem` specific - --capabilities. - completionItem?: { - --The client supports snippets as insert text. - -- - --A snippet can define tab stops and placeholders with `$1`, `$2` - --and `${3:foo}`. `$0` defines the final tab stop, it defaults to - --the end of the snippet. Placeholders with equal identifiers are linked, - --that is typing in one will update others too. - snippetSupport?: boolean; - --The client supports commit characters on a completion item. - commitCharactersSupport?: boolean - --The client supports the following content formats for the documentation - --property. The order describes the preferred format of the client. - documentationFormat?: MarkupKind[]; - --The client supports the deprecated property on a completion item. - deprecatedSupport?: boolean; - --The client supports the preselect property on a completion item. - preselectSupport?: boolean; - } - completionItemKind?: { - --The completion item kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - -- - --If this property is not present the client only supports - --the completion items kinds from `Text` to `Reference` as defined in - --the initial version of the protocol. - valueSet?: CompletionItemKind[]; - }, - --The client supports to send additional context information for a - --`textDocument/completion` request. - contextSupport?: boolean; - }; - --Capabilities specific to the `textDocument/hover` - hover?: { - --Whether hover supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports the follow content formats for the content - --property. The order describes the preferred format of the client. - contentFormat?: MarkupKind[]; - }; - --Capabilities specific to the `textDocument/signatureHelp` - signatureHelp?: { - --Whether signature help supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports the following `SignatureInformation` - --specific properties. - signatureInformation?: { - --The client supports the follow content formats for the documentation - --property. The order describes the preferred format of the client. - documentationFormat?: MarkupKind[]; - --Client capabilities specific to parameter information. - parameterInformation?: { - --The client supports processing label offsets instead of a - --simple label string. - -- - --Since 3.14.0 - labelOffsetSupport?: boolean; - } - }; - }; - --Capabilities specific to the `textDocument/references` - references?: { - --Whether references supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentHighlight` - documentHighlight?: { - --Whether document highlight supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentSymbol` - documentSymbol?: { - --Whether document symbol supports dynamic registration. - dynamicRegistration?: boolean; - --Specific capabilities for the `SymbolKind`. - symbolKind?: { - --The symbol kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - -- - --If this property is not present the client only supports - --the symbol kinds from `File` to `Array` as defined in - --the initial version of the protocol. - valueSet?: SymbolKind[]; - } - --The client supports hierarchical document symbols. - hierarchicalDocumentSymbolSupport?: boolean; - }; - --Capabilities specific to the `textDocument/formatting` - formatting?: { - --Whether formatting supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/rangeFormatting` - rangeFormatting?: { - --Whether range formatting supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/onTypeFormatting` - onTypeFormatting?: { - --Whether on type formatting supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/declaration` - declaration?: { - --Whether declaration supports dynamic registration. If this is set to `true` - --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of declaration links. - -- - --Since 3.14.0 - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/definition`. - -- - --Since 3.14.0 - definition?: { - --Whether definition supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of definition links. - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/typeDefinition` - -- - --Since 3.6.0 - typeDefinition?: { - --Whether typeDefinition supports dynamic registration. If this is set to `true` - --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of definition links. - -- - --Since 3.14.0 - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/implementation`. - -- - --Since 3.6.0 - implementation?: { - --Whether implementation supports dynamic registration. If this is set to `true` - --the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The client supports additional metadata in the form of definition links. - -- - --Since 3.14.0 - linkSupport?: boolean; - }; - --Capabilities specific to the `textDocument/codeAction` - codeAction?: { - --Whether code action supports dynamic registration. - dynamicRegistration?: boolean; - --The client support code action literals as a valid - --response of the `textDocument/codeAction` request. - -- - --Since 3.8.0 - codeActionLiteralSupport?: { - --The code action kind is support with the following value - --set. - codeActionKind: { - --The code action kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - valueSet: CodeActionKind[]; - }; - }; - }; - --Capabilities specific to the `textDocument/codeLens` - codeLens?: { - --Whether code lens supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentLink` - documentLink?: { - --Whether document link supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `textDocument/documentColor` and the - --`textDocument/colorPresentation` request. - -- - --Since 3.6.0 - colorProvider?: { - --Whether colorProvider supports dynamic registration. If this is set to `true` - --the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - } - --Capabilities specific to the `textDocument/rename` - rename?: { - --Whether rename supports dynamic registration. - dynamicRegistration?: boolean; - --The client supports testing for validity of rename operations - --before execution. - prepareSupport?: boolean; - }; - --Capabilities specific to `textDocument/publishDiagnostics`. - publishDiagnostics?: { - --Whether the clients accepts diagnostics with related information. - relatedInformation?: boolean; - --Client supports the tag property to provide meta data about a diagnostic. - --Clients supporting tags have to handle unknown tags gracefully. - --Since 3.15.0 - tagSupport?: { - --The tags supported by this client - valueSet: DiagnosticTag[]; - }; - }; - --Capabilities specific to `textDocument/foldingRange` requests. - -- - --Since 3.10.0 - foldingRange?: { - --Whether implementation supports dynamic registration for folding range providers. If this is set to `true` - --the client supports the new `(FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)` - --return value for the corresponding server capability as well. - dynamicRegistration?: boolean; - --The maximum number of folding ranges that the client prefers to receive per document. The value serves as a - --hint, servers are free to follow the limit. - rangeLimit?: number; - --If set, the client signals that it only supports folding complete lines. If set, client will - --ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. - lineFoldingOnly?: boolean; - }; -} ---]=] - ---[=[ ---Workspace specific client capabilities. -export interface WorkspaceClientCapabilities { - --The client supports applying batch edits to the workspace by supporting - --the request 'workspace/applyEdit' - applyEdit?: boolean; - --Capabilities specific to `WorkspaceEdit`s - workspaceEdit?: { - --The client supports versioned document changes in `WorkspaceEdit`s - documentChanges?: boolean; - --The resource operations the client supports. Clients should at least - --support 'create', 'rename' and 'delete' files and folders. - resourceOperations?: ResourceOperationKind[]; - --The failure handling strategy of a client if applying the workspace edit - --fails. - failureHandling?: FailureHandlingKind; - }; - --Capabilities specific to the `workspace/didChangeConfiguration` notification. - didChangeConfiguration?: { - --Did change configuration notification supports dynamic registration. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `workspace/didChangeWatchedFiles` notification. - didChangeWatchedFiles?: { - --Did change watched files notification supports dynamic registration. Please note - --that the current protocol doesn't support static configuration for file changes - --from the server side. - dynamicRegistration?: boolean; - }; - --Capabilities specific to the `workspace/symbol` request. - symbol?: { - --Symbol request supports dynamic registration. - dynamicRegistration?: boolean; - --Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. - symbolKind?: { - --The symbol kind values the client supports. When this - --property exists the client also guarantees that it will - --handle values outside its set gracefully and falls back - --to a default value when unknown. - -- - --If this property is not present the client only supports - --the symbol kinds from `File` to `Array` as defined in - --the initial version of the protocol. - valueSet?: SymbolKind[]; - } - }; - --Capabilities specific to the `workspace/executeCommand` request. - executeCommand?: { - --Execute command supports dynamic registration. - dynamicRegistration?: boolean; - }; - --The client has support for workspace folders. - -- - --Since 3.6.0 - workspaceFolders?: boolean; - --The client supports `workspace/configuration` requests. - -- - --Since 3.6.0 - configuration?: boolean; -} ---]=] +--- @diagnostic enable:no-unknown --- Gets a new ClientCapabilities object describing the LSP client --- capabilities. @@ -1250,14 +941,5 @@ protocol.Methods = { --- The `workspace/workspaceFolders` is sent from the server to the client to fetch the open workspace folders. workspace_workspaceFolders = 'workspace/workspaceFolders', } -local function freeze(t) - return setmetatable({}, { - __index = t, - __newindex = function() - error('cannot modify immutable table') - end, - }) -end -protocol.Methods = freeze(protocol.Methods) return protocol diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 3c63a12da2..7acb67b25e 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -140,7 +140,7 @@ local client_errors = { SERVER_RESULT_CALLBACK_ERROR = 7, } ---- @type table<string|integer, string|integer> +--- @type table<string,integer> | table<integer,string> --- @nodoc M.client_errors = vim.deepcopy(client_errors) for k, v in pairs(client_errors) do @@ -407,7 +407,9 @@ function Client:handle_body(body) end log.debug('rpc.receive', decoded) - if type(decoded.method) == 'string' and decoded.id then + if type(decoded) ~= 'table' then + self:on_error(M.client_errors.INVALID_SERVER_MESSAGE, decoded) + elseif type(decoded.method) == 'string' and decoded.id then local err --- @type lsp.ResponseError|nil -- Schedule here so that the users functions don't trigger an error and -- we can still use the result. @@ -502,7 +504,7 @@ function Client:handle_body(body) if decoded.error then decoded.error = setmetatable(decoded.error, { __tostring = M.format_rpc_error, - }) --- @type table + }) end self:try_call( M.client_errors.SERVER_RESULT_CALLBACK_ERROR, diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 279956658c..2ae86851d1 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -412,7 +412,7 @@ end function STHighlighter:on_win(topline, botline) for client_id, state in pairs(self.client_state) do local current_result = state.current_result - if current_result.version and current_result.version == util.buf_versions[self.bufnr] then + if current_result.version == util.buf_versions[self.bufnr] then if not current_result.namespace_cleared then api.nvim_buf_clear_namespace(self.bufnr, state.namespace, 0, -1) current_result.namespace_cleared = true @@ -773,7 +773,6 @@ function M.highlight_token(token, bufnr, client_id, hl_group, opts) }) end ---- @package --- |lsp-handler| for the method `workspace/semanticTokens/refresh` --- --- Refresh requests are sent by the server to indicate a project-wide change diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 088d57b6d6..a5cf13ed0f 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -238,6 +238,7 @@ end ---@param rows integer[] zero-indexed line numbers ---@return table<integer, string>|string a table mapping rows to lines local function get_lines(bufnr, rows) + --- @type integer[] rows = type(rows) == 'table' and rows or { rows } -- This is needed for bufload and bufloaded @@ -246,7 +247,7 @@ local function get_lines(bufnr, rows) end local function buf_lines() - local lines = {} + local lines = {} --- @type table<integer,string> for _, row in ipairs(rows) do lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1] end @@ -274,11 +275,11 @@ local function get_lines(bufnr, rows) if not fd then return '' end - local stat = uv.fs_fstat(fd) - local data = uv.fs_read(fd, stat.size, 0) + local stat = assert(uv.fs_fstat(fd)) + local data = assert(uv.fs_read(fd, stat.size, 0)) uv.fs_close(fd) - local lines = {} -- rows we need to retrieve + local lines = {} --- @type table<integer,true|string> rows we need to retrieve local need = 0 -- keep track of how many unique rows we need for _, row in pairs(rows) do if not lines[row] then @@ -307,7 +308,7 @@ local function get_lines(bufnr, rows) lines[i] = '' end end - return lines + return lines --[[@as table<integer,string>]] end --- Gets the zero-indexed line from the given buffer. @@ -322,7 +323,8 @@ local function get_line(bufnr, row) end --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position ----@param offset_encoding string|nil utf-8|utf-16|utf-32 +---@param position lsp.Position +---@param offset_encoding? string utf-8|utf-16|utf-32 ---@return integer local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed @@ -366,6 +368,7 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Fix reversed range and indexing each text_edits local index = 0 + --- @param text_edit lsp.TextEdit text_edits = vim.tbl_map(function(text_edit) index = index + 1 text_edit._index = index @@ -383,6 +386,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) end, text_edits) -- Sort text_edits + ---@param a lsp.TextEdit | { _index: integer } + ---@param b lsp.TextEdit | { _index: integer } + ---@return boolean table.sort(text_edits, function(a, b) if a.range.start.line ~= b.range.start.line then return a.range.start.line > b.range.start.line @@ -393,10 +399,11 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) if a._index ~= b._index then return a._index < b._index end + return false end) -- save and restore local marks since they get deleted by nvim_buf_set_lines - local marks = {} + local marks = {} --- @type table<string,[integer,integer]> for _, m in pairs(vim.fn.getmarklist(bufnr)) do if m.mark:match("^'[a-z]$") then marks[m.mark:sub(2, 2)] = { m.pos[2], m.pos[3] - 1 } -- api-indexed @@ -509,7 +516,6 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) and ( text_document.version and text_document.version > 0 - and M.buf_versions[bufnr] and M.buf_versions[bufnr] > text_document.version ) then @@ -2215,6 +2221,11 @@ end M._get_line_byte_from_position = get_line_byte_from_position ---@nodoc -M.buf_versions = {} ---@type table<integer,integer> +---@type table<integer,integer> +M.buf_versions = setmetatable({}, { + __index = function(t, bufnr) + return rawget(t, bufnr) or 0 + end, +}) return M diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua index bc90d490aa..0be3396464 100644 --- a/runtime/lua/vim/text.lua +++ b/runtime/lua/vim/text.lua @@ -18,15 +18,19 @@ end --- Hex decode a string. --- --- @param enc string String to decode ---- @return string : Decoded string +--- @return string? : Decoded string +--- @return string? : Error message, if any function M.hexdecode(enc) - assert(#enc % 2 == 0, 'string must have an even number of hex characters') + if #enc % 2 ~= 0 then + return nil, 'string must have an even number of hex characters' + end + local str = {} ---@type string[] for i = 1, #enc, 2 do local n = assert(tonumber(enc:sub(i, i + 1), 16)) str[#str + 1] = string.char(n) end - return table.concat(str) + return table.concat(str), nil end return M diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index eecf1ad6b1..04a3c62cf1 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -383,7 +383,6 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, end end ----@package ---@param lnum integer|nil ---@return string function M.foldexpr(lnum) diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua index 177699a207..2aedf5754e 100644 --- a/runtime/lua/vim/treesitter/_meta.lua +++ b/runtime/lua/vim/treesitter/_meta.lua @@ -33,6 +33,7 @@ error('Cannot require a meta file') ---@field iter_children fun(self: TSNode): fun(): TSNode, string ---@field field fun(self: TSNode, name: string): TSNode[] ---@field byte_length fun(self: TSNode): integer +---@field __has_ancestor fun(self: TSNode, node_types: string[]): boolean local TSNode = {} ---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string) @@ -62,6 +63,7 @@ local TSNode = {} ---@field patterns table<integer, (integer|string)[][]> --- @param lang string +--- @return table vim._ts_inspect_language = function(lang) end ---@return integer diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index d2f986b874..003f7e0169 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -47,7 +47,7 @@ function TSHighlighterQuery:get_hl_from_capture(capture) return self.hl_cache[capture] end ----@package +---@nodoc function TSHighlighterQuery:query() return self._query end @@ -75,7 +75,7 @@ local TSHighlighter = { TSHighlighter.__index = TSHighlighter ----@package +---@nodoc --- --- Creates a highlighter for `tree`. --- @@ -232,7 +232,7 @@ function TSHighlighter:on_changedtree(changes) end --- Gets the query used for @param lang ----@package +---@nodoc ---@param lang string Language used by the highlighter. ---@return vim.treesitter.highlighter.Query function TSHighlighter:get_query(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index b0812123b9..3523ea95e0 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -98,9 +98,9 @@ local LanguageTree = {} LanguageTree.__index = LanguageTree ---- @package +--- @nodoc --- ---- |LanguageTree| contains a tree of parsers: the root treesitter parser for {lang} and any +--- LanguageTree contains a tree of parsers: the root treesitter parser for {lang} and any --- "injected" language parsers, which themselves may inject other languages, recursively. --- ---@param source (integer|string) Buffer or text string to parse @@ -951,7 +951,7 @@ function LanguageTree:_edit( end end ----@package +---@nodoc ---@param bufnr integer ---@param changed_tick integer ---@param start_row integer @@ -1023,12 +1023,12 @@ function LanguageTree:_on_bytes( ) end ----@package +---@nodoc function LanguageTree:_on_reload() self:invalidate(true) end ----@package +---@nodoc function LanguageTree:_on_detach(...) self:invalidate(true) self:_do_callback('detach', ...) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index f78a082cb7..9d5d3f5b96 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -44,8 +44,13 @@ " - job_info && term_getjob -> nvim_get_chan_info " - balloon -> vim.lsp.util.open_floating_preview +func s:Echoerr(msg) + echohl ErrorMsg | echom '[termdebug] ' .. a:msg | echohl None +endfunc + " In case this gets sourced twice. if exists(':Termdebug') + call s:Echoerr('Termdebug already loaded.') finish endif @@ -67,8 +72,8 @@ command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand( let s:pc_id = 12 let s:asm_id = 13 let s:break_id = 14 " breakpoint number is added to this -let s:stopped = 1 -let s:running = 0 +let s:stopped = v:true +let s:running = v:false let s:parsing_disasm_msg = 0 let s:asm_lines = [] @@ -121,10 +126,6 @@ func s:GetCommand() return type(cmd) == v:t_list ? copy(cmd) : [cmd] endfunc -func s:Echoerr(msg) - echohl ErrorMsg | echom '[termdebug] ' .. a:msg | echohl None -endfunc - func s:StartDebug(bang, ...) " First argument is the command to debug, second core file or process ID. call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) @@ -149,9 +150,9 @@ func s:StartDebug_internal(dict) let s:ptywin = 0 let s:pid = 0 let s:asmwin = 0 - let s:asmbuf = 0 + let s:asmbufnr = 0 let s:varwin = 0 - let s:varbuf = 0 + let s:varbufnr = 0 if exists('#User#TermdebugStartPre') doauto <nomodeline> User TermdebugStartPre @@ -168,7 +169,7 @@ func s:StartDebug_internal(dict) let s:signcolumn_buflist = [bufnr()] let s:save_columns = 0 - let s:allleft = 0 + let s:allleft = v:false let wide = 0 if exists('g:termdebug_config') let wide = get(g:termdebug_config, 'wide', 0) @@ -181,11 +182,11 @@ func s:StartDebug_internal(dict) let &columns = wide " If we make the Vim window wider, use the whole left half for the debug " windows. - let s:allleft = 1 + let s:allleft = v:true endif - let s:vertical = 1 + let s:vertical = v:true else - let s:vertical = 0 + let s:vertical = v:false endif " Override using a terminal window by setting g:termdebug_use_prompt to 1. @@ -226,24 +227,24 @@ endfunc " Use when debugger didn't start or ended. func s:CloseBuffers() - exe 'bwipe! ' . s:ptybuf - if s:asmbuf > 0 && bufexists(s:asmbuf) - exe 'bwipe! ' . s:asmbuf + exe 'bwipe! ' . s:ptybufnr + if s:asmbufnr > 0 && bufexists(s:asmbufnr) + exe 'bwipe! ' . s:asmbufnr endif - if s:varbuf > 0 && bufexists(s:varbuf) - exe 'bwipe! ' . s:varbuf + if s:varbufnr > 0 && bufexists(s:varbufnr) + exe 'bwipe! ' . s:varbufnr endif - let s:running = 0 + let s:running = v:false unlet! s:gdbwin endfunc -func s:CheckGdbRunning() +func s:IsGdbStarted() if !s:gdb_running call s:Echoerr(string(s:GetCommand()[0]) . ' exited unexpectedly') call s:CloseBuffers() - return '' + return v:false endif - return 'ok' + return v:true endfunc " Open a terminal window without a job, to run the debugged program in. @@ -258,7 +259,7 @@ func s:StartDebug_term(dict) return endif let pty_job_info = nvim_get_chan_info(s:pty_job_id) - let s:ptybuf = pty_job_info['buffer'] + let s:ptybufnr = pty_job_info['buffer'] let pty = pty_job_info['pty'] let s:ptywin = win_getid() if s:vertical @@ -279,11 +280,11 @@ func s:StartDebug_term(dict) " hide terminal buffer if s:comm_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening communication terminal window') - exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:ptybufnr return elseif s:comm_job_id == -1 call s:Echoerr('Failed to open the communication terminal window') - exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:ptybufnr return endif let comm_job_info = nvim_get_chan_info(s:comm_job_id) @@ -324,7 +325,7 @@ func s:StartDebug_term(dict) let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')}) if s:gdb_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening gdb terminal window') - exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:ptybufnr return elseif s:gdb_job_id == -1 call s:Echoerr('Failed to open the gdb terminal window') @@ -334,18 +335,18 @@ func s:StartDebug_term(dict) let s:gdb_running = v:true let s:starting = v:true let gdb_job_info = nvim_get_chan_info(s:gdb_job_id) - let s:gdbbuf = gdb_job_info['buffer'] + let s:gdbbufnr = gdb_job_info['buffer'] let s:gdbwin = win_getid() " Wait for the "startupdone" message before sending any commands. let try_count = 0 while 1 - if s:CheckGdbRunning() != 'ok' + if !s:IsGdbStarted() return endif for lnum in range(1, 200) - if get(getbufline(s:gdbbuf, lnum), 0, '') =~ 'startupdone' + if get(getbufline(s:gdbbufnr, lnum), 0, '') =~ 'startupdone' let try_count = 9999 break endif @@ -371,14 +372,14 @@ func s:StartDebug_term(dict) " why the debugger doesn't work. let try_count = 0 while 1 - if s:CheckGdbRunning() != 'ok' + if !s:IsGdbStarted() return endif let response = '' for lnum in range(1, 200) - let line1 = get(getbufline(s:gdbbuf, lnum), 0, '') - let line2 = get(getbufline(s:gdbbuf, lnum + 1), 0, '') + let line1 = get(getbufline(s:gdbbufnr, lnum), 0, '') + let line2 = get(getbufline(s:gdbbufnr, lnum + 1), 0, '') if line1 =~ 'new-ui mi ' " response can be in the same line or the next line let response = line1 . line2 @@ -472,7 +473,7 @@ func s:StartDebug_prompt(dict) \ }) if s:gdbjob == 0 call s:Echoerr('Invalid argument (or job table is full) while starting gdb job') - exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:ptybufnr return elseif s:gdbjob == -1 call s:Echoerr('Failed to start the gdb job') @@ -481,7 +482,7 @@ func s:StartDebug_prompt(dict) endif exe $'au BufUnload <buffer={s:promptbuf}> ++once call jobstop(s:gdbjob)' - let s:ptybuf = 0 + let s:ptybufnr = 0 if has('win32') " MS-Windows: run in a new console window for maximum compatibility call s:SendCommand('set new-console on') @@ -498,7 +499,7 @@ func s:StartDebug_prompt(dict) return endif let pty_job_info = nvim_get_chan_info(s:pty_job_id) - let s:ptybuf = pty_job_info['buffer'] + let s:ptybufnr = pty_job_info['buffer'] let pty = pty_job_info['pty'] let s:ptywin = win_getid() call s:SendCommand('tty ' . pty) @@ -602,7 +603,7 @@ endfunc func s:SendResumingCommand(cmd) if s:stopped " reset s:stopped here, it may take a bit of time before we get a response - let s:stopped = 0 + let s:stopped = v:false " call ch_log('assume that program is running after this command') call s:SendCommand(a:cmd) " else @@ -776,16 +777,16 @@ endfunc func s:EndDebugCommon() let curwinid = win_getid() - if exists('s:ptybuf') && s:ptybuf - exe 'bwipe! ' . s:ptybuf + if exists('s:ptybufnr') && s:ptybufnr + exe 'bwipe! ' . s:ptybufnr endif - if s:asmbuf > 0 && bufexists(s:asmbuf) - exe 'bwipe! ' . s:asmbuf + if s:asmbufnr > 0 && bufexists(s:asmbufnr) + exe 'bwipe! ' . s:asmbufnr endif - if s:varbuf > 0 && bufexists(s:varbuf) - exe 'bwipe! ' . s:varbuf + if s:varbufnr > 0 && bufexists(s:varbufnr) + exe 'bwipe! ' . s:varbufnr endif - let s:running = 0 + let s:running = v:false " Restore 'signcolumn' in all buffers for which it was set. call win_gotoid(s:sourcewin) @@ -1186,7 +1187,7 @@ endfunc func s:Until(at) if s:stopped " reset s:stopped here, it may take a bit of time before we get a response - let s:stopped = 0 + let s:stopped = v:false " call ch_log('assume that program is running after this command') " Use the fname:lnum format let at = empty(a:at) ? @@ -1322,9 +1323,9 @@ func s:Evaluate(range, arg) return endif let expr = s:GetEvaluationExpression(a:range, a:arg) - let s:evalFromBalloonExpr = 1 + let s:evalFromBalloonExpr = v:true let s:evalFromBalloonExprResult = '' - let s:ignoreEvalError = 0 + let s:ignoreEvalError = v:false call s:SendEval(expr) endfunc @@ -1343,12 +1344,12 @@ func s:GetEvaluationExpression(range, arg) let expr = s:CleanupExpr(@v) call setpos('.', pos) call setreg('v', reg, regt) - let s:evalFromBalloonExpr = 1 + let s:evalFromBalloonExpr = v:true else " no evaluation provided: get from C-expression under cursor " TODO: allow filetype specific lookup #9057 let expr = expand('<cexpr>') - let s:evalFromBalloonExpr = 1 + let s:evalFromBalloonExpr = v:true endif return expr endfunc @@ -1376,8 +1377,8 @@ func s:CleanupExpr(expr) return expr endfunc -let s:ignoreEvalError = 0 -let s:evalFromBalloonExpr = 0 +let s:ignoreEvalError = v:false +let s:evalFromBalloonExpr = v:false let s:evalFromBalloonExprResult = '' let s:eval_float_win_id = -1 @@ -1419,7 +1420,7 @@ func s:HandleEvaluate(msg) if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' " Looks like a pointer, also display what it points to. - let s:ignoreEvalError = 1 + let s:ignoreEvalError = v:true call s:SendEval('*' . s:evalexpr) endif endfunc @@ -1428,8 +1429,8 @@ endfunc func s:HandleError(msg) if s:ignoreEvalError " Result of s:SendEval() failed, ignore. - let s:ignoreEvalError = 0 - let s:evalFromBalloonExpr = 0 + let s:ignoreEvalError = v:false + let s:evalFromBalloonExpr = v:false return endif let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '') @@ -1489,11 +1490,11 @@ func s:GotoAsmwinOrCreateIt() setlocal signcolumn=no setlocal modifiable - if s:asmbuf > 0 && bufexists(s:asmbuf) - exe 'buffer' . s:asmbuf + if s:asmbufnr > 0 && bufexists(s:asmbufnr) + exe 'buffer' . s:asmbufnr elseif empty(glob('Termdebug-asm-listing')) silent file Termdebug-asm-listing - let s:asmbuf = bufnr('Termdebug-asm-listing') + let s:asmbufnr = bufnr('Termdebug-asm-listing') else call s:Echoerr("You have a file/folder named 'Termdebug-asm-listing'. \ Please exit and rename it because Termdebug may not work as expected.") @@ -1561,11 +1562,11 @@ func s:GotoVariableswinOrCreateIt() setlocal signcolumn=no setlocal modifiable - if s:varbuf > 0 && bufexists(s:varbuf) - exe 'buffer' . s:varbuf + if s:varbufnr > 0 && bufexists(s:varbufnr) + exe 'buffer' . s:varbufnr elseif empty(glob('Termdebug-variables-listing')) silent file Termdebug-variables-listing - let s:varbuf = bufnr('Termdebug-variables-listing') + let s:varbufnr = bufnr('Termdebug-variables-listing') else call s:Echoerr("You have a file/folder named 'Termdebug-variables-listing'. \ Please exit and rename it because Termdebug may not work as expected.") @@ -1588,14 +1589,14 @@ func s:HandleCursor(msg) if a:msg =~ '^\*stopped' "call ch_log('program stopped') - let s:stopped = 1 + let s:stopped = v:true if a:msg =~ '^\*stopped,reason="exited-normally"' - let s:running = 0 + let s:running = v:false endif elseif a:msg =~ '^\*running' "call ch_log('program running') - let s:stopped = 0 - let s:running = 1 + let s:stopped = v:false + let s:running = v:true endif if a:msg =~ 'fullname=' diff --git a/runtime/syntax/deb822sources.vim b/runtime/syntax/deb822sources.vim index f7d337fce9..ec45605905 100644 --- a/runtime/syntax/deb822sources.vim +++ b/runtime/syntax/deb822sources.vim @@ -40,7 +40,7 @@ syn match deb822sourcesUri '\(https\?://\|ftp://\|[rs]sh://\|debtorre syn region deb822sourcesStrictField matchgroup=deb822sourcesEntryField start="^\%(Types\|URIs\|Suites\|Components\): *" end="$" contains=deb822sourcesType,deb822sourcesUri,deb822sourcesSupportedSuites,deb822sourcesUnsupportedSuites,deb822sourcesFreeComponent,deb822sourcesNonFreeComponent oneline syn region deb822sourcesField matchgroup=deb822sourcesOptionField start="^\%(Signed-By\|Check-Valid-Until\|Valid-Until-Min\|Valid-Until-Max\|Date-Max-Future\|InRelease-Path\): *" end="$" oneline syn region deb822sourcesField matchgroup=deb822sourcesMultiValueOptionField start="^\%(Architectures\|Languages\|Targets\)\%(-Add\|-Remove\)\?: *" end="$" oneline -syn region deb822sourcesStrictField matchgroup=deb822sourcesBooleanOptionField start="^\%(PDiffs\|Allow-Insecure\|Allow-Weak\|Allow-Downgrade-To-Insecure\|Trusted\|Check-Date\): *" end="$" contains=deb822sourcesYesNo oneline +syn region deb822sourcesStrictField matchgroup=deb822sourcesBooleanOptionField start="^\%(PDiffs\|Allow-Insecure\|Allow-Weak\|Allow-Downgrade-To-Insecure\|Trusted\|Check-Date\|Enabled\): *" end="$" contains=deb822sourcesYesNo oneline syn region deb822sourcesStrictField matchgroup=deb822sourcesForceBooleanOptionField start="^\%(By-Hash\): *" end="$" contains=deb822sourcesForce,deb822sourcesYesNo oneline hi def link deb822sourcesField Default diff --git a/runtime/syntax/html.vim b/runtime/syntax/html.vim index c975ae8620..d067dde83c 100644 --- a/runtime/syntax/html.vim +++ b/runtime/syntax/html.vim @@ -191,7 +191,7 @@ syn keyword htmlArg contained step title translate typemustmatch syn match htmlArg contained "\<data-\h\%(\w\|[-.]\)*\%(\_s*=\)\@=" " special characters -syn match htmlSpecialChar "&#\=[0-9A-Za-z]\{1,8};" +syn match htmlSpecialChar "&#\=[0-9A-Za-z]\{1,32};" " Comments (the real ones or the old netscape ones) if exists("html_wrong_comments") diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim index f5910a8557..6f64bdc4ed 100644 --- a/runtime/syntax/java.vim +++ b/runtime/syntax/java.vim @@ -3,7 +3,7 @@ " Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com> " Former Maintainer: Claudio Fleiner <claudio@fleiner.com> " Repository: https://github.com/zzzyxwvut/java-vim.git -" Last Change: 2024 Jun 08 +" Last Change: 2024 Jun 15 " Please check :help java.vim for comments on some of the options available. @@ -14,7 +14,6 @@ if !exists("main_syntax") endif " we define it here so that included files can test for it let main_syntax='java' - syn region javaFold start="{" end="}" transparent fold endif let s:cpo_save = &cpo @@ -40,6 +39,18 @@ else endfunction endif +function! JavaSyntaxFoldTextExpr() abort + return getline(v:foldstart) !~ '/\*\+\s*$' + \ ? foldtext() + \ : printf('+-%s%3d lines: ', + \ v:folddashes, + \ (v:foldend - v:foldstart + 1)) . + \ getline(v:foldstart + 1) +endfunction + +" E120 for "fdt=s:JavaSyntaxFoldTextExpr()" before v8.2.3900. +setlocal foldtext=JavaSyntaxFoldTextExpr() + " Admit the ASCII dollar sign to keyword characters (JLS-17, §3.8): try exec 'syntax iskeyword ' . &l:iskeyword . ',$' @@ -99,6 +110,7 @@ syn match javaClassDecl "\<record\>\%(\s*(\)\@!" syn match javaClassDecl "^class\>" syn match javaClassDecl "[^.]\s*\<class\>"ms=s+1 syn match javaAnnotation "@\%(\K\k*\.\)*\K\k*\>" +syn region javaAnnotation transparent matchgroup=javaAnnotationStart start=/@\%(\K\k*\.\)*\K\k*(/ end=/)/ skip=/\/\*.\{-}\*\/\|\/\/.*$/ contains=javaAnnotation,javaParenT,javaBlock,javaString,javaBoolean,javaNumber,javaTypedef,javaComment,javaLineComment syn match javaClassDecl "@interface\>" syn keyword javaBranch break continue nextgroup=javaUserLabelRef skipwhite syn match javaUserLabelRef "\k\+" contained @@ -237,7 +249,7 @@ if exists("java_comment_strings") syn cluster javaCommentSpecial2 add=javaComment2String,javaCommentCharacter,javaNumber,javaStrTempl endif -syn region javaComment matchgroup=javaCommentStart start="/\*" end="\*/" contains=@javaCommentSpecial,javaTodo,javaCommentError,javaSpaceError,@Spell +syn region javaComment matchgroup=javaCommentStart start="/\*" end="\*/" contains=@javaCommentSpecial,javaTodo,javaCommentError,javaSpaceError,@Spell fold syn match javaCommentStar contained "^\s*\*[^/]"me=e-1 syn match javaCommentStar contained "^\s*\*$" syn match javaLineComment "//.*" contains=@javaCommentSpecial2,javaTodo,javaCommentMarkupTag,javaSpaceError,@Spell @@ -268,7 +280,7 @@ if !exists("java_ignore_javadoc") && main_syntax != 'jsp' call s:ReportOnce(v:exception) endtry - syn region javaDocComment start="/\*\*" end="\*/" keepend contains=javaCommentTitle,@javaHtml,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag,javaTodo,javaCommentError,javaSpaceError,@Spell + syn region javaDocComment start="/\*\*" end="\*/" keepend contains=javaCommentTitle,@javaHtml,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag,javaTodo,javaCommentError,javaSpaceError,@Spell fold exec 'syn region javaCommentTitle contained matchgroup=javaDocComment start="/\*\*" matchgroup=javaCommentTitle end="\.$" end="\.[ \t\r]\@=" end="\%(^\s*\**\s*\)\@' . s:ff.Peek('80', '') . '<=@"me=s-2,he=s-1 end="\*/"me=s-1,he=s-1 contains=@javaHtml,javaCommentStar,javaTodo,javaCommentError,javaSpaceError,@Spell,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag' syn region javaCommentTitle contained matchgroup=javaDocComment start="/\*\*\s*\r\=\n\=\s*\**\s*\%({@return\>\)\@=" matchgroup=javaCommentTitle end="}\%(\s*\.*\)*" contains=@javaHtml,javaCommentStar,javaTodo,javaCommentError,javaSpaceError,@Spell,javaDocTags,javaDocSeeTag,javaDocCodeTag,javaDocSnippetTag syn region javaDocTags contained start="{@\%(li\%(teral\|nk\%(plain\)\=\)\|inherit[Dd]oc\|doc[rR]oot\|value\)\>" end="}" @@ -347,8 +359,6 @@ if exists("java_highlight_functions") " Match: [@ɐ] [abstract] [<α, β>] Τʬ[<γ>][[][]] μʭʭ(/* ... */); exec 'syn region javaFuncDef start=/' . s:ff.Engine('\%#=2', '') . '^\s\+\%(\%(@\%(\K\k*\.\)*\K\k*\>\)\s\+\)*\%(p\%(ublic\|rotected\|rivate\)\s\+\)\=\%(\%(abstract\|default\)\s\+\|\%(\%(final\|\%(native\|strictfp\)\|s\%(tatic\|ynchronized\)\)\s\+\)*\)\=\%(<.*[[:space:]-]\@' . s:ff.Peek('1', '') . '<!>\s\+\)\=\%(void\|\%(b\%(oolean\|yte\)\|char\|short\|int\|long\|float\|double\|\%(\<\K\k*\>\.\)*\<' . s:ff.UpperCase('[$_[:upper:]]', '[^a-z0-9]') . '\k*\>\%(<[^(){}]*[[:space:]-]\@' . s:ff.Peek('1', '') . '<!>\)\=\)\%(\[\]\)*\)\s\+\<' . s:ff.LowerCase('[$_[:lower:]]', '[^A-Z0-9]') . '\k*\>\s*(/ end=/)/ skip=/\/\*.\{-}\*\/\|\/\/.*$/ contains=@javaFuncParams' endif - - syn match javaBraces "[{}]" endif if exists("java_highlight_debug") @@ -403,14 +413,18 @@ if exists("java_highlight_debug") endif if exists("java_mark_braces_in_parens_as_errors") - syn match javaInParen contained "[{}]" - hi def link javaInParen javaError + syn match javaInParen contained "[{}]" + hi def link javaInParen javaError endif +" Try not to fold top-level-type bodies under assumption that there is +" but one such body. +exec 'syn region javaBlock transparent start="\%(^\|^\S[^:]\+\)\@' . s:ff.Peek('120', '') . '<!{" end="}" fold' + " catch errors caused by wrong parenthesis -syn region javaParenT transparent matchgroup=javaParen start="(" end=")" contains=@javaTop,javaParenT1 -syn region javaParenT1 transparent matchgroup=javaParen1 start="(" end=")" contains=@javaTop,javaParenT2 contained -syn region javaParenT2 transparent matchgroup=javaParen2 start="(" end=")" contains=@javaTop,javaParenT contained +syn region javaParenT transparent matchgroup=javaParen start="(" end=")" contains=@javaTop,javaInParen,javaParenT1 +syn region javaParenT1 transparent matchgroup=javaParen1 start="(" end=")" contains=@javaTop,javaInParen,javaParenT2 contained +syn region javaParenT2 transparent matchgroup=javaParen2 start="(" end=")" contains=@javaTop,javaInParen,javaParenT contained syn match javaParenError ")" " catch errors caused by wrong square parenthesis syn region javaParenT transparent matchgroup=javaParen start="\[" end="\]" contains=@javaTop,javaParenT1 @@ -444,7 +458,7 @@ endif " The @javaTop cluster comprises non-contained Java syntax groups. " Note that the syntax file "aidl.vim" relies on its availability. -syn cluster javaTop contains=TOP,javaDocComment,javaFold,javaParenError,javaParenT +syn cluster javaTop contains=TOP,javaDocComment,javaBlock,javaParenError,javaParenT if !exists("java_minlines") let java_minlines = 10 @@ -462,7 +476,6 @@ exec "syn sync ccomment javaComment minlines=" . java_minlines hi def link javaLambdaDef Function hi def link javaFuncDef Function hi def link javaVarArg Function -hi def link javaBraces Function hi def link javaBranch Conditional hi def link javaUserLabelRef javaUserLabel hi def link javaLabel Label @@ -497,6 +510,7 @@ hi def link javaConstant Constant hi def link javaTypedef Typedef hi def link javaTodo Todo hi def link javaAnnotation PreProc +hi def link javaAnnotationStart javaAnnotation hi def link javaCommentTitle SpecialComment hi def link javaDocTags Special @@ -531,4 +545,20 @@ let b:spell_options = "contained" let &cpo = s:cpo_save unlet s:module_info_cur_buf s:ff s:cpo_save +" See ":help vim9-mix". +if !has("vim9script") + finish +endif + +def! s:JavaSyntaxFoldTextExpr(): string + return getline(v:foldstart) !~ '/\*\+\s*$' + ? foldtext() + : printf('+-%s%3d lines: ', + v:folddashes, + (v:foldend - v:foldstart + 1)) .. + getline(v:foldstart + 1) +enddef + +setlocal foldtext=s:JavaSyntaxFoldTextExpr() +delfunction! g:JavaSyntaxFoldTextExpr " vim: sw=2 ts=8 noet sta diff --git a/runtime/syntax/kdl.vim b/runtime/syntax/kdl.vim new file mode 100644 index 0000000000..97e8f93b61 --- /dev/null +++ b/runtime/syntax/kdl.vim @@ -0,0 +1,48 @@ +" Vim syntax file +" Language: KDL +" Maintainer: Aram Drevekenin <aram@poor.dev> +" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com> +" Latest Revision: 2024-06-16 + +" quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +syn match kdlNode '\v(\w|-|\=)' display +syn match kdlBool '\v(true|false)' display + +syn keyword kdlTodo contained TODO FIXME XXX NOTE +syn region kdlComment start="//" end="$" contains=kdlTodo,@Spell +syn region kdlComment start="/\*" end="\*/" contains=kdlTodo,@Spell + +" Regular int like number with - + or nothing in front +syn match kdlNumber '\d\+' +syn match kdlNumber '[-+]\d\+' + +" Floating point number with decimal no E or e (+,-) +syn match kdlNumber '\d\+\.\d*' contained display +syn match kdlNumber '[-+]\d\+\.\d*' contained display + +" Floating point like number with E and no decimal point (+,-) +syn match kdlNumber '[-+]\=\d[[:digit:]]*[eE][\-+]\=\d\+' contained display +syn match kdlNumber '\d[[:digit:]]*[eE][\-+]\=\d\+' contained display + +" Floating point like number with E and decimal point (+,-) +syn match kdlNumber '[-+]\=\d[[:digit:]]*\.\d*[eE][\-+]\=\d\+' contained display +syn match kdlNumber '\d[[:digit:]]*\.\d*[eE][\-+]\=\d\+' contained display + +syn region kdlString start='"' end='"' skip='\\\\\|\\"' display + +syn region kdlChildren start="{" end="}" contains=kdlString,kdlNumber,kdlNode,kdlBool,kdlComment + +hi def link kdlTodo Todo +hi def link kdlComment Comment +hi def link kdlNode Statement +hi def link kdlBool Boolean +hi def link kdlString String +hi def link kdlNumber Number + +let b:current_syntax = "kdl" + +" vim: sw=2 sts=2 et diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 6cb75f3af4..a3b0117e9d 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -60,9 +60,9 @@ syn case ignore syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo " Default highlighting groups {{{2 -syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuExtra PmenuExtraSel Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen CursorIM LineNrAbove LineNrBelow +syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC CursorIM LineNrAbove LineNrBelow syn match vimHLGroup contained "\<Conceal\>" -syn keyword vimOnlyHLGroup contained Menu Scrollbar StatusLineTerm StatusLineTermNC ToolbarButton ToolbarLine Tooltip VisualNOS +syn keyword vimOnlyHLGroup contained Menu Scrollbar ToolbarButton ToolbarLine Tooltip VisualNOS syn keyword nvimHLGroup contained FloatBorder FloatFooter FloatTitle MsgSeparator NormalFloat NormalNC Substitute TermCursor TermCursorNC VisualNC Whitespace WinBar WinBarNC WinSeparator "}}}2 syn case match @@ -165,7 +165,7 @@ syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSub syn case match " All vimCommands are contained by vimIsCommand. {{{2 -syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimDef,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNotFunc,vimNorm,vimSet,vimSyntax,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate +syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimCatch,vimDef,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimGlobal,vimHighlight,vimLet,vimMap,vimMark,vimNotFunc,vimNorm,vimSet,vimSleep,vimSyntax,vimThrow,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate syn match vimCmdSep "[:|]\+" skipwhite nextgroup=@vimCmdList,vimSubst1 syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>" @@ -196,6 +196,11 @@ syn match vimBehave "\<be\%[have]\>" nextgroup=vimBehaveBang,vimBehaveModel,vi syn match vimBehaveBang contained "\a\@1<=!" nextgroup=vimBehaveModel skipwhite syn keyword vimBehaveModel contained mswin xterm +" Exception Handling {{{2 +syn keyword vimThrow th[row] skipwhite nextgroup=@vimExprList +syn keyword vimCatch cat[ch] skipwhite nextgroup=vimCatchPattern +syn region vimCatchPattern contained matchgroup=Delimiter start="\z([!#$%&'()*+,-./:;<=>?@[\]^_`{}~]\)" skip="\\\\\|\\\z1" end="\z1" contains=@vimSubstList oneline + " Filetypes {{{2 " ========= syn match vimFiletype "\<filet\%[ype]\(\s\+\I\i*\)*" skipwhite contains=vimFTCmd,vimFTOption,vimFTError @@ -359,7 +364,9 @@ syn region vimUserCmdBlock contained matchgroup=vimSep start="{" end="}" contain " Lower Priority Comments: after some vim commands... {{{2 " ======================= -syn region vimCommentString contained oneline start='\S\s\+"'ms=e end='"' +if get(g:, "vimsyn_comment_strings", 1) + syn region vimCommentString contained oneline start='\S\s\+"'ms=e end='"' +endif if s:vim9script syn match vimComment excludenl +\s"[^\-:.%#=*].*$+lc=1 contains=@vimCommentGroup,vimCommentString contained @@ -640,6 +647,12 @@ syn match vimNotFunc "\<if\>\|\<el\%[seif]\>\|\<retu\%[rn]\>\|\<while\>" skipwhi syn match vimNorm "\<norm\%[al]!\=" skipwhite nextgroup=vimNormCmds syn match vimNormCmds contained ".*$" +" Sleep: {{{2 +" ===== +syn keyword vimSleep sl[eep] skipwhite nextgroup=vimSleepBang,vimSleepArg +syn match vimSleepBang contained "\a\@1<=!" skipwhite nextgroup=vimSleepArg +syn match vimSleepArg contained "\<\%(\d\+\)\=m\=\>" + " Syntax: {{{2 "======= syn match vimGroupList contained "[^[:space:],]\+\%(\s*,\s*[^[:space:],]\+\)*" contains=vimGroupSpecial @@ -806,16 +819,23 @@ syn match vimCtrlChar "[--]" " Beginners - Patterns that involve ^ {{{2 " ========= if s:vim9script - syn match vimLineComment +^[ \t:]*".*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle contained - syn match vim9LineComment +^[ \t:]*#.*$+ contains=@vimCommentGroup,vimCommentString,vim9CommentTitle + syn region vim9LineComment start=+^[ \t:]*\zs#.*$+ skip=+\n\s*\\\|\n\s*#\\ + end="$" contains=@vimCommentGroup,vimCommentString,vim9CommentTitle + syn region vimLineComment start=+^[ \t:]*\zs".*$+ skip=+\n\s*\\\|\n\s*"\\ + end="$" contains=@vimCommentGroup,vimCommentString,vimCommentTitle contained else - syn match vimLineComment +^[ \t:]*".*$+ contains=@vimCommentGroup,vimCommentString,vimCommentTitle - syn match vim9LineComment +^[ \t:]*#.*$+ contains=@vimCommentGroup,vimCommentString,vim9CommentTitle contained + syn region vimLineComment start=+^[ \t:]*\zs".*$+ skip=+\n\s*\\\|\n\s*"\\ + end="$" contains=@vimCommentGroup,vimCommentString,vimCommentTitle + syn region vim9LineComment start=+^[ \t:]*\zs#.*$+ skip=+\n\s*\\\|\n\s*#\\ + end="$" contains=@vimCommentGroup,vimCommentString,vim9CommentTitle contained endif syn match vimCommentTitle '"\s*\%([sS]:\|\h\w*#\)\=\u\w*\(\s\+\u\w*\)*:'hs=s+1 contained contains=vimCommentTitleLeader,vimTodo,@vimCommentGroup syn match vim9CommentTitle '#\s*\%([sS]:\|\h\w*#\)\=\u\w*\(\s\+\u\w*\)*:'hs=s+1 contained contains=vim9CommentTitleLeader,vimTodo,@vimCommentGroup + +" allowed anywhere in the file +if !s:vim9script + syn match vimShebangError "^\s*\zs#!.*" display +endif +syn match vimShebang "\%^#!.*" display + syn match vimContinue "^\s*\zs\\" -syn match vimContinueComment '^\s*\zs["#]\\ .*' contained +syn match vimContinueComment '^\s*\zs["#]\\ .*' syn cluster vimContinue contains=vimContinue,vimContinueComment syn region vimString start="^\s*\\\z(['"]\)" skip='\\\\\|\\\z1' end="\z1" oneline keepend contains=@vimStringGroup,vimContinue syn match vimCommentTitleLeader '"\s\+'ms=s+1 contained @@ -1044,6 +1064,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimHiCtermError vimError hi def link vimHiKeyError vimError hi def link vimMapModErr vimError + hi def link vimShebangError vimError hi def link vimSubstFlagErr vimError hi def link vimSynCaseError vimError hi def link vimSynFoldMethodError vimError @@ -1064,6 +1085,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimBehaveModel vimBehave hi def link vimBehave vimCommand hi def link vimBracket Delimiter + hi def link vimCatch vimCommand hi def link vimCmplxRepeat SpecialChar hi def link vimCommand Statement hi def link vimComment Comment @@ -1181,6 +1203,10 @@ if !exists("skip_vim_syntax_inits") hi def link vimSetSep Statement hi def link vimSetString vimString hi def link vim9Vim9Script vimCommand + hi def link vimShebang PreProc + hi def link vimSleep vimCommand + hi def link vimSleepArg Constant + hi def link vimSleepBang vimBang hi def link vimSpecFile Identifier hi def link vimSpecFileMod vimSpecFile hi def link vimSpecial Type @@ -1221,6 +1247,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimSynReg Type hi def link vimSyntax vimCommand hi def link vimSynType vimSpecial + hi def link vimThrow vimCommand hi def link vimTodo Todo hi def link vimType Type hi def link vimUnlet vimCommand diff --git a/scripts/gen_vimdoc.lua b/scripts/gen_vimdoc.lua index 507426c8c0..dc384c12f5 100755 --- a/scripts/gen_vimdoc.lua +++ b/scripts/gen_vimdoc.lua @@ -421,8 +421,11 @@ local function render_type(ty, generics, default) end --- @param p nvim.luacats.parser.param|nvim.luacats.parser.field -local function should_render_param(p) - return not p.access and not contains(p.name, { '_', 'self' }) +local function should_render_field_or_param(p) + return not p.nodoc + and not p.access + and not contains(p.name, { '_', 'self' }) + and not vim.startswith(p.name, '_') end --- @param desc? string @@ -524,7 +527,7 @@ end local function render_fields_or_params(xs, generics, classes, exclude_types) local ret = {} --- @type string[] - xs = vim.tbl_filter(should_render_param, xs) + xs = vim.tbl_filter(should_render_field_or_param, xs) local indent = 0 for _, p in ipairs(xs) do diff --git a/scripts/luacats_grammar.lua b/scripts/luacats_grammar.lua index 9360eb9417..ebb0183fd9 100644 --- a/scripts/luacats_grammar.lua +++ b/scripts/luacats_grammar.lua @@ -178,7 +178,8 @@ local grammar = P { table_elem = v.table_key * colon * v.ltype, ty_table = Pf('{') * comma1(v.table_elem) * fill * P('}'), fun_param = lname * opt(colon * v.ltype), - ty_fun = Pf('fun') * paren(comma(lname * opt(colon * v.ltype))) * opt(colon * comma1(v.ltype)), + fun_ret = v.ltype + (ident * colon * v.ltype) + (P('...') * opt(colon * v.ltype)), + ty_fun = Pf('fun') * paren(comma(lname * opt(colon * v.ltype))) * opt(colon * comma1(v.fun_ret)), ty_generic = P('`') * letter * P('`'), ty_tuple = Pf('[') * comma(v.ltype) * fill * P(']'), } diff --git a/scripts/luacats_parser.lua b/scripts/luacats_parser.lua index 66fe8ed616..e73a42111d 100644 --- a/scripts/luacats_parser.lua +++ b/scripts/luacats_parser.lua @@ -46,6 +46,7 @@ local luacats_grammar = require('scripts.luacats_grammar') --- @field type string --- @field desc string --- @field access? 'private'|'package'|'protected' +--- @field nodoc? true --- @class nvim.luacats.parser.class --- @field kind 'class' @@ -270,6 +271,7 @@ local function fun2field(fun) type = table.concat(parts, ''), access = fun.access, desc = fun.desc, + nodoc = fun.nodoc, } end diff --git a/src/clint.py b/src/clint.py index 41058469b1..051f0e91e5 100755 --- a/src/clint.py +++ b/src/clint.py @@ -1995,7 +1995,7 @@ def CheckLanguage(filename, clean_lines, linenum, error): if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcpy, xmemcpyz or snprintf instead of %s' % match.group(1)) - match = Search(r'\b(STRNCAT|strncat|strcat|vim_strcat)\b', line) + match = Search(r'\b(STRNCAT|strncat|vim_strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcat or snprintf instead of %s' % match.group(1)) diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index ab6ff5ff1f..e30ec9a2ca 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -1002,7 +1002,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// Note: this function should not be called often. Rather, the callbacks /// themselves can be used to throttle unneeded callbacks. the `on_start` /// callback can return `false` to disable the provider until the next redraw. -/// Similarly, return `false` in `on_win` will skip the `on_lines` calls +/// Similarly, return `false` in `on_win` will skip the `on_line` calls /// for that window (but any extmarks set in `on_win` will still be used). /// A plugin managing multiple sources of decoration should ideally only set /// one provider, and merge the sources internally. You can use multiple `ns_id` @@ -1011,10 +1011,10 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// Note: doing anything other than setting extmarks is considered experimental. /// Doing things like changing options are not explicitly forbidden, but is /// likely to have unexpected consequences (such as 100% CPU consumption). -/// doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious +/// Doing `vim.rpcnotify` should be OK, but `vim.rpcrequest` is quite dubious /// for the moment. /// -/// Note: It is not allowed to remove or update extmarks in 'on_line' callbacks. +/// Note: It is not allowed to remove or update extmarks in `on_line` callbacks. /// /// @param ns_id Namespace id from |nvim_create_namespace()| /// @param opts Table of callbacks: diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a17e78cc31..ac621b1486 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -548,10 +548,6 @@ String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col void api_free_string(String value) { - if (!value.data) { - return; - } - xfree(value.data); } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index e5872fcc76..c32d33080e 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -711,7 +711,7 @@ char *au_event_disable(char *what) if (*what == ',' && *p_ei == NUL) { STRCPY(new_ei, what + 1); } else { - STRCAT(new_ei, what); + strcat(new_ei, what); } set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(new_ei), 0, SID_NONE); xfree(new_ei); diff --git a/src/nvim/base64.c b/src/nvim/base64.c index 39e4ec4872..99d3c5a33e 100644 --- a/src/nvim/base64.c +++ b/src/nvim/base64.c @@ -142,6 +142,7 @@ char *base64_encode(const char *src, size_t src_len) /// @param [out] out_lenp Returns the length of the decoded string /// @return Decoded string char *base64_decode(const char *src, size_t src_len, size_t *out_lenp) + FUNC_ATTR_NONNULL_ALL { assert(src != NULL); assert(out_lenp != NULL); diff --git a/src/nvim/change.c b/src/nvim/change.c index 05772d39e9..6c979df1fe 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1723,12 +1723,12 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Below, set_indent(newindent, SIN_INSERT) will insert the // whitespace needed before the comment char. for (int i = 0; i < padding; i++) { - STRCAT(leader, " "); + strcat(leader, " "); less_cols--; newcol++; } } - STRCAT(leader, p_extra); + strcat(leader, p_extra); p_extra = leader; did_ai = true; // So truncating blanks works with comments less_cols -= lead_len; diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index 0b88e2be11..f75b84c77b 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -3257,7 +3257,7 @@ void globpath(char *path, char *file, garray_T *ga, int expand_options, bool dir copy_option_part(&path, buf, MAXPATHL, ","); if (strlen(buf) + strlen(file) + 2 < MAXPATHL) { add_pathsep(buf); - STRCAT(buf, file); + strcat(buf, file); char **p; int num_p = 0; diff --git a/src/nvim/context.c b/src/nvim/context.c index 95e2618f62..2e3634329a 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -66,21 +66,11 @@ Context *ctx_get(size_t index) void ctx_free(Context *ctx) FUNC_ATTR_NONNULL_ALL { - if (ctx->regs.data) { - msgpack_sbuffer_destroy(&ctx->regs); - } - if (ctx->jumps.data) { - msgpack_sbuffer_destroy(&ctx->jumps); - } - if (ctx->bufs.data) { - msgpack_sbuffer_destroy(&ctx->bufs); - } - if (ctx->gvars.data) { - msgpack_sbuffer_destroy(&ctx->gvars); - } - if (ctx->funcs.items) { - api_free_array(ctx->funcs); - } + api_free_string(ctx->regs); + api_free_string(ctx->jumps); + api_free_string(ctx->bufs); + api_free_string(ctx->gvars); + api_free_array(ctx->funcs); } /// Saves the editor state to a context. @@ -98,19 +88,19 @@ void ctx_save(Context *ctx, const int flags) } if (flags & kCtxRegs) { - ctx_save_regs(ctx); + ctx->regs = shada_encode_regs(); } if (flags & kCtxJumps) { - ctx_save_jumps(ctx); + ctx->jumps = shada_encode_jumps(); } if (flags & kCtxBufs) { - ctx_save_bufs(ctx); + ctx->bufs = shada_encode_buflist(); } if (flags & kCtxGVars) { - ctx_save_gvars(ctx); + ctx->gvars = shada_encode_gvars(); } if (flags & kCtxFuncs) { @@ -173,33 +163,13 @@ bool ctx_restore(Context *ctx, const int flags) return true; } -/// Saves the global registers to a context. -/// -/// @param ctx Save to this context. -static inline void ctx_save_regs(Context *ctx) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_sbuffer_init(&ctx->regs); - shada_encode_regs(&ctx->regs); -} - /// Restores the global registers from a context. /// /// @param ctx Restore from this context. static inline void ctx_restore_regs(Context *ctx) FUNC_ATTR_NONNULL_ALL { - shada_read_sbuf(&ctx->regs, kShaDaWantInfo | kShaDaForceit); -} - -/// Saves the jumplist to a context. -/// -/// @param ctx Save to this context. -static inline void ctx_save_jumps(Context *ctx) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_sbuffer_init(&ctx->jumps); - shada_encode_jumps(&ctx->jumps); + shada_read_string(ctx->regs, kShaDaWantInfo | kShaDaForceit); } /// Restores the jumplist from a context. @@ -208,17 +178,7 @@ static inline void ctx_save_jumps(Context *ctx) static inline void ctx_restore_jumps(Context *ctx) FUNC_ATTR_NONNULL_ALL { - shada_read_sbuf(&ctx->jumps, kShaDaWantInfo | kShaDaForceit); -} - -/// Saves the buffer list to a context. -/// -/// @param ctx Save to this context. -static inline void ctx_save_bufs(Context *ctx) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_sbuffer_init(&ctx->bufs); - shada_encode_buflist(&ctx->bufs); + shada_read_string(ctx->jumps, kShaDaWantInfo | kShaDaForceit); } /// Restores the buffer list from a context. @@ -227,17 +187,7 @@ static inline void ctx_save_bufs(Context *ctx) static inline void ctx_restore_bufs(Context *ctx) FUNC_ATTR_NONNULL_ALL { - shada_read_sbuf(&ctx->bufs, kShaDaWantInfo | kShaDaForceit); -} - -/// Saves global variables to a context. -/// -/// @param ctx Save to this context. -static inline void ctx_save_gvars(Context *ctx) - FUNC_ATTR_NONNULL_ALL -{ - msgpack_sbuffer_init(&ctx->gvars); - shada_encode_gvars(&ctx->gvars); + shada_read_string(ctx->bufs, kShaDaWantInfo | kShaDaForceit); } /// Restores global variables from a context. @@ -246,7 +196,7 @@ static inline void ctx_save_gvars(Context *ctx) static inline void ctx_restore_gvars(Context *ctx) FUNC_ATTR_NONNULL_ALL { - shada_read_sbuf(&ctx->gvars, kShaDaWantInfo | kShaDaForceit); + shada_read_string(ctx->gvars, kShaDaWantInfo | kShaDaForceit); } /// Saves functions to a context. @@ -291,41 +241,16 @@ static inline void ctx_restore_funcs(Context *ctx) } } -/// Convert msgpack_sbuffer to readfile()-style array. -/// -/// @param[in] sbuf msgpack_sbuffer to convert. -/// -/// @return readfile()-style array representation of "sbuf". -static inline Array sbuf_to_array(msgpack_sbuffer sbuf, Arena *arena) -{ - list_T *const list = tv_list_alloc(kListLenMayKnow); - tv_list_append_string(list, "", 0); - if (sbuf.size > 0) { - encode_list_write(list, sbuf.data, sbuf.size); - } - - typval_T list_tv = (typval_T) { - .v_lock = VAR_UNLOCKED, - .v_type = VAR_LIST, - .vval.v_list = list - }; - - Array array = vim_to_object(&list_tv, arena, false).data.array; - tv_clear(&list_tv); - return array; -} - -/// Convert readfile()-style array to msgpack_sbuffer. +/// Convert readfile()-style array to String /// /// @param[in] array readfile()-style array to convert. /// @param[out] err Error object. /// -/// @return msgpack_sbuffer with conversion result. -static inline msgpack_sbuffer array_to_sbuf(Array array, Error *err) +/// @return String with conversion result. +static inline String array_to_string(Array array, Error *err) FUNC_ATTR_NONNULL_ALL { - msgpack_sbuffer sbuf; - msgpack_sbuffer_init(&sbuf); + String sbuf = STRING_INIT; typval_T list_tv; object_to_vim(ARRAY_OBJ(array), &list_tv, err); @@ -335,7 +260,6 @@ static inline msgpack_sbuffer array_to_sbuf(Array array, Error *err) api_set_error(err, kErrorTypeException, "%s", "E474: Failed to convert list to msgpack string buffer"); } - sbuf.alloc = sbuf.size; tv_clear(&list_tv); return sbuf; @@ -353,10 +277,10 @@ Dictionary ctx_to_dict(Context *ctx, Arena *arena) Dictionary rv = arena_dict(arena, 5); - PUT_C(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs, arena))); - PUT_C(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps, arena))); - PUT_C(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs, arena))); - PUT_C(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars, arena))); + PUT_C(rv, "regs", ARRAY_OBJ(string_to_array(ctx->regs, false, arena))); + PUT_C(rv, "jumps", ARRAY_OBJ(string_to_array(ctx->jumps, false, arena))); + PUT_C(rv, "bufs", ARRAY_OBJ(string_to_array(ctx->bufs, false, arena))); + PUT_C(rv, "gvars", ARRAY_OBJ(string_to_array(ctx->gvars, false, arena))); PUT_C(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, arena))); return rv; @@ -382,16 +306,16 @@ int ctx_from_dict(Dictionary dict, Context *ctx, Error *err) } if (strequal(item.key.data, "regs")) { types |= kCtxRegs; - ctx->regs = array_to_sbuf(item.value.data.array, err); + ctx->regs = array_to_string(item.value.data.array, err); } else if (strequal(item.key.data, "jumps")) { types |= kCtxJumps; - ctx->jumps = array_to_sbuf(item.value.data.array, err); + ctx->jumps = array_to_string(item.value.data.array, err); } else if (strequal(item.key.data, "bufs")) { types |= kCtxBufs; - ctx->bufs = array_to_sbuf(item.value.data.array, err); + ctx->bufs = array_to_string(item.value.data.array, err); } else if (strequal(item.key.data, "gvars")) { types |= kCtxGVars; - ctx->gvars = array_to_sbuf(item.value.data.array, err); + ctx->gvars = array_to_string(item.value.data.array, err); } else if (strequal(item.key.data, "funcs")) { types |= kCtxFuncs; ctx->funcs = copy_object(item.value, NULL).data.array; diff --git a/src/nvim/context.h b/src/nvim/context.h index 1c18a1af7c..0bb7138b6c 100644 --- a/src/nvim/context.h +++ b/src/nvim/context.h @@ -7,25 +7,19 @@ #include "nvim/api/private/defs.h" typedef struct { - msgpack_sbuffer regs; ///< Registers. - msgpack_sbuffer jumps; ///< Jumplist. - msgpack_sbuffer bufs; ///< Buffer list. - msgpack_sbuffer gvars; ///< Global variables. + String regs; ///< Registers. + String jumps; ///< Jumplist. + String bufs; ///< Buffer list. + String gvars; ///< Global variables. Array funcs; ///< Functions. } Context; typedef kvec_t(Context) ContextVec; -#define MSGPACK_SBUFFER_INIT (msgpack_sbuffer) { \ - .size = 0, \ - .data = NULL, \ - .alloc = 0, \ -} - #define CONTEXT_INIT (Context) { \ - .regs = MSGPACK_SBUFFER_INIT, \ - .jumps = MSGPACK_SBUFFER_INIT, \ - .bufs = MSGPACK_SBUFFER_INIT, \ - .gvars = MSGPACK_SBUFFER_INIT, \ + .regs = STRING_INIT, \ + .jumps = STRING_INIT, \ + .bufs = STRING_INIT, \ + .gvars = STRING_INIT, \ .funcs = ARRAY_DICT_INIT, \ } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 54335fc93e..0fe1729029 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1273,10 +1273,10 @@ void ex_diffpatch(exarg_T *eap) // Delete any .orig or .rej file created. STRCPY(buf, tmp_new); - STRCAT(buf, ".orig"); + strcat(buf, ".orig"); os_remove(buf); STRCPY(buf, tmp_new); - STRCAT(buf, ".rej"); + strcat(buf, ".rej"); os_remove(buf); // Only continue if the output file was created. @@ -1288,7 +1288,7 @@ void ex_diffpatch(exarg_T *eap) } else { if (curbuf->b_fname != NULL) { newname = xstrnsave(curbuf->b_fname, strlen(curbuf->b_fname) + 4); - STRCAT(newname, ".new"); + strcat(newname, ".new"); } // don't use a new tab page, each tab page has its own diffs diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 4247705896..6109652f7d 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -587,12 +587,13 @@ static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int vir char buf[MAXPATHL]; // When a buffer's line count has changed, make a best estimate for the full - // width of the status column by building with "w_nrwidth_line_count". Add - // potentially truncated width and rebuild before drawing anything. + // width of the status column by building with the largest possible line number. + // Add potentially truncated width and rebuild before drawing anything. if (wp->w_statuscol_line_count != wp->w_nrwidth_line_count) { wp->w_statuscol_line_count = wp->w_nrwidth_line_count; set_vim_var_nr(VV_VIRTNUM, 0); - int width = build_statuscol_str(wp, wp->w_nrwidth_line_count, 0, buf, stcp); + int width = build_statuscol_str(wp, wp->w_nrwidth_line_count, + wp->w_nrwidth_line_count, buf, stcp); if (width > stcp->width) { int addwidth = MIN(width - stcp->width, MAX_STCWIDTH - stcp->width); wp->w_nrwidth += addwidth; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4680b62ab..d52e10f61b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7154,8 +7154,8 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start) + (size_t)(in_end - expr_end) + 1); STRCPY(retval, in_start); - STRCAT(retval, temp_result); - STRCAT(retval, expr_end + 1); + strcat(retval, temp_result); + strcat(retval, expr_end + 1); } xfree(temp_result); diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 3c99ba22c2..79a62874a4 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -1238,33 +1238,33 @@ M.funcs = { base = 1, desc = [=[ The result is a Number, which is the byte index of the column - position given with {expr}. The accepted positions are: - . the cursor position - $ the end of the cursor line (the result is the - number of bytes in the cursor line plus one) - 'x position of mark x (if the mark is not set, 0 is - returned) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. + position given with {expr}. + For accepted positions see |getpos()|. + When {expr} is "$", it means the end of the cursor line, so + the result is the number of bytes in the cursor line plus one. Additionally {expr} can be [lnum, col]: a |List| with the line and column number. Most useful when the column is "$", to get the last column of a specific line. When "lnum" or "col" is out of range then col() returns zero. + With the optional {winid} argument the values are obtained for that window instead of the current window. + To get the line number use |line()|. To get both use |getpos()|. + For the screen column position use |virtcol()|. For the character position use |charcol()|. + Note that only marks in the current file can be used. + Examples: >vim echo col(".") " column of cursor echo col("$") " length of cursor line plus one echo col("'t") " column of mark t echo col("'" .. markname) " column of mark markname - <The first column is 1. Returns 0 if {expr} is invalid or when + < + The first column is 1. Returns 0 if {expr} is invalid or when the window with ID {winid} is not found. For an uppercase mark the column may actually be in another buffer. @@ -3448,7 +3448,7 @@ M.funcs = { < ]=], name = 'getchar', - params = {}, + params = { { 'expr', '0|1' } }, returns = 'integer', signature = 'getchar([{expr}])', }, @@ -3537,7 +3537,7 @@ M.funcs = { result is converted to a string. ]=], name = 'getcharstr', - params = {}, + params = { { 'expr', '0|1' } }, returns = 'string', signature = 'getcharstr([{expr}])', }, @@ -4047,8 +4047,9 @@ M.funcs = { ]=], name = 'getmarklist', - params = { { 'buf', 'any' } }, + params = { { 'buf', 'integer?' } }, signature = 'getmarklist([{buf}])', + returns = 'vim.fn.getmarklist.ret.item[]', }, getmatches = { args = { 0, 1 }, @@ -4140,9 +4141,34 @@ M.funcs = { args = 1, base = 1, desc = [=[ - Get the position for String {expr}. For possible values of - {expr} see |line()|. For getting the cursor position see - |getcurpos()|. + Get the position for String {expr}. + The accepted values for {expr} are: + . The cursor position. + $ The last line in the current buffer. + 'x Position of mark x (if the mark is not set, 0 is + returned for all values). + w0 First line visible in current window (one if the + display isn't updated, e.g. in silent Ex mode). + w$ Last line visible in current window (this is one + less than "w0" if no lines are visible). + v When not in Visual mode, returns the cursor + position. In Visual mode, returns the other end + of the Visual area. A good way to think about + this is that in Visual mode "v" and "." complement + each other. While "." refers to the cursor + position, "v" refers to where |v_o| would move the + cursor. As a result, you can use "v" and "." + together to work on all of a selection in + characterwise Visual mode. If the cursor is at + the end of a characterwise Visual area, "v" refers + to the start of the same Visual area. And if the + cursor is at the start of a characterwise Visual + area, "v" refers to the end of the same Visual + area. "v" differs from |'<| and |'>| in that it's + updated right away. + Note that a mark in another file can be used. The line number + then applies to another buffer. + The result is a |List| with four numbers: [bufnum, lnum, col, off] "bufnum" is zero, unless a mark like '0 or 'A is used, then it @@ -4153,20 +4179,25 @@ M.funcs = { it is the offset in screen columns from the start of the character. E.g., a position within a <Tab> or after the last character. - Note that for '< and '> Visual mode matters: when it is "V" - (visual line mode) the column of '< is zero and the column of - '> is a large number equal to |v:maxcol|. + + For getting the cursor position see |getcurpos()|. The column number in the returned List is the byte position within the line. To get the character position in the line, use |getcharpos()|. + + Note that for '< and '> Visual mode matters: when it is "V" + (visual line mode) the column of '< is zero and the column of + '> is a large number equal to |v:maxcol|. A very large column number equal to |v:maxcol| can be returned, in which case it means "after the end of the line". If {expr} is invalid, returns a list with all zeros. + This can be used to save and restore the position of a mark: >vim let save_a_mark = getpos("'a") " ... call setpos("'a", save_a_mark) - <Also see |getcharpos()|, |getcurpos()| and |setpos()|. + < + Also see |getcharpos()|, |getcurpos()| and |setpos()|. ]=], name = 'getpos', @@ -4404,8 +4435,8 @@ M.funcs = { difference if the buffer is displayed in a window with different 'virtualedit' or 'list' values. - Examples: > - :xnoremap <CR> + Examples: >vim + xnoremap <CR> \ <Cmd>echom getregion( \ getpos('v'), getpos('.'), #{ type: mode() })<CR> < @@ -6069,28 +6100,16 @@ M.funcs = { args = { 1, 2 }, base = 1, desc = [=[ - The result is a Number, which is the line number of the file - position given with {expr}. The {expr} argument is a string. - The accepted positions are: - . the cursor position - $ the last line in the current buffer - 'x position of mark x (if the mark is not set, 0 is - returned) - w0 first line visible in current window (one if the - display isn't updated, e.g. in silent Ex mode) - w$ last line visible in current window (this is one - less than "w0" if no lines are visible) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. - Note that a mark in another file can be used. The line number - then applies to another buffer. + See |getpos()| for accepted positions. + To get the column number use |col()|. To get both use |getpos()|. + With the optional {winid} argument the values are obtained for that window instead of the current window. + Returns 0 for invalid values of {expr} and {winid}. + Examples: >vim echo line(".") " line number of the cursor echo line(".", winid) " idem, in window "winid" @@ -6481,7 +6500,8 @@ M.funcs = { echo printf("Operator-pending mode bit: 0x%x", op_bit) ]], name = 'maplist', - params = {}, + params = { { 'abbr', '0|1' } }, + returns = 'table[]', signature = 'maplist([{abbr}])', }, mapnew = { @@ -6793,19 +6813,19 @@ M.funcs = { Examples: >vim " Assuming line 3 in buffer 5 contains "a" - :echo matchbufline(5, '\<\k\+\>', 3, 3) - [{'lnum': 3, 'byteidx': 0, 'text': 'a'}] + echo matchbufline(5, '\<\k\+\>', 3, 3) + < `[{'lnum': 3, 'byteidx': 0, 'text': 'a'}]` >vim " Assuming line 4 in buffer 10 contains "tik tok" - :echo matchbufline(10, '\<\k\+\>', 1, 4) - [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}] - < + echo matchbufline(10, '\<\k\+\>', 1, 4) + < `[{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]` + If {submatch} is present and is v:true, then submatches like "\1", "\2", etc. are also returned. Example: >vim " Assuming line 2 in buffer 2 contains "acd" - :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 + echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2 \ {'submatches': v:true}) - [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] - <The "submatches" List always contains 9 items. If a submatch + < `[{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]` + The "submatches" List always contains 9 items. If a submatch is not found, then an empty string is returned for that submatch. ]=], @@ -7025,17 +7045,17 @@ M.funcs = { option settings on the pattern. Example: >vim - :echo matchstrlist(['tik tok'], '\<\k\+\>') - [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}] - :echo matchstrlist(['a', 'b'], '\<\k\+\>') - [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}] - < + echo matchstrlist(['tik tok'], '\<\k\+\>') + < `[{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]` >vim + echo matchstrlist(['a', 'b'], '\<\k\+\>') + < `[{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]` + If "submatches" is present and is v:true, then submatches like "\1", "\2", etc. are also returned. Example: >vim - :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', + echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', \ #{submatches: v:true}) - [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}] - <The "submatches" List always contains 9 items. If a submatch + < `[{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]` + The "submatches" List always contains 9 items. If a submatch is not found, then an empty string is returned for that submatch. ]=], @@ -7244,17 +7264,14 @@ M.funcs = { When {flags} is present it must be a string. An empty string has no effect. - If {flags} contains "p" then intermediate directories are - created as necessary. + {flags} can contain these character flags: + "p" intermediate directories will be created as necessary + "D" {name} will be deleted at the end of the current + function, but not recursively |:defer| + "R" {name} will be deleted recursively at the end of the + current function |:defer| - If {flags} contains "D" then {name} is deleted at the end of - the current function, as with: >vim - defer delete({name}, 'd') - < - If {flags} contains "R" then {name} is deleted recursively at - the end of the current function, as with: >vim - defer delete({name}, 'rf') - <Note that when {name} has more than one part and "p" is used + Note that when {name} has more than one part and "p" is used some directories may already exist. Only the first one that is created and what it contains is scheduled to be deleted. E.g. when using: >vim @@ -8715,7 +8732,7 @@ M.funcs = { the following mappings: >vim nnoremap <expr> GG ":echom " .. screencol() .. "\n" nnoremap <silent> GG :echom screencol()<CR> - noremap GG <Cmd>echom screencol()<Cr> + noremap GG <Cmd>echom screencol()<CR> < ]=], name = 'screencol', @@ -9142,7 +9159,16 @@ M.funcs = { < ]=], name = 'searchpair', - params = {}, + params = { + { 'start', 'any' }, + { 'middle', 'any' }, + { 'end', 'any' }, + { 'flags', 'string' }, + { 'skip', 'any' }, + { 'stopline', 'any' }, + { 'timeout', 'integer' }, + }, + returns = 'integer', signature = 'searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeout}]]]])', }, searchpairpos = { @@ -9159,7 +9185,16 @@ M.funcs = { See |match-parens| for a bigger and more useful example. ]=], name = 'searchpairpos', - params = {}, + params = { + { 'start', 'any' }, + { 'middle', 'any' }, + { 'end', 'any' }, + { 'flags', 'string' }, + { 'skip', 'any' }, + { 'stopline', 'any' }, + { 'timeout', 'integer' }, + }, + returns = '[integer, integer]', signature = 'searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeout}]]]])', }, searchpos = { @@ -12428,7 +12463,9 @@ M.funcs = { set to 8, it returns 8. |conceal| is ignored. For the byte position use |col()|. - For the use of {expr} see |col()|. + For the use of {expr} see |getpos()| and |col()|. + When {expr} is "$", it means the end of the cursor line, so + the result is the number of cells in the cursor line plus one. When 'virtualedit' is used {expr} can be [lnum, col, off], where "off" is the offset in screen columns from the start of @@ -12438,18 +12475,6 @@ M.funcs = { beyond the end of the line can be returned. Also see |'virtualedit'| - The accepted positions are: - . the cursor position - $ the end of the cursor line (the result is the - number of displayed characters in the cursor line - plus one) - 'x position of mark x (if the mark is not set, 0 is - returned) - v In Visual mode: the start of the Visual area (the - cursor is the end). When not in Visual mode - returns the cursor position. Differs from |'<| in - that it's updated right away. - If {list} is present and non-zero then virtcol() returns a List with the first and last screen position occupied by the character. @@ -12468,7 +12493,9 @@ M.funcs = { " With text " there", with 't at 'h': echo virtcol("'t") " returns 6 - <The first column is 1. 0 or [0, 0] is returned for an error. + < + The first column is 1. 0 or [0, 0] is returned for an error. + A more advanced example that echoes the maximum length of all lines: >vim echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 00fb896797..122a1ac8ab 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -265,7 +265,7 @@ static void set_ufunc_name(ufunc_T *fp, char *name) if ((uint8_t)name[0] == K_SPECIAL) { fp->uf_name_exp = xmalloc(strlen(name) + 3); STRCPY(fp->uf_name_exp, "<SNR>"); - STRCAT(fp->uf_name_exp, fp->uf_name + 3); + strcat(fp->uf_name_exp, fp->uf_name + 3); } } @@ -2062,7 +2062,7 @@ char *get_scriptlocal_funcname(char *funcname) const int off = *funcname == 's' ? 2 : 5; char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1); STRCPY(newname, sid_buf); - STRCAT(newname, funcname + off); + strcat(newname, funcname + off); return newname; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2f7673a6f4..0aa897105e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -970,13 +970,13 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out char *t = xmalloc(len); *t = NUL; if (newcmd != NULL) { - STRCAT(t, newcmd); + strcat(t, newcmd); } if (ins_prevcmd) { - STRCAT(t, prevcmd); + strcat(t, prevcmd); } char *p = t + strlen(t); - STRCAT(t, trailarg); + strcat(t, trailarg); xfree(newcmd); newcmd = t; @@ -1029,8 +1029,8 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out } newcmd = xmalloc(strlen(prevcmd) + 2 * strlen(p_shq) + 1); STRCPY(newcmd, p_shq); - STRCAT(newcmd, prevcmd); - STRCAT(newcmd, p_shq); + strcat(newcmd, prevcmd); + strcat(newcmd, p_shq); free_newcmd = true; } if (addr_count == 0) { // :! @@ -4108,7 +4108,7 @@ skip: // the line as reference, because the substitute may // have changed the number of characters. Same for // "prev_matchcol". - STRCAT(new_start, sub_firstline + copycol); + strcat(new_start, sub_firstline + copycol); matchcol = (colnr_T)strlen(sub_firstline) - matchcol; prev_matchcol = (colnr_T)strlen(sub_firstline) - prev_matchcol; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1e2c515195..a6246afb41 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3826,8 +3826,8 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep) // No $* in arg, build "<makeprg> <arg>" instead new_cmdline = xmalloc(strlen(program) + strlen(arg) + 2); STRCPY(new_cmdline, program); - STRCAT(new_cmdline, " "); - STRCAT(new_cmdline, arg); + strcat(new_cmdline, " "); + strcat(new_cmdline, arg); } msg_make(arg); @@ -7236,7 +7236,7 @@ char *expand_sfile(char *arg) memmove(newres, result, (size_t)(p - result)); STRCPY(newres + (p - result), repl); len = strlen(newres); - STRCAT(newres, p + srclen); + strcat(newres, p + srclen); xfree(repl); xfree(result); result = newres; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 2681beb228..6e7c1ff21c 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -406,7 +406,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, bool || (ascii_isdigit(p[3]) && p[4] == ':')))))) { if (*p == NUL || p == mesg) { - STRCAT(val, mesg); // 'E123' missing or at beginning + strcat(val, mesg); // 'E123' missing or at beginning } else { // '"filename" E123: message text' if (mesg[0] != '"' || p - 2 < &mesg[1] @@ -415,7 +415,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, bool continue; } - STRCAT(val, p); + strcat(val, p); p[-2] = NUL; snprintf(val + strlen(p), strlen(" (%s)"), " (%s)", &mesg[1]); p[-2] = '"'; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 30a80773e3..b6a039af5e 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -451,7 +451,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i STRCPY(buf, ff_expand_buffer); STRCPY(buf + eb_len, search_ctx->ffsc_fix_path); if (os_isdir(buf)) { - STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path); + strcat(ff_expand_buffer, search_ctx->ffsc_fix_path); add_pathsep(ff_expand_buffer); } else { char *p = path_tail(search_ctx->ffsc_fix_path); @@ -479,7 +479,7 @@ void *vim_findfile_init(char *path, char *filename, char *stopdirs, int level, i + strlen(search_ctx->ffsc_fix_path + len) + 1); STRCPY(temp, search_ctx->ffsc_fix_path + len); - STRCAT(temp, search_ctx->ffsc_wc_path); + strcat(temp, search_ctx->ffsc_wc_path); xfree(search_ctx->ffsc_wc_path); xfree(wc_path); search_ctx->ffsc_wc_path = temp; @@ -681,7 +681,7 @@ char *vim_findfile(void *search_ctx_arg) ff_free_stack_element(stackp); goto fail; } - STRCAT(file_path, stackp->ffs_fix_path); + strcat(file_path, stackp->ffs_fix_path); if (!add_pathsep(file_path)) { ff_free_stack_element(stackp); goto fail; @@ -781,7 +781,7 @@ char *vim_findfile(void *search_ctx_arg) ff_free_stack_element(stackp); goto fail; } - STRCAT(file_path, search_ctx->ffsc_file_to_search); + strcat(file_path, search_ctx->ffsc_file_to_search); // Try without extra suffix and then with suffixes // from 'suffixesadd'. @@ -922,7 +922,7 @@ char *vim_findfile(void *search_ctx_arg) if (!add_pathsep(file_path)) { goto fail; } - STRCAT(file_path, search_ctx->ffsc_fix_path); + strcat(file_path, search_ctx->ffsc_fix_path); // create a new stack entry sptr = ff_create_stack_element(file_path, diff --git a/src/nvim/fold.c b/src/nvim/fold.c index be1b6acf2c..78fe33cc78 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -3315,7 +3315,7 @@ void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *r = xmalloc(len); snprintf(r, len, txt, dashes, count); len = strlen(r); - STRCAT(r, s); + strcat(r, s); // remove 'foldmarker' and 'commentstring' foldtext_cleanup(r + len); rettv->vval.v_string = r; diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h index cb3a84bcaf..2ba6cf5371 100644 --- a/src/nvim/highlight.h +++ b/src/nvim/highlight.h @@ -54,6 +54,8 @@ EXTERN const char *hlf_names[] INIT( = { [HLF_SPL] = "SpellLocal", [HLF_PNI] = "Pmenu", [HLF_PSI] = "PmenuSel", + [HLF_PMNI] = "PmenuMatch", + [HLF_PMSI] = "PmenuMatchSel", [HLF_PNK] = "PmenuKind", [HLF_PSK] = "PmenuKindSel", [HLF_PNX] = "PmenuExtra", @@ -78,6 +80,8 @@ EXTERN const char *hlf_names[] INIT( = { [HLF_CU] = "Cursor", [HLF_BTITLE] = "FloatTitle", [HLF_BFOOTER] = "FloatFooter", + [HLF_TS] = "StatusLineTerm", + [HLF_TSNC] = "StatusLineTermNC", }); EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context. diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 25ab9dc2d9..e0cce81166 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -101,6 +101,8 @@ typedef enum { HLF_SPL, ///< SpellLocal HLF_PNI, ///< popup menu normal item HLF_PSI, ///< popup menu selected item + HLF_PMNI, ///< popup menu matched text in normal item + HLF_PMSI, ///< popup menu matched text in selected item HLF_PNK, ///< popup menu normal item "kind" HLF_PSK, ///< popup menu selected item "kind" HLF_PNX, ///< popup menu normal item "menu" (extra text) @@ -125,6 +127,8 @@ typedef enum { HLF_CU, ///< Cursor HLF_BTITLE, ///< Float Border Title HLF_BFOOTER, ///< Float Border Footer + HLF_TS, ///< status line for terminal window + HLF_TSNC, ///< status line for non-current terminal window HLF_COUNT, ///< MUST be the last one } hlf_T; diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index c32e1f9657..3c777e7c4d 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -151,34 +151,38 @@ static const char *highlight_init_both[] = { "lCursor guifg=bg guibg=fg", // UI - "default link CursorIM Cursor", - "default link CursorLineFold FoldColumn", - "default link CursorLineSign SignColumn", - "default link EndOfBuffer NonText", - "default link FloatBorder NormalFloat", - "default link FloatFooter FloatTitle", - "default link FloatTitle Title", - "default link FoldColumn SignColumn", - "default link IncSearch CurSearch", - "default link LineNrAbove LineNr", - "default link LineNrBelow LineNr", - "default link MsgSeparator StatusLine", - "default link MsgArea NONE", - "default link NormalNC NONE", - "default link PmenuExtra Pmenu", - "default link PmenuExtraSel PmenuSel", - "default link PmenuKind Pmenu", - "default link PmenuKindSel PmenuSel", - "default link PmenuSbar Pmenu", - "default link Substitute Search", - "default link TabLine StatusLineNC", - "default link TabLineFill TabLine", - "default link TermCursorNC NONE", - "default link VertSplit WinSeparator", - "default link VisualNOS Visual", - "default link Whitespace NonText", - "default link WildMenu PmenuSel", - "default link WinSeparator Normal", + "default link CursorIM Cursor", + "default link CursorLineFold FoldColumn", + "default link CursorLineSign SignColumn", + "default link EndOfBuffer NonText", + "default link FloatBorder NormalFloat", + "default link FloatFooter FloatTitle", + "default link FloatTitle Title", + "default link FoldColumn SignColumn", + "default link IncSearch CurSearch", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", + "default link MsgSeparator StatusLine", + "default link MsgArea NONE", + "default link NormalNC NONE", + "default link PmenuExtra Pmenu", + "default link PmenuExtraSel PmenuSel", + "default link PmenuKind Pmenu", + "default link PmenuKindSel PmenuSel", + "default link PmenuMatch Pmenu", + "default link PmenuMatchSel PmenuSel", + "default link PmenuSbar Pmenu", + "default link Substitute Search", + "default link StatusLineTerm StatusLine", + "default link StatusLineTermNC StatusLineNC", + "default link TabLine StatusLineNC", + "default link TabLineFill TabLine", + "default link TermCursorNC NONE", + "default link VertSplit WinSeparator", + "default link VisualNOS Visual", + "default link Whitespace NonText", + "default link WildMenu PmenuSel", + "default link WinSeparator Normal", // Syntax "default link Character Constant", diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 574c9a9e3a..0a25b72451 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -1144,11 +1144,13 @@ static void trigger_complete_changed_event(int cur) } /// pumitem qsort compare func -static int ins_compl_fuzzy_sort(const void *a, const void *b) +static int ins_compl_fuzzy_cmp(const void *a, const void *b) { const int sa = (*(pumitem_T *)a).pum_score; const int sb = (*(pumitem_T *)b).pum_score; - return sa == sb ? 0 : sa < sb ? 1 : -1; + const int ia = (*(pumitem_T *)a).pum_idx; + const int ib = (*(pumitem_T *)b).pum_idx; + return sa == sb ? (ia == ib ? 0 : (ia < ib ? -1 : 1)) : (sa < sb ? 1 : -1); } /// Build a popup menu to show the completion matches. @@ -1228,6 +1230,9 @@ static int ins_compl_build_pum(void) } cur = i; } else if (compl_fuzzy_match) { + if (i == 0) { + shown_compl = comp; + } // Update the maximum fuzzy score and the shown match // if the current item's score is higher if (comp->cp_score > max_fuzzy_score) { @@ -1246,6 +1251,9 @@ static int ins_compl_build_pum(void) || (compl_leader == NULL || lead_len == 0))) { shown_match_ok = true; cur = 0; + if (match_at_original_text(compl_shown_match)) { + compl_shown_match = shown_compl; + } } } @@ -1284,9 +1292,12 @@ static int ins_compl_build_pum(void) } while (comp != NULL && !is_first_match(comp)); if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) { + for (i = 0; i < compl_match_arraysize; i++) { + compl_match_array[i].pum_idx = i; + } // sort by the largest score of fuzzy match qsort(compl_match_array, (size_t)compl_match_arraysize, sizeof(pumitem_T), - ins_compl_fuzzy_sort); + ins_compl_fuzzy_cmp); } if (!shown_match_ok) { // no displayed match at all @@ -1383,6 +1394,12 @@ bool compl_match_curr_select(int selected) #define DICT_FIRST (1) ///< use just first element in "dict" #define DICT_EXACT (2) ///< "dict" is the exact name of a file +/// Get current completion leader +char *ins_compl_leader(void) +{ + return compl_leader; +} + /// Add any identifiers that match the given pattern "pat" in the list of /// dictionary files "dict_start" to the list of completions. /// @@ -3681,16 +3698,16 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a while (--todo >= 0) { if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) { - compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_next - : find_comp_when_fuzzy(); + compl_shown_match = compl_fuzzy_match && compl_match_array != NULL + ? find_comp_when_fuzzy() : compl_shown_match->cp_next; found_end = (compl_first_match != NULL && (is_first_match(compl_shown_match->cp_next) || is_first_match(compl_shown_match))); } else if (compl_shows_dir_backward() && compl_shown_match->cp_prev != NULL) { found_end = is_first_match(compl_shown_match); - compl_shown_match = !compl_fuzzy_match ? compl_shown_match->cp_prev - : find_comp_when_fuzzy(); + compl_shown_match = compl_fuzzy_match && compl_match_array != NULL + ? find_comp_when_fuzzy() : compl_shown_match->cp_prev; found_end |= is_first_match(compl_shown_match); } else { if (!allow_get_expansion) { @@ -4059,7 +4076,7 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col) compl_pattern = xmalloc(7); STRCPY(compl_pattern, "\\<"); quote_meta(compl_pattern + 2, line + compl_col, 1); - STRCAT(compl_pattern, "\\k"); + strcat(compl_pattern, "\\k"); } else { compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2); STRCPY(compl_pattern, "\\<"); diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 0788670142..a6ff82e39d 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -45,8 +45,6 @@ EXTERN size_t arena_alloc_count INIT( = 0); ((v).capacity = (s), \ (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) -#define ARENA_BLOCK_SIZE 4096 - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" #endif @@ -72,5 +70,3 @@ EXTERN size_t arena_alloc_count INIT( = 0); // Like strcpy() but allows overlapped source and destination. #define STRMOVE(d, s) memmove((d), (s), strlen(s) + 1) - -#define STRCAT(d, s) strcat((char *)(d), (char *)(s)) // NOLINT(runtime/printf) diff --git a/src/nvim/memory_defs.h b/src/nvim/memory_defs.h index bde0e54f54..df271ceca5 100644 --- a/src/nvim/memory_defs.h +++ b/src/nvim/memory_defs.h @@ -11,5 +11,7 @@ typedef struct { size_t pos, size; } Arena; +#define ARENA_BLOCK_SIZE 4096 + // inits an empty arena. #define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 } diff --git a/src/nvim/menu.c b/src/nvim/menu.c index a4d49d041e..c33d3cc712 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1057,7 +1057,7 @@ char *get_menu_names(expand_T *xp, int idx) } // hack on menu separators: use a 'magic' char for the separator // so that '.' in names gets escaped properly - STRCAT(tbuffer, "\001"); + strcat(tbuffer, "\001"); str = tbuffer; } else { if (should_advance) { diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 6a0dc10214..0930f0c7b3 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -590,16 +590,16 @@ static void packer_buffer_init_channels(Channel **chans, size_t nchans, PackerBu packer->endptr = packer->startptr + ARENA_BLOCK_SIZE; packer->packer_flush = channel_flush_callback; packer->anydata = chans; - packer->anylen = nchans; + packer->anyint = (int64_t)nchans; } static void packer_buffer_finish_channels(PackerBuffer *packer) { size_t len = (size_t)(packer->ptr - packer->startptr); if (len > 0) { - WBuffer *buf = wstream_new_buffer(packer->startptr, len, packer->anylen, free_block); + WBuffer *buf = wstream_new_buffer(packer->startptr, len, (size_t)packer->anyint, free_block); Channel **chans = packer->anydata; - for (size_t i = 0; i < packer->anylen; i++) { + for (int64_t i = 0; i < packer->anyint; i++) { channel_write(chans[i], buf); } } else { @@ -610,7 +610,7 @@ static void packer_buffer_finish_channels(PackerBuffer *packer) static void channel_flush_callback(PackerBuffer *packer) { packer_buffer_finish_channels(packer); - packer_buffer_init_channels(packer->anydata, packer->anylen, packer); + packer_buffer_init_channels(packer->anydata, (size_t)packer->anyint, packer); } void rpc_set_client_info(uint64_t id, Dictionary info) diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c index 9c0d2910fa..58318b88b0 100644 --- a/src/nvim/msgpack_rpc/packer.c +++ b/src/nvim/msgpack_rpc/packer.c @@ -10,8 +10,7 @@ static void check_buffer(PackerBuffer *packer) { - ptrdiff_t remaining = packer->endptr - packer->ptr; - if (remaining < MPACK_ITEM_SIZE) { + if (mpack_remaining(packer) < MPACK_ITEM_SIZE) { packer->packer_flush(packer); } } @@ -28,15 +27,20 @@ static void mpack_w8(char **b, const char *data) #endif } +void mpack_uint64(char **ptr, uint64_t i) +{ + if (i > 0xfffffff) { + mpack_w(ptr, 0xcf); + mpack_w8(ptr, (char *)&i); + } else { + mpack_uint(ptr, (uint32_t)i); + } +} + void mpack_integer(char **ptr, Integer i) { if (i >= 0) { - if (i > 0xfffffff) { - mpack_w(ptr, 0xcf); - mpack_w8(ptr, (char *)&i); - } else { - mpack_uint(ptr, (uint32_t)i); - } + mpack_uint64(ptr, (uint64_t)i); } else { if (i < -0x80000000LL) { mpack_w(ptr, 0xd3); @@ -80,11 +84,16 @@ void mpack_str(String str, PackerBuffer *packer) abort(); } + mpack_raw(str.data, len, packer); +} + +void mpack_raw(char *data, size_t len, PackerBuffer *packer) +{ size_t pos = 0; while (pos < len) { ptrdiff_t remaining = packer->endptr - packer->ptr; size_t to_copy = MIN(len - pos, (size_t)remaining); - memcpy(packer->ptr, str.data + pos, to_copy); + memcpy(packer->ptr, data + pos, to_copy); packer->ptr += to_copy; pos += to_copy; diff --git a/src/nvim/msgpack_rpc/packer.h b/src/nvim/msgpack_rpc/packer.h index 8117bd09bd..299962bab4 100644 --- a/src/nvim/msgpack_rpc/packer.h +++ b/src/nvim/msgpack_rpc/packer.h @@ -71,6 +71,11 @@ static inline void mpack_map(char **buf, uint32_t len) } } +static inline size_t mpack_remaining(PackerBuffer *packer) +{ + return (size_t)(packer->endptr - packer->ptr); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/packer.h.generated.h" #endif diff --git a/src/nvim/msgpack_rpc/packer_defs.h b/src/nvim/msgpack_rpc/packer_defs.h index 420f3dc424..95d86caaab 100644 --- a/src/nvim/msgpack_rpc/packer_defs.h +++ b/src/nvim/msgpack_rpc/packer_defs.h @@ -19,6 +19,6 @@ struct packer_buffer_t { // these are free to be used by packer_flush for any purpose, if want void *anydata; - size_t anylen; + int64_t anyint; PackerBufferFlush packer_flush; }; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 879131c9e8..3343803bb1 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1987,7 +1987,7 @@ bool add_to_showcmd(int c) size_t overflow = old_len + extra_len - limit; memmove(showcmd_buf, showcmd_buf + overflow, old_len - overflow + 1); } - STRCAT(showcmd_buf, p); + strcat(showcmd_buf, p); if (char_avail()) { return false; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 1448896146..8f8baaf619 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2666,7 +2666,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) char *pnew = xmalloc(strlen(curr->y_array[curr->y_size - 1]) + strlen(reg->y_array[0]) + 1); STRCPY(pnew, curr->y_array[--j]); - STRCAT(pnew, reg->y_array[0]); + strcat(pnew, reg->y_array[0]); xfree(curr->y_array[j]); xfree(reg->y_array[0]); curr->y_array[j++] = pnew; @@ -3431,7 +3431,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags) totlen = strlen(y_array[y_size - 1]); char *newp = xmalloc((size_t)ml_get_len(lnum) - (size_t)col + totlen + 1); STRCPY(newp, y_array[y_size - 1]); - STRCAT(newp, ptr); + strcat(newp, ptr); // insert second line ml_append(lnum, newp, 0, false); new_lnum++; @@ -4747,7 +4747,7 @@ bool do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } } *ptr = NUL; - STRCAT(buf1, buf2); + strcat(buf1, buf2); ins_str(buf1); // insert the new number endpos = curwin->w_cursor; if (curwin->w_cursor.col) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 301e6e9cea..8fb97ed979 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -236,11 +236,11 @@ static void set_init_default_backupskip(void) == NULL) { ga_grow(&ga, (int)len); if (!GA_EMPTY(&ga)) { - STRCAT(ga.ga_data, ","); + strcat(ga.ga_data, ","); } - STRCAT(ga.ga_data, p); + strcat(ga.ga_data, p); add_pathsep(ga.ga_data); - STRCAT(ga.ga_data, "*"); + strcat(ga.ga_data, "*"); ga.ga_len += (int)len; } xfree(item); diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h index bd0fe699d9..404e58661c 100644 --- a/src/nvim/option_vars.h +++ b/src/nvim/option_vars.h @@ -55,13 +55,13 @@ #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \ "i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \ - "N:CursorLineNr,G:CursorLineSign,O:CursorLineFold" \ - "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \ - "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \ - "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ - "[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb," \ - "*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \ - "q:QuickFixLine,g:MsgArea,0:Whitespace,I:NormalNC" + "N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC," \ + "c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn," \ + "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap," \ + "R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind," \ + "]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel," \ + "_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm," \ + "Z:StatusLineTermNC,g:MsgArea,0:Whitespace,I:NormalNC" // Default values for 'errorformat'. // The "%f|%l| %m" one is used for when the contents of the quickfix window is diff --git a/src/nvim/options.lua b/src/nvim/options.lua index f323926015..efa2ea7dd9 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -4876,6 +4876,9 @@ return { between tabs and spaces and for trailing blanks. Further changed by the 'listchars' option. + When 'listchars' does not contain "tab" field, tabs are shown as "^I" + or "<09>", like how unprintable characters are displayed. + The cursor is displayed at the start of the space a Tab character occupies, not at the end as usual in Normal mode. To get this cursor position while displaying Tabs with spaces, use: >vim @@ -8037,8 +8040,7 @@ return { Some of the items from the 'statusline' format are different for 'statuscolumn': - %l line number of currently drawn line - %r relative line number of currently drawn line + %l line number column for currently drawn line %s sign column for currently drawn line %C fold column for currently drawn line @@ -8065,11 +8067,8 @@ return { handler should be written with this in mind. Examples: >vim - " Relative number with bar separator and click handlers: - set statuscolumn=%@SignCb@%s%=%T%@NumCb@%r│%T - - " Right aligned relative cursor line number: - let &stc='%=%{v:relnum?v:relnum:v:lnum} ' + " Line number with bar separator and click handlers: + set statuscolumn=%@SignCb@%s%=%T%@NumCb@%l│%T " Line numbers in hexadecimal for non wrapped part of lines: let &stc='%=%{v:virtnum>0?"":printf("%x",v:lnum)} ' diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 585c4964e2..89834bed80 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -320,9 +320,8 @@ ptrdiff_t file_write(FileDescriptor *const fp, const char *const buf, const size FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { assert(fp->wr); - ptrdiff_t space = (fp->buffer + ARENA_BLOCK_SIZE) - fp->write_pos; // includes the trivial case of size==0 - if (size < (size_t)space) { + if (size < file_space(fp)) { memcpy(fp->write_pos, buf, size); fp->write_pos += size; return (ptrdiff_t)size; diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h index e8fd2209db..523f9657a4 100644 --- a/src/nvim/os/fileio.h +++ b/src/nvim/os/fileio.h @@ -2,6 +2,7 @@ #include <stddef.h> // IWYU pragma: keep +#include "nvim/memory_defs.h" #include "nvim/os/fileio_defs.h" // IWYU pragma: keep /// file_open() flags @@ -32,6 +33,11 @@ enum { kRWBufferSize = 1024, }; +static inline size_t file_space(FileDescriptor *fp) +{ + return (size_t)((fp->buffer + ARENA_BLOCK_SIZE) - fp->write_pos); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/fileio.h.generated.h" #endif diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index b66faa2285..4b34ed1c4e 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -248,11 +248,11 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in } else { STRCPY(command, "("); } - STRCAT(command, pat[0] + 1); // exclude first backtick + strcat(command, pat[0] + 1); // exclude first backtick p = command + strlen(command) - 1; if (is_fish_shell) { *p-- = ';'; - STRCAT(command, " end"); + strcat(command, " end"); } else { *p-- = ')'; // remove last backtick } @@ -263,7 +263,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in ampersand = true; *p = ' '; } - STRCAT(command, ">"); + strcat(command, ">"); } else { STRCPY(command, ""); if (shell_style == STYLE_GLOB) { @@ -271,26 +271,26 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in // otherwise, this may set the positional parameters for the shell, // e.g. "$*". if (flags & EW_NOTFOUND) { - STRCAT(command, "set nonomatch; "); + strcat(command, "set nonomatch; "); } else { - STRCAT(command, "unset nonomatch; "); + strcat(command, "unset nonomatch; "); } } if (shell_style == STYLE_GLOB) { - STRCAT(command, "glob >"); + strcat(command, "glob >"); } else if (shell_style == STYLE_PRINT) { - STRCAT(command, "print -N >"); + strcat(command, "print -N >"); } else if (shell_style == STYLE_VIMGLOB) { - STRCAT(command, sh_vimglob_func); + strcat(command, sh_vimglob_func); } else if (shell_style == STYLE_GLOBSTAR) { - STRCAT(command, sh_globstar_opt); - STRCAT(command, sh_vimglob_func); + strcat(command, sh_globstar_opt); + strcat(command, sh_vimglob_func); } else { - STRCAT(command, "echo >"); + strcat(command, "echo >"); } } - STRCAT(command, tempname); + strcat(command, tempname); if (shell_style != STYLE_BT) { for (i = 0; i < num_pat; i++) { @@ -334,7 +334,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in } if (ampersand) { - STRCAT(command, "&"); // put the '&' after the redirection + strcat(command, "&"); // put the '&' after the redirection } // Using zsh -G: If a pattern has no matches, it is just deleted from diff --git a/src/nvim/path.c b/src/nvim/path.c index d782d1a989..aa630038a8 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -962,7 +962,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) char *file_pattern = xmalloc(len + 2); file_pattern[0] = '*'; file_pattern[1] = NUL; - STRCAT(file_pattern, pattern); + strcat(file_pattern, pattern); char *pat = file_pat_to_reg_pat(file_pattern, NULL, NULL, true); xfree(file_pattern); if (pat == NULL) { @@ -1065,7 +1065,7 @@ static void uniquefy_paths(garray_T *gap, char *pattern) rel_path = xmalloc(strlen(short_name) + strlen(PATHSEPSTR) + 2); STRCPY(rel_path, "."); add_pathsep(rel_path); - STRCAT(rel_path, short_name); + strcat(rel_path, short_name); xfree(fnames[i]); fnames[i] = rel_path; diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 324254a188..18446e749b 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -25,6 +25,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/extmark.h" #include "nvim/extmark_defs.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -48,6 +49,7 @@ #include "nvim/plines.h" #include "nvim/popupmenu.h" #include "nvim/pos_defs.h" +#include "nvim/search.h" #include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/types_defs.h" @@ -141,7 +143,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i || (State == MODE_CMDLINE && ui_has(kUIWildmenu)); } - pum_rl = (curwin->w_p_rl && State != MODE_CMDLINE); + pum_rl = State != MODE_CMDLINE && curwin->w_p_rl; do { // Mark the pum as visible already here, @@ -435,6 +437,84 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i pum_redraw(); } +/// Computes attributes of text on the popup menu. +/// Returns attributes for every cell, or NULL if all attributes are the same. +static int *pum_compute_text_attrs(char *text, hlf_T hlf) +{ + char *leader = ins_compl_leader(); + + if (leader == NULL || *leader == NUL || (hlf != HLF_PSI && hlf != HLF_PNI) + || (win_hl_attr(curwin, HLF_PMSI) == win_hl_attr(curwin, HLF_PSI) + && win_hl_attr(curwin, HLF_PMNI) == win_hl_attr(curwin, HLF_PNI))) { + return NULL; + } + + int *attrs = xmalloc(sizeof(int) * (size_t)vim_strsize(text)); + size_t leader_len = strlen(leader); + const bool in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0; + + garray_T *ga = NULL; + bool matched_start = false; + + if (in_fuzzy) { + ga = fuzzy_match_str_with_pos(text, leader); + } else { + matched_start = strncmp(text, leader, leader_len) == 0; + } + + const char *ptr = text; + int cell_idx = 0; + uint32_t char_pos = 0; + + while (*ptr != NUL) { + int new_attr = win_hl_attr(curwin, (int)hlf); + + if (ga != NULL) { + // Handle fuzzy matching + for (int i = 0; i < ga->ga_len; i++) { + if (char_pos == ((uint32_t *)ga->ga_data)[i]) { + new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI); + break; + } + } + } else if (matched_start && ptr < text + leader_len) { + new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI); + } + + int char_cells = utf_ptr2cells(ptr); + for (int i = 0; i < char_cells; i++) { + attrs[cell_idx + i] = new_attr; + } + cell_idx += char_cells; + + MB_PTR_ADV(ptr); + char_pos++; + } + + if (ga != NULL) { + ga_clear(ga); + xfree(ga); + } + return attrs; +} + +/// Displays text on the popup menu with specific attributes. +static void pum_grid_puts_with_attrs(int col, int cells, const char *text, int textlen, + const int *attrs) +{ + const int col_start = col; + const char *ptr = text; + + // Render text with proper attributes + while (*ptr != NUL && (textlen < 0 || ptr < text + textlen)) { + int char_len = utfc_ptr2len(ptr); + int attr = attrs[pum_rl ? (col_start + cells - col - 1) : (col - col_start)]; + grid_line_puts(col, ptr, char_len, attr); + col += utf_ptr2cells(ptr); + ptr += char_len; + } +} + /// Redraw the popup menu, using "pum_first" and "pum_selected". void pum_redraw(void) { @@ -446,11 +526,9 @@ void pum_redraw(void) int thumb_height = 1; int n; -#define HA(hlf) (win_hl_attr(curwin, (hlf))) - // "word" "kind" "extra text" - const int attrsNorm[3] = { HA(HLF_PNI), HA(HLF_PNK), HA(HLF_PNX) }; - const int attrsSel[3] = { HA(HLF_PSI), HA(HLF_PSK), HA(HLF_PSX) }; -#undef HA + // "word" "kind" "extra text" + const hlf_T hlfsNorm[3] = { HLF_PNI, HLF_PNK, HLF_PNX }; + const hlf_T hlfsSel[3] = { HLF_PSI, HLF_PSK, HLF_PSX }; int grid_width = pum_width; int col_off = 0; @@ -517,8 +595,9 @@ void pum_redraw(void) for (int i = 0; i < pum_height; i++) { int idx = i + pum_first; - const int *const attrs = (idx == pum_selected) ? attrsSel : attrsNorm; - int attr = attrs[0]; // start with "word" highlight + const hlf_T *const hlfs = (idx == pum_selected) ? hlfsSel : hlfsNorm; + hlf_T hlf = hlfs[0]; // start with "word" highlight + int attr = win_hl_attr(curwin, (int)hlf); grid_line_start(&pum_grid, row); @@ -540,7 +619,8 @@ void pum_redraw(void) int totwidth = 0; for (int round = 0; round < 3; round++) { - attr = attrs[round]; + hlf = hlfs[round]; + attr = win_hl_attr(curwin, (int)hlf); int width = 0; char *s = NULL; @@ -574,36 +654,50 @@ void pum_redraw(void) *p = saved; } + int *attrs = pum_compute_text_attrs(st, hlf); + if (pum_rl) { char *rt = reverse_text(st); char *rt_start = rt; - int size = vim_strsize(rt); + int cells = vim_strsize(rt); - if (size > pum_width) { + if (cells > pum_width) { do { - size -= utf_ptr2cells(rt); + cells -= utf_ptr2cells(rt); MB_PTR_ADV(rt); - } while (size > pum_width); + } while (cells > pum_width); - if (size < pum_width) { + if (cells < pum_width) { // Most left character requires 2-cells but only 1 cell // is available on screen. Put a '<' on the left of the // pum item *(--rt) = '<'; - size++; + cells++; } } - grid_line_puts(grid_col - size + 1, rt, -1, attr); + + if (attrs == NULL) { + grid_line_puts(grid_col - cells + 1, rt, -1, attr); + } else { + pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs); + } + xfree(rt_start); xfree(st); grid_col -= width; } else { - // use grid_line_puts() to truncate the text - grid_line_puts(grid_col, st, -1, attr); + if (attrs == NULL) { + grid_line_puts(grid_col, st, -1, attr); + } else { + pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs); + } + xfree(st); grid_col += width; } + xfree(attrs); + if (*p != TAB) { break; } @@ -645,7 +739,7 @@ void pum_redraw(void) if (pum_rl) { grid_line_fill(col_off - pum_base_width - n + 1, grid_col + 1, schar_from_ascii(' '), attr); - grid_col = col_off - pum_base_width - n + 1; + grid_col = col_off - pum_base_width - n; } else { grid_line_fill(grid_col, col_off + pum_base_width + n, schar_from_ascii(' '), attr); grid_col = col_off + pum_base_width + n; @@ -1144,12 +1238,14 @@ void pum_set_event_info(dict_T *dict) static void pum_position_at_mouse(int min_width) { int min_row = 0; + int min_col = 0; int max_row = Rows; int max_col = Columns; if (mouse_grid > 1) { win_T *wp = get_win_by_grid_handle(mouse_grid); if (wp != NULL) { min_row = -wp->w_winrow; + min_col = -wp->w_wincol; max_row = MAX(Rows - wp->w_winrow, wp->w_grid.rows); max_col = MAX(Columns - wp->w_wincol, wp->w_grid.cols); } @@ -1162,6 +1258,7 @@ static void pum_position_at_mouse(int min_width) } else { pum_anchor_grid = mouse_grid; } + if (max_row - mouse_row > pum_size) { // Enough space below the mouse row. pum_above = false; @@ -1178,16 +1275,29 @@ static void pum_position_at_mouse(int min_width) pum_row = min_row; } } - if (max_col - mouse_col >= pum_base_width - || max_col - mouse_col > min_width) { - // Enough space to show at mouse column. - pum_col = mouse_col; + + if (pum_rl) { + if (mouse_col - min_col + 1 >= pum_base_width + || mouse_col - min_col + 1 > min_width) { + // Enough space to show at mouse column. + pum_col = mouse_col; + } else { + // Not enough space, left align with window. + pum_col = min_col + MIN(pum_base_width, min_width) - 1; + } + pum_width = pum_col - min_col + 1; } else { - // Not enough space, right align with window. - pum_col = max_col - (pum_base_width > min_width ? min_width : pum_base_width); + if (max_col - mouse_col >= pum_base_width + || max_col - mouse_col > min_width) { + // Enough space to show at mouse column. + pum_col = mouse_col; + } else { + // Not enough space, right align with window. + pum_col = max_col - MIN(pum_base_width, min_width); + } + pum_width = max_col - pum_col; } - pum_width = max_col - pum_col; if (pum_width > pum_base_width + 1) { pum_width = pum_base_width + 1; } @@ -1269,6 +1379,7 @@ void pum_show_popupmenu(vimmenu_T *menu) pum_compute_size(); pum_scrollbar = 0; pum_height = pum_size; + pum_rl = curwin->w_p_rl; pum_position_at_mouse(20); pum_selected = -1; @@ -1348,7 +1459,9 @@ void pum_make_popup(const char *path_name, int use_mouse_pos) // Hack: set mouse position at the cursor so that the menu pops up // around there. mouse_row = curwin->w_grid.row_offset + curwin->w_wrow; - mouse_col = curwin->w_grid.col_offset + curwin->w_wcol; + mouse_col = curwin->w_grid.col_offset + + (curwin->w_p_rl ? curwin->w_width_inner - curwin->w_wcol - 1 + : curwin->w_wcol); if (ui_has(kUIMultigrid)) { mouse_grid = curwin->w_grid.target->handle; } else if (curwin->w_grid.target != &default_grid) { diff --git a/src/nvim/popupmenu.h b/src/nvim/popupmenu.h index 9e3f8f5a7f..bcf2a14290 100644 --- a/src/nvim/popupmenu.h +++ b/src/nvim/popupmenu.h @@ -15,6 +15,7 @@ typedef struct { char *pum_extra; ///< extra menu text (may be truncated) char *pum_info; ///< extra info int pum_score; ///< fuzzy match score + int pum_idx; ///< index of item before sorting by score } pumitem_T; EXTERN ScreenGrid pum_grid INIT( = SCREEN_GRID_INIT); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index be2f41da14..ed3fd83fd5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4531,7 +4531,7 @@ static char *get_mef_name(void) name = xmalloc(strlen(p_mef) + 30); STRCPY(name, p_mef); snprintf(name + (p - p_mef), strlen(name), "%d%d", start, off); - STRCAT(name, p + 2); + strcat(name, p + 2); // Don't accept a symbolic link, it's a security risk. FileInfo file_info; bool file_or_link_found = os_fileinfo_link(name, &file_info); @@ -7237,7 +7237,7 @@ static void hgr_search_files_in_dir(qf_list_T *qfl, char *dirname, regmatch_T *p // Find all "*.txt" and "*.??x" files in the "doc" directory. add_pathsep(dirname); - STRCAT(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT + strcat(dirname, "doc/*.\\(txt\\|??x\\)"); // NOLINT if (gen_expand_wildcards(1, &dirname, &fcount, &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { for (int fi = 0; fi < fcount && !got_int; fi++) { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 2ff4dbee70..5a5ba9df07 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -11451,7 +11451,7 @@ static void nfa_set_code(int c) } if (addnl == true) { - STRCAT(code, " + NEWLINE "); + strcat(code, " + NEWLINE "); } } diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 648e574b97..c479418131 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -409,7 +409,7 @@ int do_in_path(const char *path, const char *prefix, char *name, int flags, did_one = true; } else if (buflen + 2 + strlen(prefix) + strlen(name) < MAXPATHL) { add_pathsep(buf); - STRCAT(buf, prefix); + strcat(buf, prefix); tail = buf + strlen(buf); // Loop over all patterns in "name" diff --git a/src/nvim/search.c b/src/nvim/search.c index bee124e305..994a0794b0 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -21,12 +21,14 @@ #include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -3542,6 +3544,37 @@ int fuzzy_match_str(char *const str, const char *const pat) return score; } +/// Fuzzy match the position of string "pat" in string "str". +/// @returns a dynamic array of matching positions. If there is no match, returns NULL. +garray_T *fuzzy_match_str_with_pos(char *const str, const char *const pat) +{ + if (str == NULL || pat == NULL) { + return NULL; + } + + garray_T *match_positions = xmalloc(sizeof(garray_T)); + ga_init(match_positions, sizeof(uint32_t), 10); + + unsigned matches[MAX_FUZZY_MATCHES]; + int score = 0; + if (!fuzzy_match(str, pat, false, &score, matches, MAX_FUZZY_MATCHES) + || score == 0) { + ga_clear(match_positions); + xfree(match_positions); + return NULL; + } + + int j = 0; + for (const char *p = pat; *p != NUL; MB_PTR_ADV(p)) { + if (!ascii_iswhite(utf_ptr2char(p))) { + GA_APPEND(uint32_t, match_positions, matches[j]); + j++; + } + } + + return match_positions; +} + /// Copy a list of fuzzy matches into a string list after sorting the matches by /// the fuzzy score. Frees the memory allocated for "fuzmatch". void fuzzymatches_to_strmatches(fuzmatch_str_T *const fuzmatch, char ***const matches, diff --git a/src/nvim/shada.c b/src/nvim/shada.c index d5655b1754..5736ea0f09 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -42,6 +42,7 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" @@ -602,20 +603,6 @@ static void close_file(FileDescriptor *cookie) } } -/// Msgpack callback for writing to FileDescriptor* -static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) -{ - FileDescriptor *const sd_writer = (FileDescriptor *)data; - const ptrdiff_t ret = file_write(sd_writer, buf, len); - if (ret < 0) { - semsg(_(SERR "System error while writing ShaDa file: %s"), - os_strerror((int)ret)); - return -1; - } - - return 0; -} - /// Check whether writing to shada file was disabled ("-i NONE" or "--clean"). /// /// @return true if it was disabled, false otherwise. @@ -1365,6 +1352,8 @@ static char *shada_filename(const char *file) } \ } while (0) +#define SHADA_MPACK_FREE_SPACE (4 * MPACK_ITEM_SIZE) + /// Write single ShaDa entry /// /// @param[in] packer Packer used to write entry. @@ -1373,7 +1362,7 @@ static char *shada_filename(const char *file) /// restrictions. /// /// @return kSDWriteSuccessful, kSDWriteFailed or kSDWriteIgnError. -static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntry entry, +static ShaDaWriteResult shada_pack_entry(PackerBuffer *const packer, ShadaEntry entry, const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL { @@ -1657,29 +1646,26 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntr #undef CHECK_DEFAULT #undef ONE_IF_NOT_DEFAULT if (!max_kbyte || sbuf.size <= max_kbyte * 1024) { + if (mpack_remaining(packer) < SHADA_MPACK_FREE_SPACE) { + packer->packer_flush(packer); + } + if (entry.type == kSDItemUnknown) { - if (msgpack_pack_uint64(packer, entry.data.unknown_item.type) == -1) { - goto shada_pack_entry_error; - } + mpack_uint64(&packer->ptr, entry.data.unknown_item.type); } else { - if (msgpack_pack_uint64(packer, (uint64_t)entry.type) == -1) { - goto shada_pack_entry_error; - } - } - if (msgpack_pack_uint64(packer, (uint64_t)entry.timestamp) == -1) { - goto shada_pack_entry_error; + mpack_uint64(&packer->ptr, (uint64_t)entry.type); } + mpack_uint64(&packer->ptr, (uint64_t)entry.timestamp); if (sbuf.size > 0) { - if ((msgpack_pack_uint64(packer, (uint64_t)sbuf.size) == -1) - || (packer->callback(packer->data, sbuf.data, - (unsigned)sbuf.size) == -1)) { - goto shada_pack_entry_error; - } + mpack_uint64(&packer->ptr, (uint64_t)sbuf.size); + mpack_raw(sbuf.data, sbuf.size, packer); + } + + if (packer->anyint != 0) { // error code + goto shada_pack_entry_error; } } - msgpack_packer_free(spacker); - msgpack_sbuffer_destroy(&sbuf); - return kSDWriteSuccessful; + ret = kSDWriteSuccessful; shada_pack_entry_error: msgpack_packer_free(spacker); msgpack_sbuffer_destroy(&sbuf); @@ -1694,7 +1680,7 @@ shada_pack_entry_error: /// @param[in] entry Entry written. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static inline ShaDaWriteResult shada_pack_pfreed_entry(msgpack_packer *const packer, +static inline ShaDaWriteResult shada_pack_pfreed_entry(PackerBuffer *const packer, PossiblyFreedShadaEntry entry, const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE @@ -1908,7 +1894,7 @@ static inline ShaDaWriteResult shada_read_when_writing(FileDescriptor *const sd_ const unsigned srni_flags, const size_t max_kbyte, WriteMergerState *const wms, - msgpack_packer *const packer) + PackerBuffer *const packer) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { ShaDaWriteResult ret = kSDWriteSuccessful; @@ -2360,6 +2346,29 @@ static int hist_type2char(const int type) return NUL; } +static PackerBuffer packer_buffer_for_file(FileDescriptor *file) +{ + if (file_space(file) < SHADA_MPACK_FREE_SPACE) { + file_flush(file); + } + return (PackerBuffer) { + .startptr = file->buffer, + .ptr = file->write_pos, + .endptr = file->buffer + ARENA_BLOCK_SIZE, + .anydata = file, + .anyint = 0, // set to nonzero if error + .packer_flush = flush_file_buffer, + }; +} + +static void flush_file_buffer(PackerBuffer *buffer) +{ + FileDescriptor *fd = buffer->anydata; + fd->write_pos = buffer->ptr; + buffer->anyint = file_flush(fd); + buffer->ptr = fd->write_pos; +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. @@ -2408,8 +2417,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, } } - const unsigned srni_flags = (unsigned)( - kSDReadUndisableableData + const unsigned srni_flags = (unsigned)(kSDReadUndisableableData | kSDReadUnknown | (dump_history ? kSDReadHistory : 0) | (dump_registers ? kSDReadRegisters : 0) @@ -2418,8 +2426,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, | (num_marked_files ? kSDReadLocalMarks | kSDReadChanges : 0)); - msgpack_packer *const packer = msgpack_packer_new(sd_writer, - &msgpack_sd_writer_write); + PackerBuffer packer = packer_buffer_for_file(sd_writer); // Set b_last_cursor for all the buffers that have a window. // @@ -2433,7 +2440,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, find_removable_bufs(&removable_bufs); // Write header - if (shada_pack_entry(packer, (ShadaEntry) { + if (shada_pack_entry(&packer, (ShadaEntry) { .type = kSDItemHeader, .timestamp = os_time(), .data = { @@ -2462,7 +2469,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, // Write buffer list if (find_shada_parameter('%') != NULL) { ShadaEntry buflist_entry = shada_get_buflist(&removable_bufs); - if (shada_pack_entry(packer, buflist_entry, 0) == kSDWriteFailed) { + if (shada_pack_entry(&packer, buflist_entry, 0) == kSDWriteFailed) { xfree(buflist_entry.data.buffer_list.buffers); ret = kSDWriteFailed; goto shada_write_exit; @@ -2512,7 +2519,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, typval_T tgttv; tv_copy(&vartv, &tgttv); ShaDaWriteResult spe_ret; - if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { + if ((spe_ret = shada_pack_entry(&packer, (ShadaEntry) { .type = kSDItemVariable, .timestamp = cur_timestamp, .data = { @@ -2689,7 +2696,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, if (sd_reader != NULL) { const ShaDaWriteResult srww_ret = shada_read_when_writing(sd_reader, srni_flags, max_kbyte, wms, - packer); + &packer); if (srww_ret != kSDWriteSuccessful) { ret = srww_ret; } @@ -2720,7 +2727,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, do { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ if ((wms_array)[i_].data.type != kSDItemMissing) { \ - if (shada_pack_pfreed_entry(packer, (wms_array)[i_], max_kbyte) \ + if (shada_pack_pfreed_entry(&packer, (wms_array)[i_], max_kbyte) \ == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ @@ -2732,7 +2739,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, PACK_WMS_ARRAY(wms->numbered_marks); PACK_WMS_ARRAY(wms->registers); for (size_t i = 0; i < wms->jumps_size; i++) { - if (shada_pack_pfreed_entry(packer, wms->jumps[i], max_kbyte) + if (shada_pack_pfreed_entry(&packer, wms->jumps[i], max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; @@ -2741,7 +2748,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, #define PACK_WMS_ENTRY(wms_entry) \ do { \ if ((wms_entry).data.type != kSDItemMissing) { \ - if (shada_pack_pfreed_entry(packer, wms_entry, max_kbyte) \ + if (shada_pack_pfreed_entry(&packer, wms_entry, max_kbyte) \ == kSDWriteFailed) { \ ret = kSDWriteFailed; \ goto shada_write_exit; \ @@ -2767,14 +2774,14 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, for (size_t i = 0; i < file_markss_to_dump; i++) { PACK_WMS_ARRAY(all_file_markss[i]->marks); for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { - if (shada_pack_pfreed_entry(packer, all_file_markss[i]->changes[j], + if (shada_pack_pfreed_entry(&packer, all_file_markss[i]->changes[j], max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; goto shada_write_exit; } } for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) { - if (shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], + if (shada_pack_entry(&packer, all_file_markss[i]->additional_marks[j], 0) == kSDWriteFailed) { shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]); ret = kSDWriteFailed; @@ -2792,7 +2799,7 @@ static ShaDaWriteResult shada_write(FileDescriptor *const sd_writer, if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { - if (shada_pack_pfreed_entry(packer, (PossiblyFreedShadaEntry) { + if (shada_pack_pfreed_entry(&packer, (PossiblyFreedShadaEntry) { .data = cur_entry->data, .can_free_entry = cur_entry->can_free_entry, }, max_kbyte) == kSDWriteFailed) { @@ -2818,7 +2825,7 @@ shada_write_exit: }) map_destroy(cstr_t, &wms->file_marks); set_destroy(ptr_t, &removable_bufs); - msgpack_packer_free(packer); + packer.packer_flush(&packer); set_destroy(cstr_t, &wms->dumped_variables); xfree(wms); return ret; @@ -3953,16 +3960,43 @@ static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps, return jumps_size; } +static PackerBuffer packer_string_buffer(void) +{ + const size_t initial_size = 64; // must be larger than SHADA_MPACK_FREE_SPACE + char *alloc = xmalloc(initial_size); + return (PackerBuffer) { + .startptr = alloc, + .ptr = alloc, + .endptr = alloc + initial_size, + .packer_flush = flush_string_buffer, + }; +} + +static void flush_string_buffer(PackerBuffer *buffer) +{ + size_t current_capacity = (size_t)(buffer->endptr - buffer->startptr); + size_t new_capacity = 2 * current_capacity; + size_t len = (size_t)(buffer->ptr - buffer->startptr); + + buffer->startptr = xrealloc(buffer->startptr, new_capacity); + buffer->ptr = buffer->startptr + len; + buffer->endptr = buffer->startptr + new_capacity; +} + +static String packer_take_string(PackerBuffer *buffer) +{ + return (String){ .data = buffer->startptr, .size = (size_t)(buffer->ptr - buffer->startptr) }; +} + /// Write registers ShaDa entries in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_regs(msgpack_sbuffer *const sbuf) +String shada_encode_regs(void) FUNC_ATTR_NONNULL_ALL { WriteMergerState *const wms = xcalloc(1, sizeof(*wms)); shada_initialize_registers(wms, -1); - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + PackerBuffer packer = packer_string_buffer(); for (size_t i = 0; i < ARRAY_SIZE(wms->registers); i++) { if (wms->registers[i].data.type == kSDItemRegister) { if (kSDWriteFailed @@ -3972,52 +4006,53 @@ void shada_encode_regs(msgpack_sbuffer *const sbuf) } } xfree(wms); + return packer_take_string(&packer); } /// Write jumplist ShaDa entries in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_jumps(msgpack_sbuffer *const sbuf) +String shada_encode_jumps(void) FUNC_ATTR_NONNULL_ALL { Set(ptr_t) removable_bufs = SET_INIT; find_removable_bufs(&removable_bufs); PossiblyFreedShadaEntry jumps[JUMPLISTSIZE]; size_t jumps_size = shada_init_jumps(jumps, &removable_bufs); - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + PackerBuffer packer = packer_string_buffer(); for (size_t i = 0; i < jumps_size; i++) { if (kSDWriteFailed == shada_pack_pfreed_entry(&packer, jumps[i], 0)) { abort(); } } + return packer_take_string(&packer); } /// Write buffer list ShaDa entry in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_buflist(msgpack_sbuffer *const sbuf) +String shada_encode_buflist(void) FUNC_ATTR_NONNULL_ALL { Set(ptr_t) removable_bufs = SET_INIT; find_removable_bufs(&removable_bufs); ShadaEntry buflist_entry = shada_get_buflist(&removable_bufs); - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + + PackerBuffer packer = packer_string_buffer(); if (kSDWriteFailed == shada_pack_entry(&packer, buflist_entry, 0)) { abort(); } xfree(buflist_entry.data.buffer_list.buffers); + return packer_take_string(&packer); } /// Write global variables ShaDa entries in given msgpack_sbuffer. /// /// @param[in] sbuf target msgpack_sbuffer to write to. -void shada_encode_gvars(msgpack_sbuffer *const sbuf) +String shada_encode_gvars(void) FUNC_ATTR_NONNULL_ALL { - msgpack_packer packer; - msgpack_packer_init(&packer, sbuf, msgpack_sbuffer_write); + PackerBuffer packer = packer_string_buffer(); const void *var_iter = NULL; const Timestamp cur_timestamp = os_time(); do { @@ -4049,21 +4084,21 @@ void shada_encode_gvars(msgpack_sbuffer *const sbuf) } tv_clear(&vartv); } while (var_iter != NULL); + return packer_take_string(&packer); } -/// Read ShaDa from msgpack_sbuffer. +/// Read ShaDa from String. /// -/// @param[in] file msgpack_sbuffer to read from. +/// @param[in] string string to read from. /// @param[in] flags Flags, see ShaDaReadFileFlags enum. -void shada_read_sbuf(msgpack_sbuffer *const sbuf, const int flags) +void shada_read_string(String string, const int flags) FUNC_ATTR_NONNULL_ALL { - assert(sbuf != NULL); - if (sbuf->data == NULL) { + if (string.size == 0) { return; } FileDescriptor sd_reader; - file_open_buffer(&sd_reader, sbuf->data, sbuf->size); + file_open_buffer(&sd_reader, string.data, string.size); shada_read(&sd_reader, flags); close_file(&sd_reader); } diff --git a/src/nvim/shada.h b/src/nvim/shada.h index d7cac24afc..58689a5bd7 100644 --- a/src/nvim/shada.h +++ b/src/nvim/shada.h @@ -2,6 +2,8 @@ #include <msgpack.h> // IWYU pragma: keep +#include "nvim/api/private/defs.h" + /// Flags for shada_read_file and children typedef enum { kShaDaWantInfo = 1, ///< Load non-mark information diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 1ca0e846a9..605098fb66 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -126,8 +126,8 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr | (has_hl ? MT_FLAG_DECOR_SIGNHL : 0); DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } }; - extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, decor, decor_flags, true, - false, true, true, NULL); + extmark_set(buf, ns, id, MIN(buf->b_ml.ml_line_count, lnum) - 1, 0, -1, -1, + decor, decor_flags, true, false, true, true, NULL); } /// For an existing, placed sign with "id", modify the sign, group or priority. diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 39145d73ed..8ec28c7f61 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2092,7 +2092,7 @@ char *parse_spelllang(win_T *wp) } else { // One entry in 'spellfile'. copy_option_part(&spf, spf_name, MAXPATHL - 5, ","); - STRCAT(spf_name, ".spl"); + strcat(spf_name, ".spl"); int c; // If it was already found above then skip it. @@ -2677,7 +2677,7 @@ void ex_spellrepall(exarg_T *eap) char *p = xmalloc((size_t)get_cursor_line_len() + (size_t)addlen + 1); memmove(p, line, (size_t)curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); - STRCAT(p, line + curwin->w_cursor.col + repl_from_len); + strcat(p, line + curwin->w_cursor.col + repl_from_len); ml_replace(curwin->w_cursor.lnum, p, false); inserted_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col, (int)repl_from_len, (int)repl_to_len); @@ -3444,14 +3444,14 @@ static void dump_word(slang_T *slang, char *word, char *pat, Direction *dir, int // Add flags and regions after a slash. if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap) { STRCPY(badword, p); - STRCAT(badword, "/"); + strcat(badword, "/"); if (keepcap) { - STRCAT(badword, "="); + strcat(badword, "="); } if (flags & WF_BANNED) { - STRCAT(badword, "!"); + strcat(badword, "!"); } else if (flags & WF_RARE) { - STRCAT(badword, "?"); + strcat(badword, "?"); } if (flags & WF_REGION) { for (int i = 0; i < 7; i++) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 9746c3df91..ddeab218e1 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -2140,11 +2140,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) + strlen(items[1]) + 3, false); if (spin->si_info != NULL) { STRCPY(p, spin->si_info); - STRCAT(p, "\n"); + strcat(p, "\n"); } - STRCAT(p, items[0]); - STRCAT(p, " "); - STRCAT(p, items[1]); + strcat(p, items[0]); + strcat(p, " "); + strcat(p, items[1]); spin->si_info = p; } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2) && midword == NULL) { midword = getroom_save(spin, items[1]); @@ -2200,7 +2200,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) // "Na" into "Na+", "1234" into "1234+". p = getroom(spin, strlen(items[1]) + 2, false); STRCPY(p, items[1]); - STRCAT(p, "+"); + strcat(p, "+"); compflags = p; } else if (is_aff_rule(items, itemcnt, "COMPOUNDRULES", 2)) { // We don't use the count, but do check that it's a number and @@ -2221,9 +2221,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char *fname) p = getroom(spin, (size_t)l, false); if (compflags != NULL) { STRCPY(p, compflags); - STRCAT(p, "/"); + strcat(p, "/"); } - STRCAT(p, items[1]); + strcat(p, items[1]); compflags = p; } } else if (is_aff_rule(items, itemcnt, "COMPOUNDWORDMAX", 2) @@ -2844,7 +2844,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char *compflags char *p = getroom(spin, (size_t)len, false); if (spin->si_compflags != NULL) { STRCPY(p, spin->si_compflags); - STRCAT(p, "/"); + strcat(p, "/"); } spin->si_compflags = p; uint8_t *tp = (uint8_t *)p + strlen(p); @@ -3386,7 +3386,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_ MB_PTR_ADV(p); } } - STRCAT(newword, p); + strcat(newword, p); } else { // suffix: chop/add at the end of the word xstrlcpy(newword, word, MAXWLEN); @@ -3400,7 +3400,7 @@ static int store_aff_word(spellinfo_T *spin, char *word, char *afflist, afffile_ *p = NUL; } if (ae->ae_add != NULL) { - STRCAT(newword, ae->ae_add); + strcat(newword, ae->ae_add); } } diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 0e674c3178..ed1ba972d5 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -643,7 +643,7 @@ void spell_suggest(int count) int c = (int)(sug.su_badptr - line); memmove(p, line, (size_t)c); STRCPY(p + c, stp->st_word); - STRCAT(p, sug.su_badptr + stp->st_orglen); + strcat(p, sug.su_badptr + stp->st_orglen); // For redo we use a change-word command. ResetRedobuff(); @@ -1638,7 +1638,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soun // Append a space to preword when splitting. if (!try_compound && !fword_ends) { - STRCAT(preword, " "); + strcat(preword, " "); } sp->ts_prewordlen = (uint8_t)strlen(preword); sp->ts_splitoff = sp->ts_twordlen; diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index ca7083a9e3..ad7e4587a9 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -1017,7 +1017,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op int evaldepth = 0; int curitem = 0; - int foldsignitem = -1; bool prevchar_isflag = true; bool prevchar_isitem = false; @@ -1234,6 +1233,8 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op } int minwid = 0; int maxwid = 9999; + int foldsignitem = -1; // Start of fold or sign item + bool left_align_num = false; // Number item for should be left-aligned bool left_align = false; // Denotes that numbers should be left-padded with zeros @@ -1505,12 +1506,20 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op } case STL_LINE: - // Overload %l with v:lnum for 'statuscolumn' - if (stcp != NULL) { - if (wp->w_p_nu && !get_vim_var_nr(VV_VIRTNUM)) { - num = (int)get_vim_var_nr(VV_LNUM); + // Overload %l with v:(re)lnum for 'statuscolumn'. Place a sign when 'signcolumn' + // is set to "number". Take care of alignment for 'number' + 'relativenumber'. + if (stcp != NULL && (wp->w_p_nu || wp->w_p_rnu) && get_vim_var_nr(VV_VIRTNUM) == 0) { + if (wp->w_maxscwidth == SCL_NUM && stcp->sattrs[0].text[0]) { + goto stcsign; } - } else { + int relnum = (int)get_vim_var_nr(VV_RELNUM); + num = (!wp->w_p_rnu || (wp->w_p_nu && relnum == 0)) ? (int)get_vim_var_nr(VV_LNUM) : relnum; + left_align_num = wp->w_p_rnu && wp->w_p_nu && relnum == 0; + if (!left_align_num) { + stl_items[curitem].type = Separate; + stl_items[curitem++].start = out_p; + } + } else if (stcp == NULL) { num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? 0 : wp->w_cursor.lnum; } break; @@ -1609,16 +1618,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op case STL_ROFLAG: case STL_ROFLAG_ALT: - // Overload %r with v:relnum for 'statuscolumn' - if (stcp != NULL) { - if (wp->w_p_rnu && !get_vim_var_nr(VV_VIRTNUM)) { - num = (int)get_vim_var_nr(VV_RELNUM); - } - } else { - itemisflag = true; - if (wp->w_buffer->b_p_ro) { - str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"); - } + itemisflag = true; + if (wp->w_buffer->b_p_ro) { + str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"); } break; @@ -1632,21 +1634,20 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op case STL_FOLDCOL: // 'C' for 'statuscolumn' case STL_SIGNCOL: { // 's' for 'statuscolumn' +stcsign: if (stcp == NULL) { break; } - bool fold = opt == STL_FOLDCOL; - int fdc = fold ? compute_foldcolumn(wp, 0) : 0; - int width = fold ? fdc > 0 : wp->w_scwidth; + int fdc = opt == STL_FOLDCOL ? compute_foldcolumn(wp, 0) : 0; + int width = opt == STL_FOLDCOL ? fdc > 0 : opt == STL_SIGNCOL ? wp->w_scwidth : 1; if (width <= 0) { break; } foldsignitem = curitem; - char *p = NULL; - if (fold) { - schar_T fold_buf[10]; + if (fdc > 0) { + schar_T fold_buf[9]; fill_foldcolumn(wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM), 0, fdc, NULL, fold_buf); stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1); @@ -1654,31 +1655,26 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // TODO(bfredl): this is very backwards. we must support schar_T // being used directly in 'statuscolumn' for (int i = 0; i < fdc; i++) { - buflen += schar_get(out_p + buflen, fold_buf[i]); + buflen += schar_get(buf_tmp + buflen, fold_buf[i]); } - p = out_p; } - char buf[SIGN_WIDTH * MAX_SCHAR_SIZE]; - size_t buflen = 0; - varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM); + size_t signlen = 0; for (int i = 0; i < width; i++) { - if (!fold) { - SignTextAttrs *sattr = virtnum ? NULL : &stcp->sattrs[i]; - p = " "; - if (sattr && sattr->text[0]) { - describe_sign_text(buf, sattr->text); - p = buf; + stl_items[curitem].start = out_p + signlen; + if (fdc == 0) { + if (stcp->sattrs[i].text[0] && get_vim_var_nr(VV_VIRTNUM) == 0) { + SignTextAttrs sattrs = stcp->sattrs[i]; + signlen += describe_sign_text(buf_tmp + signlen, sattrs.text); + stl_items[curitem].minwid = -(stcp->sign_cul_id ? stcp->sign_cul_id : sattrs.hl_id); + } else { + buf_tmp[signlen++] = ' '; + buf_tmp[signlen++] = ' '; + buf_tmp[signlen] = NUL; + stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLS : HLF_SC) + 1); } - stl_items[curitem].minwid = -(sattr && sattr->text[0] - ? (stcp->sign_cul_id ? stcp->sign_cul_id : sattr->hl_id) - : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1); } - stl_items[curitem].type = Highlight; - stl_items[curitem].start = out_p + buflen; - xstrlcpy(buf_tmp + buflen, p, TMPLEN - buflen); - buflen += strlen(p); - curitem++; + stl_items[curitem++].type = Highlight; } str = buf_tmp; break; @@ -1851,7 +1847,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // For a 'statuscolumn' sign or fold item, add an item to reset the highlight group if (foldsignitem >= 0) { - foldsignitem = -1; stl_items[curitem].type = Highlight; stl_items[curitem].start = out_p; stl_items[curitem].minwid = 0; @@ -1956,6 +1951,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op // Item processed, move to the next curitem++; + // For a 'statuscolumn' number item that is left aligned, add a separator item. + if (left_align_num) { + stl_items[curitem].type = Separate; + stl_items[curitem++].start = out_p; + } } *out_p = NUL; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index c13619797a..d1af8f304c 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -4992,7 +4992,7 @@ static int get_id_list(char **const arg, const int keylen, int16_t **const list, } else { // Handle match of regexp with group names. *name = '^'; - STRCAT(name, "$"); + strcat(name, "$"); regmatch.regprog = vim_regcomp(name, RE_MAGIC); if (regmatch.regprog == NULL) { failed = true; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ca3885b079..8f6342bfcc 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1060,7 +1060,7 @@ static int add_llist_tags(char *tag, int num_matches, char **matches) // Precede the tag pattern with \V to make it very // nomagic. - STRCAT(cmd, "\\V"); + strcat(cmd, "\\V"); len += 2; int cmd_len = (int)(cmd_end - cmd_start + 1); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 2738eb874d..b16777be8a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -112,6 +112,9 @@ typedef struct { // libvterm. Improves performance when receiving large bursts of data. #define REFRESH_DELAY 10 +#define TEXTBUF_SIZE 0x1fff +#define SELECTIONBUF_SIZE 0x0400 + static TimeWatcher refresh_timer; static bool refresh_pending = false; @@ -127,7 +130,7 @@ struct terminal { // buffer used to: // - convert VTermScreen cell arrays into utf8 strings // - receive data from libvterm as a result of key presses. - char textbuf[0x1fff]; + char textbuf[TEXTBUF_SIZE]; ScrollbackLine **sb_buffer; // Scrollback storage. size_t sb_current; // Lines stored in sb_buffer. @@ -166,6 +169,9 @@ struct terminal { // When there is a pending TermRequest autocommand, block and store input. StringBuilder *pending_send; + char *selection_buffer; /// libvterm selection buffer + StringBuilder selection; /// Growable array containing full selection data + size_t refcount; // reference count }; @@ -179,6 +185,12 @@ static VTermScreenCallbacks vterm_screen_callbacks = { .sb_popline = term_sb_pop, }; +static VTermSelectionCallbacks vterm_selection_callbacks = { + .set = term_selection_set, + // For security reasons we don't support querying the system clipboard from the embedded terminal + .query = NULL, +}; + static Set(ptr_t) invalidated_terminals = SET_INIT; static void emit_termrequest(void **argv) @@ -315,6 +327,11 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) vterm_screen_set_damage_merge(term->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(term->vts, 1); vterm_output_set_callback(term->vt, term_output_callback, term); + + term->selection_buffer = xcalloc(SELECTIONBUF_SIZE, 1); + vterm_state_set_selection_callbacks(state, &vterm_selection_callbacks, term, + term->selection_buffer, SELECTIONBUF_SIZE); + // force a initial refresh of the screen to ensure the buffer will always // have as many lines as screen rows when refresh_scrollback is called term->invalid_start = 0; @@ -326,14 +343,6 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts) refresh_screen(term, buf); set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); - // Default settings for terminal buffers - buf->b_p_ma = false; // 'nomodifiable' - buf->b_p_ul = -1; // 'undolevels' - buf->b_p_scbk = // 'scrollback' (initialize local from global) - (p_scbk < 0) ? 10000 : MAX(1, p_scbk); - buf->b_p_tw = 0; // 'textwidth' - set_option_value(kOptWrap, BOOLEAN_OPTVAL(false), OPT_LOCAL); - set_option_value(kOptList, BOOLEAN_OPTVAL(false), OPT_LOCAL); if (buf->b_ffname != NULL) { buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname)); } @@ -769,6 +778,8 @@ void terminal_destroy(Terminal **termpp) } xfree(term->sb_buffer); xfree(term->title); + xfree(term->selection_buffer); + kv_destroy(term->selection); vterm_free(term->vt); xfree(term); *termpp = NULL; // coverity[dead-store] @@ -1198,6 +1209,54 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) return 1; } +static void term_clipboard_set(void **argv) +{ + VTermSelectionMask mask = (VTermSelectionMask)(long)argv[0]; + char *data = argv[1]; + + char regname; + switch (mask) { + case VTERM_SELECTION_CLIPBOARD: + regname = '+'; + break; + case VTERM_SELECTION_PRIMARY: + regname = '*'; + break; + default: + regname = '+'; + break; + } + + list_T *lines = tv_list_alloc(1); + tv_list_append_allocated_string(lines, data); + + list_T *args = tv_list_alloc(3); + tv_list_append_list(args, lines); + + const char regtype = 'v'; + tv_list_append_string(args, ®type, 1); + + tv_list_append_string(args, ®name, 1); + eval_call_provider("clipboard", "set", args, true); +} + +static int term_selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user) +{ + Terminal *term = user; + if (frag.initial) { + kv_size(term->selection) = 0; + } + + kv_concat_len(term->selection, frag.str, frag.len); + + if (frag.final) { + char *data = xmemdupz(term->selection.items, kv_size(term->selection)); + multiqueue_put(main_loop.events, term_clipboard_set, (void *)mask, data); + } + + return 1; +} + // }}} // input handling {{{ diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 9bb66b886e..314d322a68 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -144,11 +144,15 @@ void ui_free_all_mem(void) /// Returns true if any `rgb=true` UI is attached. bool ui_rgb_attached(void) { - if (!headless_mode && p_tgc) { + if (p_tgc) { return true; } for (size_t i = 0; i < ui_count; i++) { - if (uis[i]->rgb) { + // We do not consider the TUI in this loop because we already checked for 'termguicolors' at the + // beginning of this function. In this loop, we are checking to see if any _other_ UIs which + // support RGB are attached. + bool tui = uis[i]->stdin_tty || uis[i]->stdout_tty; + if (!tui && uis[i]->rgb) { return true; } } diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 79f897f020..2893c7cf9f 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -1274,9 +1274,9 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods) if (buf != NULL) { if (*multi_mods) { - STRCAT(buf, " "); + strcat(buf, " "); } - STRCAT(buf, mod_str); + strcat(buf, mod_str); } *multi_mods = true; diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index b32f2b1cb2..d28064447c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3887,11 +3887,11 @@ describe('API', function() )) eq( { - str = '3 ', - width = 2, + str = ' 3 ', + width = 9, highlights = { { group = 'LineNr', start = 0 }, - { group = 'ErrorMsg', start = 1 }, + { group = 'ErrorMsg', start = 8 }, }, }, api.nvim_eval_statusline('%l%#ErrorMsg# ', { use_statuscol_lnum = 3, highlights = true }) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index a53625ab1b..1bb4ce2946 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -400,9 +400,6 @@ describe('startup', function() read_file('Xtest_startup_ttyout') ) end) - if is_os('win') then - assert_log('stream write failed. RPC canceled; closing channel', testlog) - end end) it('input from pipe: has("ttyin")==0 has("ttyout")==1', function() @@ -435,9 +432,6 @@ describe('startup', function() read_file('Xtest_startup_ttyout') ) end) - if is_os('win') then - assert_log('stream write failed. RPC canceled; closing channel', testlog) - end end) it('input from pipe (implicit) #7679', function() diff --git a/test/functional/editor/macro_spec.lua b/test/functional/editor/macro_spec.lua index 27c5eddac8..680a70b78a 100644 --- a/test/functional/editor/macro_spec.lua +++ b/test/functional/editor/macro_spec.lua @@ -148,6 +148,23 @@ helloFOO]] eq({ 0, 1, 1, 0 }, fn.getpos('v')) end) + it('can be recorded and replayed in Visual mode when ignorecase', function() + command('set ignorecase') + insert('foo BAR BAR foo BAR foo BAR BAR BAR foo BAR BAR') + feed('0vqifofRq') + eq({ 0, 1, 7, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + feed('Q') + eq({ 0, 1, 19, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + feed('Q') + eq({ 0, 1, 27, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + feed('@i') + eq({ 0, 1, 43, 0 }, fn.getpos('.')) + eq({ 0, 1, 1, 0 }, fn.getpos('v')) + end) + it('can be replayed with @ in blockwise Visual mode', function() insert [[ hello diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index d50b646085..23bb9f0a2e 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -135,14 +135,15 @@ describe('lua stdlib', function() -- See MAINTAIN.md for the soft/hard deprecation policy describe(('vim.deprecate prerel=%s,'):format(prerel or 'nil'), function() - local curver = exec_lua('return vim.version()') --[[@as {major:number, minor:number}]] - -- "0.10" or "0.10-dev+xxx" - local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '') - -- "0.10" or "0.11" - local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1)) - local was_removed = prerel and 'was removed' or 'will be removed' + local curver --- @type {major:number, minor:number} + + before_each(function() + curver = exec_lua('return vim.version()') + end) it('plugin=nil, same message skipped', function() + -- "0.10" or "0.10-dev+xxx" + local curstr = ('%s.%s%s'):format(curver.major, curver.minor, prerel or '') eq( dedent( [[ @@ -162,6 +163,10 @@ describe('lua stdlib', function() end) it('plugin=nil, show error if hard-deprecated', function() + -- "0.10" or "0.11" + local nextver = ('%s.%s'):format(curver.major, curver.minor + (prerel and 0 or 1)) + + local was_removed = prerel and 'was removed' or 'will be removed' eq( dedent( [[ @@ -2023,6 +2028,10 @@ describe('lua stdlib', function() vim.cmd "enew" ]] eq(100, fn.luaeval 'vim.wo.scrolloff') + + matches('only bufnr=0 is supported', pcall_err(exec_lua, 'vim.wo[0][10].signcolumn = "no"')) + + matches('only bufnr=0 is supported', pcall_err(exec_lua, 'local a = vim.wo[0][10].signcolumn')) end) describe('vim.opt', function() diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua index 0e81e4fddb..1b56d1740a 100644 --- a/test/functional/plugin/lsp/completion_spec.lua +++ b/test/functional/plugin/lsp/completion_spec.lua @@ -81,10 +81,21 @@ describe('vim.lsp.completion: item conversion', function() -- plain text { label = 'foocar', - sortText = 'k', + sortText = 'g', insertText = 'foodar(${1:var1})', insertTextFormat = 1, }, + { + label = '•INT16_C(c)', + insertText = 'INT16_C(${1:c})', + insertTextFormat = 2, + filterText = 'INT16_C', + sortText = 'h', + textEdit = { + newText = 'INT16_C(${1:c})', + range = range0, + }, + }, } local expected = { { @@ -115,6 +126,10 @@ describe('vim.lsp.completion: item conversion', function() abbr = 'foocar', word = 'foodar(${1:var1})', -- marked as PlainText, text is used as is }, + { + abbr = '•INT16_C(c)', + word = 'INT16_C', + }, } local result = complete('|', completion_list) result = vim.tbl_map(function(x) diff --git a/test/functional/plugin/lsp/semantic_tokens_spec.lua b/test/functional/plugin/lsp/semantic_tokens_spec.lua index 7908c5d2e7..9babb080e7 100644 --- a/test/functional/plugin/lsp/semantic_tokens_spec.lua +++ b/test/functional/plugin/lsp/semantic_tokens_spec.lua @@ -111,6 +111,7 @@ describe('semantic token highlighting', function() end) it('buffer is highlighted when attached', function() + insert(text) exec_lua([[ bufnr = vim.api.nvim_get_current_buf() vim.api.nvim_win_set_buf(0, bufnr) @@ -118,8 +119,6 @@ describe('semantic token highlighting', function() client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) ]]) - insert(text) - screen:expect { grid = [[ #include <iostream> | @@ -141,6 +140,7 @@ describe('semantic token highlighting', function() end) it('use LspTokenUpdate and highlight_token', function() + insert(text) exec_lua([[ vim.api.nvim_create_autocmd("LspTokenUpdate", { callback = function(args) @@ -157,8 +157,6 @@ describe('semantic token highlighting', function() client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) ]]) - insert(text) - screen:expect { grid = [[ #include <iostream> | @@ -180,14 +178,17 @@ describe('semantic token highlighting', function() end) it('buffer is unhighlighted when client is detached', function() + insert(text) + exec_lua([[ bufnr = vim.api.nvim_get_current_buf() vim.api.nvim_win_set_buf(0, bufnr) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + vim.wait(1000, function() + return #server.messages > 1 + end) ]]) - insert(text) - exec_lua([[ vim.notify = function() end vim.lsp.buf_detach_client(bufnr, client_id) @@ -331,14 +332,13 @@ describe('semantic token highlighting', function() end) it('buffer is re-highlighted when force refreshed', function() + insert(text) exec_lua([[ bufnr = vim.api.nvim_get_current_buf() vim.api.nvim_win_set_buf(0, bufnr) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) ]]) - insert(text) - screen:expect { grid = [[ #include <iostream> | @@ -412,13 +412,14 @@ describe('semantic token highlighting', function() end) it('updates highlights with delta request on buffer change', function() + insert(text) + exec_lua([[ bufnr = vim.api.nvim_get_current_buf() vim.api.nvim_win_set_buf(0, bufnr) client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) ]]) - insert(text) screen:expect { grid = [[ #include <iostream> | @@ -597,6 +598,7 @@ describe('semantic token highlighting', function() end) it('does not send delta requests if not supported by server', function() + insert(text) exec_lua( [[ local legend, response, edit_response = ... @@ -625,7 +627,6 @@ describe('semantic token highlighting', function() edit_response ) - insert(text) screen:expect { grid = [[ #include <iostream> | @@ -1449,6 +1450,7 @@ int main() }, }) do it(test.it, function() + insert(test.text1) exec_lua(create_server_definition) exec_lua( [[ @@ -1485,8 +1487,6 @@ int main() test.response2 ) - insert(test.text1) - test.expected_screen1() local highlights = exec_lua([[ diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 50e9c0cc0f..b345a3288c 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -255,7 +255,7 @@ describe('LSP', function() return end local expected_handlers = { - { NIL, {}, { method = 'shutdown', bufnr = 1, client_id = 1 } }, + { NIL, {}, { method = 'shutdown', bufnr = 1, client_id = 1, version = 0 } }, { NIL, {}, { method = 'test', client_id = 1 } }, } test_rpc_server { @@ -948,7 +948,11 @@ describe('LSP', function() it('should forward ContentModified to callback', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, - { { code = -32801 }, NIL, { method = 'error_code_test', bufnr = 1, client_id = 1 } }, + { + { code = -32801 }, + NIL, + { method = 'error_code_test', bufnr = 1, client_id = 1, version = 0 }, + }, } local client --- @type vim.lsp.Client test_rpc_server { @@ -978,7 +982,7 @@ describe('LSP', function() it('should track pending requests to the language server', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, - { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, + { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1, version = 0 } }, } local client --- @type vim.lsp.Client test_rpc_server { @@ -1045,7 +1049,7 @@ describe('LSP', function() it('should clear pending and cancel requests on reply', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, - { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, + { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1, version = 0 } }, } local client --- @type vim.lsp.Client test_rpc_server { @@ -1084,7 +1088,7 @@ describe('LSP', function() it('should trigger LspRequest autocmd when requests table changes', function() local expected_handlers = { { NIL, {}, { method = 'finish', client_id = 1 } }, - { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1 } }, + { NIL, {}, { method = 'slow_request', bufnr = 1, client_id = 1, version = 0 } }, } local client --- @type vim.lsp.Client test_rpc_server { @@ -1364,6 +1368,7 @@ describe('LSP', function() }, bufnr = 2, client_id = 1, + version = 0, }, }, { NIL, {}, { method = 'start', client_id = 1 } }, diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 96abd9f543..365a4f8035 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -199,7 +199,7 @@ describe(':terminal buffer', function() {5:========== }| rows: 2, cols: 50 | {2: } | - {1:========== }| + {18:========== }| | ]]) @@ -340,7 +340,7 @@ describe(':terminal buffer', function() eq(termbuf, eval('g:termbuf')) end) - it('TermReqeust synchronization #27572', function() + it('TermRequest synchronization #27572', function() command('autocmd! nvim_terminal TermRequest') local term = exec_lua([[ _G.input = {} diff --git a/test/functional/terminal/clipboard_spec.lua b/test/functional/terminal/clipboard_spec.lua new file mode 100644 index 0000000000..4a1a0e29fd --- /dev/null +++ b/test/functional/terminal/clipboard_spec.lua @@ -0,0 +1,65 @@ +local t = require('test.testutil') +local n = require('test.functional.testnvim')() + +local eq = t.eq +local retry = t.retry + +local clear = n.clear +local fn = n.fn +local testprg = n.testprg +local exec_lua = n.exec_lua +local eval = n.eval + +describe(':terminal', function() + before_each(function() + clear() + + exec_lua([[ + local function clipboard(reg, type) + if type == 'copy' then + return function(lines) + local data = table.concat(lines, '\n') + vim.g.clipboard_data = data + end + end + + if type == 'paste' then + return function() + error() + end + end + + error('invalid type: ' .. type) + end + + vim.g.clipboard = { + name = 'Test', + copy = { + ['+'] = clipboard('+', 'copy'), + ['*'] = clipboard('*', 'copy'), + }, + paste = { + ['+'] = clipboard('+', 'paste'), + ['*'] = clipboard('*', 'paste'), + }, + } + ]]) + end) + + it('can write to the system clipboard', function() + eq('Test', eval('g:clipboard.name')) + + local text = 'Hello, world! This is some\nexample text\nthat spans multiple\nlines' + local encoded = exec_lua('return vim.base64.encode(...)', text) + + local function osc52(arg) + return string.format('\027]52;;%s\027\\', arg) + end + + fn.termopen({ testprg('shell-test'), '-t', osc52(encoded) }) + + retry(nil, 1000, function() + eq(text, exec_lua([[ return vim.g.clipboard_data ]])) + end) + end) +end) diff --git a/test/functional/terminal/mouse_spec.lua b/test/functional/terminal/mouse_spec.lua index ad98dfc6c3..476e2a5fe5 100644 --- a/test/functional/terminal/mouse_spec.lua +++ b/test/functional/terminal/mouse_spec.lua @@ -14,10 +14,12 @@ describe(':terminal mouse', function() before_each(function() clear() api.nvim_set_option_value('statusline', '==========', {}) - command('highlight StatusLine cterm=NONE') - command('highlight StatusLineNC cterm=NONE') - command('highlight VertSplit cterm=NONE') screen = tt.screen_setup() + command('highlight StatusLine NONE') + command('highlight StatusLineNC NONE') + command('highlight StatusLineTerm NONE') + command('highlight StatusLineTermNC NONE') + command('highlight VertSplit NONE') local lines = {} for i = 1, 30 do table.insert(lines, 'line' .. tostring(i)) diff --git a/test/functional/terminal/testutil.lua b/test/functional/terminal/testutil.lua index f3fc5d3f93..45c73b1dc6 100644 --- a/test/functional/terminal/testutil.lua +++ b/test/functional/terminal/testutil.lua @@ -92,6 +92,8 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts) api.nvim_command('highlight TermCursor cterm=reverse') api.nvim_command('highlight TermCursorNC ctermbg=11') + api.nvim_command('highlight StatusLineTerm ctermbg=2 ctermfg=0') + api.nvim_command('highlight StatusLineTermNC ctermbg=2 ctermfg=8') local screen = Screen.new(cols, 7 + extra_rows) screen:set_default_attr_ids({ @@ -111,6 +113,8 @@ local function screen_setup(extra_rows, command, cols, env, screen_opts) [14] = { underline = true, reverse = true, bold = true }, [15] = { underline = true, foreground = 12 }, [16] = { background = 248, foreground = 0 }, -- Visual in :terminal session + [17] = { background = 2, foreground = 0 }, -- StatusLineTerm + [18] = { background = 2, foreground = 8 }, -- StatusLineTermNC }) screen:attach(screen_opts or { rgb = false }) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 80df336cc4..5269af760a 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -1056,6 +1056,11 @@ describe('TUI', function() if is_ci('github') then pending('tty-test complains about not owning the terminal -- actions/runner#241') end + screen:set_default_attr_ids({ + [1] = { reverse = true }, -- focused cursor + [3] = { bold = true }, + [19] = { bold = true, background = 121, foreground = 0 }, -- StatusLineTerm + }) child_exec_lua('vim.o.statusline="^^^^^^^"') child_exec_lua('vim.cmd.terminal(...)', testprg('tty-test')) feed_data('i') @@ -1063,7 +1068,7 @@ describe('TUI', function() tty ready | {1: } | |*2 - {5:^^^^^^^ }| + {19:^^^^^^^ }| {3:-- TERMINAL --} |*2 ]]) feed_data('\027[200~') @@ -1073,7 +1078,7 @@ describe('TUI', function() tty ready | hallo{1: } | |*2 - {5:^^^^^^^ }| + {19:^^^^^^^ }| {3:-- TERMINAL --} |*2 ]]) end) @@ -1548,10 +1553,32 @@ describe('TUI', function() screen:set_rgb_cterm(true) screen:set_default_attr_ids({ [1] = { { reverse = true }, { reverse = true } }, - [2] = { { bold = true, reverse = true }, { bold = true, reverse = true } }, + [2] = { + { bold = true, background = Screen.colors.LightGreen, foreground = Screen.colors.Black }, + { bold = true }, + }, [3] = { { bold = true }, { bold = true } }, [4] = { { fg_indexed = true, foreground = tonumber('0xe0e000') }, { foreground = 3 } }, [5] = { { foreground = tonumber('0xff8000') }, {} }, + [6] = { + { + fg_indexed = true, + bg_indexed = true, + bold = true, + background = tonumber('0x66ff99'), + foreground = Screen.colors.Black, + }, + { bold = true, background = 121, foreground = 0 }, + }, + [7] = { + { + fg_indexed = true, + bg_indexed = true, + background = tonumber('0x66ff99'), + foreground = Screen.colors.Black, + }, + { background = 121, foreground = 0 }, + }, }) child_exec_lua('vim.o.statusline="^^^^^^^"') @@ -1586,7 +1613,7 @@ describe('TUI', function() {1:t}ty ready | {4:text}colortext | |*2 - {2:^^^^^^^ }| + {6:^^^^^^^}{7: }| :set notermguicolors | {3:-- TERMINAL --} | ]], @@ -1973,6 +2000,7 @@ describe('TUI', function() [3] = { bold = true }, [4] = { foreground = tonumber('0x4040ff'), fg_indexed = true }, [5] = { bold = true, reverse = true }, + [6] = { foreground = Screen.colors.White, background = Screen.colors.DarkGreen }, }) screen:attach() fn.termopen({ @@ -1998,7 +2026,7 @@ describe('TUI', function() {2:~ }│{4:~ }|*5 {2:~ }│{5:[No Name] 0,0-1 All}| {2:~ }│ | - {5:new }{1:{MATCH:<.*[/\]nvim }}| + {5:new }{6:{MATCH:<.*[/\]nvim }}| | ]]) end) diff --git a/test/functional/terminal/window_spec.lua b/test/functional/terminal/window_spec.lua index f85e26a66d..64534ca8dc 100644 --- a/test/functional/terminal/window_spec.lua +++ b/test/functional/terminal/window_spec.lua @@ -13,6 +13,26 @@ local skip = t.skip local is_os = t.is_os describe(':terminal window', function() + before_each(clear) + + it('sets local values of window options #29325', function() + command('setglobal wrap list') + command('terminal') + eq({ 0, 0, 1 }, eval('[&l:wrap, &wrap, &g:wrap]')) + eq({ 0, 0, 1 }, eval('[&l:list, &list, &g:list]')) + command('enew') + eq({ 1, 1, 1 }, eval('[&l:wrap, &wrap, &g:wrap]')) + eq({ 1, 1, 1 }, eval('[&l:list, &list, &g:list]')) + command('buffer #') + eq({ 0, 0, 1 }, eval('[&l:wrap, &wrap, &g:wrap]')) + eq({ 0, 0, 1 }, eval('[&l:list, &list, &g:list]')) + command('new') + eq({ 1, 1, 1 }, eval('[&l:wrap, &wrap, &g:wrap]')) + eq({ 1, 1, 1 }, eval('[&l:list, &list, &g:list]')) + end) +end) + +describe(':terminal window', function() local screen before_each(function() @@ -110,9 +130,9 @@ describe(':terminal window', function() ]]) feed_data('\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') screen:expect([[ - {7:++7 } | - {7:++8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| - {7:++9 }TUVWXYZ | + {7:++ 7 } | + {7:++ 8 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| + {7:++ 9 }TUVWXYZ | {7:++10 }abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR| {7:++11 }TUVWXYZrows: 6, cols: 44 | {7:++12 }{1: } | diff --git a/test/functional/terminal/window_split_tab_spec.lua b/test/functional/terminal/window_split_tab_spec.lua index 04d2e0bca7..ccd6a5218b 100644 --- a/test/functional/terminal/window_split_tab_spec.lua +++ b/test/functional/terminal/window_split_tab_spec.lua @@ -22,10 +22,12 @@ describe(':terminal', function() -- set the statusline to a constant value because of variables like pid -- and current directory and to improve visibility of splits api.nvim_set_option_value('statusline', '==========', {}) - command('highlight StatusLine cterm=NONE') - command('highlight StatusLineNC cterm=NONE') - command('highlight VertSplit cterm=NONE') screen = tt.screen_setup(3) + command('highlight StatusLine NONE') + command('highlight StatusLineNC NONE') + command('highlight StatusLineTerm NONE') + command('highlight StatusLineTermNC NONE') + command('highlight VertSplit NONE') end) after_each(function() diff --git a/test/functional/testnvim.lua b/test/functional/testnvim.lua index 746c7290ef..5a9e7f8c29 100644 --- a/test/functional/testnvim.lua +++ b/test/functional/testnvim.lua @@ -22,7 +22,7 @@ local runtime_set = 'set runtimepath^=./build/lib/nvim/' M.nvim_prog = (os.getenv('NVIM_PRG') or t.paths.test_build_dir .. '/bin/nvim') -- Default settings for the test session. M.nvim_set = ( - 'set shortmess+=IS background=light termguicolors noswapfile noautoindent startofline' + 'set shortmess+=IS background=light noswapfile noautoindent startofline' .. ' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' .. ' belloff= wildoptions-=pum joinspaces noshowcmd noruler nomore redrawdebug=invalid' ) diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 8bfceb8cce..619153724b 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -246,11 +246,11 @@ describe('ui/cursor', function() end end if m.hl_id then - m.hl_id = 64 + m.hl_id = 66 m.attr = { background = Screen.colors.DarkGray } end if m.id_lm then - m.id_lm = 69 + m.id_lm = 73 end end diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index cdaae6cfee..7aff47e50e 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -1532,9 +1532,9 @@ describe('float window', function() [2:----------------------------------------]|*6 [3:----------------------------------------]| ## grid 2 - {20:1}{19: }{20: }{22:^x}{21: }| - {14:2}{19: }{14: }{22:y} | - {14:3}{19: }{14: }{22: } | + {20: 1}{19: }{22:^x}{21: }| + {14: 2}{19: }{22:y} | + {14: 3}{19: }{22: } | {0:~ }|*3 ## grid 3 | @@ -1545,9 +1545,9 @@ describe('float window', function() ]], float_pos={[4] = {1001, "NW", 1, 4, 10, true}}} else screen:expect{grid=[[ - {20:1}{19: }{20: }{22:^x}{21: }| - {14:2}{19: }{14: }{22:y} | - {14:3}{19: }{14: }{22: } {15:x } | + {20: 1}{19: }{22:^x}{21: }| + {14: 2}{19: }{22:y} | + {14: 3}{19: }{22: } {15:x } | {0:~ }{15:y }{0: }| {0:~ }{15: }{0: }|*2 | diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index 8f8604eecb..4ea60194c2 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -1129,7 +1129,7 @@ describe("builtin popupmenu 'pumblend'", function() [10] = { foreground = tonumber('0x000002') }, }) screen:attach({ rgb = false }) - command('set notermguicolors pumblend=10') + command('set pumblend=10') insert([[ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor @@ -1177,6 +1177,8 @@ describe('builtin popupmenu', function() ks = { foreground = Screen.colors.Red, background = Screen.colors.Grey }, xn = { foreground = Screen.colors.White, background = Screen.colors.Magenta }, xs = { foreground = Screen.colors.Black, background = Screen.colors.Grey }, + mn = { foreground = Screen.colors.Blue, background = Screen.colors.Magenta }, + ms = { foreground = Screen.colors.Blue, background = Screen.colors.Grey }, }) screen:attach({ ext_multigrid = multigrid }) end) @@ -2528,6 +2530,7 @@ describe('builtin popupmenu', function() ]], } + -- oldtest: Test_wildmenu_pum_rightleft() feed('<tab>') screen:expect { grid = [[ @@ -4477,6 +4480,27 @@ describe('builtin popupmenu', function() feed('<Esc>') + command('set rightleft') + feed('/X<CR>:popup PopUp<CR>') + screen:expect([[ + evif ruof eerht owt eno| + evif ruof eerht{7:^X} owt eno dna| + {n: odnU }wt erom eno| + {1: }{n: }{1: ~}| + {1: }{n: etsaP }{1: ~}| + {1: }{n: }{1: ~}| + {1: }{n: droW tceleS }{1: ~}| + {1: }{n: ecnetneS tceleS }{1: ~}| + {1: }{n: hpargaraP tceleS }{1: ~}| + {1: }{n: eniL tceleS }{1: ~}| + {1: }{n: kcolB tceleS }{1: ~}| + {1: }{n: llA tceleS }{1: ~}| + {1: ~}|*7 + :popup PopUp | + ]]) + feed('<Esc>') + command('set norightleft') + -- Set an <expr> mapping to change a menu entry while it's displayed. -- The text should not change but the command does. -- Also verify that "changed" shows up, which means the mapping triggered. @@ -4533,6 +4557,55 @@ describe('builtin popupmenu', function() feed('<Esc>') end) + -- oldtest: Test_mouse_popup_position() + it('position of right-click menu when clicking near edge', function() + screen:try_resize(50, 20) + exec([[ + set mousemodel=popup_setpos + aunmenu * + source $VIMRUNTIME/menu.vim + call setline(1, join(range(20))) + ]]) + + api.nvim_input_mouse('right', 'press', '', 0, 0, 45 - 1) + screen:expect([[ + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ^18 19 | + {1:~ }{n: Undo }| + {1:~ }{n: }| + {1:~ }{n: Paste }| + {1:~ }{n: }| + {1:~ }{n: Select Word }| + {1:~ }{n: Select Sentence }| + {1:~ }{n: Select Paragraph}| + {1:~ }{n: Select Line }| + {1:~ }{n: Select Block }| + {1:~ }{n: Select All }| + {1:~ }|*8 + | + ]]) + feed('<Esc>') + + command('set rightleft') + api.nvim_input_mouse('right', 'press', '', 0, 0, 50 - 45) + screen:expect([[ + 91 8^1 71 61 51 41 31 21 11 01 9 8 7 6 5 4 3 2 1 0| + {n: odnU }{1: ~}| + {n: }{1: ~}| + {n: etsaP }{1: ~}| + {n: }{1: ~}| + {n: droW tceleS }{1: ~}| + {n: ecnetneS tceleS }{1: ~}| + {n:hpargaraP tceleS }{1: ~}| + {n: eniL tceleS }{1: ~}| + {n: kcolB tceleS }{1: ~}| + {n: llA tceleS }{1: ~}| + {1: ~}|*8 + | + ]]) + feed('<Esc>') + command('set norightleft') + end) + describe('"kind" and "menu"', function() before_each(function() screen:try_resize(30, 8) @@ -4585,6 +4658,167 @@ describe('builtin popupmenu', function() ]]) end) end) + + -- oldtest: Test_pum_highlights_match() + it('can highlight matched text', function() + exec([[ + func Omni_test(findstart, base) + if a:findstart + return col(".") + endif + return { + \ 'words': [ + \ { 'word': 'foo', 'kind': 'fookind' }, + \ { 'word': 'foofoo', 'kind': 'fookind' }, + \ { 'word': 'foobar', 'kind': 'fookind' }, + \ { 'word': 'fooBaz', 'kind': 'fookind' }, + \ { 'word': 'foobala', 'kind': 'fookind' }, + \ { 'word': '你好' }, + \ { 'word': '你好吗' }, + \ { 'word': '你不好吗' }, + \ { 'word': '你可好吗' }, + \]} + endfunc + set omnifunc=Omni_test + set completeopt=menu,noinsert,fuzzy + hi PmenuMatchSel guifg=Blue guibg=Grey + hi PmenuMatch guifg=Blue guibg=Magenta + ]]) + feed('i<C-X><C-O>') + local pum_start = [[ + ^ | + {s:foo fookind }{1: }| + {n:foofoo fookind }{1: }| + {n:foobar fookind }{1: }| + {n:fooBaz fookind }{1: }| + {n:foobala fookind }{1: }| + {n:你好 }{1: }| + {n:你好吗 }{1: }| + {n:你不好吗 }{1: }| + {n:你可好吗 }{1: }| + {1:~ }|*9 + {2:-- }{5:match 1 of 9} | + ]] + screen:expect(pum_start) + feed('fo') + screen:expect([[ + fo^ | + {ms:fo}{s:o fookind }{1: }| + {mn:fo}{n:ofoo fookind }{1: }| + {mn:fo}{n:obar fookind }{1: }| + {mn:fo}{n:oBaz fookind }{1: }| + {mn:fo}{n:obala fookind }{1: }| + {1:~ }|*13 + {2:-- }{5:match 1 of 9} | + ]]) + feed('<Esc>S<C-X><C-O>') + screen:expect(pum_start) + feed('你') + screen:expect([[ + 你^ | + {ms:你}{s:好 }{1: }| + {mn:你}{n:好吗 }{1: }| + {mn:你}{n:不好吗 }{1: }| + {mn:你}{n:可好吗 }{1: }| + {1:~ }|*14 + {2:-- }{5:match 1 of 9} | + ]]) + feed('吗') + screen:expect([[ + 你吗^ | + {ms:你}{s:好}{ms:吗}{s: }{1: }| + {mn:你}{n:不好}{mn:吗}{n: }{1: }| + {mn:你}{n:可好}{mn:吗}{n: }{1: }| + {1:~ }|*15 + {2:-- }{5:match 1 of 9} | + ]]) + feed('<C-E><Esc>') + + command('set rightleft') + feed('S<C-X><C-O>') + local pum_start_rl = [[ + ^ | + {1: }{s: dnikoof oof}| + {1: }{n: dnikoof oofoof}| + {1: }{n: dnikoof raboof}| + {1: }{n: dnikoof zaBoof}| + {1: }{n: dnikoof alaboof}| + {1: }{n: 好你}| + {1: }{n: 吗好你}| + {1: }{n: 吗好不你}| + {1: }{n: 吗好可你}| + {1: ~}|*9 + {2:-- }{5:match 1 of 9} | + ]] + screen:expect(pum_start_rl) + feed('fo') + screen:expect([[ + ^ of| + {1: }{s: dnikoof o}{ms:of}| + {1: }{n: dnikoof oofo}{mn:of}| + {1: }{n: dnikoof rabo}{mn:of}| + {1: }{n: dnikoof zaBo}{mn:of}| + {1: }{n: dnikoof alabo}{mn:of}| + {1: ~}|*13 + {2:-- }{5:match 1 of 9} | + ]]) + feed('<Esc>S<C-X><C-O>') + screen:expect(pum_start_rl) + feed('你') + screen:expect([[ + ^ 你| + {1: }{s: 好}{ms:你}| + {1: }{n: 吗好}{mn:你}| + {1: }{n: 吗好不}{mn:你}| + {1: }{n: 吗好可}{mn:你}| + {1: ~}|*14 + {2:-- }{5:match 1 of 9} | + ]]) + feed('吗') + screen:expect([[ + ^ 吗你| + {1: }{s: }{ms:吗}{s:好}{ms:你}| + {1: }{n: }{mn:吗}{n:好不}{mn:你}| + {1: }{n: }{mn:吗}{n:好可}{mn:你}| + {1: ~}|*15 + {2:-- }{5:match 1 of 9} | + ]]) + feed('<C-E><Esc>') + command('set norightleft') + + command('set completeopt-=fuzzy') + feed('S<C-X><C-O>') + screen:expect(pum_start) + feed('fo') + screen:expect([[ + fo^ | + {ms:fo}{s:o fookind }{1: }| + {mn:fo}{n:ofoo fookind }{1: }| + {mn:fo}{n:obar fookind }{1: }| + {mn:fo}{n:oBaz fookind }{1: }| + {mn:fo}{n:obala fookind }{1: }| + {1:~ }|*13 + {2:-- }{5:match 1 of 9} | + ]]) + feed('<C-E><Esc>') + + command('set rightleft') + feed('S<C-X><C-O>') + screen:expect(pum_start_rl) + feed('fo') + screen:expect([[ + ^ of| + {1: }{s: dnikoof o}{ms:of}| + {1: }{n: dnikoof oofo}{mn:of}| + {1: }{n: dnikoof rabo}{mn:of}| + {1: }{n: dnikoof zaBo}{mn:of}| + {1: }{n: dnikoof alabo}{mn:of}| + {1: ~}|*13 + {2:-- }{5:match 1 of 9} | + ]]) + feed('<C-E><Esc>') + command('set norightleft') + end) end end diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 8bdf528412..493493da60 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -345,12 +345,19 @@ describe('search highlighting', function() bar baz foo bar foo baz]]) feed('/foo') + screen:set_default_attr_ids({ + [1] = { bold = true, foreground = Screen.colors.Blue }, + [2] = { background = Screen.colors.Yellow }, -- Search + [3] = { reverse = true }, + [4] = { bold = true, reverse = true }, + [5] = { foreground = Screen.colors.White, background = Screen.colors.DarkGreen }, + }) screen:expect([[ {3:foo} bar baz │{MATCH:%d+}: {2:foo}{MATCH:%s+}| bar baz {2:foo} │{MATCH:%d+}: {2:foo}{MATCH:%s+}| bar {2:foo} baz │{MATCH:%d+}: {2:foo}{MATCH:%s+}| {1:~ }│{MATCH:.*}|*2 - {5:[No Name] [+] }{3:term }| + {4:[No Name] [+] }{5:term }| /foo^ | ]]) end) diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua index b353b3738a..6f4bf5695d 100644 --- a/test/functional/ui/sign_spec.lua +++ b/test/functional/ui/sign_spec.lua @@ -577,4 +577,34 @@ describe('Signs', function() ]]) eq({}, eval('sign_getdefined()')) end) + + it('no crash when unplacing signs beyond end of buffer', function() + exec([[ + sign define S1 text=S1 + sign define S2 text=S2 + sign place 1 line=8 name=S1 + sign place 2 line=9 name=S2 + ]]) + -- Now placed at end of buffer + local s1 = { + grid = [[ + S2^ | + {0:~ }|*12 + | + ]], + } + screen:expect(s1) + -- Signcolumn tracking used to not count signs placed beyond end of buffer here + exec('set signcolumn=auto:9') + screen:expect({ + grid = [[ + S2S1^ | + {0:~ }|*12 + | + ]], + }) + -- Unplacing the sign does not crash by decrementing tracked signs below zero + exec('sign unplace 1') + screen:expect(s1) + end) end) diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index faf94bccbe..c388d347e2 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -94,46 +94,80 @@ describe('statuscolumn', function() end) it("works with 'number' and 'relativenumber'", function() - command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) - screen:expect([[ - {8:4 │}aaaaa | - {8:5 │}aaaaa | - {8:6 │}aaaaa | - {8:7 │}aaaaa | - {8:8 │}^aaaaa | - {8:9 │}aaaaa | - {8:10│}aaaaa | - {8:11│}aaaaa | - {8:12│}aaaaa | - {8:13│}aaaaa | - {8:14│}aaaaa | - {8:15│}aaaaa | - {8:16│}aaaaa | - | - ]]) - command([[set stc=%l%=%{&rnu?'\ ':''}%r│]]) + screen:expect([[ + {8: 4 }aaaaa | + {8: 5 }aaaaa | + {8: 6 }aaaaa | + {8: 7 }aaaaa | + {8: 8 }^aaaaa | + {8: 9 }aaaaa | + {8:10 }aaaaa | + {8:11 }aaaaa | + {8:12 }aaaaa | + {8:13 }aaaaa | + {8:14 }aaaaa | + {8:15 }aaaaa | + {8:16 }aaaaa | + | + ]]) + command([[set stc=%l\ ]]) screen:expect_unchanged() - command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) command('set relativenumber') screen:expect([[ - {8:4 4│}aaaaa | - {8:5 3│}aaaaa | - {8:6 2│}aaaaa | - {8:7 1│}aaaaa | - {8:8 0│}^aaaaa | - {8:9 1│}aaaaa | - {8:10 2│}aaaaa | - {8:11 3│}aaaaa | - {8:12 4│}aaaaa | - {8:13 5│}aaaaa | - {8:14 6│}aaaaa | - {8:15 7│}aaaaa | - {8:16 8│}aaaaa | - | - ]]) - command([[set stc=%l%=%{&rnu?'\ ':''}%r│]]) + {8: 4 }aaaaa | + {8: 3 }aaaaa | + {8: 2 }aaaaa | + {8: 1 }aaaaa | + {8:8 }^aaaaa | + {8: 1 }aaaaa | + {8: 2 }aaaaa | + {8: 3 }aaaaa | + {8: 4 }aaaaa | + {8: 5 }aaaaa | + {8: 6 }aaaaa | + {8: 7 }aaaaa | + {8: 8 }aaaaa | + | + ]]) + command('set stc=') + screen:expect_unchanged() + command([[set nonu stc=%l\ ]]) + screen:expect([[ + {8: 4 }aaaaa | + {8: 3 }aaaaa | + {8: 2 }aaaaa | + {8: 1 }aaaaa | + {8: 0 }^aaaaa | + {8: 1 }aaaaa | + {8: 2 }aaaaa | + {8: 3 }aaaaa | + {8: 4 }aaaaa | + {8: 5 }aaaaa | + {8: 6 }aaaaa | + {8: 7 }aaaaa | + {8: 8 }aaaaa | + | + ]]) + command('set nuw=1 stc=') screen:expect_unchanged() - command([[set stc=%{&nu?v:lnum:''}%=%{&rnu?'\ '.v:relnum:''}│]]) + -- Correct alignment with items before and after number column + command([[set nu stc=foo\ %l\ bar]]) + screen:expect([[ + {8:foo 4 bar}aaaaa | + {8:foo 3 bar}aaaaa | + {8:foo 2 bar}aaaaa | + {8:foo 1 bar}aaaaa | + {8:foo 8 bar}^aaaaa | + {8:foo 1 bar}aaaaa | + {8:foo 2 bar}aaaaa | + {8:foo 3 bar}aaaaa | + {8:foo 4 bar}aaaaa | + {8:foo 5 bar}aaaaa | + {8:foo 6 bar}aaaaa | + {8:foo 7 bar}aaaaa | + {8:foo 8 bar}aaaaa | + | + ]]) end) it("works with highlighted 'statuscolumn'", function() @@ -160,36 +194,36 @@ describe('statuscolumn', function() ]]) command('set relativenumber') screen:expect([[ - {1:4 }{8: 4│}aaaaa | - {1:5 3}{8:│}aaaaa | - {1:6 }{8: 2│}aaaaa | - {1:7 1}{8:│}aaaaa | - {1:8 }{8: 0│}^aaaaa | - {1:9 1}{8:│}aaaaa | - {1:10}{8: 2│}aaaaa | - {1:11 3}{8:│}aaaaa | - {1:12}{8: 4│}aaaaa | - {1:13 5}{8:│}aaaaa | - {1:14}{8: 6│}aaaaa | - {1:15 7}{8:│}aaaaa | - {1:16}{8: 8│}aaaaa | + {1:4 }{8: 4│}aaaaa | + {1:5 3}{8:│}aaaaa | + {1:6 }{8: 2│}aaaaa | + {1:7 1}{8:│}aaaaa | + {1:8 }{8: 0│}^aaaaa | + {1:9 1}{8:│}aaaaa | + {1:10 }{8: 2│}aaaaa | + {1:11 3}{8:│}aaaaa | + {1:12 }{8: 4│}aaaaa | + {1:13 5}{8:│}aaaaa | + {1:14 }{8: 6│}aaaaa | + {1:15 7}{8:│}aaaaa | + {1:16 }{8: 8│}aaaaa | | ]]) command('set nonumber') screen:expect([[ - {8:4│}aaaaa | - {1:3}{8:│}aaaaa | - {8:2│}aaaaa | - {1:1}{8:│}aaaaa | - {8:0│}^aaaaa | - {1:1}{8:│}aaaaa | - {8:2│}aaaaa | - {1:3}{8:│}aaaaa | - {8:4│}aaaaa | - {1:5}{8:│}aaaaa | - {8:6│}aaaaa | - {1:7}{8:│}aaaaa | - {8:8│}aaaaa | + {1: }{8:4│}aaaaa | + {1: 3}{8:│}aaaaa | + {1: }{8:2│}aaaaa | + {1: 1}{8:│}aaaaa | + {1: }{8:0│}^aaaaa | + {1: 1}{8:│}aaaaa | + {1: }{8:2│}aaaaa | + {1: 3}{8:│}aaaaa | + {1: }{8:4│}aaaaa | + {1: 5}{8:│}aaaaa | + {1: }{8:6│}aaaaa | + {1: 7}{8:│}aaaaa | + {1: }{8:8│}aaaaa | | ]]) end) @@ -305,36 +339,36 @@ describe('statuscolumn', function() -- v:relnum is the same value on wrapped lines command([[set stc=%C%=\ %{v:relnum}│%s\ ]]) screen:expect([[ - {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: 4│}{2: }{1: }aaaaaa | - {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: 3│}{2: }{1: }aaaaaa | - {2: }{1: 2│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: 2│}{2: }{1: }aaaaaa | - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: 1│}{2: }{1: }aaaaaa | - {2:+}{4: 0│}{2: }{4: }{6:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: 1│}{2: }{1: }aaaaaa | - {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: 2│}{2: }{1: }aaaaaa | + {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 4│}{2: }{1: }aaaaaaa | + {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 3│}{2: }{1: }aaaaaaa | + {2: }{1: 2│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 2│}{2: }{1: }aaaaaaa | + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 1│}{2: }{1: }aaaaaaa | + {2:+}{4: 0│}{2: }{4: }{6:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 1│}{2: }{1: }aaaaaaa | + {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: 2│}{2: }{1: }aaaaaaa | | ]]) command([[set stc=%C%=\ %{v:virtnum?'':v:relnum}│%s\ ]]) screen:expect([[ - {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 2│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaa | - {2:+}{4: 0│}{2: }{4: }{6:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaa | - {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaa | + {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaa | + {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaa | + {2: }{1: 2│}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaa | + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaa | + {2:+}{4: 0│}{2: }{4: }{6:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaa | + {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaa | | ]]) -- Up to 9 signs in a line @@ -347,75 +381,75 @@ describe('statuscolumn', function() command('sign place 10 line=6 name=piet2 buffer=1') command('sign place 11 line=6 name=piet1 buffer=1') screen:expect([[ - {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 2│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | - {2:+}{4: 0│}{2: }{4: }{6:^+-- 1 line: aaaaaaaaaaaaaaaaa}| - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaa | + {2: }{1: 4│>>}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 2│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaaa | + {2:+}{4: 0│}{2: }{4: }{6:^+-- 1 line: aaaaaaaaaaaaaaaa}| + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }{1: │}{2: }{1: }aaaaaaaaaaaaaaaaaaaaa | | ]]) -- Also test fold and sign column when 'cpoptions' includes "n" command('set cpoptions+=n') feed('Hgjg0') screen:expect([[ - {2: }{4: 0│}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: }{5:^aaaaaaaaaaaaaaaaaaaa }| - {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 2│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2:+}{1: 4│}{2: }{1: }{3:+-- 1 line: aaaaaaaaaaaaaaaaa}| - {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | + {2: }{4: 0│}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {2: }{5:^aaaaaaaaaaaaaaaaaaaaa }| + {2: }{1: 3│}{0:>!}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 2│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2:+}{1: 4│}{2: }{1: }{3:+-- 1 line: aaaaaaaaaaaaaaaa}| + {2: }{1: 1│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | | ]]) command('set breakindent') command('sign unplace 2') feed('J2gjg0') screen:expect([[ - {2: }{4: 0│}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: } {5:aaaaaaaaaaaaaaaaaaaa aaaaaaaaa}| - {2: } {5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: } {5:^aaaaaaaaaaa }| - {2: }{1: 1│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: } aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: } aaaaaaaaaaaaaaaaaaaa | - {2:+}{1: 3│}{2: }{1: }{3:+-- 1 line: aaaaaaaaaaaaaaaaa}| - {2: }{1: 4│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: } aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 5│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: } aaaaaaaaaaaaaaaaaaaa | + {2: }{4: 0│}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {2: } {5:aaaaaaaaaaaaaaaaaaaaa aaaaaaa}| + {2: } {5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {2: } {5:^aaaaaaaaaaaaaa }| + {2: }{1: 1│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: } aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: } aaaaaaaaaaaaaaaaaaaaa | + {2:+}{1: 3│}{2: }{1: }{3:+-- 1 line: aaaaaaaaaaaaaaaa}| + {2: }{1: 4│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: } aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 5│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: } aaaaaaaaaaaaaaaaaaaaa | | ]]) command('set nobreakindent') feed('$g0') screen:expect([[ - {2: }{4: 0│}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: }{5:aaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaa}| + {2: }{4: 0│}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {2: }{5:aaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa}| {2: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| - {2: }{5:^aaa }| - {2: }{1: 1│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2:+}{1: 3│}{2: }{1: }{3:+-- 1 line: aaaaaaaaaaaaaaaaa}| - {2: }{1: 4│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | - {2: }{1: 5│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| - {2: }aaaaaaaaaaaaaaaaaaaa | + {2: }{5:^aaaa }| + {2: }{1: 1│>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>>}{0:>!}{1:>> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 2│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2:+}{1: 3│}{2: }{1: }{3:+-- 1 line: aaaaaaaaaaaaaaaa}| + {2: }{1: 4│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | + {2: }{1: 5│}{2: }{1: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {2: }aaaaaaaaaaaaaaaaaaaaa | | ]]) command('silent undo') @@ -427,7 +461,23 @@ describe('statuscolumn', function() virt_lines_above = true, virt_lines = {{{"virt_line above", ""}}} }) vim.api.nvim_buf_set_extmark(0, ns, 4, 0, { virt_lines = {{{"virt_line", ""}}} }) ]]) - command('set foldcolumn=0 signcolumn=no') + command('set foldcolumn=0 signcolumn=number stc=%l') + screen:expect([[ + {1:>>}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1: 5}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1: }virt_line | + {1: }virt_line above | + {1:>>}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1: 7}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {4: 8}{6:^+-- 1 line: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {1: 9}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1:10}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1:11}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1:12}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1:13}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + {1:14}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + | + ]]) command( [[set stc=%{v:virtnum<0?'virtual':(!v:virtnum?'buffer':'wrapped')}%=%{'\ '.v:virtnum.'\ '.v:lnum}]] ) @@ -533,8 +583,8 @@ describe('statuscolumn', function() command([[set stc=%6s\ %l]]) exec_lua('vim.api.nvim_buf_set_extmark(0, ns, 7, 0, {sign_text = "𒀀"})') screen:expect([[ - {8: 𒀀 8 }^aaaaa | - {8: }{7: }{8: 9 }aaaaa | + {8: 𒀀 8}^aaaaa | + {8: }{7: }{8: 9}aaaaa | | ]]) end) @@ -644,26 +694,26 @@ describe('statuscolumn', function() -- clicking an item does not drag mouse api.nvim_input_mouse('left', 'press', '', 0, 0, 0) screen:expect([[ - {0:8 }^aaaaa | + {0: 8}^aaaaa | {1: Echo } | ]]) api.nvim_input_mouse('left', 'press', '', 0, 1, 5) api.nvim_input_mouse('left', 'release', '', 0, 1, 5) screen:expect([[ - {0:8 }^aaaaa | + {0: 8}^aaaaa | 0 1 l 8 | ]]) command('echo') -- clicking outside to close the menu does not drag mouse api.nvim_input_mouse('left', 'press', '', 0, 0, 0) screen:expect([[ - {0:8 }^aaaaa | + {0: 8}^aaaaa | {1: Echo } | ]]) api.nvim_input_mouse('left', 'press', '', 0, 0, 10) api.nvim_input_mouse('left', 'release', '', 0, 0, 10) screen:expect([[ - {0:8 }^aaaaa | + {0: 8}^aaaaa | | ]]) end) @@ -749,9 +799,9 @@ describe('statuscolumn', function() it('works with cmdwin', function() feed(':set stc=%l<CR>q:k$') screen:expect([[ - {8:7 }aaaaa | - {8:8 }aaaaa | - {8:9 }aaaaa | + {8: 7}aaaaa | + {8: 8}aaaaa | + {8: 9}aaaaa | {8:10}aaaaa | {2:[No Name] [+] }| {1::}{8:1}set stc=%^l | @@ -899,16 +949,16 @@ describe('statuscolumn', function() command([[set spell stc=%l\ ]]) command('call setline(8, "This is a line that contains ᶏ multibyte character.")') screen:expect([[ - {8:8 }^This is a line that contains {31:ᶏ}| + {8: 8 }^This is a line that contains {31:ᶏ}| {8: } {31:multibyte} character. | - {8:9 }{31:aaaaa} | + {8: 9 }{31:aaaaa} | | ]]) end) it('line increase properly redraws buffer text with relativenumber #27709', function() screen:try_resize(33, 4) - command([[set rnu nuw=3 stc=%l\ ]]) + command([[set rnu nuw=3 stc=%{v:lnum}\ ]]) command('call setline(1, range(1, 99))') feed('Gyyp') screen:expect([[ diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 0feec6bd03..6299852a69 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -199,9 +199,13 @@ describe("'wildmenu'", function() feed((':terminal "%s" REP 5000 !terminal_output!<cr>'):format(testprg('shell-test'))) feed('G') -- Follow :terminal output. feed([[:sign <Tab>]]) -- Invoke wildmenu. + screen:set_default_attr_ids { + [31] = { foreground = Screen.colors.Black, background = Screen.colors.Yellow }, + [32] = { bold = true, foreground = Screen.colors.White, background = Screen.colors.DarkGreen }, + } -- NB: in earlier versions terminal output was redrawn during cmdline mode. -- For now just assert that the screen remains unchanged. - screen:expect { any = '{31:define}{3: jump list > }|\n:sign define^ |' } + screen:expect { any = '{31:define}{32: jump list > }|\n:sign define^ |' } screen:expect_unchanged() -- cmdline CTRL-D display should also be preserved. @@ -259,9 +263,13 @@ describe("'wildmenu'", function() feed([[<C-\><C-N>]]) feed([[:<Tab>]]) -- Invoke wildmenu. + screen:set_default_attr_ids { + [31] = { foreground = Screen.colors.Black, background = Screen.colors.Yellow }, + [32] = { bold = true, foreground = Screen.colors.White, background = Screen.colors.DarkGreen }, + } -- Check only the last 2 lines, because the shell output is -- system-dependent. - screen:expect { any = '{31:!}{3: # & < = > @ > }|\n:!^' } + screen:expect { any = '{31:!}{32: # & < = > @ > }|\n:!^' } -- Because this test verifies a _lack_ of activity, we must wait the full timeout. -- So make it reasonable. screen:expect_unchanged(false, 1000) diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 443539fbfd..f83f782817 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -2905,6 +2905,24 @@ func Test_wildmenu_pum_odd_wildchar() call StopVimInTerminal(buf) endfunc +" Test that 'rightleft' should not affect cmdline completion popup menu. +func Test_wildmenu_pum_rightleft() + CheckFeature rightleft + CheckScreendump + + let lines =<< trim END + set wildoptions=pum + set rightleft + END + call writefile(lines, 'Xwildmenu_pum_rl', 'D') + let buf = RunVimInTerminal('-S Xwildmenu_pum_rl', #{rows: 10, cols: 50}) + + call term_sendkeys(buf, ":sign \<Tab>") + call VerifyScreenDump(buf, 'Test_wildmenu_pum_rl', {}) + + call StopVimInTerminal(buf) +endfunc + " Test for completion after a :substitute command followed by a pipe (|) " character func Test_cmdline_complete_substitute() diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index fa62765398..ec9d88d3bf 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -670,6 +670,7 @@ func s:GetFilenameChecks() abort \ 'smith': ['file.smt', 'file.smith'], \ 'smithy': ['file.smithy'], \ 'sml': ['file.sml'], + \ 'snakemake': ['file.smk', 'Snakefile'], \ 'snobol4': ['file.sno', 'file.spt'], \ 'solidity': ['file.sol'], \ 'solution': ['file.sln'], @@ -991,6 +992,7 @@ func s:GetScriptChecks() abort \ ['#!/path/regina']], \ 'janet': [['#!/path/janet']], \ 'dart': [['#!/path/dart']], + \ 'vim': [['#!/path/vim']], \ } endfunc diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim index fa9ae63381..81e843ba8d 100644 --- a/test/old/testdir/test_functions.vim +++ b/test/old/testdir/test_functions.vim @@ -3448,6 +3448,33 @@ func Test_glob() call assert_fails("call glob('*', 0, {})", 'E728:') endfunc +func Test_glob2() + call mkdir('[XglobDir]', 'R') + call mkdir('abc[glob]def', 'R') + + call writefile(['glob'], '[XglobDir]/Xglob') + call writefile(['glob'], 'abc[glob]def/Xglob') + if has("unix") + call assert_equal([], (glob('[XglobDir]/*', 0, 1))) + call assert_equal([], (glob('abc[glob]def/*', 0, 1))) + call assert_equal(['[XglobDir]/Xglob'], (glob('\[XglobDir]/*', 0, 1))) + call assert_equal(['abc[glob]def/Xglob'], (glob('abc\[glob]def/*', 0, 1))) + elseif has("win32") + let _sl=&shellslash + call assert_equal([], (glob('[XglobDir]\*', 0, 1))) + call assert_equal([], (glob('abc[glob]def\*', 0, 1))) + call assert_equal([], (glob('\[XglobDir]\*', 0, 1))) + call assert_equal([], (glob('abc\[glob]def\*', 0, 1))) + set noshellslash + call assert_equal(['[XglobDir]\Xglob'], (glob('[[]XglobDir]/*', 0, 1))) + call assert_equal(['abc[glob]def\Xglob'], (glob('abc[[]glob]def/*', 0, 1))) + set shellslash + call assert_equal(['[XglobDir]/Xglob'], (glob('[[]XglobDir]/*', 0, 1))) + call assert_equal(['abc[glob]def/Xglob'], (glob('abc[[]glob]def/*', 0, 1))) + let &shellslash=_sl + endif +endfunc + " Test for browse() func Test_browse() CheckFeature browse diff --git a/test/old/testdir/test_ins_complete.vim b/test/old/testdir/test_ins_complete.vim index fd77da67f8..0f974ebb5b 100644 --- a/test/old/testdir/test_ins_complete.vim +++ b/test/old/testdir/test_ins_complete.vim @@ -2620,6 +2620,13 @@ func Test_complete_fuzzy_match() call feedkeys("S\<C-x>\<C-o>fb\<C-p>\<C-p>\<C-p>\<C-p>", 'tx') call assert_equal('fooBaz', g:word) + func Comp() + call complete(col('.'), ["fooBaz", "foobar", "foobala"]) + return '' + endfunc + call feedkeys("i\<C-R>=Comp()\<CR>", 'tx') + call assert_equal('fooBaz', g:word) + " respect noselect set completeopt+=noselect call feedkeys("S\<C-x>\<C-o>fb", 'tx') @@ -2627,6 +2634,19 @@ func Test_complete_fuzzy_match() call feedkeys("S\<C-x>\<C-o>fb\<C-n>", 'tx') call assert_equal('fooBaz', g:word) + " avoid breaking default completion behavior + set completeopt=fuzzy,menu + call setline(1, ['hello help hero h']) + " Use "!" flag of feedkeys() so that ex_normal_busy is not set and + " ins_compl_check_keys() is not skipped. + " Add a "0" after the <Esc> to avoid waiting for an escape sequence. + call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!') + call assert_equal('hello help hero hello', getline('.')) + set completeopt+=noinsert + call setline(1, ['hello help hero h']) + call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!') + call assert_equal('hello help hero h', getline('.')) + " clean up set omnifunc= bw! @@ -2635,8 +2655,29 @@ func Test_complete_fuzzy_match() augroup! AAAAA_Group delfunc OnPumChange delfunc Omni_test + delfunc Comp unlet g:item unlet g:word endfunc +" Check that tie breaking is stable for completeopt+=fuzzy (which should +" behave the same on different platforms). +func Test_complete_fuzzy_match_tie() + new + set completeopt+=fuzzy,noselect + call setline(1, ['aaabbccc', 'aaabbCCC', 'aaabbcccc', 'aaabbCCCC', '']) + + call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-Y>", 'tx') + call assert_equal('aaabbccc', getline('.')) + call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-N>\<C-Y>", 'tx') + call assert_equal('aaabbCCC', getline('.')) + call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-N>\<C-N>\<C-Y>", 'tx') + call assert_equal('aaabbcccc', getline('.')) + call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-N>\<C-N>\<C-N>\<C-Y>", 'tx') + call assert_equal('aaabbCCCC', getline('.')) + + bwipe! + set completeopt& +endfunc + " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim index 5ae2ed5ed5..6c14fb3a6e 100644 --- a/test/old/testdir/test_popup.vim +++ b/test/old/testdir/test_popup.vim @@ -910,6 +910,13 @@ func Test_popup_command_dump() call term_sendkeys(buf, "\<Esc>") + if has('rightleft') + call term_sendkeys(buf, ":set rightleft\<CR>") + call term_sendkeys(buf, "/X\<CR>:popup PopUp\<CR>") + call VerifyScreenDump(buf, 'Test_popup_command_rl', {}) + call term_sendkeys(buf, "\<Esc>:set norightleft\<CR>") + endif + " Set a timer to change a menu entry while it's displayed. The text should " not change but the command does. Making the screendump also verifies that " "changed" shows up, which means the timer triggered. @@ -932,6 +939,37 @@ func Test_popup_command_dump() call StopVimInTerminal(buf) endfunc +" Test position of right-click menu when clicking near window edge. +func Test_mouse_popup_position() + CheckFeature menu + CheckScreendump + + let script =<< trim END + set mousemodel=popup_setpos + source $VIMRUNTIME/menu.vim + call setline(1, join(range(20))) + func Trigger(col) + call test_setmouse(1, a:col) + call feedkeys("\<RightMouse>", 't') + endfunc + END + call writefile(script, 'XmousePopupPosition', 'D') + let buf = RunVimInTerminal('-S XmousePopupPosition', #{rows: 20, cols: 50}) + + call term_sendkeys(buf, ":call Trigger(45)\<CR>") + call VerifyScreenDump(buf, 'Test_mouse_popup_position_01', {}) + call term_sendkeys(buf, "\<Esc>") + + if has('rightleft') + call term_sendkeys(buf, ":set rightleft\<CR>") + call term_sendkeys(buf, ":call Trigger(50 + 1 - 45)\<CR>") + call VerifyScreenDump(buf, 'Test_mouse_popup_position_02', {}) + call term_sendkeys(buf, "\<Esc>:set norightleft\<CR>") + endif + + call StopVimInTerminal(buf) +endfunc + func Test_popup_complete_backwards() new call setline(1, ['Post', 'Port', 'Po']) @@ -1348,4 +1386,92 @@ func Test_pum_highlights_custom() call StopVimInTerminal(buf) endfunc +" Test match relate highlight group in pmenu +func Test_pum_highlights_match() + CheckScreendump + let lines =<< trim END + func Omni_test(findstart, base) + if a:findstart + return col(".") + endif + return { + \ 'words': [ + \ { 'word': 'foo', 'kind': 'fookind' }, + \ { 'word': 'foofoo', 'kind': 'fookind' }, + \ { 'word': 'foobar', 'kind': 'fookind' }, + \ { 'word': 'fooBaz', 'kind': 'fookind' }, + \ { 'word': 'foobala', 'kind': 'fookind' }, + \ { 'word': '你好' }, + \ { 'word': '你好吗' }, + \ { 'word': '你不好吗' }, + \ { 'word': '你可好吗' }, + \]} + endfunc + set omnifunc=Omni_test + set completeopt=menu,noinsert,fuzzy + hi PmenuMatchSel ctermfg=6 ctermbg=7 + hi PmenuMatch ctermfg=4 ctermbg=225 + END + call writefile(lines, 'Xscript', 'D') + let buf = RunVimInTerminal('-S Xscript', {}) + call TermWait(buf) + call term_sendkeys(buf, "i\<C-X>\<C-O>") + call TermWait(buf, 50) + call term_sendkeys(buf, "fo") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_highlights_03', {}) + call term_sendkeys(buf, "\<Esc>S\<C-X>\<C-O>") + call TermWait(buf, 50) + call term_sendkeys(buf, "你") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_highlights_04', {}) + call term_sendkeys(buf, "吗") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_highlights_05', {}) + call term_sendkeys(buf, "\<C-E>\<Esc>") + + if has('rightleft') + call term_sendkeys(buf, ":set rightleft\<CR>") + call TermWait(buf, 50) + call term_sendkeys(buf, "S\<C-X>\<C-O>") + call TermWait(buf, 50) + call term_sendkeys(buf, "fo") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_highlights_06', {}) + call term_sendkeys(buf, "\<Esc>S\<C-X>\<C-O>") + call TermWait(buf, 50) + call term_sendkeys(buf, "你") + call VerifyScreenDump(buf, 'Test_pum_highlights_06a', {}) + call term_sendkeys(buf, "吗") + call VerifyScreenDump(buf, 'Test_pum_highlights_06b', {}) + call term_sendkeys(buf, "\<C-E>\<Esc>") + call term_sendkeys(buf, ":set norightleft\<CR>") + call TermWait(buf) + endif + + call term_sendkeys(buf, ":set completeopt-=fuzzy\<CR>") + call TermWait(buf) + call term_sendkeys(buf, "S\<C-X>\<C-O>") + call TermWait(buf, 50) + call term_sendkeys(buf, "fo") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_highlights_07', {}) + call term_sendkeys(buf, "\<C-E>\<Esc>") + + if has('rightleft') + call term_sendkeys(buf, ":set rightleft\<CR>") + call TermWait(buf, 50) + call term_sendkeys(buf, "S\<C-X>\<C-O>") + call TermWait(buf, 50) + call term_sendkeys(buf, "fo") + call TermWait(buf, 50) + call VerifyScreenDump(buf, 'Test_pum_highlights_08', {}) + call term_sendkeys(buf, "\<C-E>\<Esc>") + call term_sendkeys(buf, ":set norightleft\<CR>") + endif + + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_signs.vim b/test/old/testdir/test_signs.vim index d7baa7e870..69fefccb3f 100644 --- a/test/old/testdir/test_signs.vim +++ b/test/old/testdir/test_signs.vim @@ -89,8 +89,9 @@ func Test_sign() " Place a sign without specifying the filename or buffer sign place 77 line=9 name=Sign2 let a=execute('sign place') + " Nvim: sign line clamped to buffer length call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n" . - \ " line=9 id=77 name=Sign2 priority=10\n", a) + \ " line=4 id=77 name=Sign2 priority=10\n", a) sign unplace * " Check :jump with file=... @@ -799,10 +800,11 @@ func Test_sign_group() set buftype=nofile sign place 25 line=76 name=sign1 priority=99 file=foo let a = execute('sign place') + " Nvim: sign line clamped to buffer length call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 name=sign1 priority=10\n" . \ "Signs for foo:\n" . - \ " line=76 id=25 name=sign1 priority=99\n", a) + \ " line=1 id=25 name=sign1 priority=99\n", a) close bwipe foo diff --git a/test/old/testdir/test_termdebug.vim b/test/old/testdir/test_termdebug.vim index fd0c850577..38278cb326 100644 --- a/test/old/testdir/test_termdebug.vim +++ b/test/old/testdir/test_termdebug.vim @@ -340,5 +340,16 @@ func Test_termdebug_bufnames() unlet g:termdebug_config endfunc +function Test_termdebug_save_restore_variables() + let &mousemodel='' + Termdebug + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + call WaitForAssert({-> assert_match(&mousemodel, 'popup_setpos')}) + wincmd t + quit! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call WaitForAssert({-> assert_true(empty(&mousemodel))}) +endfunction + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim index 0b840944bf..5307c49012 100644 --- a/test/old/testdir/test_visual.vim +++ b/test/old/testdir/test_visual.vim @@ -2709,4 +2709,13 @@ func Test_visual_block_cursor_delete() bwipe! endfunc +func Test_visual_block_cursor_insert_enter() + new + call setline(1, ['asdf asdf', 'asdf asdf', 'asdf asdf', 'asdf asdf']) + call cursor(1, 5) + exe ":norm! \<c-v>3jcw\<cr>" + call assert_equal(['asdfw', 'asdf', 'asdfasdf', 'asdfasdf', 'asdfasdf'], getline(1, '$')) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab |